# -*- mode: Perl -*-
# /=====================================================================\ #
# | revtex3 support | #
# | Implementation for LaTeXML | #
# |=====================================================================| #
# | Part of LaTeXML: | #
# | Public domain software, produced as part of work done by the | #
# | United States Government & not subject to copyright in the US. | #
# |---------------------------------------------------------------------| #
# | Thanks to Ioan Alexandru Sucan <i.sucan@iu-bremen.de> | #
# | of the arXMLiv group for initial implementation | #
# | http://arxmliv.kwarc.info/ | #
# | Released to the Public Domain | #
# |---------------------------------------------------------------------| #
# | Bruce Miller <bruce.miller@nist.gov> #_# | #
# | http://dlmf.nist.gov/LaTeXML/ (o o) | #
# \=========================================================ooo==U==ooo=/ #
package LaTeXML::Package::Pool;
use strict;
use warnings;
use LaTeXML::Package;
RequirePackage('pstricks');
our $PSLineAttr;
##############################################################
## Parameter type definitions
##############################################################
sub ReadRoundParenFloat {
my ($gullet) = @_;
$gullet->skipSpaces;
if ($gullet->ifNext(T_OTHER('('))) {
$gullet->readToken; $gullet->skipSpaces;
my $f = $gullet->readFloat();
$gullet->skipSpaces; $gullet->readToken;
return $f; }
else {
return; } }
DefParameterType('RoundParenFloat', \&ReadRoundParenFloat,
reversion => sub { ($_[0] ? (T_OTHER('('), Revert($_[0]), T_OTHER(')')) : ()); },
optional => 1);
###########################################################################
## Functions for node positions storage / retrieval
###########################################################################
sub storeNode {
my ($name, $address) = @_;
$name = ToString($name);
AssignValue('_psNODE' . $name, ActiveTransform()->apply($address || ZeroPair()), 'global');
return; }
sub getNode {
my ($name) = @_;
return ActiveTransform()->unapply(LookupValue('_psNODE' . ToString($name)) || ZeroPair()); }
sub nConnShiftUp {
my ($p) = @_;
if (my $off = $p && LookupValue('\psoffset')) {
return Pair($p->getX, $p->getY->add($off)); }
else {
return $p } }
sub nConnShiftDown {
my ($p) = @_;
if (my $off = $p && LookupValue('\psoffset')) {
return Pair($p->getX, $p->getY->subtract($off)); }
else {
return $p } }
# should also handle nodesep when boundary of text can be determined
sub getANode {
my ($name) = @_;
return nConnShiftUp(getNode($name)); }
sub getBNode {
my ($name) = @_;
return nConnShiftDown(getNode($name)); }
###########################################################################
## Default values for attributes referring to pst-node
###########################################################################
our %KeyAsValue; our %KeyHasABVariants;
%KeyAsValue = (%KeyAsValue, radius => 1, vref => 1, nodesep => 1, offset => 1, arm => 1, armA => 1, armB => 1,
loopsize => 1, framesize => 1);
%KeyHasABVariants = (%KeyHasABVariants, angle => 1, arcangle => 1, arm => 1, nodesep => 1, ncurv => 1);
DefKeyVal('pstricks', 'angle', 'PSAngle');
DefKeyVal('pstricks', 'angleA', 'PSAngle');
DefKeyVal('pstricks', 'angleB', 'PSAngle');
DefKeyVal('pstricks', 'arcangle', 'PSAngle');
DefKeyVal('pstricks', 'arcangleA', 'PSAngle');
DefKeyVal('pstricks', 'arcangleB', 'PSAngle');
DefKeyVal('pstricks', 'nodesep', 'PSDimension');
DefKeyVal('pstricks', 'nodesepA', 'PSDimension');
DefKeyVal('pstricks', 'nodesepB', 'PSDimension');
DefKeyVal('pstricks', 'offset', 'PSDimension');
DefKeyVal('pstricks', 'arm', 'PSDimension');
DefKeyVal('pstricks', 'armA', 'PSDimension');
DefKeyVal('pstricks', 'armB', 'PSDimension');
DefKeyVal('pstricks', 'ncurv', 'Float');
DefKeyVal('pstricks', 'loopsize', 'PSDimension');
DefKeyVal('pstricks', 'radius', 'PSDimension');
DefKeyVal('pstricks', 'framesize', 'PSDimDim');
AssignValue('\psarm', Dimension('10pt'));
AssignValue('\psarmA', Dimension('10pt'));
AssignValue('\psarmB', Dimension('10pt'));
AssignValue('\psangle', 0);
AssignValue('\psangleA', 0);
AssignValue('\psangleB', 0);
AssignValue('\psarcangle', 8);
AssignValue('\psarcangleA', 8);
AssignValue('\psarcangleB', 8);
AssignValue('\psnodesep', 0);
AssignValue('\psnodesepA', 0);
AssignValue('\psnodesepB', 0);
AssignValue('\psloopsize', Dimension('1cm'));
AssignValue('\psradius', Dimension('2pt'));
AssignValue('\psncurv', 0.67);
AssignValue('\psncurvA', 0.67);
AssignValue('\psncurvB', 0.67);
AssignValue('\pshref', 0);
AssignValue('\psvref', Dimension('0.7ex'));
AssignValue('\psframesize', Pair(Dimension('10pt'), Dimension('10pt')));
###########################################################################
## Functions for computing node connections
###########################################################################
sub nodeConnection {
my (@nodes) = @_;
my $nc = PairList(@nodes);
AssignValue('_ps@LastNodeConnection', $nc, 'global');
return $nc; }
sub getLastConnection {
return LookupValue('_ps@LastNodeConnection') || PairList(ZeroPair()); }
our %defaultLabelPos = (bar => 1.5, diag => 1.5, angle => 1.5, angles => 1.5, loop => 2.5, circle => 1.5);
sub getDefaultLabelPos {
my $cmd = LookupValue('_ps@LastPSCmd') || '';
return 0 unless $cmd =~ s/^\\(n|p)c//;
return $defaultLabelPos{$cmd} || 0.5; }
sub nodeExt {
my ($N, $arm, $angle) = @_;
$angle = radians((ref $angle ? $angle->valueOf : $angle));
return Pair($N->getX->add($arm->multiply(cos($angle))),
$N->getY->add($arm->multiply(sin($angle)))); }
sub nodeExtA {
my ($N) = @_;
return nodeExt($N, LookupValue('\psarmA'), LookupValue('\psangleA')); }
sub nodeExtB {
my ($N) = @_;
return nodeExt($N, LookupValue('\psarmB'), LookupValue('\psangleB')); }
###########################################################################
## Constructors
###########################################################################
DefConstructor('\rnode [] {} {}', '#3',
afterDigest => sub { storeNode($_[1]->getArg(2)); });
DefMacro('\Rnode PSCoord', sub {
my ($gullet, $coord) = @_;
if ($coord) {
$coord = ToString($_[1]); }
else {
my $rref = LookupDefinition(T_CS('\RnodeRef'));
my @rref = ($rref ? $rref->invoke($gullet) : ());
$coord = join('', map { ToString($_) } @rref) if @rref;
$coord = (LookupValue('\pshref')) . ',' . (ToString(LookupValue('\psvref'))) unless $coord; }
(T_CS('\@Rnode'), T_OTHER('('), Explode($coord), T_OTHER(')')); });
DefConstructor('\@Rnode PSCoord {} {}', '#3',
alias => '\Rnode',
afterDigest => sub { storeNode($_[1]->getArg(2), $_[1]->getArg(1)); });
DefConstructor('\pnode ZeroPSCoord {}', '',
afterDigest => sub { storeNode($_[1]->getArg(2), $_[1]->getArg(1)); });
DefPSConstructor('\cnode', 'ZeroPSCoord {PSDimension} {}',
"<ltx:circle " . $PSLineAttr . "%&PairAttr(#2,x,y) r='&ptValue(#3)' fill='#fill' />",
attributes => [qw(fill linecolor linewidth dash)],
afterDigest => sub { storeNode($_[1]->getArg(4), $_[1]->getArg(2)); });
DefPSConstructor('\Cnode', 'ZeroPSCoord {}',
"<ltx:circle " . $PSLineAttr . "%&PairAttr(#2,x,y) r='&ptValue(#radius)' fill='#fill' />",
attributes => [qw(fill linecolor linewidth dash radius)],
afterDigest => sub { storeNode($_[1]->getArg(4), $_[1]->getArg(2)); });
DefPSConstructor('\circlenode', '{} {}',
"<ltx:g framed='true' fillframe='true' frametype='circle'"
. " fill='#fillcolor' ?#1(stroke='#fillcolor')(stroke='#linecolor')> #3 </ltx:g>",
attributes => [qw(linecolor fillcolor)],
afterDigest => sub { storeNode($_[1]->getArg(2)); });
DefPSConstructor('\fnode', 'ZeroPSCoord {}',
"<ltx:rect $PSLineAttr %&PairAttr(#pz,x,y) %&PairAttr(#framesize,width,height)"
. " rx='#arcval' fill='#fill' />",
attributes => [qw(fill linecolor linewidth dash framesize)],
afterDigest => sub {
my ($stomach, $whatsit) = @_;
my $fs = LookupValue('\psframesize'); my $dt = $fs->multiply(-0.5);
my $pz = Pair($whatsit->getArg(2)->getX->add($dt->getX),
$whatsit->getArg(2)->getY->add($dt->getY));
storeNode($whatsit->getArg(3), $whatsit->getArg(2));
$whatsit->setProperties(pz => $pz, arcval => arcValue($fs)); });
DefMacro('\cnodeput OptionalMatch:* [] OptionalBracketed ZeroPSCoord {} {}', sub {
my ($gullet, $star, $par, $angle, $coord, $name, $body) = @_;
my @exp = (T_CS('\rput'));
push(@exp, T_BEGIN, $angle->unlist, T_END) if $angle;
push(@exp, T_OTHER('('), Explode(ToString($coord)), T_OTHER(')'), T_BEGIN, T_CS('\circlenode'));
push(@exp, T_OTHER('*')) if $star;
push(@exp, T_OTHER('['), $par->unlist, T_OTHER(']')) if $par;
push(@exp, T_BEGIN, $name->unlist, T_END, T_BEGIN, $body->unlist, T_END, T_END);
@exp; });
DefPSConstructor('\ovalnode', '{} {}',
"<ltx:g framed='true' fillframe='true' frametype='oval'"
. " fill='#fillcolor' ?#1(stroke='#fillcolor')(stroke='#linecolor')> #3 </ltx:g>",
attributes => [qw(linecolor fillcolor)],
afterDigest => sub { storeNode($_[1]->getArg(2)); });
DefPSConstructor('\ncline', '{} {}',
"<ltx:line $PSLineAttr #!ARROWS points='&ptValue(#points)' fill='#fill' />",
attributes => [qw(fill linecolor linewidth dash showpoints)],
properties => { points => sub { nodeConnection(getANode($_[3]), getBNode($_[4])); } });
DefPSConstructor('\pcline', 'PSCoord PSCoord',
"<ltx:line $PSLineAttr #!ARROWS points='&ptValue(#points)' fill='#fill' />",
attributes => [qw(fill linecolor linewidth dash showpoints)],
properties => { points => sub { nodeConnection($_[3], $_[4]); } });
DefPSConstructor('\ncLine', '{} {}',
"<ltx:line $PSLineAttr #!ARROWS points='&ptValue(#points)' fill='#fill' />",
attributes => [qw(fill linecolor linewidth dash showpoints)],
properties => { points => sub { nodeConnection(getANode($_[3]), getBNode($_[4])); } });
sub curvePoints {
my ($A, $B) = @_;
my ($na, $nb, $aa, $ab) = map { LookupValue($_) } qw(\psncurvA \psncurvB \psangleA \psangleB);
my $d = pointPointDist($A, $B);
my $A1 = nodeExt($A, Dimension($d * $na * 0.5), $aa);
my $B1 = nodeExt($B, Dimension($d * $nb * 0.5), $ab);
return ($A, $A1, $B1, $B); }
DefPSConstructor('\nccurve', '{} {}',
"<ltx:bezier $PSLineAttr #!ARROWS points='&ptValue(#points)' fill='#fill' />",
attributes => [qw(fill linecolor linewidth dash)],
properties => { points => sub { nodeConnection(curvePoints(getANode($_[3]), getBNode($_[4]))); } });
DefPSConstructor('\pccurve', 'PSCoord PSCoord',
"<ltx:bezier $PSLineAttr #!ARROWS points='&ptValue(#points)'"
. " angleA='#angleA' angleB='#angleB' fill='#fill' />",
attributes => [qw(fill linecolor linewidth dash nodesep offset ncurv angleA angleB)],
properties => { points => sub { nodeConnection(curvePoints($_[3], $_[4])); } });
sub carcPoints {
my ($A, $B) = @_;
my ($na, $nb, $aa, $ab) = map { LookupValue($_) } qw(\psncurvA \psncurvB \psarcangleA \psarcangleB);
my $d = pointPointDist($A, $B); my $a = lineAngle($A, $B);
my $A1 = nodeExt($A, Dimension($d * $na * 0.5), $a + $aa);
my $B1 = nodeExt($B, Dimension($d * $nb * 0.5), $a + 180 - $ab);
return ($A, $A1, $B1, $B); }
DefPSConstructor('\ncarc', '{} {}',
"<ltx:bezier $PSLineAttr #!ARROWS points='&ptValue(#points)' fill='#fill' />",
attributes => [qw(fill linecolor linewidth dash)],
properties => { points => sub { nodeConnection(carcPoints(getANode($_[3]), getBNode($_[4]))); } });
DefPSConstructor('\pcarc', 'PSCoord PSCoord',
"<ltx:bezier $PSLineAttr #!ARROWS points='&ptValue(#points)' fill='#fill' />",
attributes => [qw(fill linecolor linewidth dash)],
properties => { points => sub { nodeConnection(carcPoints($_[3], $_[4])); } });
sub after_ncbar {
my ($A, $B) = @_;
my ($armA, $armB, $angle) = map { LookupValue($_) } qw(\psarmA \psarmB \psangleA);
my ($A1, $B1) = (nodeExt($A, $armA, $angle), nodeExt($B, $armB, $angle));
my (@l1, @l2);
@l1 = lineParams($A1, $angle); @l2 = lineParams($B1, $angle, 1);
my $A1E = lineIntersect(\@l1, \@l2);
@l1 = lineParams($A1, $angle, 1); @l2 = lineParams($B1, $angle);
my $B1E = lineIntersect(\@l1, \@l2);
if (pointPointDist($A, $A1E) > pointPointDist($A1, $A1E)) {
$A1 = $A1E; }
else {
$B1 = $B1E; }
return nodeConnection($A, $A1, $B1, $B); }
DefPSConstructor('\ncbar', '{} {}',
"<ltx:line $PSLineAttr #!ARROWS arc='&ptValue(#linearc)'"
. " points='&ptValue(#points)' fill='#fill'/>",
attributes => [qw(fill linecolor linewidth dash showpoints linearc)],
properties => { points => sub { after_ncbar(getANode($_[3]), getBNode($_[4])); } });
DefPSConstructor('\pcbar', 'PSCoord PSCoord',
"<ltx:line $PSLineAttr #!ARROWS arc='&ptValue(#linearc)'"
. " points='&ptValue(#points)' fill='#fill'/>",
attributes => [qw(fill linecolor linewidth dash showpoints linearc)],
properties => { points => sub { after_ncbar($_[3], $_[4]); } });
DefPSConstructor('\ncdiag', '{} {}',
"<ltx:line $PSLineAttr #!ARROWS arc='&ptValue(#linearc)'"
. " points='&ptValue(#points)' fill='#fill'/>",
attributes => [qw(fill linecolor linewidth dash showpoints linearc)],
properties => { points => sub { my ($A, $B) = (getANode($_[3]), getBNode($_[4]));
nodeConnection($A, nodeExtA($A), nodeExtB($B), $B); } });
DefPSConstructor('\pcdiag', 'PSCoord PSCoord',
"<ltx:line $PSLineAttr #!ARROWS arc='&ptValue(#linearc)'"
. " points='&ptValue(#points)' fill='#fill'/>",
attributes => [qw(fill linecolor linewidth dash showpoints linearc)],
properties => { points => sub { my ($A, $B) = ($_[3], $_[4]);
nodeConnection($A, nodeExtA($A), nodeExtB($B), $B); } });
DefPSConstructor('\ncdiagg', '{} {}',
"<ltx:line $PSLineAttr #!ARROWS arc='&ptValue(#linearc)'"
. " points='&ptValue(#points)' fill='#fill'/>",
attributes => [qw(fill linecolor linewidth dash showpoints linearc)],
properties => { points => sub { my ($A, $B) = (getANode($_[3]), getBNode($_[4]));
nodeConnection($A, nodeExtA($A), $B); } });
DefPSConstructor('\pcdiagg', 'PSCoord PSCoord',
"<ltx:line $PSLineAttr #!ARROWS arc='&ptValue(#linearc)'"
. " points='&ptValue(#points)' fill='#fill'/>",
attributes => [qw(fill linecolor linewidth dash showpoints linearc)],
properties => { points => sub { my ($A, $B) = ($_[3], $_[4]);
nodeConnection($A, nodeExtA($A), $B); } });
sub after_ncangle {
my ($A, $B) = @_;
my $angleA = LookupValue('\psangleA'); my $B1 = nodeExtB($B);
my @l1 = lineParams($A, $angleA); my @l2 = lineParams($B1, $angleA, 1);
return nodeConnection($B, $B1, lineIntersect(\@l1, \@l2), $A); }
DefPSConstructor('\ncangle', '{} {}',
"<ltx:line $PSLineAttr #!ARROWS arc='&ptValue(#linearc)'"
. " points='&ptValue(#points)' fill='#fill'/>",
attributes => [qw(fill linecolor linewidth dash showpoints linearc)],
properties => { points => sub { after_ncangle(getANode($_[3]), getBNode($_[4])); } });
DefPSConstructor('\pcangle', 'PSCoord PSCoord',
"<ltx:line $PSLineAttr #!ARROWS arc='&ptValue(#linearc)'"
. " points='&ptValue(#points)' fill='#fill'/>",
attributes => [qw(fill linecolor linewidth dash showpoints linearc)],
properties => { points => sub { after_ncangle($_[3], $_[4]); } });
sub after_ncangles {
my ($A, $B) = @_;
my ($A1, $B1) = (nodeExtA($A), nodeExtB($B)); my $angleA = LookupValue('\psangleA');
my @l1 = lineParams($A1, $angleA, 1); my @l2 = lineParams($B1, $angleA);
return nodeConnection($B, $B1, lineIntersect(\@l1, \@l2), $A1, $A); }
DefPSConstructor('\ncangles', '{} {}',
"<ltx:line $PSLineAttr #!ARROWS arc='&ptValue(#linearc)'"
. " points='&ptValue(#points)' fill='#fill'/>",
attributes => [qw(fill linecolor linewidth dash showpoints linearc)],
properties => { points => sub { after_ncangles(getANode($_[3]), getBNode($_[4])); } });
DefPSConstructor('\pcangles', 'PSCoord PSCoord',
"<ltx:line $PSLineAttr #!ARROWS arc='&ptValue(#linearc)'"
. " points='&ptValue(#points)' fill='#fill'/>",
attributes => [qw(fill linecolor linewidth dash showpoints linearc)],
properties => { points => sub { after_ncangles($_[3], $_[4]); } });
sub after_ncloop {
my ($A, $B) = @_;
my ($angleA, $loop) = map { LookupValue($_) } qw(\psangleA \psloopsize);
my ($A1, $B1) = (nodeExtA($A), nodeExtB($B));
my @l1 = lineParams($A1, $angleA, 1);
my ($x0, $y0) = ($A1->getX->valueOf, $A1->getY->valueOf); my $absq = $l1[0]**2 + $l1[1]**2;
my $sgn = ($angleA > 270 || $angleA <= 90) ? 1 : -1;
my $D = $sgn * sqrt($absq * ($loop->valueOf()**2) - ($l1[0] * $x0 + $l1[1] * $y0 + $l1[2])**2);
$absq = 1 / $absq;
my $L = Pair(Dimension($absq * ($l1[1]**2 * $x0 - $l1[0] * ($l1[2] + $l1[1] * $y0) - $l1[1] * $D)),
Dimension($absq * ($l1[0]**2 * $y0 - $l1[1] * ($l1[2] + $l1[0] * $x0) + $l1[0] * $D)));
@l1 = lineParams($B1, $angleA, 1); my @l2 = lineParams($L, $angleA);
return nodeConnection($A, $A1, $L, lineIntersect(\@l1, \@l2), $B1, $B); }
DefPSConstructor('\ncloop', '{} {}',
"<ltx:line $PSLineAttr #!ARROWS arc='&ptValue(#linearc)'"
. " points='&ptValue(#points)' fill='#fill'/>",
attributes => [qw(fill linecolor linewidth dash showpoints linearc)],
properties => { points => sub { after_ncloop(getANode($_[3]), getBNode($_[4])); } });
DefPSConstructor('\pcloop', 'PSCoord PSCoord',
"<ltx:line $PSLineAttr #!ARROWS arc='&ptValue(#linearc)'"
. " points='&ptValue(#points)' fill='#fill'/>",
attributes => [qw(fill linecolor linewidth dash showpoints linearc)],
properties => { points => sub { after_ncloop($_[3], $_[4]); } });
DefPSConstructor('\nccircle', '{} {PSDimension}',
"<ltx:arc $PSLineAttr #!ARROWS %&PairAttr(#pz,x,y)"
. " r='&ptValue(#4)' angle1='&trunc2(#angleA)' " .
"angle2='360' fill='#fill' />",
attributes => [qw(linecolor linewidth dash angleA)],
properties => { pz => sub { my $nd = getNode($_[3]); nodeConnection($nd); $nd; } });
############################################################
## Positioning macros
############################################################
sub computeLabelPos {
my ($pos) = @_;
my ($lp, $nc) = ($pos ? $pos->valueOf : getDefaultLabelPos(), getLastConnection());
my ($n, $s) = ($nc->getCount(), int($lp)); my $f = $lp - $s; my $p;
if ($n == 1) { $p = $nc->getPair(0); }
else { $s = $n - 2 if $s + 1 >= $n;
my ($na, $nb) = ($nc->getPair($s), $nc->getPair($s + 1));
my $dx = $nb->getX->subtract($na->getX)->multiply($f);
my $dy = $nb->getY->subtract($na->getY)->multiply($f);
$p = Pair($na->getX->add($dx), $na->getY->add($dy)); }
return $p; }
DefMacro('\lput OptionalMatch:* [] OptionalBracketed RoundParenFloat', sub {
( ## T_BEGIN,
T_CS('\lput@start'),
$_[1] ? T_OTHER('*') : (),
$_[2] ? (T_OTHER('['), $_[2]->unlist(), T_OTHER(']')) : (),
$_[3] ? (T_BEGIN, $_[3]->unlist, T_END) : (),
$_[4] ? (T_OTHER('('), Explode(ToString($_[4])), T_OTHER(')')) : (),
T_CS('\put@end')); });
DefConstructor('\lput@start OptionalMatch:* [] BracketedPSAngle RoundParenFloat',
"<ltx:g transform='&ptValue(#transform)' pos='#2'"
. " ?#1(framed='true' fillframe='true' boxsep='no' fill='#fillcolor')>",
afterDigest => sub { finishLabelPut($_[1], $_[1]->getArg(3), computeLabelPos($_[1]->getArg(4))); },
alias => '\lput');
DefMacro('\aput OptionalMatch:* [] OptionalBracketed RoundParenFloat', sub {
( ## T_BEGIN,
T_CS('\aput@start'),
$_[1] ? T_OTHER('*') : (),
$_[2] ? (T_OTHER('['), $_[2]->unlist(), T_OTHER(']')) : (),
$_[3] ? (T_BEGIN, $_[3]->unlist, T_END) : (),
$_[4] ? (T_OTHER('('), Explode(ToString($_[4])), T_OTHER(')')) : (),
T_CS('\put@end')); });
DefConstructor('\aput@start OptionalMatch:* [PSDimension] BracketedPSAngle RoundParenFloat',
"<ltx:g transform='&ptValue(#transform)'"
. " ?#1(framed='true' fillframe='true' boxsep='no' fill='#fillcolor')>",
afterDigest => sub {
my ($whatsit, $labelsep, $angle, $pos) = ($_[1], map { $_[1]->getArg($_) } 2, 3, 4);
my $p = computeLabelPos($pos);
$labelsep = $labelsep || LookupValue('\pslabelsep');
$p = Pair($p->getX->add($labelsep), $p->getY->add($labelsep));
finishLabelPut($whatsit, $angle, $p); },
alias => '\aput');
DefMacro('\bput OptionalMatch:* [] OptionalBracketed RoundParenFloat', sub {
( ## T_BEGIN,
T_CS('\bput@start'),
$_[1] ? T_OTHER('*') : (),
$_[2] ? (T_OTHER('['), $_[2]->unlist(), T_OTHER(']')) : (),
$_[3] ? (T_BEGIN, $_[3]->unlist, T_END) : (),
$_[4] ? (T_OTHER('('), Explode(ToString($_[4])), T_OTHER(')')) : (),
T_CS('\put@end')); });
DefConstructor('\bput@start OptionalMatch:* [PSDimension] BracketedPSAngle RoundParenFloat',
"<ltx:g transform='&ptValue(#transform)'"
. " ?#1(framed='true' fillframe='true' boxsep='no' fill='#fillcolor')>",
afterDigest => sub {
my ($whatsit, $labelsep, $angle, $pos) = ($_[1], map { $_[1]->getArg($_) } 2, 3, 4);
my $p = computeLabelPos($pos);
$labelsep = $labelsep || LookupValue('\pslabelsep');
$p = Pair($p->getX->subtract($labelsep), $p->getY->subtract($labelsep));
finishLabelPut($whatsit, $angle, $p); },
alias => '\bput');
DefMacro('\mput OptionalMatch:* []', sub {
( ##T_BEGIN,
T_CS('\mput@start'),
$_[1] ? T_OTHER('*') : (),
$_[2] ? (T_OTHER('['), $_[2]->unlist(), T_OTHER(']')) : (),
T_CS('\put@end')); });
DefConstructor('\mput@start OptionalMatch:* []',
"<ltx:g transform='&ptValue(#transform)' pos='#2'"
. " ?#1(framed='true' fillframe='true' boxsep='no' fill='#fillcolor')>",
afterDigest => sub { finishLabelPut($_[1], undef, computeLabelPos()); },
alias => '\mput');
DefMacro('\Aput OptionalMatch:* []', sub {
( ##T_BEGIN,
T_CS('\Aput@start'),
$_[1] ? T_OTHER('*') : (),
$_[2] ? (T_OTHER('['), $_[2]->unlist(), T_OTHER(']')) : (),
T_CS('\put@end')); });
DefConstructor('\Aput@start OptionalMatch:* [PSDimension]',
"<ltx:g transform='&ptValue(#transform)'"
. " ?#1(framed='true' fillframe='true' boxsep='no' fill='#fillcolor')>",
afterDigest => sub {
my ($stomach, $whatsit, $labelsep) = @_;
my $p = computeLabelPos();
$labelsep = $labelsep || LookupValue('\pslabelsep');
$p = Pair($p->getX->add($labelsep), $p->getY->add($labelsep));
finishLabelPut($whatsit, undef, $p); },
alias => '\Aput');
DefMacro('\Bput OptionalMatch:* []', sub {
( ##T_BEGIN,
T_CS('\Bput@start'),
$_[1] ? T_OTHER('*') : (),
$_[2] ? (T_OTHER('['), $_[2]->unlist(), T_OTHER(']')) : (),
T_CS('\put@end')); });
DefConstructor('\Bput@start OptionalMatch:* [PSDimension]',
"<ltx:g transform='&ptValue(#transform)'"
. " ?#1(framed='true' fillframe='true' boxsep='no' fill='#fillcolor')>",
afterDigest => sub {
my ($stomach, $whatsit, $labelsep) = @_;
my $p = computeLabelPos();
$labelsep = $labelsep || LookupValue('\pslabelsep');
$p = Pair($p->getX->subtract($labelsep), $p->getY->subtract($labelsep));
finishLabelPut($whatsit, undef, $p); },
alias => '\Bput');
# some obsolete macros:
# reduce Lput to lput and Rput; Rput will get reduced to uput in some later stage
DefMacro('\Lput OptionalMatch:* OptionalBracketed [] OptionalBracketed RoundParenFloat {}', sub {
my ($gullet, $star, $labelsep, $refpoint, $rotation, $pos, $stuff) = @_;
(T_CS('\lput'),
$star ? T_OTHER('*') : (),
$pos ? (T_OTHER('('), Explode(ToString($pos)), T_OTHER(')')) : (),
T_BEGIN, T_CS('\Rput'),
$labelsep ? (T_BEGIN, $labelsep->unlist, T_END) : (),
$refpoint ? (T_OTHER('['), $refpoint->unlist, T_OTHER(']')) : (),
$rotation ? (T_BEGIN, $rotation->unlist, T_END) : (),
T_OTHER('('), Explode(ToString(ZeroPair())), T_OTHER(')'),
T_BEGIN, $stuff->unlist, T_END, T_END); });
DefMacro('\Mput', '\Lput'); # Lput code is general enough to work for Mput too
1;