3 # chem - a groff preprocessor for producing chemical structure diagrams
5 # Source file position: <groff-source>/contrib/chem/chem.pl
6 # Installed position: <prefix>/bin/chem
8 # Copyright (C) 2006 Free Software Foundation, Inc.
9 # Written by Bernd Warken.
11 # This file is part of `chem', which is part of `groff'.
13 # `groff' is free software; you can redistribute it and/or modify it
14 # under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2, or (at your option)
18 # `groff' is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with `groff'; see the files COPYING and LICENSE in the top
25 # directory of the `groff' source. If not, write to the Free Software
26 # Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301,
29 ########################################################################
31 ########################################################################
33 my $Program_Version = '0.3.1';
34 my $Last_Update = '11 Nov 2006';
36 # this setting of the groff version is only used before make is run,
37 # otherwise @VERSION@ will set it.
38 my $Groff_Version_Preset='1.19.3preset';
40 # test on Perl version
44 ########################################################################
46 ########################################################################
55 # $Bin is the directory where this script is located
65 my $before_make; # script before run of `make'
68 $before_make = 1 if '@VERSION@' eq "${at}VERSION${at}";
74 my $chem_dir = $FindBin::Bin
;
75 $at_at{'BINDIR'} = $chem_dir;
77 $File_chem_pic = File
::Spec
->catfile($chem_dir, 'chem.pic');
78 $File_pic_tmac = File
::Spec
->catfile($chem_dir, '..', 'pic.tmac');
82 $Groff_Version = '@VERSION@';
83 $at_at{'BINDIR'} = '@BINDIR@';
85 $at_at{'PICDIR'} = '@PICDIR@';
86 $at_at{'TMACDIR'} = '@MACRODIR@';
88 File
::Spec
->catfile($at_at{'PICDIR'}, 'chem.pic');
89 $File_pic_tmac = File
::Spec
->catfile($at_at{'TMACDIR'}, 'pic.tmac');
90 $Chem_Name = $at_at{'G'} . 'chem';
96 ########################################################################
97 # check the parameters
98 ########################################################################
101 # process any FOO=bar switches
102 # eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_0-9]+=)(.*)/ && shift;
109 # ignore FOO=bar switches
110 push @filespec, $_ if -f
;
115 push @filespec, $_ if -s
$_;
117 warn "chem: argument $_ is not an existing file.\n";
130 if (/^-h$/ or '--help' =~ /^$_/) {
134 if (/^-v$/ or '--version' =~ /^$_/) {
139 push @filespec, $_ if -s
$_;
143 warn "chem: wrong option ${_}.\n";
145 warn "chem: argument $_ is not an existing file.\n";
155 } else { # @ARGV is empty
156 @ARGV = ('-') unless @ARGV;
160 ########################################################################
162 ########################################################################
164 my %Dc = ( 'up' => 0, 'right' => 90, 'down' => 180, 'left' => 270,
165 'ne' => 45, 'se' => 135, 'sw' => 225, 'nw' => 315,
166 0 => 'n', 90 => 'e', 180 => 's', 270 => 'w',
167 30 => 'ne', 45 => 'ne', 60 => 'ne',
168 120 => 'se', 135 => 'se', 150 => 'se',
169 210 => 'sw', 225 => 'sw', 240 => 'sw',
170 300 => 'nw', 315 => 'nw', 330 => 'nw',
187 'OTHER' => 'O' # manifests
210 my $former_line = '';
220 # for centralizing the pic code
221 open TMAC
, "<$File_pic_tmac" and print <TMAC
>;
225 $count_minus++ if /^-$/;
228 foreach my $arg (@ARGV) {
235 $File_Name = 'standard input';
237 &main_line
($_) foreach @stdin;
240 if ($count_minus <= 1) {
253 } else { # $arg is not -
256 &main_line
($_) while <FILE
>;
271 # $Last_Type = $Types{'OTHER'};
277 $line = $former_line . $line if $former_line;
278 if ($line =~ /^(.*)\\$/) {
293 $s =~ s/\s+#.*$// if $is_pic;
296 $line =~ s/^\s*|\s*$//g;
299 $s =~ /^([^"]*)\s("[^"]*"?\S*)(.*)$/;
304 $s1 =~ s/^\s*|\s*$//g;
305 push @Words, split(/\s+/, $s1) if $s1;
309 $s =~ s/^\s*|\s*$//g;
310 push @Words, split(/\s+/, $s) if $s;
315 # @Words = split(/\s+/, $s);
316 return 1 unless @Words;
317 # foreach my $i (0..$#Words) {
318 # if ($Words[$i] =~ /^\s*#/) {
323 # return 1 unless @Words;
326 if ($line =~ /^([\.']\s*PS\s*)|([\.']\s*PS\s.+)$/) {
335 if ( $line =~ /^([\.']\s*PE\s*)|([\.']\s*PE\s.+)$/ ) {
344 if ($line =~ /^[\.']\s*cstart\s*$/) {
347 &error
("additional `.cstart'; chem is already active.");
354 $is_chem = '.cstart';
359 if ($line =~ /^\s*begin\s+chem\s*$/) {
363 &error
("additional `begin chem'; chem is already active.");
366 $is_chem = 'begin chem';
373 if ($line =~ /^[\.']\s*cend\s*/) {
376 &error
("you end chem with `.cend', but started it with `begin chem'.")
377 if $is_chem eq 'begin chem';
378 if ($is_pic eq 'by chem') {
388 if ($line =~ /^\s*end\s*$/) {
391 &error
("you end chem with `end', but started it with `.cstart'.")
392 if $is_chem eq '.cstart';
393 if ($is_pic eq 'by chem') {
409 if ($line =~ /^[.']/) {
415 if ($Words[0] eq 'pic') {
417 return 1 if $#Words == 0;
419 $s =~ /^\s*pic\s*(.*)$/;
422 $Last_Type = $Types{'OTHER'};
423 $Define{ $Words[2] } = 1 if $#Words >= 2 && $Words[1] eq 'define';
427 if ($Words[0] eq 'textht') {
429 &error
("`textht' needs a single argument.");
432 &error
("only the last argument is taken for `textht', " .
433 "all others are ignored.")
434 unless $#Words <= 1 or ($#Words == 2 && $Words[1] =~ /^=/);
435 $Params{'textht'} = $Words[$#Words];
439 if ($Words[0] eq 'cwid') { # character width
441 &error
("`cwid' needs a single argument.");
444 &error
("only the last argument is taken for `cwid', " .
445 "all others are ignored.")
446 unless $#Words <= 1 or ($#Words == 2 && $Words[1] =~ /^=/);
447 $Params{'cwid'} = $Words[$#Words];
450 if ($Words[0] eq 'db') { # bond length
452 &error
("`db' needs a single argument.");
455 &error
("only the last argument is taken for `db', " .
456 "all others are ignored.")
457 unless $#Words <= 1 or ($#Words == 2 && $Words[1] =~ /^=/);
458 $Params{'db'} = $Words[$#Words];
461 if ($Words[0] eq 'size') { # size for all parts of the whole diagram
464 &error
("`size' needs a single argument.");
467 &error
("only the last argument is taken for `size', " .
468 "all others are ignored.")
469 unless $#Words <= 1 or ($#Words == 2 && $Words[1] =~ /^=/);
470 if ($Words[$#Words] <= 4) {
471 $size = $Words[$#Words];
473 $size = $Words[$#Words] / 10;
480 print "\n#", $Line, "\n"; # debugging, etc.
482 # $Last_Type = $Types{'OTHER'};
485 if ($Words[0] =~ /^[A-Z].*:$/) {
486 # label; falls thru after shifting left
489 $Last_Name =~ s/:$//;
494 $line =~ s/^\s*$w\s*//;
501 if ($Words[0] eq 'define') {
503 $Define{ $Words[1] } = 1 if $#Words >= 1;
504 $Last_Type = $Types{'OTHER'};
507 if ($Words[0] =~ /^[\[\]{}]/) {
509 $Last_Type = $Types{'OTHER'};
513 if ($Words[0] =~ /^"/) {
514 print 'Last: ', $line, "\n";
515 $Last_Type = $Types{'OTHER'};
519 if ($Words[0] =~ /bond/) {
525 if ($Words[0] =~ /^(double|triple|front|back)$/ &&
526 $Words[1] eq 'bond') {
527 my $w = shift @Words;
528 $Words[0] = $w . $Words[0];
532 if ($Words[0] eq 'aromatic') {
533 my $temp = $Words[0];
534 $Words[0] = $Words[1] ?
$Words[1] : '';
539 if ($Words[0] =~ /ring|benz/) {
543 if ($Words[0] eq 'methyl') {
544 # left here as an example
548 if ($Words[0] =~ /^[A-Z]/) {
552 if ($Words[0] eq 'left') {
554 $left{++$stack} = &fields
(1, $#Words);
555 printf (("Last: [\n"));
558 if ($Words[0] eq 'right') {
563 if ($Words[0] eq 'label') { # prints the vertex numbers in a ring
564 if ( exists $Labtype{$Words[1]} and
565 $Labtype{$Words[1]} =~ /^$Types{'RING'}/ ) {
566 my $v = substr($Labtype{$Words[1]}, 1, 1);
567 $Words[1] = '' unless $Words[1];
568 foreach my $i ( 1..$v ) {
569 printf "\"\\s-3%d\\s0\" at 0.%d<%s.C,%s.V%d>\n", $i, $v + 2,
570 $Words[1], $Words[1], $i;
573 &error
("$Words[1] is not a ring.");
578 if ( exists $Define{ $Words[0] } ) {
580 $Last_Type = $Types{'OTHER'};
583 return 1 unless $line;
584 # print STDERR "# $Line\n";
585 # &error('This is not a chem command. To include a command for pic, ' .
586 # "add `pic' as the first word to the command.");
588 $Last_Type = $Types{'OTHER'};
594 ########################################################################
596 ########################################################################
602 # convert CH3 to atom(...)
604 my ($i, $n, $nsub, $cloc, $nsubc, @s);
610 $cloc = index($s, 'C');
611 if (! defined($cloc) || $cloc < 0) {
619 $nsubc++ if $i < $cloc;
623 $s =~ s/([0-9]+\.[0-9]+)|([0-9]+)/\\s-3\\d$&\\u\\s+3/g;
624 if ($s =~ /([^0-9]\.)|(\.[^0-9])/) { # centered dot
625 $s =~ s/\./\\v#-.3m#.\\v#.3m#/g;
627 sprintf( "atom(\"%s\", %g, %g, %g, %g, %g, %g)",
628 $s, ($n - $nsub / 2) * $Params{'cwid'}, $Params{'textht'},
629 ($cloc - $nsubc / 2 + 0.5) * $Params{'cwid'}, $Params{'crh'},
630 $Params{'crw'}, $Params{'dav'}
640 my ($i, $moiety, $from, $leng);
642 for ($i = 1; $i <= $#Words; $i++) {
643 if ($Words[$i] eq ';') {
644 &error
("a colon `;' must be followed by a space and a single word.")
645 if $i != $#Words - 1;
646 $moiety = $Words[$i + 1] if $#Words > $i;
651 $leng = $Params{'db'}; # bond length
653 for ($Word_Count = 1; $Word_Count <= $#Words; ) {
654 if ($Words[$Word_Count] =~
655 /(\+|-)?\d+|up|down|right|left|ne|se|nw|sw/) {
656 $Dir = &cvtdir
($Dir);
657 } elsif ($Words[$Word_Count] =~ /^leng/) {
658 $leng = $Words[$Word_Count + 1] if $#Words > $Word_Count;
660 } elsif ($Words[$Word_Count] eq 'to') {
662 $from = &fields
($Word_Count, $#Words);
664 } elsif ($Words[$Word_Count] eq 'from') {
667 } elsif ($Words[$Word_Count] =~ /^#/) {
668 $Word_Count = $#Words + 1;
671 $from = &fields
($Word_Count, $#Words);
676 if ($from =~ /( to )|^to/) { # said "from ... to ...", so zap length
678 } elsif (! $from) { # no from given at all
679 $from = 'from Last.' . &leave
($Last_Type, $Dir) . ' ' .
680 &fields
($Word_Count, $#Words);
682 printf "Last: %s(%g, %g, %s)\n", $type, $leng, $Dir, $from;
683 $Last_Type = $Types{'BOND'};
684 $Labtype{$Last_Name} = $Last_Type if $Last_Name;
698 if ($Words[1] && $Words[1] eq ')') {
703 printf "%s from last [].sw+(%g,0) to last [].sw to last [].nw to last " .
704 "[].nw+(%g,0)\n", $t, $Params{'dbrack'}, $Params{'dbrack'};
705 printf "%s from last [].se-(%g,0) to last [].se to last [].ne to last " .
706 "[].ne-(%g,0)\n", $t, $Params{'dbrack'}, $Params{'dbrack'};
707 if ($Words[2] && $Words[2] eq 'sub') {
708 printf "\" %s\" ljust at last [].se\n", &fields
(3, $#Words);
716 # Return the corner name next to the given angle.
720 $Dc{ (45 * int(($d + 22.5) / 45)) % 360 };
727 # Maps "[pointing] somewhere" to degrees.
731 if ($Words[$Word_Count] eq 'pointing') {
734 if ($Words[$Word_Count] =~ /^[+\\-]?\d+/) {
735 return ( $Words[$Word_Count++] % 360 );
736 } elsif ($Words[$Word_Count] =~ /left|right|up|down|ne|nw|se|sw/) {
737 return ( $Dc{$Words[$Word_Count++]} % 360 );
751 # should canonicalize to i,i+1 mod v
752 $d = $Words[$Word_Count];
753 for ($Word_Count++; $Word_Count <= $#Words &&
754 $Words[$Word_Count] =~ /^[1-9]/; $Word_Count++) {
755 $v1 = substr($Words[$Word_Count], 0, 1);
756 $v2 = substr($Words[$Word_Count], 2, 1);
757 if ($v2 == $v1 + 1 || $v1 == $v && $v2 == 1) { # e.g., 2,3 or 5,1
759 } elsif ($v1 == $v2 + 1 || $v2 == $v && $v1 == 1) { # e.g., 3,2 or 1,5
762 &error
(sprintf("weird %s bond in\n\t%s", $d, $_));
773 $Word_Count++; # skip "from"
774 $n = $Words[$Word_Count];
775 if (defined $Labtype{$n}) { # "from Thing" => "from Thing.V.s"
776 return 'from ' . $n . '.' . &leave
($Labtype{$n}, $Dir);
778 if ($n =~ /^\.[A-Z]/) { # "from .V" => "from Last.V.s"
779 return 'from Last' . $n . '.' . &corner
($Dir);
781 if ($n =~ /^[A-Z][^.]*\.[A-Z][^.]*$/) { # "from X.V" => "from X.V.s"
782 return 'from ' . $n . '.' . &corner
($Dir);
784 &fields
($Word_Count - 1, $#Words);
793 printf STDERR
"chem: error in %s on line %d: %s\n",
794 $File_Name, $Line_No, $s;
807 foreach my $i ($n1..$n2) {
808 if ($Words[$i] =~ /^#/) {
811 $s = $s . $Words[$i] . ' ';
822 printf "copy \"%s\"\n", $File_chem_pic;
823 printf "\ttextht = %g; textwid = .1; cwid = %g\n",
824 $Params{'textht'}, $Params{'cwid'};
825 printf "\tlineht = %g; linewid = %g\n",
826 $Params{'lineht'}, $Params{'linewid'};
829 printf "Last: 0,0\n";
830 $Last_Type = $Types{'OTHER'};
841 # return vertex of $last in direction $d
842 if ( $last eq $Types{'BOND'} ) {
846 if ( $last =~ /^$Types{'RING'}/ ) {
847 return &ringleave
($last, $d);
849 if ( $last eq $Types{'MOL'} ) {
850 if ($d == 0 || $d == 180) {
852 } elsif ($d > 0 && $d < 180) {
857 if (defined $Dc{$d}) {
862 return sprintf('%s.%s', $c, $c1);
864 if ( $last eq $Types{'OTHER'} ) {
872 # makering(<type>, <pt>, <v>)
875 my ($type, $pt, $v) = @_;
876 my ($i, $j, $a, $r, $rat, $fix, $c1, $c2);
877 if ($type =~ /flat/) {
882 $r = $Params{'ringside'} / (2 * sin(pi / $v));
884 for ($i = 0; $i <= $v + 1; $i++) {
885 $a = (($i - 1) / $v * 360 + $pt) / 57.29578; # 57. is $deg
886 printf "\tV%d: (%g,%g)\n", $i, $r * sin($a), $r * cos($a);
888 if ($type =~ /flat/) {
889 printf "\tV4: V5; V5: V6\n";
895 for ($i = 1; $i <= $v; $i++) {
897 if ($Put{$i} ne '') {
898 printf "\tV%d: ellipse invis ht %g wid %g at V%d\n",
899 $i, $Params{'crh'}, $Params{'crw'}, $i;
900 printf "\t%s at V%d\n", $Put{$i}, $i;
908 if ($Put{$j} ne '') {
911 printf "\tline from V%d to V%d chop %g chop %g\n", $i, $j, $c1, $c2;
912 if ($Dbl{$i} ne '') {
914 if ($type =~ /flat/ && $i == 3) {
921 if ($Put{$i} eq '') {
924 $c1 = $Params{'cr'} / $fix;
926 if ($Put{$j} eq '') {
929 $c2 = $Params{'cr'} / $fix;
931 printf "\tline from %g<C,V%d> to %g<C,V%d> chop %g chop %g\n",
932 $rat, $i, $rat, $j, $c1, $c2;
933 if ($Dbl{$i} eq 'triple') {
934 printf "\tline from %g<C,V%d> to %g<C,V%d> chop %g chop %g\n",
935 2 - $rat, $i, 2 - $rat, $j, $c1, $c2;
942 for ($i = 1; $i <= $v; $i++) {
947 printf "\tline from V%d to V%d\n", $i, $j;
948 if ($Dbl{$i} ne '') {
950 if ($type =~ /flat/ && $i == 3) {
955 printf "\tline from %g<C,V%d> to %g<C,V%d>\n",
957 if ($Dbl{$i} eq 'triple') {
958 printf "\tline from %g<C,V%d> to %g<C,V%d>\n",
959 2 - $rat, $i, 2 - $rat, $j;
965 # punt on triple temporarily
967 if ($type =~ /benz/ || $Aromatic > 0) {
968 if ($type =~ /flat/) {
973 printf "\tcircle rad %g at 0,0\n", $r;
986 $Words[0] = "\"\" ht 0 wid 0";
987 $type = $Types{'OTHER'};
989 $Words[0] = &atom
($n);
990 $type = $Types{'MOL'};
993 $n =~ s/[^A-Za-z0-9]//g; # for stuff like C(OH3): zap non-alnum
995 printf "Last: %s: %s with .%s at Last.%s\n",
996 $n, join(' ', @Words), &leave
($type, $Dir + 180),
997 &leave
($Last_Type, $Dir);
1001 printf "Last: %s: %s with .%s at Last.%s\n",
1002 $n, join(' ', @Words), &leave
($type, $Dir + 180),
1003 &leave
($Last_Type, $Dir);
1004 } elsif ($#Words >= 1 and $Words[1] eq 'below') {
1005 $Words[2] = '' if ! $Words[2];
1006 printf "Last: %s: %s with .n at %s.s\n", $n, $Words[0], $Words[2];
1007 } elsif ($#Words >= 1 and $Words[1] eq 'above') {
1008 $Words[2] = '' if ! $Words[2];
1009 printf "Last: %s: %s with .s at %s.n\n", $n, $Words[0], $Words[2];
1010 } elsif ($#Words >= 2 and $Words[1] eq 'left' && $Words[2] eq 'of') {
1011 $Words[3] = '' if ! $Words[3];
1012 printf "Last: %s: %s with .e at %s.w+(%g,0)\n",
1013 $n, $Words[0], $Words[3], $Params{'dew'};
1014 } elsif ($#Words >= 2 and $Words[1] eq 'right' && $Words[2] eq 'of') {
1015 $Words[3] = '' if ! $Words[3];
1016 printf "Last: %s: %s with .w at %s.e-(%g,0)\n",
1017 $n, $Words[0], $Words[3], $Params{'dew'};
1019 printf "Last: %s: %s\n", $n, join(' ', @Words);
1026 $Labtype{$Last_Name} = $Last_Type;
1028 $Labtype{$n} = $Last_Type;
1033 # print_hash(<hash_or_ref>)
1035 # print the elements of a hash or hash reference
1041 print STDERR
"empty hash\n;";
1044 if (ref($_[0]) eq 'HASH') {
1047 warn 'print_hash(): the argument is not a hash or hash reference;';
1052 warn 'print_hash(): the arguments are not a hash;';
1062 print STDERR
"empty hash\n";
1065 print STDERR
"hash (ignore the ^ characters):\n";
1066 for my $k (sort keys %$hr) {
1068 print STDERR
" $k => ";
1070 print STDERR
"^$hk^";
1072 print STDERR
"undef";
1100 # collect "put Mol at n"
1104 $mol = $Words[$Word_Count++];
1105 if ($Words[$Word_Count] eq 'at') {
1108 $n = $Words[$Word_Count];
1109 if ($n !~ /^\d+$/) {
1111 $n = 0 if $n !~ /^\d+$/;
1112 error
('use single digit as argument for "put at"');
1114 if ($n >= 1 && $n <= $v) {
1116 $m =~ s/[^A-Za-z0-9]//g;
1117 $Put{$n} = $m . ':' . &atom
($mol);
1119 error
('argument of "put at" must be a single digit');
1121 error
('argument of "put at" is too large');
1132 my ($typeint, $pt, $verts, $i, $other, $fused, $withat);
1133 $pt = 0; # points up by default
1134 if ($type =~ /([1-8])$/) {
1136 } elsif ($type =~ /flat/) {
1141 $fused = $other = '';
1142 for ($i = 1; $i <= $verts; $i++) {
1143 $Put{$i} = $Dbl{$i} = '';
1145 $Nput = $Aromatic = $withat = 0;
1146 for ($Word_Count = 1; $Word_Count <= $#Words; ) {
1147 if ($Words[$Word_Count] eq 'pointing') {
1149 } elsif ($Words[$Word_Count] eq 'double' ||
1150 $Words[$Word_Count] eq 'triple') {
1152 } elsif ($Words[$Word_Count] =~ /arom/) {
1154 $Word_Count++; # handled later
1156 } elsif ($Words[$Word_Count] eq 'put') {
1159 } elsif ($Words[$Word_Count] =~ /^#/) {
1160 $Word_Count = $#Words + 1;
1163 if ($Words[$Word_Count] eq 'with' || $Words[$Word_Count] eq 'at') {
1166 $other = $other . ' ' . $Words[$Word_Count];
1170 $typeint = $Types{'RING'} . $verts . $pt; # RING | verts | dir
1172 # join a ring to something
1173 if ( $Last_Type =~ /^$Types{'RING'}/ ) {
1175 if (substr($typeint, 2) eq substr($Last_Type, 2)) {
1176 # fails if not 6-sided
1177 $fused = 'with .V6 at Last.V2';
1181 $fused = sprintf('with .%s at Last.%s',
1182 &leave
($typeint, $Dir + 180), &leave
($Last_Type, $Dir));
1185 &makering
($type, $pt, $verts);
1186 printf "] %s %s\n", $fused, $other;
1187 $Last_Type = $typeint;
1188 $Labtype{$Last_Name} = $Last_Type if $Last_Name;
1193 # ringleave(<last>, <d>)
1196 my ($last, $d) = @_;
1198 # return vertex of ring in direction d
1199 $verts = substr($last, 1, 1);
1200 $rd = substr($last, 2);
1201 sprintf('V%d.%s', int( (($d - $rd) % 360) / (360 / $verts)) + 1,
1207 # setparams(<scale>)
1211 $Params{'lineht'} = $scale * 0.2;
1212 $Params{'linewid'} = $scale * 0.2;
1213 $Params{'textht'} = $scale * 0.16;
1214 $Params{'db'} = $scale * 0.2; # bond length
1215 $Params{'cwid'} = $scale * 0.12; # character width
1216 $Params{'cr'} = $scale * 0.08; # rad of invis circles at ring vertices
1217 $Params{'crh'} = $scale * 0.16; # ht of invis ellipse at ring vertices
1218 $Params{'crw'} = $scale * 0.12; # wid
1219 $Params{'dav'} = $scale * 0.015; # vertical shift up for atoms in atom macro
1220 $Params{'dew'} = $scale * 0.02; # east-west shift for left of/right of
1221 $Params{'ringside'} = $scale * 0.3; # side of all rings
1222 $Params{'dbrack'} = $scale * 0.1; # length of bottom of bracket
1229 # Print usage information for --help.
1236 Usage: $Chem_Name [option]... [filespec]...
1238 $Chem_Name is a groff preprocessor for producing chemical structure
1239 diagrams. The output suits to the pic preprocessor.
1241 "filespec" is one of
1242 "filename" name of a readable file
1243 "-" for standard input
1245 All available options are
1247 -h --help print this usage message.
1248 -v --version print version information.
1257 # Get version information from version.sh and print a text with this.
1260 $Groff_Version = $Groff_Version_Preset unless $Groff_Version;
1261 my $year = $Last_Update;
1264 $Chem_Name $Program_Version of $Last_Update (Perl version)
1265 is part of groff version $Groff_Version.
1266 Copyright (C) $year Free Software Foundation, Inc.
1267 GNU groff and chem come with ABSOLUTELY NO WARRANTY.
1268 You may redistribute copies of groff and its subprograms
1269 under the terms of the GNU General Public License.