home *** CD-ROM | disk | FTP | other *** search
- /*
- ***********************************************************************
- *
- * Displaying a full-index-color image as a custom dialog item
- *
- ***********************************************************************
- */
-
- #include "image.h"
- #include "ImageViews.h"
- #include <Palettes.h>
-
- // Kind of a formal constructor, merely an initialization
- ImageView::ImageView(const IMAGE& _image)
- : OffScreenBuffer(_image,elevation_color_CLUT_id), image(_image),
- viewer_pos(110,128, 1<<14,0)
- {
- // Move pixels from IMAGE to a off-screen pixmap
- // Note that the rows of pixmap may be padded, by
- // as much as off_to_next_row bytes. So we need
- // to skip over the padding as we move pixels row
- // by row
- class ImageToPixMap : public PixelPrimAction
- {
- PixMapHandle pixmap;
- unsigned char * pixp;
- const card height, width, off_to_next_row;
- card curr_row, curr_col;
- void operation(GRAY& pixel) // put a pixel to pixmap[row,col]
- {
- *pixp++ = pixel;
- if( ++curr_col >= width )
- curr_col = 0, curr_row++, pixp += off_to_next_row;
- }
- public:
- ImageToPixMap(OffScreenBuffer& buffer)
- : pixmap(buffer.get_pixmap()),
- height(buffer.height()),
- width(buffer.width()),
- off_to_next_row(buffer.bytes_per_row()-buffer.width()),
- curr_row(0), curr_col(0)
- {
- assert( LockPixels(pixmap) );
- pixp = (unsigned char *)GetPixBaseAddr(pixmap);
- }
- ~ImageToPixMap(void)
- {
- assert( curr_row == height && curr_col == 0 );
- UnlockPixels(pixmap); pixmap = 0;
- }
- };
- ((IMAGE&)image).apply(ImageToPixMap(*this));
- }
-
- // This is a real constructor, but called late
- void ImageView::bind(const ModelessDialog& the_dialog, const int item_no)
- {
- UserItem::bind(the_dialog,item_no);
- PmForeColor(255);
- }
-
- // Draw a plane mark on the map indicating the view point
- // and the gaze direction
- // We assume the the grafport is set up properly
- // and that |g| = 1<<14
- void ImageView::draw_mark(void)
- {
- PenSize(1,1);
- MoveTo(viewer_pos.xe - (viewer_pos.gx>>13) + rect.left, // draw a vector in the direction
- viewer_pos.ye - (viewer_pos.gy>>13) + rect.top); // of a gaze
- Line(viewer_pos.gx>>11,viewer_pos.gy>>11);
- MoveTo(viewer_pos.xe - (viewer_pos.gy>>13) + rect.left, // draw a crossbar in the perpendicular
- viewer_pos.ye + (viewer_pos.gx>>13) + rect.top); // direction, that is, (gy,-gx)
- Line(viewer_pos.gy>>12,-(viewer_pos.gx>>12));
- }
-
- // Erase the mark of the plane off the map
- // by redrawing a corresponding rectange of the map
- void ImageView::undraw_mark(void)
- {
- Rect map_rect = {viewer_pos.ye-4,viewer_pos.xe-4,viewer_pos.ye+4,viewer_pos.xe+4};
- Rect where_rect = map_rect;
- where_rect.left += rect.left; where_rect.right += rect.left;
- where_rect.top += rect.top; where_rect.bottom += rect.top;
- OffScreenBuffer::draw(where_rect,map_rect);
- }
-
- // Initialize the observer's position and prepare for
- // circling aroung (by setting up the radius and
- // computing the center)
- // Note, vector g is perpendicular to the radius,
- // so radius vector is r=(gy,-gx). Then the center
- // C = E - r = (xe-radius*gy/|g|,ye+radius*gx/|g|)
- ViewerPosition::ViewerPosition(const int _xe, const int _ye, const int _gx, const int _gy)
- : xe(_xe), ye(_ye), gx(_gx), gy(_gy), g_per_rad((float)g_unit/flying_radius)
- {
- assert( gx*gx + gy*gy == g_unit*g_unit ); // Just to test our assumptions
- xc = xe - ((flying_radius * gy)>>14);
- yc = ye + ((flying_radius * gx)>>14);
- rx = xe - xc; ry = ye - yc;
- }
-
-
- // Perform one step of circling around, that is, rotate a vector
- // (xe-xc, ye-yc) to become
- // (1 -s ) (xe-xc)
- // (s 1-s^2 ) (ye-yc)
- // where 's' is a rotation constant (sine of the rotation angle
- // to be precise). It can be any number within [-1,1], though
- // it's better be small. Note that the determinant of the matrix
- // is exactly 1, no matter what s is. That is, even repeated application
- // of the matrix would not cause size distortions.
- // The formulas could be written in a better way:
- // if r' = rotate(r=E-C) then
- // r'x = rx - s*ry
- // r'y = s*r'x + ry
- // The gaze vector has to be corrected, too
- // g' = |g|*(-r'y,r'x)/r
- // to keep it orthogonal to r'=CE' (note, |r'|=|r|=r)
- // Yes, we have to use floting point here. But it's not that big
- // deal on PowerPC. Animation is way too fast anyway.
- void ViewerPosition::do_one_turn(void)
- {
- const float s = 0.04;
- rx -= s*ry; // Do rotation
- ry += s*rx; // note, it's new rx we're using here, see formulas above
- gx = (int)(-ry*g_per_rad);
- gy = (int)(rx*g_per_rad);
- xe = xc + (int)rx; ye = yc + (int)ry;
- // _error("correction factor %d, rpx %d, rpy %d, new %d, %d",corr_factor,rpx,rpy,new_xe,new_ye);
-
- if( gx < 2 && gx > -2 ) // Do recovery to compensate for the lost precision
- {
- gx = 0;
- if( gy > 0 )
- gy = g_unit, xe = xc + flying_radius, rx = flying_radius;
- else
- gy = -g_unit, xe = xc - flying_radius, rx = -flying_radius;
- ye = yc, ry = 0;
- }
- }
-
-