# vector algebra in pstricks / postscript?

Denis Girou Denis.Girou at idris.fr
Wed Jun 3 23:15:50 CEST 1998

-----------------------------------------------------------------------------
This is the PSTricks mailing list, devoted to discussions about computational
graphics in (La)TeX using the PSTricks package from Timothy van Zandt.
For help using this mailing list, see instructions at the end of message.
-----------------------------------------------------------------------------

> From: Lindsay Errington <le at doc.ic.ac.uk>
> Newsgroups: comp.text.tex
> Subject: vector algebra in pstricks / postscript?
> Date: Tue, 19 May 1998 16:49:23 +0000

Lindsay.Errington> Perhaps there's a pstricks hack who can help with this one.

Lindsay.Errington> I'm using pst-node and want to do a little vector algebra. In
Lindsay.Errington> particular, given nodes a, b and c,  I want to draw a line from c in the
Lindsay.Errington> direction of the of the vector from a to b. So a line from c to d = c +
Lindsay.Errington> t(b - a). (If it helps, t is usually 1). I thought I could do it with
Lindsay.Errington> \SpecialCoor but I haven't got quite enough information. I don't have
Lindsay.Errington> the coordinates for a,b or c nor the angle of the line from a to b.

This is this last condition which make this problem specially difficult...

Lindsay.Errington> The way I see it there are two options:

Lindsay.Errington> 1) somehow to retrieve the coordinates for a, b and c and do the
Lindsay.Errington> calculations in tex. I'm not sure this is possible as the coordinates
Lindsay.Errington> may only be known when the PostScript is run. If it is possible, it
Lindsay.Errington> requires a good undertanding of the internals of pstricks.

You emphasize one of the major points here: there is _no_ possibility to
transmit informations from PostScript to TeX. We can only use the other way,
from TeX to PostScript. But in your case, as on the TeX side you only know the
node names, their coordinates can only be known by PostScript and not later by
TeX. So, the only way to proceed is to embed all the required PostScript code
in TeX, which clearly make programming more difficult.

You can found more explanations about PSTricks implementation and how TeX
communicate with PostScript in:
Timothy van Zandt and Denis Girou, Inside PSTricks, TUG 1994 Annual Meeting
Proceedings, TUGboat, Volume 15, Number 3, September 1994, pages 239-246.

Lindsay.Errington> 2) to do the calculations with raw PostScript using the (!ps ...)
Lindsay.Errington> feature within \SpecialCoor. This requires an understanding of both the
Lindsay.Errington> internals of pstricks and a little PostScript.

As we discuss together, a better way than to define a new specialized macro
is to extend coordinates mechanism (using \SpecialCoor) to accept "vector
coordinates".

For that, I define the new parameters VectorDirection, VectorLength
(if we want to fix it and not having for it the length of the primary vector),
VectorScale and VectorEndPointName.

So, the syntax is like:

\psline(X)([VectorDirection={(A,B)}]X)

Lindsay.Errington> I imagine there are people out there for whom this is a trivial exercise
Lindsay.Errington> and I would be very grateful if someone could spare a moment to have a
Lindsay.Errington> go.

Lindsay.Errington> Lindsay

As you have seen according our discussions and my various versions
- and that can be seen studying the following code -, it is not at all an easy
problem... And to allow to define a node name at the ending point is not easy
too. Nevertheless, as I found the problem interesting and as I think that it
can be really useful, for instance to mathematicians and physicians, I try
to implement it completely. Today my version seems to work now on my tests...

P.S. I thank you Lindsay Errington and Philippe Esperet for feedback on my
preliminary versions.

D.G.

\documentclass{article}

\usepackage{pst-node}

\makeatletter

% DG addition begin - Vector coordinates - June 1998

% VectorScale parameter (default = 1)
\def\psset at VectorScale#1{\pst at checknum{#1}\Pst at VectorScale}
\psset at VectorScale{1}

% VectorLength parameter
% (default = 0, i.e. the length of the original line)
\def\psset at VectorLength#1{\pst at getlength{#1}{\Pst at VectorLength}}
\psset at VectorLength{0}

% VectorDirection parameter (default = empty)
\def\psset at VectorDirection#1{\psset@@VectorDirection#1}
\def\psset@@VectorDirection(#1,#2){%
\edef\Pst at VectorDirectionA{#1}%
\edef\Pst at VectorDirectionB{#2}}
\psset at VectorDirection{(,)}

% VectorEndPointName parameter (default = empty)
\def\psset at VectorEndPointName#1{\edef\Pst at VectorEndPointName{#1}}
\psset at VectorEndPointName{}

% \Node at coor macro modified
\def\Node at coor[#1]#2;#3\@nil{%
\begingroup
\psset{#1}%
\ifx\Pst at VectorDirectionA\empty
\@ifnextchar\bgroup{\Node@@@coor}{\Node@@coor}#2\@nil
\else
\ifx\Pst at VectorDirectionB\empty
\@ifnextchar\bgroup{\Node@@@coor}{\Node@@coor}#2\@nil
\else
\ifx\Pst at VectorEndPointName\empty
\def\Pst at NodeOrigin{}%
\else
\pnode{@Origin}
\def\Pst at NodeOrigin{%
N@@Origin begin 0 0 NodeMtrx transform CM itransform end
/yORIG ED
/xORIG ED}%
\fi
\ifdim\Pst at VectorLength pt=\z@
\def\Pst@@VectorLength{xD xC sub dup mul yD yC sub dup mul add sqrt }%
\else
% We transform negative lengths in positive ones with negative scaling
\ifdim\Pst at VectorLength pt<\z@
\ifdim\Pst at VectorScale pt>\z@
\edef\Pst at VectorLength{\Pst at VectorLength abs }%
\edef\Pst at VectorScale{-\Pst at VectorScale}%
\fi
\fi
\def\Pst@@VectorLength{%
\Pst at VectorLength \Pst at VectorScale mul \Pst at VectorScale 0 lt { neg } if }%
\fi
\def\psk at nodesepA{\Pst at VectorParallel\space \Pst@@VectorLength}%
\def\psk at angleA{yD yC sub xD xC sub \tx at Atan}%
\Node@@@@coor#2\@nil
\pst at getnode{\Pst at VectorDirectionA}\pst at tempa
\pst at getnode{\Pst at VectorDirectionB}\pst at tempb
\pst at getnode{#2}\pst at tempc
\pst at Verb{%
gsave
tx at Dict begin
STV CP
tx at NodeDict begin
\Pst at NodeOrigin
\pst at tempa load \tx at GetCenter /yA ED /xA ED
\pst at tempb load \tx at GetCenter /yB ED /xB ED
\pst at tempc load \tx at GetCenter /yC ED /xC ED
\Pst at VectorParallel\space
end
end
grestore}%
% If we must define a node at the ending point
\ifx\Pst at VectorEndPointName\empty
\else
\pst at Verb{%
tx at NodeDict begin
{xD xORIG sub yD yORIG sub}
false
/N@\Pst at VectorEndPointName\space
10
{InitPnode}
NewNode
end}%
\fi
\fi
\fi
\endgroup
\let\pst at coor\pst at tempg}

\def\Node@@@@coor#1\@nil{%
\pst at getnode{#1}\pst at tempg
\xdef\pst at tempg{%
\pst at nodedict
tx at NodeDict \pst at tempg known
{ \psk at nodesepA \psk at angleA
\pst at tempg load \psk at nodeseptypeA
% Adapted from GetCenter PostScript macro, changing NodePos definition
dup 0 eq
{pop begin 1 0 NodeMtrx dtransform CM idtransform exch atan sub dup sin
/Sin ED cos /Cos ED /NodeSep ED
NodeSep Cos mul NodeSep Sin mul
NodeMtrx dtransform CM idtransform end}
{1 eq {{exch}} {{}} ifelse
/Do ED
pop
XYPos} ifelse
\psk at offsetA \psk at angleA \tx at AddOffset
\pst at tempg load \tx at GetCenter
{ CP }
ifelse
end }}%

% Parallel vector to (A,B) starting from C
\newcommand{\Pst at VectorParallel}{%
/xBA xB xA sub \Pst at VectorScale mul def
/yBA yB yA sub \Pst at VectorScale mul def
0 \Pst at VectorLength eq
{% We use : (xB - xA) / (xD - xC) = length(A,B) / length(C,D)
% so xD = xC + (xB - xA) * length(C,D) / length(A,B)
/xD xBA \Pst at VectorLength mul \Pst at VectorScale mul % (xB - xA) * length(C,D)
xBA dup mul yBA dup mul add sqrt div % / len(A,B)
\Pst at VectorScale 0 lt { neg } if
% We use : length(C,D)^2 = (xD - xC)^2 + (yD - yC)^2
% so yD = yC + sqrt(length(C,D)^2 - (xD - xC)^2)
/yD xD xC sub dup mul \Pst at VectorLength \Pst at VectorScale mul dup mul exch
sub abs sqrt
yA yB gt { neg } if
\Pst at VectorScale 0 lt { neg } if

\makeatother

\pagestyle{empty}

\begin{document}

\psset{subgriddiv=0}

\begin{pspicture}(-4,0)(6,6)\psgrid
\Cnode(1,2){A}
\Cnode(3,1){B}
\Cnode(1,4){C}
\Cnode(4,3){D}
\SpecialCoor
\psline(A)(B)
\psline[linecolor=red](C)([VectorDirection={(A,B)},VectorEndPointName=W]C)
\pscircle*[linecolor=red](W){0.2}
\psline[linecolor=cyan](C)([VectorDirection={(A,B)},VectorLength=1]C)
\psline[linecolor=blue](C)([VectorDirection={(A,D)},VectorScale=1.5,
VectorEndPointName=X]C)
\pscircle*[linecolor=blue](X){0.2}
\psline[linecolor=green](C)([VectorDirection={(A,D)}]C)
\psline[linecolor=magenta](C)([VectorDirection={(A,D)},VectorLength=2cm,
VectorScale=-1.5,VectorEndPointName=Y]C)
\pscircle*[linecolor=magenta](Y){0.2}
\psline[linecolor=yellow](C)([VectorDirection={(A,D)},VectorLength=-2]C)
\psline(C)([VectorDirection={(B,D)},VectorLength=1,VectorScale=2,
VectorEndPointName=Z]C)
\pscircle*(Z){0.2}
\end{pspicture}

\vspace{1cm}
\begin{pspicture}(-4,0)(6,6)\psgrid
\Cnode(1,2){A}
\Cnode(3,1){B}
\Cnode(1,4){C}
\Cnode(4,3){D}
\SpecialCoor
\psline(A)(B)
\psline[linecolor=red](C)([VectorDirection={(B,A)},VectorEndPointName=W]C)
\pscircle*[linecolor=red](W){0.2}
\psline[linecolor=cyan](C)([VectorDirection={(B,A)},VectorLength=1]C)
\psline[linecolor=blue](C)([VectorDirection={(D,A)},VectorScale=1.5,
VectorEndPointName=X]C)
\pscircle*[linecolor=blue](X){0.2}
\psline[linecolor=green](C)([VectorDirection={(D,A)}]C)
\psline[linecolor=magenta](C)([VectorDirection={(D,A)},VectorLength=2cm,
VectorScale=-1.5,VectorEndPointName=Y]C)
\pscircle*[linecolor=magenta](Y){0.2}
\psline[linecolor=yellow](C)([VectorDirection={(D,A)},VectorLength=-2]C)
\psline(C)([VectorDirection={(D,B)},VectorLength=1,VectorScale=2,
VectorEndPointName=Z]C)
\pscircle*(Z){0.2}
\end{pspicture}

\vspace{1cm}
\begin{pspicture}(5,5)\psgrid
\psset{linewidth=1mm}
\pnode(1,1.5){A}
\pnode(3,1.5){B}
\ncline{A}{B}
\pnode(1,3){C}
\SpecialCoor
\psline[linecolor=red](C)([VectorDirection={(A,B)}]C)
\pnode(2,4){D}
\psline[linecolor=green](D)([VectorDirection={(A,B)},VectorLength=2]D)
\pnode(0,5){E}
\psline[linecolor=cyan](E)([VectorDirection={(A,B)},VectorLength=4]E)
\end{pspicture}
\hfill
\begin{pspicture}(5,5)\psgrid
\psset{linewidth=1mm,unit=0.5} % Work only if we change unit, not
% xunit or yunit
\pnode(1,1){A}
\pnode(3,1){B}
\ncline{A}{B}
\pnode(1,3){C}
\SpecialCoor
\psline[linecolor=red](C)([VectorDirection={(A,B)}]C)
\pnode(2,4){D}
\psline[linecolor=green](D)([VectorDirection={(A,B)},VectorLength=2]D)
\pnode(2,5){E}
\psline[linecolor=magenta](E)([VectorDirection={(A,B)},VectorLength=2cm]E)
\pnode(0,6){F}
\psline[linecolor=cyan](F)([VectorDirection={(A,B)},VectorLength=4]F)
\end{pspicture}

\vspace{1cm}
\begin{pspicture}(5,5)\psgrid
\pnode(0,0){A}
\pnode(2,2){B}
\ncline{A}{B}
\pnode(1,3){C}
\SpecialCoor
\psline[linecolor=red](C)([VectorDirection={(A,B)}]C)
\pnode(2,3){D}
\psline[linecolor=green](D)([VectorDirection={(A,B)},VectorLength=2]D)
\pscircle*[linecolor=green](3.414,4.414){0.1}
\pnode(2.5,3){E}
\psline[linecolor=magenta](E)([VectorDirection={(A,B)},VectorLength=2,
VectorScale=-1]E)
\end{pspicture}
\hfill
\begin{pspicture}(5,5)\psgrid
\pnode(0,2){A}
\pnode(2,0){B}
\ncline{A}{B}
\pnode(1,3){C}
\SpecialCoor
\psline[linecolor=red](C)([VectorDirection={(A,B)}]C)
\pnode(2,3){D}
\psline[linecolor=green](D)([VectorDirection={(A,B)},VectorLength=2]D)
\pnode(2.5,3){E}
\psline[linecolor=magenta](E)([VectorDirection={(A,B)},VectorLength=2,
VectorScale=-1]E)
\pnode(3,3){F}
\psline[linecolor=cyan](F)([VectorDirection={(A,B)},VectorLength=2,
VectorScale=1.5]F)
\end{pspicture}

\vspace{1cm}
\begin{pspicture}(5,5)\psgrid
\pnode(2,2){A}
\pnode(0,0){B}
\ncline{A}{B}
\pnode(3,4){C}
\SpecialCoor
\psline[linecolor=red](C)([VectorDirection={(A,B)}]C)
\pnode(4,4){D}
\psline[linecolor=green](D)([VectorDirection={(A,B)},VectorLength=2]D)
\end{pspicture}
\hfill
\begin{pspicture}(5,5)\psgrid
\psset{linewidth=1mm}
\pnode(1.5,2){A}
\pnode(1.5,0){B}
\ncline{A}{B}
\pnode(3,4){C}
\SpecialCoor
\psline[linecolor=red](C)([VectorDirection={(A,B)}]C)
\pnode(4,4){D}
\psline[linecolor=green](D)([VectorDirection={(A,B)},VectorLength=2]D)
\pnode(5,4){E}
\psline[linecolor=cyan](E)([VectorDirection={(A,B)},VectorLength=2,
VectorScale=1.5]E)
\end{pspicture}

\vspace{1cm}
\begin{pspicture}(-1,0)(5,5)\psgrid
\pnode(0,1){A}
\pnode(2,3){B}
\ncline{A}{B}
\pnode(1,3){C}
\SpecialCoor
\psline[linecolor=red](C)([VectorDirection={(A,B)}]C)
\psline[linecolor=magenta](C)([VectorDirection={(B,A)}]C)
\pnode(2,1){D}
\psline[linecolor=green](D)([VectorDirection={(A,B)},VectorScale=1.5]D)
\pnode(2.5,1){E}
\psline[linecolor=cyan](E)([VectorDirection={(A,B)},VectorScale=0.5]E)
\psline[linecolor=blue](E)([VectorDirection={(A,B)},VectorScale=-0.5]E)
\end{pspicture}
\hfill
\begin{pspicture}(5,5)\psgrid
\pnode(0,5){A}
\pnode(1,3){B}
\ncline{A}{B}
\pnode(2,4){C}
\SpecialCoor
\psline[linecolor=red,doubleline=true,arrowscale=2]{->}%
(C)([VectorDirection={(A,B)}]C)
\end{pspicture}

% Example transmitted by Lindsay Errington

% Definition of pullback
\newcommand{\pbkb}[3]{%
% Place a pair of nodes
\psline[linestyle=none]
(#1)([VectorDirection={(#1,#2)},VectorLength=.4,
VectorEndPointName=PBK1]#1)
\psline[linestyle=none]
(#1)([VectorDirection={(#1,#3)},VectorLength=.4,
VectorEndPointName=PBK2]#1)
% Draw the lines of the pullback
\psline[linecolor=green,nodesepA=3pt](PBK2)([VectorDirection={(#1,PBK1)}]PBK2)
\psline[linecolor=red,nodesepA=3pt](PBK1)([VectorDirection={(#1,PBK2)}]PBK1)}

$\psset{labelsep=1pt,arrows=->,nodesep=2.5pt,linewidth=.5pt,dotsep=6pt} \SpecialCoor \begin{psmatrix}[rowsep=.75] [name=p] X \\[0pt] [name=q] X' & [name=b] B \\[0pt] & [name=c] \theta \ncline{p}{a} \ncline{p}{b} \ncline{>->}{c}{b} \ncline{>->}{q}{p} \ncline{q}{c} \pbkb{q}{c}{p} \end{psmatrix} \qquad\qquad \begin{psmatrix} [name=p] X & [name=b] B \\[0pt] [name=q] X' & [name=c] \theta \ncline{p}{a} \ncline{p}{b} \ncline{>->}{c}{b} \ncline{>->}{q}{p} \ncline{q}{c} \pbkb{q}{c}{p} \end{psmatrix}$

\end{document}

-----------------------------------------------------------------------------