C Copyright(C) 2011-2017 National Technology & Engineering Solutions
C of Sandia, LLC (NTESS).  Under the terms of Contract DE-NA0003525 with
C NTESS, the U.S. Government retains certain rights in this software.
C 
C Redistribution and use in source and binary forms, with or without
C modification, are permitted provided that the following conditions are
C met:
C 
C * Redistributions of source code must retain the above copyright
C    notice, this list of conditions and the following disclaimer.
C           
C * Redistributions in binary form must reproduce the above
C   copyright notice, this list of conditions and the following
C   disclaimer in the documentation and/or other materials provided
C   with the distribution.
C                         
C * Neither the name of NTESS nor the names of its
C   contributors may be used to endorse or promote products derived
C   from this software without specific prior written permission.
C                                                 
C THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
C "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
C LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
C A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
C OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
C SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
C LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
C DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
C THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
C (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
C OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

C=======================================================================
      SUBROUTINE COMAND (IDNPS, IDESS, IDNSET, IDESET,
     &     BLKTYP, IBPARM, IDELB, NUMELB, NUMLNK, NAMELB, ELATTR,
     &     XN, YN, A, *)
C=======================================================================

C   --*** COMAND *** (GEN3D) Input and process commands
C   --   Written by Amy Gilkey - revised 05/09/88
C   --   Modified by Greg Sjaardema - 02/06/89
C   --       Added Warp Function
C   --       Added Gradient to Rotations (not for center blocks)
C   --  5/11 Added GETINT call to get intervals/distance/gradient
C   --       Added INTERVALS command to set  ""    ""       ""
C   --
C   --COMAND inputs and executes an user command.
C   --
C   --Parameters:
C   --   IDNPS - IN - the IDs of existing node sets
C   --   IDESS - IN - the IDs of existing side sets
C   --   IDNSET - OUT - the IDs of the front and back surface node sets;
C   --      (0) = length
C   --   IDESET - OUT - the IDs of the front and back surface side sets;
C   --      (0) = length
C   --   BLKTYP - IN - the element block type
C   --   IBPARM - IN - the block parameters (defined by the block type)
C   --   IDELB - IN - the ids for each block
C   --   NUMELB - IN - the number of elements for each block
C   --   NUMLNK - IN - the number of nodes per element for each block
C   --   XN, YN - IN - the 2D nodal coordinates
C   --   * - return statement for QUIT
C   --
C   --Common Variables:
C   --   Sets NDIM, NUMNP, NUMEL, NELBLK,
C   --      NUMNPS, LNPSNL, NUMESS, LESSEL, LESSNL of /DBNUMS/
C   --   Sets ITRANT, NNREPL, NEREPL, DIM3, NRTRAN, D3TRAN, ZGRAD,
C   --      CENTER, NUMCOL of /PARAMS/
C   --   Sets XOFFS, YOFFS, ZOFFS of /XYZOFF/
C   --   Sets ROT3D, ROTMAT, ROTCEN of /XYZROT/

      PARAMETER (MAXFLD = 1000)

      INCLUDE 'g3_dbnums.blk'
      INCLUDE 'g3_params.blk'
      INCLUDE 'g3_xyzoff.blk'
      INCLUDE 'g3_xyzrot.blk'
      INCLUDE 'g3_xyzmir.blk'
      INCLUDE 'g3_xyzero.blk'
      INCLUDE 'g3_xyzscl.blk'
      INCLUDE 'g3_twist.blk'
      INCLUDE 'g3_cmdsho.blk'
      INCLUDE 'g3_xxxxx.blk'
      INCLUDE 'g3_splxyz.blk'

      INTEGER IDNPS(*), IDESS(*)
      INTEGER IDNSET(0:MAXSET,2), IDESET(0:MAXSET,2)
      CHARACTER BLKTYP(*)
      CHARACTER*32 NAMELB(*)
      REAL ELATTR(*)
      INTEGER IBPARM(4,*)
      INTEGER IDELB(*)
      INTEGER NUMELB(*)
      INTEGER NUMLNK(*)
      REAL XN(*), YN(*)
      REAL A(*)

      LOGICAL FFEXST, MATSTR, HELP, FFNUMB

      CHARACTER*8 WORD, VERB, MYNAME
      INTEGER     INTYP(MAXFLD+1)
      CHARACTER*8 CFIELD(MAXFLD)
      INTEGER     IFIELD(MAXFLD)
      REAL        RFIELD(MAXFLD)
      LOGICAL ISFRON
      LOGICAL ANYCEN
      LOGICAL ISHELP

      CHARACTER*8 CMDTBL(36)
      SAVE CMDTBL
C      --CMDTBL - the valid commands table

C   --Command table follows.  Remember to change the dimensioned size when
C   --changing the table.
      DATA CMDTBL /
     1   'TRANSLAT', 'ROTATE  ', 'OFFSET  ', 'REVOLVE ', 'REVCEN  ',
     2   'WARP    ', 'BLOCK   ', 'CENTER  ', 'TUNNEL  ', 'SPECIAL ',
     3   'NSETS   ', 'NODESETS', 'SSETS   ', 'SIDESETS', 'MIRROR  ',
     4   'LIST    ', 'SHOW    ', 'HELP    ', 'ZERO    ', 'SCALE   ',
     5   'END     ', 'EXIT    ', 'QUIT    ', 'TWIST   ', 'PROJECT ',
     6   'SUMMARY ', 'INTERVAL', 'ROTCEN  ', 'ROTAXIS ', 'SPLINE  ',
     7   'CHANGE  ', 'TRANSPLI', 'SHIFT   ', 'LIMITS  ', 'ATTRIBUT',
     &   '        ' /

      DATA MYNAME /'GEN3D   '/

C   --Initialize

      LNAM = LENSTR(MYNAME)
      ITRANT = 0
      NEREPL = 1
      NNREPL = NEREPL + 1
      DIM3 = 1.0
      NRTRAN(1) = NEREPL
      D3TRAN(1) = DIM3
      ZGRAD(1) = 1.0
      RGRAD = 1.0
      NDEGR = 0
      XOFFS = 0.0
      YOFFS = 0.0
      ZOFFS = 0.0
      XMIRR = 1.0
      YMIRR = 1.0
      ZMIRR = 1.0
      XZERO = 0.0
      YZERO = 0.0
      ZZERO = 0.0
      XSCAL = 1.0
      YSCAL = 1.0
      ZSCAL = 1.0
      TWYCEN = 0.0
      TWXCEN = 0.0
      ROT3D = .FALSE.
      ISCENT = .FALSE.
      ROTAX = 0
      CALL INIREA (3*3, 0.0, ROTMAT)
      DO 10 I = 1, 3
         ROTMAT(I,I) = 1.0
   10 CONTINUE

C     -- Zero the elattr array here.  If set by user, then copy these 
C     -- attributes into the attrib array in wrelb.  Else, use the 
C     -- attributes set in the input genesis file.
      call inirea (nelblk, 0.0, elattr)

      CALL MINMAX (NUMNP, XN, XMIN, XMAX)
      ROTCEN(1) = XMIN
      CALL MINMAX (NUMNP, YN, YMIN, YMAX)
      ROTCEN(2) = YMIN
      ROTCEN(3) = 0.0
      CPOINT = .FALSE.

      IDNSET(0,1) = 0
      IDNSET(0,2) = 0
      IDESET(0,1) = 0
      IDESET(0,2) = 0

      CALL INISTR (NELBLK, ' ', BLKTYP)
      CALL INIINT (4 * NELBLK, 0, IBPARM)
      NUMCOL = 0
      NUMCDM = MAX(1, NUMCOL)
      NUMROW = 0
      ANYCEN = .FALSE.
   20 CONTINUE

C   --Read command line

      WRITE (*, *)
      CALL FREFLD (0, 0, 'GEN3D> ', MAXFLD,
     &   IOSTAT, NUMFLD, INTYP, CFIELD, IFIELD, RFIELD)
      IF (IOSTAT .LT. 0) GOTO 220
      IF (NUMFLD .EQ. 0) GOTO 20
      INTYP(MIN(MAXFLD,NUMFLD)+1) = -999

      IFLD = 1
      CALL FFCHAR (IFLD, INTYP, CFIELD, ' ', WORD)
      CALL ABRSTR (VERB, WORD, CMDTBL)
      IF (VERB .EQ. ' ') VERB = WORD

C   --Perform command

      IF (VERB .EQ. 'TRANSLAT') THEN
         ITRANT = 1
         CALL GETINT ('translation', IFLD, INTYP, IFIELD, RFIELD,
     *      NBLK, NRTRAN, D3TRAN, ZGRAD, NEREPL, NNREPL, DIM3,
     *      MAXINT, *170)
         NUMCOL = -999
         NUMCDM = MAX(1, NUMCOL)

      ELSE IF (VERB .EQ. 'TRANSPLI') THEN
         ITRANT = 128
         CALL GETINT ('translation', IFLD, INTYP, IFIELD, RFIELD,
     *      NBLK, NRTRAN, D3TRAN, ZGRAD, NEREPL, NNREPL, DIM3,
     *      MAXINT, *170)
         NUMCOL = -999
         NUMCDM = MAX(1, NUMCOL)

         CALL GETSPT (A)

      ELSE IF (VERB .EQ. 'SPLINE') THEN
         ITRANT = 64
         CALL GETINT ('thickness', IFLD, INTYP, IFIELD, RFIELD,
     *      NBLK, NRTRAN, D3TRAN, ZGRAD, NEREPL, NNREPL, DIM3,
     *      MAXINT, *170)
         NUMCOL = -999
         NUMCDM = MAX(1, NUMCOL)

         CALL GETSPL (A) 

C --- Beginning of Intervals Command
      ELSE IF (VERB .EQ. 'INTERVAL') THEN
         CALL GETINT ('translation', IFLD, INTYP, IFIELD, RFIELD,
     *      NBLK, NRTRAN, D3TRAN, ZGRAD, NEREPL, NNREPL, DIM3,
     *      MAXINT, *170)

         IF (ITRANT .EQ. 2) THEN
           NDEGR = NINT (DIM3)
           IF (NDEGR .LT. 360) THEN
             NNREPL = NEREPL + 1
           ELSE
             NNREPL = NEREPL
           END IF
           
           IF (NDEGR .EQ. 360) THEN
             IF (IDNSET(0,2) .GT. 0) THEN
               CALL PRTERR ('CMDWARN',
     &           'Back node sets are deleted'
     &           // ' for 360-degree rotation')
             END IF
             IF (IDESET(0,2) .GT. 0) THEN
               CALL PRTERR ('CMDERR',
     &           'Back side sets are deleted'
     &           // ' for 360-degree rotation')
             END IF
           END IF
         END IF
C --- BEGINNING OF WARP
       ELSE IF (VERB .EQ. 'WARP') THEN
         ITRANT = 4
         CALL FFCHAR (IFLD, INTYP, CFIELD,
     *      'POINT', WORD)
         IF((.NOT. MATSTR(WORD, 'LINE',    1)) .AND.
     *      (.NOT. MATSTR(WORD, 'XAXIS',   1)) .AND.
     *      (.NOT. MATSTR(WORD, 'YAXIS',   1)) .AND.
     *      (.NOT. MATSTR(WORD, 'ELLIPSE', 1)) .AND.
     *      (.NOT. MATSTR(WORD, 'POINT',   1))) THEN
            CALL PRTERR ('CMDERR', 'Invalid WARP Option')
            GOTO 170
         END IF

         CALL GETINT ('translation', IFLD, INTYP, IFIELD, RFIELD,
     *      NBLK, NRTRAN, D3TRAN, ZGRAD, NEREPL, NNREPL, DIM3, 1,
     *      *170)

         IF (MATSTR(WORD, 'LINE', 1)) THEN
            IWARP = 2
C            CALL FFREAL (IFLD, INTYP, RFIELD,
C     *         'line X1 coordinate', 0.0, CWARP(1), *200)
C            CALL FFREAL (IFLD, INTYP, RFIELD,
C     *         'line Y1 coordinate', 0.0, CWARP(2), *200)
C            CALL FFREAL (IFLD, INTYP, RFIELD,
C     *         'line Z1 coordinate', 0.0, CWARP(3), *200)
C            CALL FFREAL (IFLD, INTYP, RFIELD,
C     *         'line X2 coordinate', 0.0, CWARP(4), *200)
C            CALL FFREAL (IFLD, INTYP, RFIELD,
C     *         'line Y2 coordinate', 0.0, CWARP(5), *200)
C            CALL FFREAL (IFLD, INTYP, RFIELD,
C     *         'line Z2 coordinate', 0.0, CWARP(6), *200)

         ELSE IF (MATSTR(WORD, 'XAXIS', 1)) THEN
            IWARP = -1

         ELSE IF (MATSTR(WORD, 'YAXIS', 1)) THEN
            IWARP = -2

         ELSE IF (MATSTR(WORD, 'POINT', 1)) THEN
            IWARP = 1
C            CALL FFREAL (IFLD, INTYP, RFIELD,
C     *         'point X coordinate', 0.0, CWARP(1), *110)
C            CALL FFREAL (IFLD, INTYP, RFIELD,
C     *         'point Y coordinate', 0.0, CWARP(2), *110)
C            CALL FFREAL (IFLD, INTYP, RFIELD,
C     *         'point Z coordinate', 0.0, CWARP(3), *110)
         ELSE IF (MATSTR(WORD, 'ELLIPSE', 1)) THEN
            IWARP = 2
            CALL FFREAL (IFLD, INTYP, RFIELD,
     *         'ellipse major axis', 0.0, HRAD, *170)
         ELSE
            IWARP = 0
            GO TO 170
         END IF
         CALL FFREAL (IFLD, INTYP, RFIELD,
     *      'warping distance', 0.0, DWARP, *170)

         CALL FFCHAR (IFLD, INTYP, CFIELD,
     *      'RADIAL', WORD)

         IF (MATSTR(WORD, 'VERTICAL', 1)) THEN
            VEDGE = .TRUE.
         ELSE IF (MATSTR(WORD, 'RADIAL', 1)) THEN
            VEDGE = .FALSE.
         ELSE
            CALL PRTERR ('CMDERR', 'Invalid edge specification')
            GOTO 170
         END IF
         NUMCOL = -999
         NUMCDM = MAX(1, NUMCOL)

C --- END OF WARP

      ELSE IF (VERB .EQ. 'ROTATE') THEN
         ITRANT = 2
         CALL GETINT ('rotation', IFLD, INTYP, IFIELD, RFIELD,
     *     NBLK, NRTRAN(1), D3TRAN(1), ZGRAD(1), NEREPL, NNREPL,
     *     DIM3, 1, *170)

         CALL FFREAL (IFLD, INTYP, RFIELD,
     &      'center of rotation', 0.0, CENTER, *170)

         NEREPL = NRTRAN(1)
         NDEGR = NINT (D3TRAN(1))
         IF (NDEGR .LT. 360) THEN
            NNREPL = NEREPL + 1
         ELSE
            NNREPL = NEREPL
         END IF
         DIM3 = D3TRAN(1)

         IF (NDEGR .EQ. 360) THEN
            IF (IDNSET(0,2) .GT. 0) THEN
               CALL PRTERR ('CMDWARN',
     &            'Back node sets are deleted'
     &            // ' for 360-degree rotation')
            END IF
            IF (IDESET(0,2) .GT. 0) THEN
               CALL PRTERR ('CMDERR',
     &            'Back side sets are deleted'
     &            // ' for 360-degree rotation')
            END IF
         END IF

         NUMCOL = -999
         NUMCDM = MAX(1, NUMCOL)

         IF (ANYCEN .AND. (.NOT. CPOINT)) THEN
            NUMCOL = -1
            NUMCDM = MAX(1, NUMCOL)

            IF (MOD (NDEGR, 90) .NE. 0) THEN
               CALL PRTERR ('CMDWARN', 'Rotation around the mesh edge'
     &            // ' must be 90, 180, 270 or 360 degrees')
               GOTO 30
            END IF

            N45 = NINT (DIM3 / 45.0)
            IF (MOD (NEREPL, N45) .NE. 0) THEN
               CALL PRTERR ('CMDERR', 'Number of rotations'
     &            // ' around the mesh edge is incorrect')
               WRITE (*, 250) '(multiple of 2 for 90 degrees,'
     &            , ' 4 for 180, 6 for 270, 8 for 360)'
               GOTO 30
            END IF

            NUMCOL = -999
            NUMCDM = MAX(1, NUMCOL)
   30       CONTINUE
         END IF

       ELSE IF (VERB .EQ. 'ROTAXIS ') THEN
         CALL FFCHAR (IFLD, INTYP, CFIELD, 'Y', WORD)
         IF (MATSTR(WORD, 'X', 1)) THEN
           ROTAX = 1
         ELSE IF (MATSTR(WORD, 'Y', 1)) THEN
           ROTAX = 0
         ELSE
           CALL PRTERR('CMDERR',
     *       'Invalid ROTAXIS Option, Expected "X" or "Y"')
           GOTO 170
         END IF
         
C --- Experimental Rotate Routine -- Sets Center, Intervals set by
C        Intervals command
      ELSE IF (VERB .EQ. 'ROTCEN  ') THEN
         ITRANT = 32

         CALL FFREAL (IFLD, INTYP, RFIELD,
     &      'center of rotation', 0.0, CENTER, *170)

         NEREPL = 0
         DEGR = 0.
         DO 40 IBLK = 1, NBLK
            NEREPL = NEREPL + NRTRAN(IBLK)
            DEGR = DEGR + D3TRAN(IBLK)
   40    CONTINUE

C     ... Give tolerance of 1.0e-5
         deg_min = 360.0 - 1.0e-5
         deg_max = 360.0 + 1.0e-5

         if (degr .gt. deg_min .and. degr .lt. deg_max) then
            degr = 360.0
         end if
         NDEGR = NINT (degr)

         IF (DEGR .GT. 360.0) THEN
            CALL PRTERR ('CMDERR',
     &         'Total number of degrees exceeds 360')
            GOTO 170
         END IF

         IF (NEREPL .EQ. 0) THEN
            CALL PRTERR ('CMDERR',
     $           'INTERVAL command must be specified before ROTCEN')
            GO TO 170
         END IF

         IDEGR = NINT (DEGR/NEREPL)
         IF (IDEGR .GE. 180) THEN
            CALL PRTERR ('CMDERR',
     &         'Single rotation cannot cover 180 degrees')
            GOTO 170
         END IF
         IF (DEGR .LT. 360.0) THEN
            NNREPL = NEREPL + 1
         ELSE
            NNREPL = NEREPL
         END IF

         IF (DEGR .EQ. 360.0) THEN
            IF (IDNSET(0,2) .GT. 0) THEN
               CALL PRTERR ('CMDWARN',
     &            'Back node sets are deleted'
     &            // ' for 360-degree rotation')
            END IF
            IF (IDESET(0,2) .GT. 0) THEN
               CALL PRTERR ('CMDERR',
     &            'Back side sets are deleted'
     &            // ' for 360-degree rotation')
            END IF
         END IF

         NUMCOL = -999
         NUMCDM = MAX(1, NUMCOL)

         IF (ANYCEN .AND. (.NOT. CPOINT)) THEN
            NUMCOL = -1
            NUMCDM = MAX(1, NUMCOL)

            IF (MOD (NDEGR, 90) .NE. 0) THEN
               CALL PRTERR ('CMDWARN', 'Rotation around the mesh edge'
     &            // ' must be 90, 180, 270 or 360 degrees')
               GOTO 50
            END IF

            N45 = NINT (DIM3 / 45.0)
            IF (MOD (NEREPL, N45) .NE. 0) THEN
               CALL PRTERR ('CMDERR', 'Number of rotations'
     &            // ' around the mesh edge is incorrect')
               WRITE (*, 250) '(multiple of 2 for 90 degrees,'
     &            , ' 4 for 180, 6 for 270, 8 for 360)'
               GOTO 50
            END IF

            NUMCOL = -999
            NUMCDM = MAX(1, NUMCOL)
   50       CONTINUE
         END IF

      ELSE IF (VERB .EQ. 'TWIST') THEN
         ITRANT = 8
         CALL FFREAL (IFLD, INTYP, RFIELD,
     *      'twist angle', TWANGL, TWANGL, *170)
         IF (.NOT. FFNUMB (IFLD, INTYP)) GO TO 60
         CALL FFREAL (IFLD, INTYP, RFIELD,
     *      'twist X center', TWXCEN, TWXCEN, *170)
         IF (.NOT. FFNUMB (IFLD, INTYP)) GO TO 60
         CALL FFREAL (IFLD, INTYP, RFIELD,
     *      'twist Y center', TWYCEN, TWYCEN, *170)

   60    CONTINUE

         CALL FFCHAR (IFLD, INTYP, CFIELD,
     *      'TRANSLAT', WORD)
         IF (MATSTR(WORD, 'TRANSLAT', 1)) THEN
            ITWTYP = 1
            CALL GETINT ('translation', IFLD, INTYP, IFIELD, RFIELD,
     *         NBLK, NRTRAN, D3TRAN, ZGRAD, NEREPL, NNREPL, DIM3,
     *         MAXINT, *170)
            NUMCOL = -999
            NUMCDM = MAX(1, NUMCOL)

         ELSE IF (MATSTR(WORD, 'ROTATE', 1)) THEN
            ITWTYP = 2
            CALL FFREAL (IFLD, INTYP, RFIELD,
     &         'center of rotation', 0.0, CENTER, *170)

            CALL GETINT ('rotation', IFLD, INTYP, IFIELD, RFIELD,
     *         NBLK, NRTRAN, D3TRAN, ZGRAD, NEREPL, NNREPL, DIM3,
     *         MAXINT, *170)

         ELSE
            CALL PRTERR ('CMDERR', 'Invalid TWIST Option')
            GOTO 170
         END IF

      ELSE IF (VERB .EQ. 'PROJECT') THEN
         ITRANT = 16
         CALL GETINT ('translation', IFLD, INTYP, IFIELD, RFIELD,
     *      NBLK, NRTRAN, D3TRAN, ZGRAD, NEREPL, NNREPL, DIM3,
     *      MAXINT, *170)

         NUMCOL = -999
         NUMCDM = MAX(1, NUMCOL)

         CALL GETPRO (NEREPL, NNREPL, *170)

      ELSE IF (VERB .EQ. 'OFFSET' .OR. VERB .EQ. 'SHIFT') THEN

C     ... Originally offset just asked for NDIM values.  It was changed
C     to go by axis type (OFFSET Y 1.0). We do maintain compatibility
C     and check for both types of input.

         RMULT = 0.0
         IF (INTYP(IFLD) .EQ. 0) THEN
  930       CONTINUE
            IF (FFEXST (IFLD, INTYP)) THEN
               CALL FFCHAR (IFLD, INTYP, CFIELD, ' ', WORD)
               IF (MATSTR (WORD, 'RESET', 1)) THEN
                  XOFFS = 0.0
                  YOFFS = 0.0
                  ZOFFS = 0.0
                  RMULT = 0.0
               ELSE IF (MATSTR (WORD, 'ADD', 2)) THEN
C     ... Set for cumulative offsets/shifts
                  RMULT = 1.0
               ELSE IF (MATSTR (WORD, 'ALL', 2)) THEN
                  CALL FFREAL (IFLD, INTYP, RFIELD,
     &                 'model offset', 0.0, TOFFS, *170)
                  XOFFS = RMULT * XOFFS + TOFFS
                  YOFFS = RMULT * YOFFS + TOFFS
                  ZOFFS = RMULT * ZOFFS + TOFFS
               ELSE IF (WORD .EQ. 'X') THEN
                  CALL FFREAL (IFLD, INTYP, RFIELD,
     &                 'X coordinate offset', 0.0, TOFFS, *170)
                  XOFFS = RMULT * XOFFS + TOFFS
               ELSE IF (WORD .EQ. 'Y') THEN
                  CALL FFREAL (IFLD, INTYP, RFIELD,
     &                 'Y coordinate offset', 0.0, TOFFS, *170)
                  YOFFS = RMULT * YOFFS + TOFFS
               ELSE IF (WORD .EQ. 'Z') THEN
                  CALL FFREAL (IFLD, INTYP, RFIELD,
     &                 'Z coordinate offset', 0.0, TOFFS, *170)
                  ZOFFS = RMULT * ZOFFS + TOFFS
               ELSE
                  CALL PRTERR ('CMDERR',
     &              'Expected "X", "Y", "Z", "ALL", "ADD", or "RESET"')
                  GOTO 170
               END IF
               GOTO 930
            END IF
         ELSE
            CALL FFREAL (IFLD, INTYP, RFIELD,
     &           'X coordinate offset', XOFFS, XOFFS, *170)
            CALL FFREAL (IFLD, INTYP, RFIELD,
     &           'Y coordinate offset', YOFFS, YOFFS, *170)
            CALL FFREAL (IFLD, INTYP, RFIELD,
     &           'Z coordinate offset', ZOFFS, ZOFFS, *170)
         END IF

      ELSE IF (VERB .EQ. 'SCALE') THEN
   70    CONTINUE
         IF (FFEXST (IFLD, INTYP)) THEN
            CALL FFCHAR (IFLD, INTYP, CFIELD, ' ', WORD)
            IF (MATSTR (WORD, 'RESET', 1)) THEN
               XSCAL = 1.0
               YSCAL = 1.0
               ZSCAL = 1.0
            ELSE IF (MATSTR (WORD, 'ALL', 1)) THEN
               CALL FFREAL (IFLD, INTYP, RFIELD,
     &            'model scale factor', 1.0, XSCAL, *80)
               XSCAL = ABS(XSCAL)
               YSCAL = XSCAL
               IF (NDIM .EQ. 3) ZSCAL = XSCAL
            ELSE IF (WORD .EQ. 'X') THEN
               CALL FFREAL (IFLD, INTYP, RFIELD,
     &            'X scale factor', 1.0, XSCAL, *80)
               XSCAL = ABS(XSCAL)
            ELSE IF (WORD .EQ. 'Y') THEN
               CALL FFREAL (IFLD, INTYP, RFIELD,
     &            'Y scale factor', 1.0, YSCAL, *80)
               YSCAL = ABS(YSCAL)
            ELSE IF (WORD .EQ. 'Z') THEN
               CALL FFREAL (IFLD, INTYP, RFIELD,
     &            'Z scale factor', 1.0, ZSCAL, *80)
               ZSCAL = ABS(ZSCAL)
            ELSE
               CALL PRTERR ('CMDERR',
     &            'Expected "X", "Y", "Z", "ALL", or "RESET"')
               GOTO 170
            END IF
            GOTO 70
         END IF

   80    CONTINUE

      ELSE IF (VERB .EQ. 'ZERO') THEN
   90    CONTINUE
         IF (FFEXST (IFLD, INTYP)) THEN
            CALL FFCHAR (IFLD, INTYP, CFIELD, ' ', WORD)
            IF (MATSTR (WORD, 'RESET', 1)) THEN
               XZERO = 0.0
               YZERO = 0.0
               ZZERO = 0.0
            ELSE IF (WORD .EQ. 'X') THEN
               CALL FFREAL (IFLD, INTYP, RFIELD,
     &            'minimum X coordinate', 0.0, XZERO, *100)
            ELSE IF (WORD .EQ. 'Y') THEN
               CALL FFREAL (IFLD, INTYP, RFIELD,
     &            'minimum Y coordinate', 0.0, YZERO, *100)
            ELSE IF (WORD .EQ. 'Z') THEN
               CALL FFREAL (IFLD, INTYP, RFIELD,
     &            'minimum Z coordinate', 0.0, ZZERO, *100)
            ELSE
               CALL PRTERR ('CMDERR',
     &            'Expected "X", "Y", "Z" or "RESET"')
               GOTO 100
            END IF
            GOTO 90
         END IF

  100    CONTINUE

      ELSE IF (VERB .EQ. 'MIRROR') THEN

  110    CONTINUE
         IF (FFEXST (IFLD, INTYP)) THEN
            CALL FFCHAR (IFLD, INTYP, CFIELD, ' ', WORD)
            IF (MATSTR (WORD, 'RESET', 1)) THEN
               XMIRR = 1.
               YMIRR = 1.
               ZMIRR = 1.
            ELSE IF (WORD .EQ. 'X') THEN
               XMIRR = -1.
            ELSE IF (WORD .EQ. 'Y') THEN
               YMIRR = -1.
            ELSE IF (WORD .EQ. 'Z') THEN
               ZMIRR = -1.
            ELSE
               CALL PRTERR ('CMDERR',
     &            'Expected "X", "Y", "Z" or "RESET"')
               GOTO 120
            END IF
            GOTO 110
         END IF

  120    CONTINUE

      ELSE IF (VERB .EQ. 'REVOLVE') THEN
         DEGANG = ATAN2(0.0, -1.0) / 180.0

  130    CONTINUE
         IF (FFEXST (IFLD, INTYP)) THEN
            CALL FFCHAR (IFLD, INTYP, CFIELD, ' ', WORD)
            IF (MATSTR (WORD, 'RESET', 1)) THEN
               ROT3D = .FALSE.
               CALL INIREA (3*3, 0.0, ROTMAT)
               DO 140 I = 1, 3
                  ROTMAT(I,I) = 1.0
  140          CONTINUE
            ELSE IF ((WORD .EQ. 'X') .OR. (WORD .EQ. 'Y')
     &         .OR. (WORD .EQ. 'Z')) THEN
               CALL FFREAL (IFLD, INTYP, RFIELD,
     &            'angle of rotation', 0.0, DEG, *150)
               ROT3D = .TRUE.
               CALL ROTXYZ (WORD, DEG * DEGANG, ROTMAT)
            ELSE
               CALL PRTERR ('CMDERR',
     &            'Expected "X", "Y", "Z" or "RESET"')
               GOTO 150
            END IF
            GOTO 130
         END IF

  150    CONTINUE

      ELSE IF (VERB .EQ. 'REVCEN') THEN
         CALL MINMAX (NUMNP, XN, XMIN, XMAX)
         CALL MINMAX (NUMNP, YN, YMIN, YMAX)

         CALL FFREAL (IFLD, INTYP, RFIELD,
     &      'X revolution center', XMIN, ROTCEN(1), *170)
         CALL FFREAL (IFLD, INTYP, RFIELD,
     &      'Y revolution center', YMIN, ROTCEN(2), *170)
         CALL FFREAL (IFLD, INTYP, RFIELD,
     &      'Z revolution center', 0.0, ROTCEN(3), *170)

      ELSE IF (VERB .EQ. 'BLOCK') THEN
         CALL USBLK (IFLD, INTYP, CFIELD, IFIELD,
     &      ' ', NELBLK, IDELB, BLKTYP, *170)

         NUMCOL = -999
         NUMCDM = MAX(1, NUMCOL)

      ELSE IF (VERB .EQ. 'CENTER') THEN
         IF (ITRANT .NE. 2) THEN
            CALL PRTERR ('CMDERR', 'Rotation must be selected')
            GOTO 170
         END IF

         if (rgrad .ne. 1.0) then
            rgrad = 1.0
            call prterr ('CMDWARN',
     $           'Gradient must equal 1.0 for center rotations')
         end if

         IF (.NOT. CPOINT) THEN
            IF (MOD (NDEGR, 90) .NE. 0) THEN
               CALL PRTERR ('CMDERR', 'Rotation must be 90, 180, 270'
     &            // ' or 360 degrees')
               GOTO 170
            END IF

            N45 = NINT (DIM3 / 45.0)
            IF (MOD (NEREPL, N45) .NE. 0) THEN
               CALL PRTERR ('CMDERR',
     &            'Number of rotations is incorrect')
               WRITE (*, 250) '(multiple of 2 for 90 degrees,'
     &            , ' 4 for 180, 6 for 270, 8 for 360)'
               GOTO 170
            END IF
         END IF

         CALL USBLK (IFLD, INTYP, CFIELD, IFIELD,
     &      'C', NELBLK, IDELB, BLKTYP, *170)

         ISCENT = .TRUE.
         NUMCOL = -999
         NUMCDM = MAX(1, NUMCOL)

      ELSE IF (VERB .EQ. 'TUNNEL') THEN
c         IF (ITRANT .NE. 1) THEN
c            CALL PRTERR ('CMDERR', 'Translation must be selected')
c            GOTO 170
c         END IF

         CALL FFINTG (IFLD, INTYP, IFIELD,
     &      'block id', 0, ID, *170)
         IELB = LOCINT (ID, NELBLK, IDELB)
         IF (IELB .LE. 0) THEN
            CALL PRTERR ('CMDERR', 'Invalid block id')
            GOTO 170
         END IF

         CALL FFINTG (IFLD, INTYP, IFIELD,
     &      'first tunnel', 2, IPARM1, *170)
         IPARM1 = MAX (IPARM1, 1)
         CALL FFINTG (IFLD, INTYP, IFIELD,
     &      'ending tunnel', 0, IPARM2, *170)
         CALL FFINTG (IFLD, INTYP, IFIELD,
     &      'tunnel increment', 1, IPARM3, *170)
         IF ((IPARM2 .GT. 0) .AND. (IPARM1 .GT. IPARM2)) THEN
            CALL PRTERR ('CMDERR', 'Starting level after ending level')
            GOTO 170
         END IF
         IF (IPARM3 .LE. 0) THEN
            CALL PRTERR ('CMDERR', 'Tunnel increment must be positive')
            GOTO 170
         END IF

         BLKTYP(IELB) = 'T'
         IBPARM(1,IELB) = IPARM1
         IBPARM(2,IELB) = IPARM2
         IBPARM(3,IELB) = IPARM3

         IF (IPARM1 .LE. 1) IPARM1 = IPARM3 + 1
         IF (IPARM1 .GT. NEREPL) THEN
            CALL PRTERR ('CMDWARN', 'Not enough levels are defined')
         END IF

         NUMCOL = -999
         NUMCDM = MAX(1, NUMCOL)

      ELSE IF (VERB .EQ. 'SPECIAL') THEN
         IF (ITRANT .NE. 1) THEN
            CALL PRTERR ('CMDERR', 'Translation must be selected')
            GOTO 170
         END IF
         IF (NEREPL .EQ. NRTRAN(1)) THEN
            CALL PRTERR ('CMDERR',
     &         'Multiple translations must be selected')
            GOTO 170
         END IF

         CALL USBLK (IFLD, INTYP, CFIELD, IFIELD,
     &      'S', NELBLK, IDELB, BLKTYP, *170)

         NUMCOL = -999
         NUMCDM = MAX(1, NUMCOL)

      ELSE IF ((VERB .EQ. 'NSETS') .OR. (VERB .EQ. 'NODESETS')
     &   .OR. (VERB .EQ. 'SSETS') .OR. (VERB .EQ. 'SIDESETS')) THEN
         CALL FFCHAR (IFLD, INTYP, CFIELD, ' ', WORD)
         IF ((.NOT. MATSTR (WORD, 'FRONT', 1)) .AND.
     &      (.NOT. MATSTR (WORD, 'BACK', 1))) THEN
            CALL PRTERR ('CMDERR', 'Expected FRONT or BACK')
            GOTO 170
         END IF
         ISFRON = MATSTR (WORD, 'FRONT', 1)
         IF ((.NOT. ISFRON)
     &      .AND. (ITRANT .EQ. 2) .AND. (NDEGR .EQ. 360)) THEN
            CALL PRTERR ('CMDERR', 'Back sets are not allowed'
     &         // ' for 360-degree rotation')
            GOTO 170
         END IF

         IF ((VERB .EQ. 'NSETS') .OR. (VERB .EQ. 'NODESETS')) THEN
            IF (ISFRON) THEN
               CALL USIDS (IFLD, INTYP, CFIELD, IFIELD,
     &            NUMNPS, IDNPS, IDNSET(0,2), IDNSET(1,2),
     &            IDNSET(0,1), IDNSET(1,1), MAXSET, *170)
            ELSE
               CALL USIDS (IFLD, INTYP, CFIELD, IFIELD,
     &            NUMNPS, IDNPS, IDNSET(0,1), IDNSET(1,1),
     &            IDNSET(0,2), IDNSET(1,2), MAXSET, *170)
            END IF
         ELSE
            IF (ISFRON) THEN
               CALL USIDS (IFLD, INTYP, CFIELD, IFIELD,
     &            NUMESS, IDESS, IDESET(0,2), IDESET(1,2),
     &            IDESET(0,1), IDESET(1,1), MAXSET, *170)
            ELSE
               CALL USIDS (IFLD, INTYP, CFIELD, IFIELD,
     &            NUMESS, IDESS, IDESET(0,1), IDESET(1,1),
     &            IDESET(0,2), IDESET(1,2), MAXSET, *170)
            END IF
         END IF

      ELSE IF (VERB .EQ. 'LIMITS') THEN
         WRITE (*, *) ' Input Mesh Limits:'
         WRITE (*, 45) 'X', XMIN, 'X', XMAX, XMAX-XMIN
         WRITE (*, 45) 'Y', YMIN, 'Y', YMAX, YMAX-YMIN
         VERB = ' '
 45      FORMAT( '  Minimum ',A1,' = ',1PE12.5,', Maximum ',A1,' = ',
     &        1PE12.5,', Range = ',1PE12.5)
      ELSE IF (VERB .EQ. 'SUMMARY') THEN
         PRINT 160
  160    FORMAT (
     &      /' TRANSLATE,   num_trans, tot_trans, gradient'
     &      /' ROTATE,      num_rotat, tot_rotat, gradient'
     &      , ', center_of_rotation'
     &      /' WARP, POINT, num_trans, tot_trans, gradient'
     &      , ', distance, edge'
     &      /' WARP, axis,  num_trans, tot_trans, gradient'
     &      , ', distance, edge'
     &      /' OFFSET,  xoffset, yoffset, zoffset'
     &      /' MIRROR,  axis, ... '
     &      /' REVOLVE, axis, number_of_degrees, ...'
     &      /' REVCEN,  xcenter, ycenter, zcenter'
     &      /' BLOCK,   id1, id2, ...'
     &      /' CENTER,  id1, id2, ...'
     &      /' TUNNEL,  id, starting_level, ending_level'
     &      , ', level_increment'
     &      /' SPECIAL, id1, id2, ...'
     &      /' NSETS,   FRONT_or_BACK, id1, id2, ...'
     &      /' SSETS,   FRONT_or_BACK, id1, id2, ...'
     &      )
         VERB = ' '

      ELSE IF ((VERB .EQ. 'SHOW') .OR. (VERB .EQ. 'LIST')) THEN
         CALL FFCHAR (IFLD, INTYP, CFIELD, ' ', WORD)
         IF (MATSTR (WORD, 'COMMANDS', 3)) THEN
            CALL SHOCMD ('COMMANDS', CMDTBL)
         ELSE
            CALL ABRSTR (VERB, WORD, CMDTBL)
            CALL SHOW (VERB, WORD, IDNPS, IDESS, IDNSET, IDESET,
     &         BLKTYP, IBPARM, IDELB, NUMELB, NUMLNK)
         END IF
         VERB = ' '

      ELSE IF (VERB .EQ. 'ATTRIBUT') THEN
         CALL FFINTG (IFLD, INTYP, IFIELD,
     *      'block ID', 0, ID, *170)
         CALL FFREAL (IFLD, INTYP, RFIELD,
     *      'element attribute', 0.0, ATTRIB, *170)
         if (id .lt. 0) then
            call  prterr( 'ERROR', 'Invalid Block ID')
            go to 170
         end if

         imat = locint (id, nelblk, idelb)
         IF (IMAT .EQ. 0) THEN
            CALL PRTERR ('ERROR', 'Invalid Block ID')
         else if (namelb(imat)(:3) .eq. 'BAR'
     &        .or. namelb(imat)(:4) .eq. 'BEAM'
     *        .or. namelb(imat)(:4) .eq. 'TRUS') then
            ELATTR(imat) = ATTRIB
            write (*, 165) id, attrib
 165        FORMAT(1x, 'Attribute for block ',i5,' set to ',1pe10.3)
         else
            CALL PRTERR ('ERROR', 'Block is not a beam, bar, or truss')
         end if
         verb = ' '
         
      ELSE IF (VERB .EQ. 'CHANGE') THEN
         CALL FFCHAR (IFLD, INTYP, CFIELD, ' ', WORD)
         CALL FFINTG (IFLD, INTYP, IFIELD,
     *      'old ID', 0, IDOLD, *170)
         CALL FFINTG (IFLD, INTYP, IFIELD,
     *      'new ID', 0, IDNEW, *170)

         IF (MATSTR(WORD,'MATERIAL',1)) THEN
            CALL NEWID ('M', IDELB, NELBLK, IDNEW, IDOLD)
         ELSE IF (MATSTR(WORD,'NODESET',1)) THEN
            CALL NEWID ('N', IDNPS, NUMNPS, IDNEW, IDOLD)
         ELSE IF (MATSTR(WORD,'SIDESET',1)) THEN
            CALL NEWID ('S', IDESS, NUMESS, IDNEW, IDOLD)
         END IF
         VERB = ' '

      ELSE IF (VERB .EQ. 'HELP') THEN
         ISHELP = HELP (MYNAME(:LNAM), 'COMMANDS', CFIELD(IFLD))
         IF (.NOT. ISHELP) CALL SHOCMD ('COMMANDS', CMDTBL)
         VERB = ' '

      ELSE IF ((VERB .EQ. 'END') .OR. (VERB .EQ. 'EXIT')) THEN
         CALL SCNEOF
         GOTO 220

      ELSE IF (VERB .EQ. 'QUIT') THEN
         CALL SCNEOF
         RETURN 1

      ELSE
         CALL PRTERR ('CMDERR', '"' // VERB(:LENSTR(VERB))
     &      // '" is an invalid command')
         VERB = ' '
      END IF

  170 CONTINUE

C   --Reset block types, ANYCEN, and NUMCOL

      IF (NUMCOL .LT. 0) THEN
         IF (ITRANT .NE. 2) THEN
            IF (NEREPL .EQ. NRTRAN(1)) THEN
               DO 180 IELB = 1, NELBLK
                  IF (BLKTYP(IELB) .EQ. 'S') BLKTYP(IELB) = ' '
  180          CONTINUE
            END IF

            DO 190 IELB = 1, NELBLK
               IF (BLKTYP(IELB) .EQ. 'C') BLKTYP(IELB) = ' '
  190       CONTINUE
            ANYCEN = .FALSE.
            NUMCOL = 0
            NUMCDM = MAX(1, NUMCOL)

         ELSE
            DO 200 IELB = 1, NELBLK
               IF (BLKTYP(IELB) .EQ. 'S') BLKTYP(IELB) = ' '
  200       CONTINUE

            ANYCEN = .FALSE.
            DO 210 IELB = 1, NELBLK
               IF (BLKTYP(IELB) .EQ. 'C') ANYCEN = .TRUE.
  210       CONTINUE

            IF (.NOT. ANYCEN) THEN
               NUMCOL = 0
            ELSE IF (CPOINT) THEN
               NUMCOL = 1
            ELSE IF (NUMCOL .NE. -1) THEN
               N45 = NINT (DIM3 / 45.0)
               NUMCOL = NEREPL / N45 + 1
            END IF
            NUMCDM = MAX(1, NUMCOL)
         END IF
      END IF

      IF (VERB .NE. ' ') THEN
         CALL SHOW (VERB, ' ', IDNPS, IDESS, IDNSET, IDESET,
     &      BLKTYP, IBPARM, IDELB, NUMELB, NUMLNK)
      END IF

      GOTO 20

  220 CONTINUE
      IF (ITRANT .EQ. 0) ITRANT = 1

C   --Delete back sets on 360-degree rotations

      IF ((ITRANT .EQ. 2) .AND. (NDEGR .EQ. 360)) THEN
         IF (IDNSET(0,2) .GT. 0) IDNSET(0,2) = 0
         IF (IDESET(0,2) .GT. 0) IDESET(0,2) = 0
      END IF

C   --Delete center block types if invalid

      IF (NUMCOL .EQ. -1) THEN
         DO 230 IELB = 1, NELBLK
            IF (BLKTYP(IELB) .EQ. 'C') BLKTYP(IELB) = ' '
  230    CONTINUE
      END IF

C   --Center of rotation is meaningless if center blocks defined

      IF (NUMCOL .GT. 0) THEN
         CENTER = 0.0
      END IF

C   --Fix up tunnel block type parameters

      DO 240 IELB = 1, NELBLK
         IF (BLKTYP(IELB) .EQ. 'T') THEN
            IF (IBPARM(1,IELB) .GT. NEREPL) THEN
               BLKTYP(IELB) = ' '
            ELSE
               IF (IBPARM(1,IELB) .LE. 1)
     &            IBPARM(1,IELB) = IBPARM(3,IELB) + 1
               IF ((IBPARM(2,IELB) .LE. 0)
     &            .OR. (IBPARM(2,IELB) .GT. NEREPL))
     &            IBPARM(2,IELB) = NEREPL
            END IF
         END IF
  240 CONTINUE

      RETURN
  250 FORMAT (5X, 5A)
      END
