Анализ и разработка алгоритма

Курсовая работа

по курсу «Технология программирования»

на тему: «Объектно-ориентированная технология программирования»

Вариант 41

 

Разработал студент группы 3-78-2 Ситдиков Р.М.

Принял к.т.н., доцент кафедры ВТ Гафаров Р.М.

 

 

Ижевск 2009


 

 

Содержание

Задание………………………………………………………..……...3

Цель работы…………………………………………………...……..3

Постановка задачи…………………………………………..……….3

Анализ и разработка алгоритма……………………………..……..3

Алгоритм программы…………………………………………...…..8

Текст программы……………………………………………...……..9

Результаты моделирования программы на ЭВМ……………..….16

Вывод...………………………………………………….…………...17

Литература………………………………………………….……….18


ЛАБОРАТОРНАЯ РАБОТА №1.

ТЕХНОЛОГИЯ РАЗРАБОТКИ ПРОГРАММЫ ПРИ ОБЪЕКТНО-ОРИЕНТИРОВАННОМ ПОДХОДЕ

 

Задание

Разработать программную модель взаимного качения квадрата и эллипса по плоской поверхности.(рис. 1.1)

Рис. 1.1- Задание на лабораторную работу

 

Цель работы

Повторение и закрепление знаний в области технологии разработки ПО при объектно-ориентированном подходе на примере разработки программы моделирования взаимного качения двух объектов.

 

Постановка задачи

В целом задача сводится к реализации вращения квадрата и эллипса вокруг заданной оси и перемещения оси вращения.

 

Анализ и разработка алгоритма

В результате выполнения программы на экране должно быть следующее (рис 1.2):

 
 


Рис. 1.2- Постановка задачи

 

Здесь К – это ось вращения квадрата, а Э – ось вращения эллипса. Основными структурными элементами (объектами) этого изображения являются точки и линии, и из них могут быть построены квадрат и эллипс и скомпонована вся сцена на экране, причем эти элементы образуют следующую иерархию:

 
 

 


 

Опишем эти объекты в соответствии с положениями ООП-программирования.

· Объект типа TPoint является точкой с координатами x,y вещественного типа и цветом Pcolor. Вещественный тип координат точки определяется тем, что этот объект предназначен для описания вершин квадрата и эллипса, а при вращении они могут принимать вещественные значения. Кроме очевидных необходимых методов Init, Show и Destructor добавим в их число и виртуальный метод Rotate реализующий плоское вращение точки вокруг заданной оси. Вращение точек вокруг заданной оси вращения осуществляется по формуле:

,где х0 , у0 – начальные координаты точки; х, у – координаты после поворота; а – угол поворота; OsX, OsY – координаты оси вращения.

Координаты оси вращения имеют вещественный тип и задаются извне (являются входными параметрами метода). Вещественные значения координат оси обусловлены тем, что длина отрезков, из которых состоит эллипс, может быть как целой, так и вещественной.

Методы TPoint достаточно простые: Constructor Init заполняет О-поля объекта; процедура Rotate, как описано выше вычисляет новые координаты точки по формулам геометрического поворота вокруг заданной оси на угол step, величина которого зависит от требуемой скорости вращения объекта и процедура Show скорее служит прототипом для дальнейшего наследования, чем для самостоятельного использования.

· Изображение эллипса может быть получено с помощью стандартной для модуля graph процедуры Ellipse, но построенный таким образом эллипс невозможно вращать, поэтому будем рассматривать его как многоугольник и строить с помощью процедур moveto и lineto по координатам вершин. Таким образом объект типа TEllipse конструируется из точек типа TPoint, количество которых определяется значением константы det, и переменной EColor для задания его цвета а его виртуальные методы Show и Rotate обеспечивают полиморфические свойства отображения и поворота. Координаты вершин вычисляются по формулам:

x=a*cos(t); y=b*sin(t), t=

где а и b – горизонтальный и вертикальный радиусы эллипса, а t вычисляется в зависимости от номера точки i и значения константы det. Процедура инициализации располагает центр эллипса в верхнем левом углу экрана.

· Объект типа TLine наследует все поля и методы TPoint, но перекрывает своими виртуальными методами Show и Rotate соответствующие методы родителя. Кроме унаследованных полей x и y, этот тип содержит два поля pn и pk типа TPoint, которые являются объектами и описывают две точки, задающие отрезок прямой на плоскости – будущую сторону квадрата. Виртуальные методы Show и Rotate позволяют отображать этот отрезок на экране цветом Lcolor и поворачивать его вокруг заданной оси. На первый взгляд, непонятна роль унаследованных полей x и y. Тем более, что можно было в типе TLine обойтись только одним полем pn, а координаты конца отрезка поместить в x и y. Однако, такой подход привел бы к разным синтаксическим конструкциям при обращении к начальной и конечной точкам отрезка, что повлияло бы на единообразие стилистики программы и затруднило бы ее восприятие. Кроме того, потомки данного типа могут использовать эти поля для своих специфических нужд, например, помещая в них координаты середины отрезка.

· Объектный тип TSquare (квадрат) конструируется из 4-х отрезков типа ТLine, длины стороны квадрата as и переменной Scolor для задания его цвета, а виртуальные методы Show и Rotate обеспечивают его полиморфические свойства отображения и поворота. Процедура инициализации располагает квадрат в верхнем левом углу экрана.

· Тип TScreen, находясь на самом нижнем уровне иерархии, описывает самый сложный объект – экран, со всеми его «действующими лицами»: катящимся эллипсом, катящимся по поверхности эллипса квадратом и поверхностью качения. Этот тип включает в себя эллипс, унаследованный из родительского типа, объект типа TSquare, О-поля для задания положения и цвета поверхности качения (поля Gdisp и Gcolor) и соответствующий метод DrawGround ее прорисовки на экране, поля line0, sides0 для сохранения предыдущих координат эллипса и квадрата, а также поля nom, OsXЕ,OsYЕ для номера точки эллипса, являющейся текущей осью вращения квадрата и текущих координат положения оси вращения эллипса соответственно. Методы ShiftOsXY и ShiftOsXYЕ отвечают за своевременное перемещение осей вращения квадрата и эллипса при качении и являются виртуальными, поскольку в потомках может возникнуть необходимость их модификации. Метод ShiftOsXYЕ перемещает ось вращения эллипса при достижении какой-либо из его точек поверхности качения. Метод ShiftOsXY контролирует контакт эллипса со сторонами квадрата. Для определения момента входа эллипса в пределы квадрата после очередного поворота его на небольшой угол step проводятся испытания всех точек эллипса на предмет входа их в пределы кавдрата. С этой целью вычисляются координаты каждой из этих точек в локальной системе координат X10Y1квадрата. Для этого используются методы CalcABC и Dist. В методе CalcABC вычисляются параметры прямой Ax+By+C=0– одной из осей локальной системы координат X10Y1 (линии, проходящей через центры противоположных сторон квадрата) по формулам:

A=yk - yn; B := xn - xk; C := xk * yn - xn * yk,

где xn, yn, xk, yk – координаты начала и конца отрезка прямой.

В методе Dist вычисляется расстояние (координата) точки (вершины эллипса) до соответствующей оси по формуле:

где xi,yi - координаты точки эллипса в глобальной (экранной) системе координат.
Dist= ,

 

Если обе полученные координаты по абсолютной величине меньше половины длины стороны квадрата, то данная точка эллипса входит в пределы квадрата, поэтому ось вращения перемещается в эту вершину эллипса. Факт смены оси вращения отмечается в результате функции ShiftOsXY значением True. Метод Rotateall обеспечивает вращение квадрата и эллипса вокруг точки вращения эллипса. Метод Go реализует продвижение изображения на один кадр при срабатывании таймера и регенерацию сцены при достижении эллипсом края окна.

 


Алгоритм программы

Блок-схема основной процедуры программы TScreen.go:

 

                   
   
 
 
   
 
 
   
 
   

Текст программы

program Lab;

 

uses

Forms,

Kurspas in 'Kurspas.pas' {Form1},

ElSq in 'ElSq.pas' {Form2};

 

{$R *.res}

 

begin

Application.Initialize;

Application.CreateForm(TForm1, Form1);

Application.Run;

end.

 

unit Kurspas;

 

interface

 

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls, ExtCtrls, ElSq;

Const

sizeSq = 100; { размер квадрата }

colorEl = clBlue; {цвет эллипса}

colorSq = clYellow; { цвет квадрата }

colorG = ClGreen; { цвет поверхности качения }

type {описание формы}

TForm1 = class(TForm)

Image1: TImage;

Button1: TButton;

Button2: TButton;

Timer1: TTimer;

Button3: TButton;

procedure Button1Click(Sender: TObject);

procedure Button2Click(Sender: TObject);

procedure Timer1Timer(Sender: TObject);

procedure FormResize(Sender: TObject);

procedure FormCreate(Sender: TObject);

procedure Button3Click(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

implementation

Var screen: TScreen; {определение объекта типа TScreen}

{$R *.dfm}

 

procedure TForm1.Button1Click(Sender: TObject); {при нажатии кнопки Go}

begin

timer1.Enabled:=true; {активировать таймер}

end;

 

procedure TForm1.Button2Click(Sender: TObject); {при нажатии кнопки Exit}

begin

screen.Done; {вызвать деструктор}

Close; {завершить выполнение приложения}

end;

 

procedure TForm1.Timer1Timer(Sender: TObject); {при срабатывании таймера}

begin

Screen .Go; {запустить процедуру анимации экрана}

end;

 

procedure TForm1.FormCreate(Sender: TObject); {при запуске приложения}

begin

Screen.Init(sizeSq, colorEl, colorSq, colorG, Image1.height-30,Image1 );

{инициализировать объект Screen}

end;

 

procedure TForm1.Button3Click(Sender: TObject); {при нажатии кнопки Stop}

begin

timer1.Enabled:=false; {остановить таймер}

end;

end.

 

unit ElSq;

 

interface

 

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, ExtCtrls;

Const

det = 36; {количество вершин для построения эллипса}

xrad = 40; {горизонтальный радиус эллипса}

yrad = 25; {вертикальный радиус эллипса}

kv = 4; {количество сторон квадрата}

speed1 = 0.9; {скорость вращения эллипса}

speed = 1.5; {скорость вращения квадрата}

one = pi/180; {один градус в радинах}

step = one*speed; {шаг поворота квадрата}

step1 = one*speed1; {шаг поворота эллипса}

Type TPoint = Object {О-тип точка}

x,y :Real; {координаты точки}

Pcolor :Byte; {цвет точки}

Constructor Init ( xx,yy :Real; col :Byte );

Procedure Rotate ( xOs, yOs, spd :real ); Virtual;

Procedure Show ( col :Byte; var image1:TImage ); Virtual;

Destructor Done;

End;

 

TSide = array [1..det] of TPoint; {тип для описания вершин эллипса}

TEllipse=Object ( TPoint ) {О-тип эллипс}

line :TSide; {вершины эллипса}

EColor :Byte; {его цвет}

Constructor init (colE :byte);

procedure Rotate (xOsE, yOsE, spd:real); Virtual;

Procedure Show (col:Byte; var image1:TImage); Virtual;

Destructor Done;

end;

 

 

TLine = Object ( TPoint ) {О-тип отрезок}

pn, pk :TPoint; {начальная и конечная точки отрезка}

Lcolor :Byte; {его цвет}

Constructor Init ( x1,y1,x2,y2 :Real; col :Byte );

Procedure Rotate ( xOs, yOs, spd :real ); Virtual;

Procedure Show ( col :Byte; var image1:TImage ); Virtual;

Destructor Done;

End;

 

TSides = Array [ 0..kv-1 ] Of TLine; {тип для описания сторон квадрата}

TSquare = Object ( TLine ) {О-тип квадрат}

as :Byte; {длина стороны квадрата}

Sides :TSides; {стороны квадрата}

Scolor :Byte; {его цвет}

Constructor Init ( aa, colK :Byte );

Procedure Rotate ( xOs, yOs, spd :real ); Virtual;

Procedure Show ( col :Byte; var image1:TImage ); Virtual;

Destructor Done;

End;

 

TScreen = Object ( TEllipse ) {О-тип сцена}

image1 :TImage; {адрес картинки}

Elps :TEllipse; {эллипс}

Sqre :Tsquare; {квадрат}

Gdisp :Integer; {смещение поверхности качения}

Gcolor :TColor; {цвет поверхности качения}

line0 :TSide; {переменные для запоминание текущего положения фигур}

sides0 :TSides;

OsXE,OsYE,nom :Integer; {переменные для хранения координат осей вращения}

Constructor Init ( aa:Byte; colE, colK, colG :TColor; dG :Integer; var image:TImage );

Function ShiftOsXY :Boolean; Virtual;

Function ShiftOsXYE :Boolean;

Procedure CalcABC( Var S1,S2 :TLine; Var A,B,C :Real );

Function Dist( A,B,C, xx,yy :Real) :Real;

Procedure Rotateall(xOs,yOs:Integer; spd:real);

Procedure Go; Virtual;

Procedure DrawGround; Virtual;

Destructor Done;

End;

 

{TForm2 = class(TForm)

private }

{ Private declarations}

{ public }

{ Public declarations }

{ end;

 

var

Form2: TForm2;}

 

implementation

 

 

{---------------------------------------------------------------}

Constructor TPoint .Init ( xx, yy :Real; col :TColor );

Begin x:=xx; y:=yy; Pcolor := col; End;

 

Procedure TPoint .Rotate ( xOs,yOs :Integer; spd:real );

Var xx, yy :Real;

Begin xx := (x - xOs)*Cos(spd) - (y - yOs)*Sin(spd) + xOs;

yy := (x - xOs)*Sin (spd) + (y - yOs)*Cos(spd) + yOs;

x :=xx; y:=yy;

End;

 

Procedure TPoint .Show ;

Begin

Image1.Canvas.pixels[Round(x),Round(y)]:=col;

End;

 

Destructor TPoint .Done;

Begin End;

{---------------------------------------------------------------}

Constructor TLine .Init ( x1,y1,x2,y2 :Real; col :TColor );

Begin pn.Init(x1,y1,col); pk.Init(x2,y2,col); Lcolor:=col; End;

 

Procedure TLine .Rotate ( xOs,yOs :Integer; spd:real );

Begin pn.Rotate( xOs,yOs,spd ); pk.Rotate( xOs,yOs,spd ); End;

 

Procedure TLine .Show ;

Begin If col=clWhite Then image1.canvas.pen.color:= col Else image1.canvas.pen.color:= Lcolor;

image1.Canvas.MoveTo(Round(pn.x),Round(pn.y));

image1.canvas.LineTo(Round(pk.x),Round(pk.y));

End;

 

Destructor TLine .Done;

Begin End;

{---------------------------------------------------------------}

Constructor TEllipse.init(colE:byte); {инициализация эллипса}

var i:byte;

px,py:real;

Begin

EColor:=colE;

for i:=1 to det do begin

px:=xrad*cos((i-1)*2*pi/det); {определение координат точек}

py:=yrad*sin((i-1)*2*pi/det);

with line[i] do init(px,py,colE);

end;

end;

 

Procedure TEllipse.rotate(xOsE, yOsE, spd :real ); {вращение эллипса}

Var i:byte;

begin

for i:=1 to det do line[i].rotate(xOsE,yOsE,spd); {вращение каждой точки}

end; {эллипса}

 

procedure TEllipse.Show; {процедура отображения (стирания)}

var i:byte; {эллипса}

begin image1.Canvas.Pen.Color:=col; {установка цвета эллипса}

image1.canvas.moveto(Round(line[1].x),round(line[1].y));{помещение текущего указателя в первую вершину эллипса}

for i:=det downto 1 do

with line[i] do image1.canvas.lineto(round(x),round(y));{прорисовка эллипса по точкам}

end;

 

destructor TEllipse.done;

begin end;

{---------------------------------------------------------------}

Constructor TSquare .Init ( aa, colK :Byte ); {инициализация квадрата}

Begin

as := aa; {установка размера стороны квадрата}

Sides[0]. Init ( as, as, 0, as, colK ); {инициализация сторон квадрата}

Sides[1]. Init ( 0, as, 0, 0, colK );

Sides[2]. Init ( 0, 0, as, 0, colK );

Sides[3]. Init ( as, 0, as, as, colK );

Scolor := colK;

End;

 

Procedure TSquare .Rotate ( xOs, yOs, spd:real ); {вращение квадрата}

Var i :Byte;

Begin

For i:=0 To kv-1 Do Sides[i] .Rotate ( xOs,yOs,spd );

End;

 

Procedure TSquare .Show; {отображение(стирание) квадрата}

Var i :Byte;

Begin For i := 0 To kv-1 Do Sides[i].Show ( col,image1 ); End;

 

Destructor TSquare .Done;

Begin End;

{---------------------------------------------------------------}

Constructor TScreen .Init ( aa:Byte; colE, colK, colG :TColor; dG :Integer; var image:TImage );

Var i :Byte;

Begin

{закрашиваем экран белым}

image1:=image; {принимаем адрес нашего экрана для рисования}

image1.Canvas.Brush.Color:=clWhite;

Image1.Canvas.Brush.Style:=bsSolid;

image1.Canvas.FillRect(rect(0,0,image1.Width,image1.Height));

Sqre.Init ( aa, colK ); {инициализируем квадрат}

Elps.init(colE); {инициализируем эллипс}

Gdisp := dG;

For i := 0 To kv-1 Do With Sqre.Sides[i] Do Begin {ставим квадрат на эллипс}

pn.y := pn.y + Gdisp - Sqre.ss-2*yrad-1;

pk.y := pk.y + Gdisp - Sqre.ss-2*yrad-1;

pn.x := pn.x + xrad;

pk.x := pk.x + xrad;

End;

For i:=1 to Det do With Elps.line[i] do begin {ставим эллипс на поверхность}

y:=y+Gdisp-yrad;

x:=x+xrad; end;

Gcolor := colG; {принимаем цвет поверхности}

nom:=det-1; {устанавливаем начальные значения координат осей вращения}

OsXE:= xrad;

OsYE:= Gdisp;

DrawGround; {рисуем поверхность}

End;

 

Procedure TScreen .DrawGround; {процедура прорисовки поверхности}

Begin Image1.canvas.pen.color:= Gcolor; {установка цвета прорисовки прямоугольника}

Image1.Canvas.Brush.Color:=Gcolor; {и цвета заливки}

image1.Canvas.MoveTo(0, Gdisp + 1); {прорисовка линий}

Image1.canvas.LineTo( Image1.Width, Gdisp + 1 );

Image1.Canvas.Brush.Style:=bsBDiagonal; {установка стиля заливки}

Image1.canvas.FloodFill(2,Gdisp+2,Gcolor, fsBorder); {заливка поверхности}

image1.Canvas.Refresh;

End;

 

Function TScreen .ShiftOsXYE :Boolean; {смещение оси вращения эллипса}

var i:byte;

Begin

ShiftOsXYE := False;

for i:=1 to det do {перебираем все точки}

If elps.line[i].y>Gdisp Then {если точка оказалась ниже поверхности качения}

if Round(elps.line[i].x)>OsXE then Begin {если координата х этой точки отличается от текужей х координаты оси вращения эллипса}

elps.line:=line0; {то восстанавливаем предыдущее положение фигур}

sqre.Sides:=sides0;

OsXE := Round(elps.line[i].x); {смещаем ось вращения}

ShiftOsXYE := True; End;

End;

{следующие 2 процедуры как в методичке, принцип действия объяснен в анализе алгоритма}

Procedure TScreen.CalcABC( Var S1,S2 :TLine; Var A,B,C :Real );

Var xn,yn,xk,yk :Real;

Begin xn := (S1.pn.x+S2.pk.x)/2; yn := (S1.pn.y+S2.pk.y)/2;

xk := (S1.pk.x+S2.pn.x)/2; yk := (S1.pk.y+S2.pn.y)/2;

A := yk - yn; B := xn - xk; C := xk * yn - xn * yk;

End;

 

Function TScreen.Dist( A,B,C, xx,yy :Real) :Real;

Begin Dist := Abs((A*xx+B*yy+C) / Sqrt(A*A+B*B)); End;

 

Function TScreen.ShiftOsXY :Boolean; {смещение оси вращения квадрата}

Var Ax, Bx, Cx, Ay, By, Cy, xx, yy :Real;

i :Integer;

Begin

ShiftOsXY := False;

{подсчет параметров новой системы координат}

CalcABC( Sqre.Sides[1], Sqre.Sides[3], Ax, Bx, Cx );

CalcABC( Sqre.Sides[0], Sqre.Sides[2], Ay, By, Cy );

For i := 1 To Det Do {перебор всех точек эллипса}

Begin

yy := Dist( Ay, By, Cy, Elps.line[i].x, Elps.line[i].y ); {подсчет координат точки в новой системе координат}

xx := Dist( Ax, Bx, Cx, Elps.line[i].x, Elps.line[i].y );

If ( xx <= Sqre.ss/2 ) and ( yy <= Sqre.ss/2) {если точка внутри квадрата}

Then If i<>nom then {то если ее номер отличается от текущего номера}

Begin

nom:=i; {смещаем ось вращения}

ShiftOsXY := True;

sqre.Sides:=sides0; {восстанавливаем предыдущее положение квадрата}

Exit;

End;

End;

End;

 

Procedure TScreen.Rotateall(xOs,yOs:Integer; spd:real); {вращение всех фигур}

var xx,yy:real;

Begin Sqre.rotate(xOs,yOs,spd);

Elps.rotate(xOs,yOs,spd);

End;

 

Procedure TScreen.Go; {продвижение на 1 кадр}

Begin

Sqre.Show ( clWhite,image1 ); {стираем фигуры}

Elps.show ( clWhite,image1 );

sides0:=sqre.Sides; {запоминаем положение квадрата}

repeat

sqre.Rotate ( Round(elps.line[nom].x), Round(elps.line[nom].y), step ); {вращаем квадрат}

until not ShiftOsXY; {пока вращение не пройдет без необходимости сместить ось вращения}

sides0:=sqre.Sides; {запоминаем положение обеих фигур}

line0:=elps.line;

Repeat

Rotateall( OsXE, OsYE, step1 ); {вращаем обе фигуры}

until not ShiftOsXYE; {пока вращение не пройдет без смещения оси вращения}

Sqre.Show ( Sqre.Scolor, Image1 ); {прорисовываем фигуры}

Elps.Show (Elps.Ecolor,image1); image1.Refresh;

If OsXE > Image1.Width then {если дошли до края окна, то инициализируем сцену заново}

Init ( Sqre.ss, Elps.Ecolor, Sqre.Scolor, Gcolor, Gdisp,image1 );

{Until False;}

End;

 

Destructor TScreen.Done;

Begin End;

{---------------------------------------------------------------}

end.