proc _Min { a b } {
    if { $a < $b } {
	return $a
    } else {
	return $b
    }
}

proc _Max { a b } {
    if { $a > $b } {
	return $a
    } else {
	return $b
    }
}

proc poImgApplyTfm { x y } {
    global __tfm

    set u [expr {$x * [lindex $__tfm 0] + $y * [lindex $__tfm 2] + [lindex $__tfm 4]}]
    set v [expr {$x * [lindex $__tfm 1] + $y * [lindex $__tfm 3] + [lindex $__tfm 5]}]
    # puts [format "poImgApplyTfm (%.2f, %.2f) --> (%.2f, %.2f)" $x $y  $u $v]
    return [list $u $v]
}

proc poImgApplyTfmDeriv { x y } {
    global __tfm

    set ux [lindex $__tfm 0]
    set uy [lindex $__tfm 2]
    set vx [lindex $__tfm 1]
    set vy [lindex $__tfm 3]
    return [list $ux $uy $vx $vy]
}

###########################################################################
#[
#       Function Name:  poImgTransform
#
#       Description:    Apply a transformation matrix to an image.
#
#       Usage:          poImgTransform (srcimg, destimg, T, oor)
#
#                       srcimg:         image
#                       destimg:        image
#                       T:              TRM2D
#                       oor:            integer, optional (IMGFILL)
#
#                       Image "srcimg" is transformed geometrically according
#                       to transformation matrix "T".  The result is stored in
#                       image "destimg":
#                       Each pixel in destimg, at position "d", is filled with
#                       the color at position "s" in "srcimg", where
#
#                           s = dT.
#
#                       If "s" is not within the boundaries of the source
#                       image, "oor" determines what color is assigned to the
#                       pixel at position "d".  Acceptable values for "oor"
#                       are IMGFILL, IMGCLIP and IMGWRAP; see the description
#                       of the "WarpFunct" command for an explanation.
#
#                       The x and y coordinates in "srcimg" and "destimg"
#                       increase from left to right and from bottom to top;
#                       the lower left corners are at (0.0, 0.0);
#                       the upper right corners are at (1.0, 1.0).
#
#                       Note that parts of poImgTransform can run concurrently
#                       on multiple processors; see the desciption of the
#                       "set_numproc" command.
#
#       Return Value:   None.
#]
###########################################################################

proc poImgTransform { srcimg destimg T { oor $IMGFILL } } {
    global __tfm IMGFILL

    poVec TfmCopy2D $T __tfm
    $srcimg GetImgInfo w h
    set size [_Max $w $h]
    $destimg WarpFunct $srcimg poImgApplyTfm poImgApplyTfmDeriv $size $oor
}

###########################################################################
#[
#       Function Name:  poImgRotate
#
#       Description:    Rotate an image by an arbitray angle.
#
#       Usage:          poImgRotate (srcimg, destimg, angle, xcen, ycen, oor)
#
#                       srcimg:         image
#                       destimg:        image
#                       angle:          float
#                       xcen, ycen:     float, optional (0.5, 0.5)
#                       oor:            integer, optional (IMGFILL)
#
#                       Image "srcimg" is rotated counterclockwise by
#                       "angle" around position (xcen, ycen).  The result
#                       is stored in image "destimg".
#
#                       "Oor" determines how pixels in "destimg", which are
#                       not covered by the rotated version of "srcimg", are
#                       treated.  Acceptable values for "oor" are IMGFILL,
#                       IMGCLIP and IMGWRAP; see the description of the "WarpFunct"
#                       command for an explanation.
#
#                       The x and y coordinates in "srcimg" increase from
#                       bottom to top; the lower left corner is at (0.0, 0.0);
#                       the upper right corner is at (1.0, 1.0).
#
#                       "Angle" is measured in radians. Use the "deg_rad"
#                       function to convert an angle given in degrees into
#                       radians.
#
#                       Note that parts of poImgRotate can run concurrently
#                       on multiple processors; see the desciption of the
#                       "set_numproc" command.
#
#       Return Value:   None.
#]
###########################################################################

proc poImgRotate { srcimg destimg angle {xcen 0.5} {ycen 0.5} {oor 0} } {

    # Get hold of the rotation center and of the out-of-range
    # pixel treatment mode; supply default values for data not
    # specified by the user.

    set center [list [expr (-1.0 * $xcen)] [expr (-1.0 * $ycen)]]

    # Build the transformation matrix to do the rotation:
    # First shift the image so that the intended center of rotation
    # coincides with the coordinate origin.

    poVec TfmBuildTrans2D $center tfmTrans1

    # Scale the image in y to compensate for the aspect ratio of the
    # destination image.

    $destimg GetImgInfo w h aspect
    set scale [list 1.0 [expr (1.0 / $aspect)]]
    poVec TfmConcatScale2D $scale $tfmTrans1 tfmScale1

    # Rotate the image.

    set rot [expr (-1.0 * $angle)]
    poVec TfmConcatRot2D $rot $tfmScale1 tfmRot1

    # Scale the image in y a second time, to compensate for the aspect
    # ratio of the source image.

    $srcimg GetImgInfo w h aspect
    set scale [list 1.0 $aspect]
    poVec TfmConcatScale2D $scale $tfmRot1 tfmScale2

    # Shift the image so that the center of rotation moves back to its
    # original position.

    poVec VecScale2D -1 $center center2
    poVec TfmConcatTrans2D $center2 $tfmScale2 tfmTrans2

    # Finally, rotate the source image according to the transformation
    # matrix.

    poImgTransform $srcimg $destimg $tfmTrans2 $oor
}

proc XXXpoImgRotateAdjust { srcimg angle {xcen 0.5} {ycen 0.5} {oor 0} } {
    # First determine the size of the new image, so that no pixels of the
    # original image are cut-off.
    set center [list [expr {-1.0 * $xcen}] [expr {-1.0 * $ycen}]]

    # Build the transformation matrix to do the rotation:
    # First shift the image so that the intended center of rotation
    # coincides with the coordinate origin.
    poVec TfmBuildTrans2D $center tfmTrans1

    # Rotate the image.
    set rot [expr (-1.0 * $angle)]
    poVec TfmConcatRot2D $rot $tfmTrans1 tfmRot1

    # Shift the image so that the center of rotation moves back to its
    # original position.
    poVec VecScale2D -1 $center center2
    poVec TfmConcatTrans2D $center2 $tfmRot1 tfm

    # Transform the 4 border points of the image with the transformation matrix
    # to get the correct size for the new image.
    $srcimg GetImgInfo w h a g
    # puts "IN: $w $h $a"
    set p(1) [list 0 0]
    set p(2) [list 0 1]
    set p(3) [list 1 0]
    set p(4) [list 1 1]
    poVec TfmApply2D $p(1) $tfm sp(1)
    poVec TfmApply2D $p(2) $tfm sp(2)
    poVec TfmApply2D $p(3) $tfm sp(3)
    poVec TfmApply2D $p(4) $tfm sp(4)
#    puts "$p(1) -> $sp(1)"
#    puts "$p(2) -> $sp(2)"
#    puts "$p(3) -> $sp(3)"
#    puts "$p(4) -> $sp(4)"

    set wmin 1
    set wmax 0
    set hmin 1
    set hmax 0
    for { set i 1 } { $i <= 4 } { incr i } {
        set wmin [_Min $wmin [lindex $sp($i) 0]]
        set wmax [_Max $wmax [lindex $sp($i) 0]]
        set hmin [_Min $hmin [lindex $sp($i) 1]]
        set hmax [_Max $hmax [lindex $sp($i) 1]]
    }
    puts "wmin/wmax = $wmin $wmax"
    puts "hmin/hmax = $hmin $hmax"
    set sw [expr int (($wmax - $wmin) * $w + 1.0)]
    set sh [expr int (($hmax - $hmin) * $h + 1.0)]
    puts "sw/sh = $sw $sh"

    set sa [expr {double ($sw)/ double($sh)}]
#puts "OUT: $sw $sh $sa"

    poVec TfmBuildTrans2D $center tfmFrom

    set scale [list [expr double ($sw)/double ($w)] [expr double ($sh)/double ($h)]]
    poVec TfmConcatScale2D $scale $tfmFrom tfmTo
    poVec TfmCopy2D $tfmTo tfmFrom

    set rot [expr (-1.0 * $angle)]
    poVec TfmConcatRot2D $rot $tfmFrom tfmTo
    poVec TfmCopy2D $tfmTo tfmFrom

#    set scale [list [expr double ($w)/double ($sw)] [expr double ($h)/double ($sh)]]
#    poVec TfmConcatScale2D $scale $tfmFrom tfmTo
#    poVec TfmCopy2D $tfmTo tfmFrom

    poVec VecScale2D -1 $center center2
    poVec TfmConcatTrans2D $center2 $tfmFrom tfmTo
    poVec TfmCopy2D $tfmTo tfmFrom

    poVec TfmCopy2D $tfmFrom tfm

    set destimg [poImage NewImage $sw $sh $sa $g]
    $destimg Blank

    # Finally, rotate the source image according to the transformation matrix.
poImgTransform $srcimg $destimg $tfm $oor
#    poImgRotate $srcimg $destimg $angle $xcen $ycen $oor

    # Return the new rotated image.
    return $destimg
}
