Пятница, 24.11.2017, 14:16
ОТКРЫТАЯ ИНФОРМАТИКА
Приветствую Вас Гость | RSS
Главная Графика Регистрация Вход
Меню сайта

Форма входа

Поиск

Календарь
«  Ноябрь 2017  »
ПнВтСрЧтПтСбВс
  12345
6789101112
13141516171819
20212223242526
27282930

Работа с графикой в ЯП Паскаль

Урок 1.

Стандартная VGA-графика Паскаля (640x480 точек, 16 цветов) не очень годится для анимации — в ней маловато цветов. Поэтому попробуем использовать для работы графический режим VGA с шестнадцатеричным номером 13h (320x200 точек, 256 цветов). Доступ к видеопамяти в этом режиме очень простой, а перевести монитор в данный режим можно вызовом 10h прерывания BIOS. И никакого модуля Graph!
Пишем текст: 
begin {установить видеорежим 13h — всего 2 строчки на встроенном ассемблере}
asm
mov ax, 13h {пересылка номера видеорежима в регистр}
int 10h {вызов прерывания с шестнадцатеричным номером 10h}
end;
Readln
end.
Запустив программу, мы увидим, что на черном экране нет курсора — это верный признак графического видеорежима. Прежде чем нажать , попробуем печатать на клавиатуре. Не правда ли, экзотический вид у букв, как в старых играх для DOS? 
 Теперь попытаемся зажечь на экране какой-нибудь пиксель. Для этого достаточно вывести в оперативную память видеокарты (она начинается с адреса $A000:0000 байт, значение которого равно номеру цвета в палитре (0ЈномерЈ255). Для доступа к памяти используем стандартный массив Паскаля Mem. Итак, 
begin
asm
mov ax, 13h
int 10h
end;
Mem[$A000:0]:=14; {14 — желтый цвет}
Readln
end.

Ура! В верхнем левом углу экрана загорелась желтая звездочка! 
Теперь напишем процедуру для вывода точки в любом месте экрана, а затем с ее помощью нарисуем прямоугольник, закрашенный в цвета палитры. 
procedure PutPixel(x,y: integer; color: byte); {0ЈxЈ319;0ЈyЈ199;0ЈcolorЈ255}
var a:word;
begin
a:=320*y+x;
Mem[$A000:a]:=color
end;
var
i,j: integer;
begin
asm
mov ax, 13h
int 10h
end;
for i:=0 to 255 do
for j:=0 to 10 do
PutPixel(i,j,i);
Readln
end.

Младшие 16 цветов соответствуют 16-цветной палитре режима VGA 640x480. Далее идут оттенки серого и 3 набора цветов различной интенсивности. 
Палитра устанавливается 10h прерыванием BIOS во время инициализации видеорежима. Если она нас не устроит, ее нетрудно будет сменить. Почему в процедуре PutPixel вспомогательная переменная a описана как word, а не как integer? 
В нашем графическом режиме экран разбит на 320x200=64 000 точек, а максимальное значение для integer 32 767. Для типа word максимальное значение 65 535. 

Урок 2.

Теперь можно перенести все процедуры в самодельную библиотеку (файл сохранить как Graph13h.pas):
Unit Graph13h;
interface
procedure Screen13h;
procedure PutPixel(x,y: integer; color: byte);
procedure FillScreen(color: byte);
procedure FillRect(x1,y1,x2,y2:integer;color:byte);
procedure HLine(x1,y,x2: integer; color: byte);
procedure VLine(x,y1,y2: integer; color: byte);
implementation
procedure Screen13h; assembler;
asm
mov ax, 13h
int 10h
end;

procedure PutPixel(x,y: integer; color: byte);
var a:word;
begin
a:=320*y+x;
mem[$a000:a]:=color
end;
procedure FillScreen(color: byte);
var m,n: integer;
begin
for m:=0 to 199 do
for n:=0 to 319 do PutPixel(n,m,color)
end;
{Заливка прямоугольника}
procedure FillRect(x1,y1,x2,y2:integer;color:byte);
var m,n:integer;
begin
for m:=x1 to x2 do
for n:=y1 to y2 do PutPixel(m,n,color)
end;

procedure HLine(x1,y,x2: integer; color: byte);
var m,n,k: integer;
begin
if x1
for m:=n to k do PutPixel(m,y,color)
end;

procedure VLine(x,y1,y2: integer; color: byte);
var m,n,k: integer;
begin
if y1
for m:=n to k do PutPixel(x,m,color)
end;
end.

Откомпилируем файл Graph13h.pas. Будет создан файл библиотеки Graph13h.tpu с объектным кодом процедур. 
Программа проверки созданных инструментов существенно упростится: 

uses Graph13h;
begin
Screen13h;
FillScreen(3);
HLine (0, 150, 50, 2);
VLine (150, 0, 100, 4);
readln;
end.

и можно будет приступить к созданию объектов.

Урок 3.

Создадим некий родительский объектный тип. Это будет прародитель целого семейства разнообразных по сложности графических образов, обладающих некоторыми общими характеристиками, а именно: x, y — координаты левого верхнего угла прямоугольника, описанного около объекта; color — его цвет. Кроме этого, наши объекты будут обладать следующими свойствами:каждый из них может быть

- создан вызовом специальной процедуры-конструктора Init; 
- нарисован цветом color вызовом процедуры Show;
- спрятан вызовом процедуры Hide (т.е. нарисован, например, цветом фона);
- перемещен вызовом процедуры Move (сперва спрятан, затем, после увеличения координат объекта на величину перемещения по каждой из осей, нарисован вновь).

По синтаксису объекты представляют собой объединение под одним именем данных различных типов. В набор процедур добавим также процедуру Draw, имеющую служебное назначение. В нашем абстрактном объекте она ровным счетом ничего не делает, так как мы не заключали никаких договоренностей о рисунке (форме объекта). Этот метод будет переопределяться в потомках нашего абстрактного объекта. Для того чтобы конкретный тип потомка можно было бы задавать на этапе выполнения программы, процедура Draw объявлена виртуальной. Это позволяет менять вид объектов в нашем скрин-сейвере "на лету”. 

type
GOb=object
x,y: integer;
color: byte;
constructor Init(ax,ay:integer;acolor: byte);
procedure Draw(acolor: byte); virtual;
procedure Show;
procedure Hide;
procedure Move(dx,dy: integer);
end;


Характеристики (x,y,color) называются ПОЛЯМИ объекта, а процедуры/функции (Init, Draw, Show, Hide), определяющие его свойства, — МЕТОДАМИ объекта. 
В объектно-ориентированном программировании считается дурным тоном менять напрямую поля объекта (хотя Турбо Паскаль это допускает). Лучше использовать для этого специально разработанные методы. Итак, опишем все методы

uses graph13h;
type
GOb=object
x,y: integer;
color: byte;
constructor Init(ax,ay: integer;acolor: byte);
procedure Draw(acolor: byte); virtual;
procedure Show;
procedure Hide;
procedure Move(dx,dy: integer);
end;

constructor GOb.Init(ax,ay: integer;acolor: byte);
begin
x:=ax; y:=ay;
color:=acolor;
end;
procedure GOb.Draw(acolor: byte);
begin
end;
procedure GOb.Show;
begin
Draw(color);
end;
procedure GOb.Hide;
begin
Draw(0);
end;
procedure GOb.Move(dx,dy: integer);
begin
Hide;
x:=x+dx;
y:=y+dy;
Show;
end;
var
a: GOb;
begin

Screen13h;
a.init(160,100,14);
a.show;
readln;
end.

 Запускаем программу, и... видим на экране августовскую ночь. Ничего удивительного — ведь наш объектный тип-прародитель имеет пустой метод Draw. Кстати, в Паскале даже есть процедура Abstract, генерирующая ошибку при обращении к экземплярам абстрактных объектов. Следовательно, надо породить от нашего GOb’а жизнеспособных потомков. Простейшим потомком абстрактного объекта GOb будет, по всей видимости, просто точка. Тип Point объекта-потомка GOb опишем следующим образом после описания объекта-родителя: 

type
{... здесь будет описание родительского типа GOb}
Point=object(GOb);
procedure Draw; virtual;
end;

 Нас вполне устраивает поведение предка (его методы) и его данные (поля). Нужно всего лишь перекрыть (т.е. заменить одноименным) единственный метод Draw. В предке метод Draw ничего не делает. Новый Draw виртуальный — так же, как и в объекте-предке. Напомним, что объявление метода виртуальным дает возможность потомкам различных типов обращаться к методам общего предка на этапе выполнения программы. В нашем случае различные методы Draw потомков будут вызываться методами Show и Hide, как предписано в объекте GOb. 
Все остальные методы и поля наш объект Point наследует от предка. 
Содержание метода Draw элементарно: 

procedure Point.Draw;
begin

PutPixel(x,y,color) 
end;
Теперь можно наконец получить работающую пробную программу. Для большей красоты мы создадим сотню-другую объектов-точек. 

{$R-,Q-}
uses graph13h
;
type
GOb=object
x,y: integer;
color: byte;
constructor Init(ax,ay: integer;acolor: byte);
procedure Draw(acolor: byte); virtual;
procedure Show;
procedure Hide;
procedure Move(dx,dy:integer);
end;
Point=object(GOb)
procedure Draw(acolor: byte); virtual;
end;
constructor GOb.Init(ax,ay:integer;acolor: byte);
begin
x:=ax; y:=ay;
color:=acolor;
end;
procedure GOb.Draw(acolor: byte);
begin
end;
procedure GOb.Show;
begin
Draw(color);
end;
procedure GOb.Hide;
begin
Draw(0);
end;
procedure GOb.Move(dx,dy: integer);
begin
Hide;
x:=x+dx; y:=y+dy;
Show;
end;
procedure Point.Draw(acolor: byte);
begin
PutPixel(x,y,acolor)
end;
const max=100;
var
a: array [1..max] of Point;
i: integer;
begin
Screen13h;
for i:=1 to max do
a[i].Init(Random(320),Random(200),Random(256));
for i:=1 to max do a[i].Show;
readln;
end.
 Запускаем программу, и... видим на экране августовскую ночь, но уже звездную. Раздел описаний (намного больший, чем раздел операторов) включает: 
описание типов (одного предка и одного потомка); 
описание процедур (методов наших объектов);
описание констант;
описание переменных

Урок 4.

Добавляя аналогичным образом все новых потомков, можно легко разнообразить "ассортимент” живущих на экране объектов. Попробуйте поселить одновременно на экране и точки, и крестики. И пусть, например, точки изображают падающие снежинки, а крестики (или другие придуманные вами объекты) движутся случайным образом. Но сначала избавимся от необходимости переносить из программы в программу описание Адама нашей иерархии объектов — создадим еще один модуль с описанием родительского объекта GOb: 

{$R-,Q-}
unit MyObj;
Interface

type
GOb=object
x,y:word;
color:byte;
constructor Init(ax,ay:integer;acolor:byte);
procedure Draw(acolor:byte);virtual;
procedure Show;
procedure Hide;
procedure Move(dx,dy:integer);
end;
Implementation
constructor GOb.Init(ax,ay:integer;acolor:byte);
begin
x:=ax;y:=ay;color:=acolor
end;
procedure GOb.Draw(acolor:byte);
begin
end;
procedure GOb.Show;
begin
Draw(color)
end;
procedure GOb.Hide;
begin
Draw(0)
end;
procedure GOb.Move(dx,dy:integer);
begin
Hide;
x:=x+dx; y:=y+dy;
Show
end;
end.

Файл надо сохранить как MyObj.pas и затем откомпилировать. Получим еще один модуль — MyObj.tpu. Теперь программы будут выглядеть не так "тяжеловесно”. 
{$R-,Q-}
uses graph13h,MyObj,crt;
type

Point=object(GOb)
procedure Draw(acolor:byte);virtual;
end;
Cross=object(GOb)
procedure Draw(acolor:byte);virtual;
end;
procedure Point.Draw(acolor:byte);
begin
PutPixel(x,y,acolor)
end;
procedure Cross.Draw(acolor:byte);
begin
HLine(x,y+2,x+4,acolor);VLine(x+2,y,y+4,acolor)
end;
const max=100;
var a:array[1..max] of Point;b:array[1..max] of Cross;
i:integer;
begin
Screen13h;
for i:=1 to max do
begin
a[i].Init(random(320),random(200),14);
{это 100 желтых точек}
b[i].Init(random(320),random(200),random(256));
{а это 100 разноцветных крестиков}
end;
for i:=1 to max do begin a[i].Show;b[i].Show end;
readln;
while not keypressed do
for i:=1 to max do
begin
a[i].Move(-2+random(5),-2+random(5));
{точки изображают броуновское движение}
b[i].Move(1,1); delay(1)
{а крестики дружно плывут на юго-восток}
end;
readln
end.

А если мы захотим существенно изменить поведение наших объектов? Пусть нам необходимо, чтобы объекты летали внутри некоторой области и отражались от ее границ. Тогда нашим объектам понадобятся дополнительные характеристики — скорости по осям vx и vy. При достижении вертикальной границы скорость vx объекта должна менять знак, а при "столкновении” с горизонтальной границей меняет знак вертикальная составляющая vy. 
В ООП не принято "тревожить” первичный объект в иерархии без крайней необходимости. Нужно порождать потомков, имеющих все необходимые поля и методы, определяющие их поведение. Нам требуется, чтобы точка и крестик отражались от стенок.
Давайте попробуем создать потомка объекта Gob по имени Ball, который "знает”, как обеспечивать зеркальное отражение, а от него уже породим Point и Cross. 
Итак, изменим схему наследования
{$R-,Q-}
uses graph13h,MyObj,crt;

type
Ball=object(GOb)
vx,vy:integer;
constructor Init(ax,ay,avx,avy:integer;acolor:byte);
procedure Move;
end;
Cross=object(Ball)

procedure Draw(acolor:byte);virtual;
end;
constructor Ball.Init(ax,ay,avx,avy:integer;acolor:byte);
begin
inherited Init(ax,ay,acolor);
{унаследованная процедура инициализации}
vx:=avx;
vy:=avy;
{плюс еще два поля (две характеристики объекта): скорости по осям}
end;
procedure Ball.Move;
begin
inherited Move(vx,vy);
if (x<20) or (x>299) then vx:=-vx;
if (y<20) or (y>179) then vy:=-vy;
{добавление к унаследованной процедуре движения}
{отражения от вертикальных и горизонтальных "стенок"}
end;
procedure Cross.Draw(acolor:byte);
begin
HLine(x,y+2,x+4,acolor);VLine(x+2,y,y+4,acolor)
end;
const max=100;
var a:array[1..max] of Cross;
i:integer;
begin
Randomize;
Screen13h;
for i:=1 to max do
begin
a[i].Init(20+random(280),20+random(160),random(5),random(3), random(255));
a[i].Show;
{каждый крестик при "рождении" в случайном месте наделяется
случайными скоростями по осям}
end;
readln;
while not keypressed do
for i:=1 to max do
begin
a[i].Move;delay(1)
end;
readln
end.

Урок 5.

Попробуем создать графический объект произвольной формы SimSprite, своего рода спрайт, только примитивный. Он может передвигаться только на черном фоне, т.к. наследует алгоритм движения предка (стирает свое изображение, перерисовывая себя черным цветом). Попробуем создать синего жучка. Его изображение вполне можно разместить в матрице 9x9 точек.
Для задания спрайта определим тип ImageArr — массив 9 на 9 байт. 
{$R-,Q-}
uses Graph13h,MyObj,crt;

type
ImageArr=array[0..8,0..8] of byte;
SimSprite=object(GOb)
image: ImageArr;
constructor Init(var aimage:ImageArr;ax,ay:integer;
acolor:byte);
procedure Draw(acolor:byte); virtual;
end;
constructor SimSprite.Init(var image:ImageArr;ax,ay:integer;
acolor:byte);
begin
inherited Init(ax,ay,acolor);
Image:=aImage;
end;
procedure SimSprite.Draw(acolor:byte);
var
i,j: byte;
begin
for i:=0 to 8 do
for j:=0 to 8 do
if acolor<>0 then PutPixel(x+j,y+i,image[i,j])
else PutPixel(x+i,y+j,0)
end;
const max=50;
var a:array[1..max] of SimSprite;
i:byte;
const bug:ImageArr=((0,0,0,0,9,0,0,0,0),
(0,0,1,1,1,1,1,0,0),
(1,0,1,1,9,1,1,0,1),
(0,1,1,6,9,6,1,1,0),
(0,1,1,1,9,1,1,1,0),
(0,1,1,1,1,1,1,1,0),
(1,0,1,1,1,1,1,0,1),
(0,0,0,1,1,1,0,0,0),
(0,0,0,0,14,0,0,0,0));
begin
Screen13h;
Randomize;
for i:=1 to max do
a[i].Init(bug,20+random(280),20+random(160),1);
for i:=1 to max do a[i].Show;
readln;
while not keypressed do
for i:=1 to max do
a[i].Move(random (3)-2,random(3)-2);
readln
end.

Получилось! Жучки целеустремленно бегут к левому верхнему углу экрана, где и пропадают с тем, чтобы вновь появиться в правом нижнем углу. 
Примечание Вопрос: Как сделать, чтобы "жучок” бегал по травке? 
Ответ: Для этого придется усовершенствовать метод Draw. Перед выводом спрайта необходимо запомнить изображение под ним в массиве 9ґ9 (придется добавить еще одно поле в объект SimSprite, его можно назвать, например, Background). Для того чтобы сохранить картинку под спрайтом, ее можно просканировать по точкам с помощью процедуры GetPixel, возвращающей цвет точки экрана с координатами x, y. 

Function GetPixel(x,y: integer): byte;
begin
GetPixel:=Mem[$A000:(y*320 + x)];
end;

 Для того чтобы стереть спрайт, достаточно вывести по точкам массив Background. Если нужно нарисовать спрайт, мы выведем только те точки спрайта, цвет которых отличается от "прозрачного”. Номер прозрачного цвета принимают, как правило, за нуль. Можно написать так: 
... 
if image[i,j]<>0 then PutPixel(x+j,y+i,image[i,j]) 

Урок 6. 
На этом занятии наша задача — моделирование движения живого существа (жучка), меняющего направление своего движения, но при этом передвигающегося всегда головой вперед. 
Для этого прежде всего нужно научиться "поворачивать” матрицу, задающую изображение. 
Для поворота спрайта достаточно поменять местами строки и столбцы матрицы, изменив их последовательность на противоположную (иначе получится просто зеркальное отражение). Поэкспериментируйте на бумаге с простым спрайтом 4ґ4 точки. Для формирования повернутой картинки используем временный массив TempArr. 

procedure Bug.RT90;
var
i,j:byte;
TempArr:ImageArr;
begin
for i:=0 to 8 do
for j:=0 to 8 do TempArr[i,j]:=image[8-j,i];
for i:=0 to 8 do
for j:=0 to 8 do image[i,j]:=TempArr[i,j];
end;

procedure Bug.LT90;
var
i,j:byte;
TempArr:ImageArr;
begin
for i:=0 to 8 do
for j:=0 to 8 do TempArr[i,j]:=image[j,8-i];
for i:=0 to 8 do
for j:=0 to 8 do image[i,j]:=TempArr[i,j];
end;

Теперь попробуем создать специализированного потомка GOb по имени Bug. Данный объект умеет перемещаться в направлении Direction (0ЈDirectionЈ3) со скоростью velocity 
Характеристика Direction определяет направление согласно рисунку. 

{$R-,Q-}
uses Graph13h,MyObj,crt;
type
ImageArr=array[0..8,0..8] of byte;
Bug=object(GOb)
image: ImageArr;
direction,
velocity:integer;
constructor Init(var aimage:ImageArr;ax,ay:integer;
acolor,avelocity:byte);
procedure Draw(acolor:byte); virtual;
procedure RT90;
procedure LT90;
procedure Move;
end;
constructor Bug.Init(var aimage: ImageArr;ax,ay:integer;acolor,avelocity:byte);
begin
inherited Init(ax,ay,acolor);
Image:=aImage;
direction:=0; {все жучки в момент рождения повернуты головой на север}
velocity:=avelocity;
end;
procedure Bug.Draw(acolor:byte);
var
i,j: byte;
begin
for i:=0 to 8 do
for j:=0 to 8 do
if acolor<>0 then PutPixel(x+j,y+i,image[i,j])
else PutPixel(x+j,y+i,0)
end;
procedure Bug.RT90;
var
i,j:byte;
TempArr:ImageArr;
begin
for i:=0 to 8 do
for j:=0 to 8 do TempArr[i,j]:=image[8-j,i];
for i:=0 to 8 do
for j:=0 to 8 do image[i,j]:=TempArr[i,j];
direction:=direction+1;
if direction>3 then direction:=0;
{наши методы RT90 и LT90 не только поворачивают спрайт,
но и меняют направление движения}
end;
procedure Bug.LT90;
var
i,j:byte;
TempArr:ImageArr;
begin
for i:=0 to 8 do
for j:=0 to 8 do TempArr[i,j]:=image[j,8-i];
for i:=0 to 8 do
for j:=0 to 8 do image[i,j]:=TempArr[i,j];
direction:=direction-1;
if direction<0 then direction:=3;>
end;
procedure Bug.Move;
begin
case direction of
0: inherited move(0,-velocity);
1: inherited move(velocity,0);
2: inherited move(0,velocity);
3: inherited move(-velocity,0);
end;
end;
const max=30;
var a:array[1..max] of Bug;
i:integer;

const {спрайт-матрица 9x9 точек:}
BugSprite:ImageArr=((0,0,0,0,9,0,0,0,0),
(0,0,6,1,1,1,6,0,0),
(1,0,1,1,1,1,1,0,1),
(0,1,1,1,9,1,1,1,0),
(0,1,1,9,9,9,1,1,0),
(0,1,1,1,9,1,1,1,0),
(1,0,1,1,1,1,1,0,1),
(0,0,0,1,1,1,0,0,0),
(0,0,0,0,14,0,0,0,0));
begin
Screen13h;
Randomize;
for i:=1 to max do
a[i].Init(BugSprite,20+random(280),20+random(160),1,random(3)+1);
for i:=1 to max do a[i].Show;
readln;
while not keypressed do
begin
a[1+random(max)].RT90;a[1+random(max)].LT90;
for i:=1 to max do
begin
a[i].Move;
delay(2);
end;
end;
readln
end.

Заключение 
При разработке стандартных элементов сложной программы было бы нерационально писать весь код "с нуля”. Достаточно взять за основу ряд объектов, разработанных профессиональными программистами, и дополнить их своими полями данных и методами, чтобы они удовлетворяли вашим требованиям. Некоторые стандартные методы можно переписать заново (перекрыть), если они вас не устраивают. Так проявляются два фундаментальных свойства объектов
наследование (способность использовать методы и поля предка, причем доступ к его исходному тексту необязателен); 
инкапсуляция (способность объекта включать в себя новые поля, методы и даже объекты). В последнем случае объект называют группой.
Мы не смогли рассмотреть третье важное свойство объектов — полиморфизм. Оно упоминалось в неявном виде, когда шла речь о конструкторе и виртуальных методах. Для демонстрации полиморфизма необходимо научиться создавать и уничтожать объекты во время выполнения программы, что требует владения техникой работы с динамическими переменными. 

Авторы: А.А. Семенов, А.Г. Юдина

По материалам:  http://inf.1september.ru


Домашнее задание 

1. Написать следующие процедуры: 

FillScreen(color: byte) — заливает экран цветом color;
HLine(x1,x2,y: integer; color: byte) — рисует горизонтальную линию цвета color;
VLine(x,y1,y2: integer; color: byte) — рисует вертикальную линию цвета color.

Примечание. Почему адрес ячейки оперативной памяти (например, $A000:16) записывается так странно?

2. Дополнить модуль Screen13h процедурой FillRect(x1,y1,x2,y2:word;color:byte), которая заливает прямоугольник с заданными вершинами цветом color.

3*. Дополнить библиотеку процедурой Line(x1,y1,x2,y2:word;color:byte) для рисования произвольного отрезка, заданного двумя точками.

4. Сделать так, чтобы звезды двигались с одной скоростью паралелльно диагонали экрана или случайным образом (как броуновские частицы). Дополнить листинг объектным типом Cross, экземпляры которого на экране выглядят как крестики 3ґ3 точки. Если новый тип создан, то простой заменой описания 
var a: array [1..max] of Point;
на
var
a: array [1..max] of Cross;
точки превращаются в элегантные крестики! А поведение крестиков останется тем же, что и у точек. Это и неудивительно, ведь у объектов Point и Cross общий предок GOb.

5. Разработать методы LT90 и RT90, поворачивающие спрайт налево и направо на 90 градусов. Как вы уже догадались, мы хотим научить жучков поворачиваться в процессе движения и бежать все время головой вперед, как поступают все нормальные насекомые. В следующий раз наш объект получит новые методы и будет моделировать поведение живых существ! 

6*. Программы: аквариум с рыбками, тетрис.

Для любознательных:

Златопольский Д.М. Кривая дракона. 

Наш опрос
Сколько времени вы обычно проводите за комьпютером?
Всего ответов: 970

Друзья сайта
  • Министерство образования РБ
  • Официальный портал подготовки к ГИА и ЕГЭ
  • Всероссийская олимпиада школьников
  • Федеральный портал Российского образования
  • Институт развития образования РБ

  • Статистика

    Онлайн всего: 1
    Гостей: 1
    Пользователей: 0

    Copyright MyCorp © 2017 Бесплатный конструктор сайтов - uCoz