i21edu/Mod/Черепашка.odc

MODULE  i21eduЧерепашка;
	IMPORT  Log := StdLog,  In := i21sysIn,  Math,
		Views, Stores, Controllers, Ports, Files, Converters, Properties, HostCmds, 
		Windows, Documents, W := i21sysWindows, Services, Dialog, Fonts, National;

(**
	project	= "Informatika-21"
	organization	= "Informatika-21"
	contributors	= "Fyodor Tkachov, INR RAS"
	version	= "2"
	copyright	= ""
	license	= ""
	changes	= "
	- YYYYMMDD, nn, ...
	- 20111204, Ф.В.Ткачев
		информация о том, опущено ли перо, и о цвете -- теперь в глобальных переменных; с рисунком не сохраняется;
		экспортирована глобальная переменная пероОпущено;
		теперь картинка очищается по дв. клику без Ctrl, а "рисовать сюда" -- с Ctrl.
	"
	issues	= "
	- ...
	"
**)
	
	CONST
		version = 2;
		размер = 100*Ports.mm;
	
	TYPE
		Рисунок = POINTER TO RECORD ( Views.View )
			(* параметры окна *)
			w, h: INTEGER;
			(* позиция начала координат (точки зеро) в окне, в пикселях: *)
			shiftX, shiftY: INTEGER;
			(* состояние черепашки: *)
			позиция: Точка; (* текущая позиция *)
			угол: REAL; (* куда смотрит, в градусах *)
			(* список нарисованных сегментов: *)
			первая: Линия;
			справка: BOOLEAN;
		END;
		
		Линия = POINTER TO RECORD
			т0, т1: Точка;
			цвет: Ports.Color;
			след: Линия
		END;
		
		Точка = RECORD (* пикселя *)
			x, y: REAL
		END;
		
	VAR
		pi180: REAL;
		loc: Files.Locator;
		рисунок: Рисунок; (* рисунок в фокусе *)
		пероОпущено-: BOOLEAN;
		чернила: Ports.Color; (* цвет рисования *)
	
	
	PROCEDURE Очисть ( рис: Рисунок );
	BEGIN
		рис.позиция.x := 0;
		рис.позиция.y := 0;
		рис.угол := 0;
		рис.первая := NIL;
		рис.справка := TRUE;
		Views.RevalidateView( рис );
		(* Views.SetDirty( рис ); *)
		Views.Update( рис, Views.keepFrames );
	END Очисть;

	PROCEDURE РазмерПикселя ( f: Ports.Frame ): INTEGER; (* из TPGraphics*)
		VAR  res: INTEGER;
	BEGIN
		res := MAX( f.unit, 9525 ); (* на экране в пикселях, при печати в неких условных единицах, чтобы не было слишком мелко *)
		RETURN  res
	END РазмерПикселя;
	
	PROCEDURE Отобразить ( т, зеро: Точка;  пиксель: INTEGER;  OUT x, y: INTEGER );
	(* зеро = сдвиг начала координат в окне в пикселях *)
	BEGIN
		x := SHORT( ENTIER( ( зеро.x + т.x ) * пиксель ) );  
		y := SHORT( ENTIER( ( зеро.y - т.y ) * пиксель ) );
	END Отобразить;
	
	PROCEDURE ( VAR т: Точка ) Externalize ( wr: Stores.Writer ), NEW;
	BEGIN
		wr.WriteReal( т.x );  wr.WriteReal( т.y );
	END Externalize;
	
	PROCEDURE ( VAR т: Точка ) Internalize ( rd: Stores.Reader ), NEW;
	BEGIN
		rd.ReadReal( т.x );  rd.ReadReal( т.y );
	END Internalize;
	
	
	PROCEDURE ( л: Линия ) Рисовать ( f: Ports.Frame;  зеро: Точка;  пиксель: INTEGER ), NEW;
		VAR  x0, y0, x1, y1: INTEGER;
	BEGIN
		Отобразить( л.т0, зеро, пиксель, x0, y0 );
		Отобразить( л.т1, зеро, пиксель, x1, y1 );
		f.DrawLine( x0, y0, x1, y1, 0, л.цвет )
	END Рисовать;
	
	PROCEDURE ( л: Линия ) Externalize ( wr: Stores.Writer ), NEW;
	BEGIN
		л.т0.Externalize( wr );  л.т1.Externalize( wr );  wr.WriteInt( л.цвет );
	END Externalize;
	
	PROCEDURE ( л: Линия ) Internalize ( rd: Stores.Reader ), NEW;
	BEGIN
		л.т0.Internalize( rd );  л.т1.Internalize( rd );  rd.ReadInt( л.цвет );
	END Internalize;
	
	
	PROCEDURE РисоватьОси ( f: Views.Frame;  зеро: Точка;  пиксель, w, h: INTEGER );
		VAR  цвет: INTEGER;  x, y: INTEGER;  p0: Точка;
	BEGIN
		p0.x := 0;  p0.y := 0;
		Отобразить ( p0, зеро, пиксель, x, y );
		цвет := Ports.grey6;
		f.DrawLine( x, 0, x, h, 0, цвет );
		f.DrawLine( 0, y, w, y, 0, цвет );
	END РисоватьОси;
	
	PROCEDURE РисоватьЧерепаху ( f: Views.Frame;  зеро: Точка;  пиксель: INTEGER;  угол: REAL );
		CONST  r = 15;
		VAR  d, n, p0, p1, p2, p3: Точка;  rp: INTEGER;
			рис: Рисунок;  цвет: INTEGER;  p: ARRAY 10 OF Ports.Point;
	BEGIN
		цвет := Ports.RGBColor( 180, 180, 0 );
		рис := f.view( Рисунок );
		d.x := r * Math.Cos( угол * pi180 );
		d.y := r * Math.Sin( угол * pi180 );  
		n.x := d.y / 2;  n.y := - d.x / 2;
		p0.x := рис.позиция.x;
		p0.y := рис.позиция.y;
		p2.x := p0.x + d.x * 0.65;
		p2.y := p0.y + d.y * 0.65;
		p1.x := p0.x - d.x/2.5 + n.x;
		p1.y := p0.y - d.y/2.5 + n.y;
		p3.x := p0.x - d.x/2.5 - n.x;
		p3.y := p0.y - d.y/2.5 - n.y;
		Отобразить ( p0, зеро, пиксель, p[0].x, p[0].y );
		Отобразить ( p1, зеро, пиксель, p[1].x, p[1].y );
		Отобразить ( p2, зеро, пиксель, p[2].x, p[2].y );
		Отобразить ( p3, зеро, пиксель, p[3].x, p[3].y );
		(* панцирь: *)
		rp := r * пиксель * 5 DIV 11;
		f.DrawOval( p[0].x - rp, p[0].y - rp, p[0].x + rp, p[0].y + rp, -1, цвет );
		(* голова-ноги: *)
		f.DrawPath( p, 4, 0, Ports.black, Ports.closedPoly );
(*
		(* передние ножки *)
		p1.x := p0.x + d.x/4 + n.x*1.2;
		p1.y := p0.y + d.y/4 + n.y*1.2;
		p3.x := p0.x + d.x/4 - n.x*1.2;
		p3.y := p0.y + d.y/4 - n.y*1.2;
		Отобразить ( p1, зеро, пиксель, x2, y2 );
		Отобразить ( p3, зеро, пиксель, x3, y3 );
		f.DrawLine( x2, y2, x3, y3, 0, цвет );
*)
	END РисоватьЧерепаху;
	
	PROCEDURE ( рис: Рисунок ) HandlePropMsg- ( VAR p: Views.PropMessage );
	BEGIN
		WITH p: Properties.SizePref  DO
			p.fixedW := FALSE;
			p.fixedH := FALSE;
			p.w := рис.w;
			p.h := рис.h;
		| p: Properties.ResizePref  DO
			p.fixed := FALSE;
			p.horFitToWin := TRUE;
			p.verFitToWin := TRUE;
		| p: Properties.FocusPref  DO
			p.setFocus := TRUE
		ELSE
		END
	END HandlePropMsg;
	
	PROCEDURE GetSize ( v: Views.View;  OUT w, h: INTEGER );
		VAR  win: Windows.Window;
	BEGIN
		win := Windows.dir.First();
		WHILE ( win # NIL ) & ( win.doc.ThisView() # v ) DO
			win := Windows.dir.Next( win );
		END;
		ASSERT( win # NIL );
		win.GetSize( w, h );
	END GetSize;

	PROCEDURE SetSize ( v: Views.View;  w, h: INTEGER );
		VAR  win: Windows.Window;
	BEGIN
		win := Windows.dir.First();
		WHILE ( win # NIL ) & ( win.doc.ThisView() # v ) DO
			win := Windows.dir.Next( win );
		END;
		ASSERT( win # NIL );
		win.SetSize( w, h );
	END SetSize;

	PROCEDURE ( рис: Рисунок ) Restore* ( f: Views.Frame; l, t, r, b: INTEGER );
		VAR  w, h: INTEGER;  пиксель: INTEGER;  зеро: Точка;  л: Линия;
			x, y, col: INTEGER;  s: ARRAY 100 OF CHAR;  font: Fonts.Font;
	BEGIN
		рис.context.GetSize( w, h );
		рис.w := w;
		рис.h := h;
		пиксель := РазмерПикселя( f );
		зеро.x := w DIV 2 DIV пиксель + рис.shiftX; (* зеро всегда в центре *)
		зеро.y := h DIV 2 DIV пиксель + рис.shiftY;
		РисоватьОси( f, зеро, пиксель, w, h );
		РисоватьЧерепаху( f, зеро, пиксель, рис.угол );
		л := рис.первая;
		WHILE  л # NIL  DO
			л.Рисовать( f, зеро, пиксель );
			л := л.след
		END;
		
		IF рис.справка THEN
			x := Ports.mm DIV 2;
			col := Ports.grey50;
			font := Fonts.dir.This( Fonts.default, 8*Fonts.point, {}, Fonts.normal );
			y := h - 4 * 7*x;
			y := y + 7*x;
			s := "Очистить: двойной клик.";
			f.DrawString( x, y, col, s, font );
			y := y + 7*x;
			s := "Двигать рисунок: стрелки, Home, End.";
			f.DrawString( x, y, col, s, font );
			y := y + 7*x;
			s := "Рисовать сюда: Ctrl+дв. клик.  Открыть новый рис.: Shift+дв. клик.";
			f.DrawString( x, y, col, s, font );

			y := y + 7*x;
			s := "Спрятать/показать подсказку: h.";
			f.DrawString( x, y, col, s, font );
		END;
	END Restore;
	
	PROCEDURE ( рис: Рисунок ) Externalize- ( VAR wr: Stores.Writer );
		VAR  л: Линия;
	BEGIN
		wr.WriteVersion( version );
		wr.WriteInt( рис.w );  
		wr.WriteInt( рис.h );  
		wr.WriteInt( рис.shiftX );
		wr.WriteInt( рис.shiftY );
		рис.позиция.Externalize( wr );
		wr.WriteReal( рис.угол );
		wr.WriteBool( рис.справка );
		л := рис.первая;
		WHILE  л # NIL  DO
			wr.WriteBool( TRUE );
			л.Externalize( wr );
			л := л.след
		END;
		wr.WriteBool( FALSE );
	END Externalize;
	
	PROCEDURE ( рис: Рисунок ) Internalize- ( VAR rd: Stores.Reader );
		VAR  ver: INTEGER;  л, нов: Линия;  s: Stores.Store;  естьЕще: BOOLEAN;
			dummy0: BOOLEAN;  dummy1: INTEGER;
	BEGIN
		ASSERT( рис.первая = NIL, 20 );
		rd.ReadVersion( 1, version, ver );
		IF ~rd.cancelled THEN
			rd.ReadInt( рис.w );  
			rd.ReadInt( рис.h );  
			rd.ReadInt( рис.shiftX );  
			rd.ReadInt( рис.shiftY );  
			рис.позиция.Internalize( rd );
			rd.ReadReal( рис.угол );
			IF ver = 1 THEN
				rd.ReadBool( dummy0 );
				rd.ReadInt( dummy1 );
			END;
			rd.ReadBool( рис.справка );  рис.справка := TRUE;
			нов := NIL;
			rd.ReadBool( естьЕще );
			WHILE  естьЕще  DO
				NEW( л );  л.след := нов;  нов := л;
				л.Internalize( rd );
				rd.ReadBool( естьЕще );
			END;
			(* обращаем для аккуратности*)
			WHILE  нов # NIL  DO
				л := нов;  нов := нов.след;
				л.след := рис.первая;  рис.первая := л;
			END
		END;
	END Internalize;
	
	PROCEDURE ( рис: Рисунок ) CopyFromSimpleView- ( source: Views.View );
		VAR  нов, л, л1, первая: Линия;  n: INTEGER;
	BEGIN
		ASSERT( рис.первая = NIL, 20 );
		WITH source: Рисунок DO
			рис.w := source.w;
			рис.h := source.h;
			рис.shiftX := source.shiftX;
			рис.shiftY := source.shiftY;
			рис.позиция := source.позиция;
			рис.угол := source.угол;
			рис.справка := source.справка;
			первая := NIL;
			л := source.первая;
			WHILE  л # NIL  DO
				NEW( нов );
				нов^ := л^;
				нов.след := первая;  первая := нов;
				л := л.след
			END;
			(* обращаем для аккуратности*)
			WHILE  первая # NIL  DO
				л := первая;  первая := первая.след;
				л.след := рис.первая;  рис.первая := л;
			END;
			
			л := рис.первая;  л1 := source.первая;
			WHILE ( л # NIL ) & ( л.цвет = л1.цвет ) DO
				л := л.след;  л1 := л1.след;
			END;
		END
	END CopyFromSimpleView;
	
	PROCEDURE^ Новый*;
	
	PROCEDURE ( рис: Рисунок ) HandleCtrlMsg* ( f: Views.Frame;  VAR msg: Views.CtrlMessage;  
																							VAR focus: Views.View );
		VAR  type: ARRAY 1000 OF CHAR;  пиксель: INTEGER;
	BEGIN
		WITH  msg: Controllers.TrackMsg  DO
			IF   ( Controllers.doubleClick IN msg.modifiers ) 
				& ( Controllers.modify IN msg.modifiers ) 
				& ~( Controllers.extend IN msg.modifiers ) THEN
				(* Log.Set( msg.modifiers );  Log.Ln; там еще HostPorts.left -- мол, левой кнопкой *)
				рисунок := рис;
			ELSIF ( Controllers.doubleClick IN msg.modifiers ) 
				& ~( Controllers.modify IN msg.modifiers ) 
				& ~( Controllers.extend IN msg.modifiers ) THEN
				Очисть( рис );
				рисунок := рис;
			ELSIF ( Controllers.doubleClick IN msg.modifiers ) 
				& ~( Controllers.modify IN msg.modifiers ) 
				& ( Controllers.extend IN msg.modifiers ) THEN
				Новый;
			END
		|  msg: Controllers.PollOpsMsg  DO  (* Services.GetTypeName( рис, msg.type ); *)
			msg.type := "i21eduЧерепашка.Рисунок"
		|  msg: Controllers.EditMsg  DO
			пиксель := РазмерПикселя( f );
			IF  msg.op = Controllers.pasteChar  THEN
				IF       msg.char = 1CX (* arrow left *)	THEN  DEC( рис.shiftX, 10 )
				ELSIF  msg.char = 1DX (* arrow right *)	THEN  INC( рис.shiftX, 10 )
				ELSIF  msg.char = 1EX (* arrow up *)	THEN  DEC( рис.shiftY, 10 )
				ELSIF  msg.char = 1FX (* arrow down *)	THEN  INC( рис.shiftY, 10 )
				ELSIF  msg.char = 1FX (* arrow down *)	THEN  INC( рис.shiftY, 10 )
				ELSIF  msg.char = 14X (* Home *)  THEN
					рис.shiftX := 0;  рис.shiftY := 0;
				ELSIF  msg.char = 15X (* End *)  THEN
					рис.shiftX := -SHORT( ENTIER( рис.позиция.x * пиксель ) DIV пиксель );
					рис.shiftY :=   SHORT( ENTIER( рис.позиция.y * пиксель ) DIV пиксель );
				ELSIF ( CAP( msg.char ) = "H" ) OR ( National.Cap( msg.char ) = "Р" ) THEN
					рис.справка := ~рис.справка;
				ELSE
					(* Log.Int( ORD(msg.char) );  Log.Ln; *)
				END
			END;
			(* Views.SetDirty( рис ); *)
			Views.Update( рис, Views.keepFrames );
		ELSE
		END;
		focus := рис
	END HandleCtrlMsg;
	
	PROCEDURE Сообщение ( IN str: ARRAY OF CHAR );
		VAR  res: INTEGER;
	BEGIN
		Dialog.Beep;
		Dialog.GetOK( str, '', '', '', {Dialog.ok}, res );
	END Сообщение;
	
	PROCEDURE НайтиРисунок0 ( OUT рис: Рисунок;  OUT win: Windows.Window );
		VAR  n: INTEGER;  w, w1: Windows.Window;  v, v1: Views.View;
	BEGIN
		n := 0;  рис := NIL;  win := NIL;  v1 := NIL;  w1 := NIL;
		w := Windows.dir.First();
		IF  w # NIL  THEN  v := w.doc.ThisView()  END;
		WHILE ( w # NIL ) & ( v # рисунок ) DO
			WITH  v: Рисунок  DO
				INC( n );
				IF  n = 1  THEN  v1 := v;  w1 := w  END;
			ELSE
			END;
			w := Windows.dir.Next( w );
			IF  w # NIL  THEN  v := w.doc.ThisView()  END;
		END;
		IF  w # NIL  THEN  ASSERT( v = рисунок );
			рис := v( Рисунок );
			win := w;
		ELSIF  n = 1  THEN
			рис := v1( Рисунок );
			win := w1;
		ELSE
			рис := NIL;
			win := NIL;
		END;
	END НайтиРисунок0;
	
	PROCEDURE НайтиРисунок;
		VAR  рис: Рисунок;  win: Windows.Window;
	BEGIN
		НайтиРисунок0( рис, win );
		рисунок := рис;
		IF  рисунок # NIL  THEN
			(* Windows.dir.Select( win, Windows.lazy ) *)
		ELSE
			Сообщение("Куда рисовать?");  HALT( 128 )
		END
	END НайтиРисунок;
	

	
	PROCEDURE Очистить*;
	BEGIN
		НайтиРисунок;
		IF  рисунок # NIL  THEN
			Очисть( рисунок );
			пероОпущено := TRUE;
			чернила := Ports.black;
			Views.Update( рисунок, Views.keepFrames );
			(* Views.SetDirty( рис ); *)
		END;
	END Очистить;

	PROCEDURE Идти* ( расстояние: INTEGER ); (* в пикселях *)
		CONST  limit = 10000;
		VAR  т0, т1: Точка;  нов, л: Линия;  res: INTEGER;
	BEGIN
		IF  расстояние <= 0  THEN
			Dialog.GetOK('Черепашка ходит только вперёд!', '', '', '', {Dialog.ok}, res );
			HALT( 128 );
		END;
		НайтиРисунок;
		IF  рисунок # NIL  THEN
			т0 := рисунок.позиция;
			т1.x := т0.x + расстояние * Math.Cos( pi180 * рисунок.угол );
			т1.y := т0.y + расстояние * Math.Sin( pi180 * рисунок.угол );
			IF  ( ABS( расстояние ) >= limit ) 
			OR ( ABS( т1.x ) >= limit ) 
			OR ( ABS( т1.y ) >= limit )  THEN
				Dialog.GetOK('Хотим прогнать черепашку?', '', '', '', {Dialog.ok}, res );
				HALT( 128 );
			END;
			рисунок.позиция := т1;
			IF  пероОпущено  THEN
				NEW( нов );
				нов.т0 := т0;
				нов.т1 := т1;
				нов.цвет := чернила;
				(* хотим подсоединить в хвост: *)
				л := рисунок.первая;
				WHILE  ( л # NIL ) & ( л.след # NIL )  DO
					л := л.след
				END;
				IF  л = NIL  THEN
					рисунок.первая := нов;
				ELSE
					ASSERT( л.след = NIL );
					л.след := нов;  ASSERT( нов.след = NIL );
				END;
			END;
			Views.Update( рисунок, Views.keepFrames );
			(* Views.SetDirty( рис ); *)
		END;
	END Идти;

	PROCEDURE Повернуть* ( угол: INTEGER ); (* против часовой стрекли *)
		VAR  a: REAL;
	BEGIN
		НайтиРисунок;
		IF  рисунок # NIL  THEN
			угол := угол MOD 360;
			a := рисунок.угол + угол;
			WHILE  a >= 360  DO  a := a - 360  END;
			WHILE  a < 0        DO  a := a + 360  END;
			рисунок.угол := a;
			Views.Update( рисунок, Views.keepFrames );
			(* Views.SetDirty( рис ); *)
		END;
	END Повернуть;


	PROCEDURE ПоднятьПеро*;
	BEGIN
		НайтиРисунок;
		IF  рисунок # NIL  THEN
			Views.Update( рисунок, Views.keepFrames );
			(* Views.SetDirty( рис ); *)
		END;
		пероОпущено := FALSE;
	END ПоднятьПеро;

	PROCEDURE ОпуститьПеро*;
	BEGIN
		НайтиРисунок;
		IF  рисунок # NIL  THEN
			Views.Update( рисунок, Views.keepFrames );
			(* Views.SetDirty( рис ); *)
		END;
		пероОпущено := TRUE;
	END ОпуститьПеро;

	
	PROCEDURE РисоватьЦветом ( цвет: INTEGER );
	BEGIN
		НайтиРисунок;
		IF  рисунок # NIL  THEN
			Views.Update( рисунок, Views.keepFrames );
			(* Views.SetDirty( рис ); *)
		END;
		чернила := цвет;
	END РисоватьЦветом;
	
	
	PROCEDURE РисоватьЧерным*;
	BEGIN  РисоватьЦветом( Ports.black )
	END РисоватьЧерным;

	PROCEDURE РисоватьКрасным*;
	BEGIN  РисоватьЦветом( Ports.red )
	END РисоватьКрасным;

	PROCEDURE РисоватьСиним*;
	BEGIN  РисоватьЦветом( Ports.blue )
	END РисоватьСиним;

	PROCEDURE РисоватьЗеленым*;
	BEGIN  РисоватьЦветом( Ports.green )
	END РисоватьЗеленым;

	PROCEDURE РисоватьЖелтым*;
	BEGIN  РисоватьЦветом( Ports.RGBColor( 255, 255, 0 ) )
	END РисоватьЖелтым;

	
	PROCEDURE РисоватьСюда*; (* рисовать во вьюшку, которая в фокусе *)
		VAR  v: Views.View;  win: Windows.Window;
	BEGIN
		v := Controllers.FocusView();
		IF  v = NIL  THEN
			win := Windows.dir.Focus( TRUE );
			IF  win # NIL  THEN
				v := win.doc
			END;
		END;
		
		IF ( v # NIL ) & ( v IS Documents.Document ) THEN
			v := v( Documents.Document ).ThisView();
		END;
		
		IF ( v # NIL ) & ( v IS Рисунок ) THEN
			рисунок := v( Рисунок )
		END
	END РисоватьСюда;

	PROCEDURE Новый*; (* начинаем новый рисунок *)
		VAR рис: Рисунок;
	BEGIN
		NEW( рис );
		рис.w := размер;
		рис.h := размер;
		рис.справка := TRUE;
		Views.OpenView( рис );
		W.AlignTopRight;
		рисунок := рис;
		пероОпущено := TRUE;
		чернила := Ports.black;
	END Новый;
	
	PROCEDURE Открыть*;
		VAR  w: Windows.Window;
	BEGIN
		w := Windows.dir.First();
		WHILE  ( w # NIL ) & ~( w.doc.ThisView() = рисунок )  DO
			w := Windows.dir.Next( w );
		END;
		IF  w # NIL  THEN
			Windows.dir.Select( w, Windows.lazy );
		ELSE
			Views.OpenView( рисунок );
		END;
	END Открыть;
	
	PROCEDURE ЗакрытьВсехЧерепах*;
		VAR  w: Windows.Window;  нашли: BOOLEAN;
	BEGIN
		REPEAT
			w := Windows.dir.First();
			WHILE ( w # NIL ) & ~( w.doc.ThisView() IS Рисунок ) DO
				w := Windows.dir.Next( w );
			END;
			нашли := ( w # NIL );
			IF  нашли  THEN
				Windows.dir.Select( w, ~Windows.lazy );
				(* HostCmds.Save; *)
				Windows.dir.Close( w );
			END;
		UNTIL  ~нашли
	END ЗакрытьВсехЧерепах;
	
	PROCEDURE Guard* ( VAR par: Dialog.Par );
		VAR  w: Windows.Window;  v: Views.View;
	BEGIN
		w := Windows.dir.Focus(TRUE);
		IF  w = NIL  THEN  HALT( 128 )  END;
		v := w.doc.ThisView();
		IF ( v = NIL ) OR ~( v IS Рисунок ) THEN
			par.disabled := TRUE
		ELSE
			par.disabled := FALSE
		END;
	END Guard;
	
BEGIN
	loc := Files.dir.This('');
	pi180 := Math.Pi() / 180;
	пероОпущено := TRUE;
	чернила := Ports.black;
CLOSE
	ЗакрытьВсехЧерепах
END  i21eduЧерепашка.

StdLog.Clear
i21eduЧерепашка.Новый
i21eduЧерепашка.НачатьРаботу

"
i21eduЧерепашка.Идти(30); 
i21eduЧерепашка.Повернуть(30); 
i21eduЧерепашка.РисоватьКрасным;
 i21eduЧерепашка.Идти(30); 
i21eduЧерепашка.Повернуть(80); 
i21eduЧерепашка.РисоватьСиним; 
i21eduЧерепашка.Идти(30)"
"i21eduЧерепашка.ПоднятьПеро; i21eduЧерепашка.Идти(30); i21eduЧерепашка.ОпуститьПеро;
i21eduЧерепашка.РисоватьЧерным; i21eduЧерепашка.Повернуть(130); i21eduЧерепашка.Идти(50)"

"i21eduЧерепашка.Повернуть(-30)"
"i21eduЧерепашка.Идти(1000)"
i21eduЧерепашка.РисоватьКрасным
i21eduЧерепашка.РисоватьЗеленым
i21eduЧерепашка.РисоватьСиним
i21eduЧерепашка.РисоватьЖелтым
i21eduЧерепашка.РисоватьЧерным
i21eduЧерепашка.ПоднятьПеро;
i21eduЧерепашка.ОпуститьПеро;

i21eduЧерепашка.Очистить