Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
25
Добавлен:
10.12.2013
Размер:
21 Кб
Скачать
{ ============================= TURTLE ================================

  This unit contains procedures and functions that implement turtle
  graphics. Turtle graphics is a model for specifying relative movements
  of an imaginary pointer whose direction, color, visibility, and other
  attributes are given default values using turtle functions. To use the
  turtle module, specify Turtle in the USES section of your program.

  The following routines are public :

	InitTurtle      - Initiate turtle graphics
	Home            - Reset turtle defaults
	PenDown         - Set pen visibility
	PenState        - Get pen visibility
	FillState       - Get fill state
	FillOn          - Set fill state
	PenColor        - Set pen color index
	GetPenColor     - Get pen color index
	BorderColor     - Set border color index
	GetBorderColor  - Get border color index
	Turn            - Set direction relative to current
	TurnTo          - Set absolute direction
	GetTurn         - Get current turn angle
	Move            - Move in current direction
	MoveTo          - Move to absolute location
	Poly            - Draw a polygon
	Circle          - Draw a circle with center at current location
	Ellipse         - Draw an ellipse with center at current location
	Rectangle       - Draw a rectangle with center at current location
	ImageSize       - Get size of rectangle with top-left origin
	GetImage        - Get rectangular image with top-left origin
	PutImage        - Put rectangular image with top-left origin
	Paint           - Fill from the current location to border
	NextColorIndex  - Rotate to next color index
	NextColorValue  - Rotate to next color value
	OnScreen        - Report whether current location is on screen
	SetTWindow      - Set turtle window (viewport)
	GetTWindow      - Get turtle window (viewport)
	TUnit           - Get current turtle unit
	TCurX           - Get current x coordinate
	TCurY           - Get current y coordinate
	TurtleStat      - Get return status of turtle routine
}

UNIT turtle;

{ ===========================================================}
							INTERFACE
{ ===========================================================}

USES
	MSGraph;

TYPE
	degree = -360..360;
	twindow = RECORD
		Left, Right, Top, Bottom : Integer;
		END;

CONST
	{ Constants }
	circumference     = 360;
	halfcircumference = 180;
	maxitem = 20;
	default = -1;
	limited = 0;

	{ Screen Aspect - Adjust for your screen }
	TRatioYX : Real = 1.39;

VAR
	{ Global variables }
	tmaxx, tmaxy : Double;
	tcolorindexes : Integer;
	tcolorvalues  : Integer;

	{ ===================== Public Routines ====================== }

	PROCEDURE Move( dxy : Double );
	PROCEDURE moveto( x, y : Double );
	PROCEDURE turnto( cur_angle : degree );
	PROCEDURE turn( cur_angle : degree );
	PROCEDURE poly( n_sides : Integer; side_len : Double );
	FUNCTION  getturn : degree;

	PROCEDURE pendown( pen_on  : Boolean );
	PROCEDURE fillon( fill_on : Boolean );
	FUNCTION  penstate  : Boolean;
	FUNCTION  fillstate : Boolean;

	FUNCTION  initturtle : Boolean;
	PROCEDURE home;

	PROCEDURE bordercolor( border : Integer );
	FUNCTION  getbordercolor : Integer;
	PROCEDURE pencolor( cur_index : Integer );
	FUNCTION  getpencolor : Integer;

	FUNCTION  nextcolorindex( cur_index : Integer ) : Integer;
	PROCEDURE nextcolorvalue( action : Integer );

	PROCEDURE circle( r : Double );
	PROCEDURE ellipse( w, h : Double );
	PROCEDURE rectangle( w, h : Double );

	FUNCTION  imagesize( w, h : Double ) : LongInt;
	PROCEDURE getimage( w, h : Double; VAR buf );
	PROCEDURE putimage( VAR buf; Act : Integer );
	PROCEDURE paint;

	FUNCTION  onscreen( xcur, xMax, ycur, yMax : Double ) : Boolean;

	PROCEDURE settwindow( Left, Top, Right, Bottom : Integer );
	PROCEDURE gettwindow( VAR Left, Top, Right, Bottom : Integer );

	FUNCTION  tunit : Double;
	FUNCTION  tcurx : Double;
	FUNCTION  tcury : Double;

	FUNCTION  turtlestat : Boolean;

{ ===========================================================}
						 IMPLEMENTATION
{ ===========================================================}

{ Record for configuration and other data - hidden from user }
TYPE
	turtlerec = RECORD
		stat            : Boolean;
		pen_on          : Boolean;
		fill_on         : Integer;
		yunit           : Double;
		xcur, ycur      : Double;
		cur_index       : Integer;
		border_col      : Integer;
		cur_angle       : degree;
		palette_on      : Boolean;
		x_left, x_right : Integer;
		y_top, y_bottom : Integer
		END;

VAR
	long_colors : ARRAY[0..255] OF LongInt;
	vc : _VideoConfig;
	tc : turtlerec;


{ ======================== RGB ==============================
  Creates a composite red-green-blue color value from three
  byte values.
}
FUNCTION rgb( r, g, b : Word ) : LongInt;
BEGIN
	rgb := LongInt( LongInt( LongInt( b SHL 8 ) OR g ) SHL 8 ) OR r;
END; { rgb }

{ ======================== TurtleStat ============================
  Returns the completion status of various turtle routines.
}
FUNCTION turtlestat : Boolean;
BEGIN
	turtlestat := tc.stat;
END;  { turtlestat }

{ ======================== OnScreen ==============================
  Checks that a specified point is on the screen.
}
FUNCTION onscreen( xcur, xMax, ycur, yMax : Double ) : Boolean;
BEGIN
	IF  ((tc.xcur < -tmaxx) OR (tc.xcur > tmaxx)) OR
		((tc.ycur < -tmaxy) OR (tc.ycur > tmaxy)) THEN
		onscreen := False
	ELSE
		onscreen := True;
END; { onscreen }

{ ============================ initturtle ====================================
  Initializes all turtle defaults. This function should be called at
  least once (after _SetVideoMode and _GetVideoConfig) and additionally
  after any change to a new graphics mode.

  Params: vc - videoconfig record

  Return: True or False

  Uses:   tc record variable, long_colors array
}
FUNCTION initturtle : Boolean;
CONST
	Mode : Integer = -1;    { Impossible value }

VAR
	i, incr : Integer;
	r, g, b  : Word;

BEGIN
	_GetVideoConfig( vc  );

	{ Terminate if not graphics mode. }
	IF (vc.NumXPixels = 0) THEN
		BEGIN
		initturtle := False;
		Exit;
		END
	ELSE
		initturtle := True;

	{ If mode has changed, set window coordinates. }
	IF (Mode <> vc.Mode) THEN
		BEGIN
		Mode := vc.Mode;
		tc.x_left := 0;
		tc.y_top := 0;
		tc.x_right := vc.NumXPixels - 1;
		tc.y_bottom := vc.NumYPixels - 1;
		END;

	{ Set palette flag. }
	CASE vc.Adapter OF
		_MDPA,
		_CGA,
		_OCGA,
		_HGC :
			tc.palette_on := False;
		ELSE
			tc.palette_on := True
	END; { CASE }

	CASE vc.Mode OF
		_HResBW,
		_HercMono,
		_EResNoColor,
		_OResColor,
		_VRes2Color :
			BEGIN
			tcolorvalues := 0;
			tcolorindexes := 2;
			home;
			Exit;
			END;
									{ Active bits in this order:          }
		_MRes256Color :
			BEGIN                   { ???????? ??bbbbbb ??gggggg ??rrrrrr }
			incr := 12;
			tcolorvalues := 125;
			tcolorindexes := 125;
			END;

		_EResColor :
			IF (vc.Memory = 64) THEN
				BEGIN               { ???????? ??Bb???? ??Gg???? ??Rr???? }
				incr := 32;
				tcolorvalues := 16;
				tcolorindexes := 4;
				END
			ELSE
				BEGIN               { ???????? ??bb???? ??gg???? ??rr???? }
				incr := 16;
				tcolorvalues := 64;
				tcolorindexes := 16;
				END;

		_VRes16Color:
			BEGIN                   { ???????? ??bb???? ??gg???? ??rr???? }
			incr := 16;
			tcolorvalues := 64;
			tcolorindexes := 16;
			END;

		_MRes4Color,
		_MResNoColor:
			BEGIN                   { ???????? ??Bb???? ??Gg???? ??Rr???? }
			incr := 32;
			tcolorvalues := 16;
			tcolorindexes := 4;
			END;

		_MRes16Color,
		_HRes16Color :
			BEGIN                     { ???????? ??????Bb ??????Gg ??????Rr }
			incr := 32;
			tcolorindexes := 16;
			tcolorvalues := 16;
			END
		END; { CASE }

	{ Fill palette arrays. }
	i := 0;
	b := 0;
	WHILE (b < 64) DO
		BEGIN
		g := 0;
		WHILE (g < 64) DO
			BEGIN
			r := 0;
			WHILE (r < 64) DO
				BEGIN
				long_colors[i] := rgb( r, g, b );
				{ Special case: 6 bits for 16 colors (RGBI).
				  If both bits are on for any color, intensity is on.
				  If one bit is set for a color, that color is on.
				}
				IF (incr = 32) THEN
					long_colors[i+8] := long_colors[i] OR ( long_colors[i] SHR 1 );
				Inc( i );
				Inc( r, incr );
				END;
			Inc( g, incr );
			END;
		Inc( b, incr );
		END;

	long_colors[tcolorvalues - 1] := _BrightWhite;
	nextcolorvalue( default );

	home;
END; { initturtle }

{ ============================== home ==================================
  Resets turtle defaults. This procedure can be called if you have
  not changed the video mode, but you want to put the turtle back in
  the center of the window and restore all defaults. For example, you can
  change the absolute window corners and then call it to set a new
  turtle window.

  Params: vc - pointer to videoconfig structure

}
PROCEDURE home;
VAR
	prev : LongInt;

BEGIN

	_SetViewport( tc.x_left, tc.y_top, tc.x_right, tc.y_bottom );
	tmaxy := 500.0;
	tmaxx := tmaxy * TRatioYX;

	_SetWindow( False, -tmaxx, -tmaxy, tmaxx, tmaxy );
	IF (_GrStatus <> _GrOk) THEN
		BEGIN
		tc.stat := False;
		Exit;
		END;

	tc.xcur := 0.0;
	tc.ycur := 0.0;
	_MoveTo_w( tc.xcur, tc.ycur );
	turnto( 0 );
	pendown( True );
	fillon( False );

	prev := _RemapPalette( tcolorindexes - 1, _BrightWhite );
	bordercolor( tcolorindexes - 1 );
	pencolor( tcolorindexes - 1 );

END; { home }

{ ============================== pendown ==================================

  Sets the visibility of the pen used by Move and MoveTo.

  Params: pen_on - True or False
}
PROCEDURE pendown( pen_on : Boolean );
BEGIN
	tc.pen_on := pen_on;
END;  { pendown }

{ ============================== penstate ==================================
  Gets the visibility of the pen used by Move and MoveTo.

  Return: True or False
}
FUNCTION penstate : Boolean;
BEGIN
	penstate := tc.pen_on;
END;  { penstate }

{ ============================== fillon ==================================
  FillOn - Sets the state of Filling figures such as Rectangle,
  Circle, and Ellipse.

  Params: fill_on - True or False
}
PROCEDURE fillon( fill_on : Boolean );
BEGIN
	IF fill_on THEN
		tc.fill_on := _GFillInterior
	ELSE
		tc.fill_on := _GBorder;
END;  { fillon }

{ ============================== fillstate ==================================
  Gets the state of Painting figures such as Rectangle,
  Circle, and Ellipse.

  Return: True or False
}
FUNCTION fillstate : Boolean;
BEGIN
	IF (tc.fill_on = _GBorder) THEN
		fillstate := False
	ELSE
		fillstate := True;
END;  { fillstate }

{ ============================== pencolor ==================================
 Sets the color index of the pen.

  Params: cur_index - a color index
}
PROCEDURE pencolor( cur_index : Integer );
BEGIN
	tc.cur_index := cur_index;
	_SetColor( tc.cur_index );
END;  { pencolor }

{ ============================== getpencolor  ===========================
  Gets the color index of the pen.

  Return: current color index
}
FUNCTION getpencolor : Integer;
BEGIN
	getpencolor := tc.cur_index;
END;  { getpencolor }

{ ============================== bordercolor  ===========================
  BorderColor - Sets the color index of the border that will be recognized
  by fills.

  Params: border_col - any color index
}
PROCEDURE bordercolor( border : Integer );
BEGIN
	tc.border_col := border;
END;   { bordercolor }

{ ========================== getbordercolor ============================
  Gets the color index of the border that will be recognized by fills.

  Return: current border color index
}
FUNCTION getbordercolor : Integer;
BEGIN
	getbordercolor := tc.border_col;
END;  { getbordercolor }

{ ============================== turn ==================================
  Sets a new direction relative to the current direction.

  Params: cur_angle - a positive (clockwise) or negative (counterclockwise)
			angle in degrees
}
PROCEDURE turn( cur_angle : degree );
BEGIN
	tc.cur_angle := (tc.cur_angle + cur_angle) MOD circumference;
END;  { turn }


{ ============================== turnto ==================================
  Sets a new absolute direction.

  Params: cur_angle - a positive (clockwise) or negative (counterclockwise)
			angle in degrees (0 points to 12 o'clock)

  Uses:   tc
}
PROCEDURE turnto( cur_angle : degree );
BEGIN
	IF (cur_angle < 0) THEN
		tc.cur_angle := circumference - (cur_angle MOD circumference)
	ELSE
		tc.cur_angle := cur_angle MOD circumference;
END;  { turnto }


{ ============================== getturn ==================================
  Gets the current absolute angle.

  Return: Angle between 0 and 359
}
FUNCTION getturn : degree;
BEGIN
	getturn := tc.cur_angle;
END;  { getturn }


{ ============================== move ==================================
  Moves from the current position in the current direction for a
  specified distance. A line is drawn if the pen is down. The current
  position is reset to the destination.

  Params: dxy - difference between current xy and new xy
}
PROCEDURE move( dxy : Double );
VAR
	dx, dy, angt : Double;

BEGIN
	angt := (tc.cur_angle - 90) * (Pi / halfcircumference);
	dx := dxy * Cos( angt );
	dy := dxy * Sin( angt );

	IF tc.pen_on THEN
		_LineTo_w( tc.xcur + dx, tc.ycur + dy )
	ELSE
		_MoveTo_w( tc.xcur + dx, tc.ycur + dy );

	tc.xcur := tc.xcur + dx;
	tc.ycur := tc.ycur + dy;

	IF onscreen( tc.xcur, tmaxx, tc.ycur, tmaxy ) THEN
		tc.stat := True
	ELSE
		tc.stat := False;
END;  { move }

{ ============================== moveto ==================================
  Moves from the current position to a specified position. A
  line is drawn if the pen is down. The current position is reset to the
  destination. The current direction is not changed.

  Params: x and y - destination position
}
PROCEDURE moveto( x, y : Double );
BEGIN
	IF tc.pen_on THEN
		_LineTo_w( x, y )
	ELSE
		_MoveTo_w( x, y );
	tc.xcur := x;
	tc.ycur := y;
	IF onscreen( tc.xcur, tmaxx, tc.ycur, tmaxy ) THEN
		tc.stat := True
	ELSE
		tc.stat := False;
END;  { moveto }

{ ============================== poly ==================================
  Draws a polygon.

  Params: n_sides - count of polygon sides
		  side_len - distance of each side

  Return: 0 if any part of polygon is off screen, nonzero if on screen
}
PROCEDURE poly( n_sides : Integer; side_len : Double );
VAR
	i, angle   : Integer;
	on, pen_on : Boolean;

BEGIN
	on := True;
	pendown( True );
	pen_on := turtlestat;
	angle := Round( 360 / n_sides );

	FOR i := 1 TO n_sides DO
		BEGIN
		Move( side_len );
		on := on AND turtlestat;
		turn( angle );
		END;

	pendown( pen_on );
	tc.stat := on;
END;  { poly }

{ ============================== nextcolorindex ========================
  Rotate to next color index. First attribute (normally background) and
  last attribute (white) are skipped.

  Params: cur_index - Specify DEFAULT to use color index from last call,
			or specify a new color to rotate from

  Return: rotated color index

  Uses:   tc
}
FUNCTION nextcolorindex( cur_index : Integer ) : Integer;
CONST
	prev_index : Integer = 0;

BEGIN
	{ Assign new current value if supplied. }
	IF (cur_index <> default) THEN
		prev_index := cur_index;

	{ Toggle for 2-color modes; rotate for multi-color modes. }

	IF (tcolorindexes = 2) THEN
		BEGIN
		prev_index := NOT prev_index;
		nextcolorindex := prev_index;
		END
	ELSE
		BEGIN
		Inc( prev_index );
		prev_index := prev_index MOD (tcolorindexes - 1);
		nextcolorindex := prev_index;
		END;
END; { nextcolorindex }

{ =========================== nextcolorvalue ===========================
  Rotate to next color value for adapters (EGA and higher) that support
  remappable palettes.

  Params: action - DEFAULT (rotate all) or LIMITED (rotate first
				   14 only)
}
PROCEDURE nextcolorvalue( action : Integer );
CONST
	cur_val   : LongInt = 1;
	cur_index : Integer = 1;
VAR
	temp_val, i : Integer;
	prev    : LongInt;

BEGIN

	{ Ignore modes with no palette values. }

	IF ((NOT tc.palette_on) OR (tcolorvalues = 0)) THEN
		Exit;

	{ Increment and rotate color value index. }
	Inc( cur_val );
	temp_val := cur_val MOD (tcolorvalues - 2) + 1;

	{ DEFAULT - Remap all color indexes, 14 at a time. For most modes,
	  this is all the indexes except first and last. For 256-color
	  mode, rotating all available indexes would be too slow.
	}
	IF (action = default) THEN
		FOR i := 1 TO 14 DO
			BEGIN
			prev := _RemapPalette( (cur_index MOD (tcolorindexes - 2 ) ) + 1,
								   long_colors[(temp_val MOD (tcolorvalues - 2)) + 1] );
			Inc( cur_index );
			Inc( temp_val );
			END
	{ LIMITED - Rotate only the first 14 color indexes. }
	ELSE
		FOR i := 1 TO 14 DO
			BEGIN
			prev := _RemapPalette( i,
						long_colors[(temp_val MOD (tcolorvalues - 1)) + 1] );
			Inc( temp_val );
			END;

END; { nextcolorvalue }

{ =============================== circle ===========================
  Put a circle with radius <r> at current location.
}
PROCEDURE circle( r : Double );
BEGIN
	_Ellipse_w( tc.fill_on, tc.xcur - r, tc.ycur - r,
						   tc.xcur + r, tc.ycur + r );
	tc.stat := (_GrStatus = _GrOk);
END;  { circle }

{ ============================== ellipse==================================
  Puts  an ellipse with width <w> and height <h> at current location.
}
PROCEDURE ellipse( w, h : Double );
BEGIN
	_Ellipse_w( tc.fill_on, tc.xcur - (w / 2), tc.ycur - (h / 2),
						   tc.xcur + (w / 2), tc.ycur + (h / 2) );
	tc.stat := (_GrStatus = _GrOk);
END;  { ellipse }

{ ============================== rectangle ==================================
  Puts the center of a rectangle with width <w> and height <h>
  at current location.
}
PROCEDURE rectangle( w, h : Double );
BEGIN
	_Rectangle_w( tc.fill_on, tc.xcur - (w / 2), tc.ycur - (h / 2),
							 tc.xcur + (w / 2), tc.ycur + (h / 2) );
	tc.stat := (_GrStatus = _GrOk);
END;  { rectangle }

{ ============================== imagesize ============================
  Gets the size of an image with width <w> and height <h>
  with left-top at current location.
}
FUNCTION imagesize( w, h : Double ) : LongInt;
BEGIN
	imagesize := _ImageSize_w( tc.xcur,     tc.ycur,
							   tc.xcur + w, tc.ycur + h );
	tc.stat := (_GrStatus = _GrOk);
END;   { imagesize }

{ ============================== getimage ==============================
  Gets an image with width <w> and height <h> with left-top
  at current location. Returns image buffer.
}
PROCEDURE getimage( w, h : Double; VAR buf );
BEGIN
	_GetImage_w( tc.xcur, tc.ycur, tc.xcur + w, tc.ycur + h, buf );
	tc.stat := (_GrStatus = _GrOk);
END;  { getimage }

{ ============================== putimage ==================================
  Puts the top-left corner of a specified image at current location
  using a specified action (_GPSET, _GPRESET, _GAND, _GOR, _GXOR).
}
PROCEDURE putimage( VAR buf; act : Integer );
BEGIN
	_PutImage_w( tc.xcur, tc.ycur, buf, act );
	tc.stat := (_GrStatus = _GrOk);
END;   { putimage }

{ ============================== paint ================================
  Paints from the current location to the border.
}
PROCEDURE paint;
BEGIN
	_FloodFill_w( tc.xcur, tc.ycur, tc.border_col );
	tc.stat := (_GrStatus = _GrOk);
END;  { paint }

{ ============================== gettwindow ==============================
  Returns the coordinates of the turtle window (viewport).
}
PROCEDURE gettwindow( VAR Left, Top, Right, Bottom : Integer );
BEGIN
	Left    := tc.x_left;
	Right   := tc.x_right;
	Top     := tc.y_top;
	Bottom  := tc.y_bottom;
END;    { gettwindow }

{ ============================== settwindow ==============================
  Sets the coordinates of the turtle window (viewport) .
}
PROCEDURE settwindow( Left, Top, Right, Bottom : Integer );
BEGIN
	tc.x_left   := Left;
	tc.x_right  := Right;
	tc.y_top    := Top;
	tc.y_bottom    := Bottom;
	_SetViewport( tc.x_left, tc.y_top, tc.x_right, tc.y_bottom );
	tc.stat := (_GrStatus = _GrOk);

END;  { settwindow }

{ ============================== tunit ==============================
  Returns the value of a turtle unit.
}
FUNCTION tunit : Double;
VAR
	xy1, xy2  : _WXYCoord;

BEGIN
	_GetWindowCoord( 1, 1, xy1 );
	_GetWindowCoord( 1, 2, xy2 );
	tunit := xy2.Wy - xy1.Wy;
END;   { tunit }

{ ============================== tcurx ==============================
  Returns the current x coordinate.
}
FUNCTION tcurx : Double;
BEGIN
	tcurx := tc.xcur;
END;  { tcurx }

{ ============================== tcury ==============================
  Returns the current y-coordinate.
}
FUNCTION tcury : Double;
BEGIN
	tcury := tc.ycur;
END;  { tcury }

END. { UNIT }



Соседние файлы в папке SAMPLES