|
Программирование >> Рекурсивные объекты и фрактальные узоры
struct TMirror { Idouble X, y; Idouble angle, k; Idouble b; Idouble y above, y below; крайние ординаты зеркала bool prev iter reflected; отражало ли это зеркало на предыдущей итерации int pos; int length; номер отразившего зеркала длина зеркала vector <TMirror> mirrors; TLaser laser; массив зеркал лазер Функция-обработчик события OnClick кнопки B Initialize заполняет объекты laser и mirrors данными, считанными из полям Initialize: void { .fastcall TF Main::B InitializeClick(TObject *Sender) очистка экрана (рисование будет осуществляться прямо по канве формы) F Main->Canvas->FillRect(Rect(0, О, Width, Height)); считывание в поток нулевой строки из ТМешо; istringstream is(Il Initialize->Lines->Strings[0].c str()); инициализация лазера is laser.х; is laser.у; is laser.angle; ось ординат направлена вниз laser.angle = (-1)*laser.angle*M PI/180.; составляем уравнение прямой лазера laser.к = tan(laser.angle); laser.b = laser.у - laser.k*laser.x; заполняем массив зеркал и выводим зеркала синим цветом F Main->Canvas->Pen->Color = clBlue; mirrors.clear(); TMirror mirr; for (int i = 1; i < M Initialize->Lines->Count; i++) istringstream is(M Initialize->Lines->Strings[i].c str()) is mirr.x; is mirr.y; is mirr.angle; ось ординат направлена вниз mirr.angle = -mirr.angle*M PI/180.; is mirr.length; mirr.к = tan(mirr.angle); mirr.b = mirr.y - mirr.k*mirr.x; рисуем зеркала F Main->Canvas->MoveTo(mirr.x - mirr.length*cos(mirr.angle), mirr.y below = mirr.y - mirr.length*sin(mirr.angle)) F Main->Canvas->LineTo(mirr.x + mirr.length*cos(mirr.angle), mirr.y above = mirr.y + mirr.length*sin(mirr.angle)) mirr.prev iter reflected = false; mirrors.push back(mirr); laser.у - 4, laser.у + 4); графически обозначим позицию и направление движения луча лазера F Main->Canvas->Pen->Color = clRed; F Main->Canvas->Ellipse(laser.X - 4, laser.X + 4, F Main->Canvas->Pen->Style = psDot; F Main->Canvas->MoveTo(laser.X, laser.у); F Main->Canvas->LineTo(laser.X + 50*cos(laser.angle), laser.у + 50*sin(laser.angle)); F Main->Canvas->Pen->Style = psSolid; Обработчик события OnClick кнопки B Step выводит очередной отрезок лазерного луча. И зеркало, и луч представляют собой участки прямых, заданных линейными уравнениями Y = КХ + и У = КХ + В. Эти прямые либо пересекаются, и абсцисса точки пересечения равна (В, - B2)/(Kj - Kj), либо параллельны (К = Kj). Существование точки пересечения прямых ещё не гарантирует встречи луча с зеркалом, потому что эта точка может лежать за пределами зеркала. typedef long double Idouble; struct TLaser { Idouble X, y; отрезок задается выражением вида у = к*х + b Idouble angle, к; Idouble b; зеркало лазер Рнс 2.5. Проверка на 1ум9надпежносгъ зеркалу точки пересечения гцммых Функция GetlntersectionPoint(int indx, TMirrors mirror) проверяет, попадает ли лазер в зеркало mirrors [ indx], записывает точку пересечения в поля х и у переданного объекта mirror и возвращает true в случае успеха: bool GetlntersectionPoint(int indx, TMirror& mirror) { mirror.x = (laser.b - mirrors [indx] .b) / (mirrors [indxl . к - laser.к); mirror .у = mirror. x*laser .к + laser-:b; return (mirror.у <= mirrors[indx].y below && mirror.у >= mirrors[indx].y above); Эта йроверка не учитывает, во-первых, направление лазера (так, вызов GetlntersectionPoint О для зеркала, расположенного за лазерной пушкой, вернёт true), а во-вторых, что лазер изменит направление, встретившись с первым же зеркалом на своём пути. Поиск ближайшего зеркала - задача простая. Определив набор точек пересечения, можно просто выбрать точку с ближайшей к лазеру абсциссой (этот критерий достаточен,-поскольку ордината точки пропорциональна её абсциссе - луч лазера ведь лежит на прямой линии). С направлением же дело обстоит несколько сложнее, но здесь можно немного схитрить. Важность зачёта направления луча иллюстрирует рис. 2.6. Если просто выбрать ближайшее к точке последнего отражения лазера зеркало, луч прожжёт только что отразившее его зеркало насквозь и устремится вверх. Рис 2.6. Учат напрваленмя пуча лазера при поиске точек соприкосновения Теперь - обещанная хитрость (там, где кончается наука - математика, начинается искусство - программирование). Всё, что надо сделать - это продолжить луч на небольшой отрезок и выяснить, приближается луч к зеркалу или удаляется от него. Если зеркало выбрано правильно, луч, конечно же, будет двигаться в его сторону. Функция isDirectionCorrect (const TMirrors m) возвращает true, если зеркало m расположено по ходу луча: bool isDirectionCorrect(const TMirror& m) { return (sqrt( pow((laser.x + cos(laser.angle) - m.x), 2) + pow((laser.y + sin(laser.angle) - m.y), 2)) < sqrt( pow((laser.X - m.x), 2) + pow((laser.y - m.y), 2) )); Здесь луч продолжается на отрезок единичной длины (можно продолжить и на меньшую длину, если зеркала расположены очень близко друг к другу). Затем расстояния между зеркалом и каждой из двух точек лазера (исходный конец луча, продолженный конец луча), вычисленные по формуле R = sqrt((X, - ХУ + (Y, - Yj)), сравниваются между собой. К тому же при решении системы уравнений не учитывается направление лазерного луча. Простую проверку на попадание лазера в зеркало можно организовать, сравнивая ординату найденной точки пересечения прямых с ординатами крайних точек зеркала (рис. 2.5). TMirror ш; int i ; static int rem index; номер зеркала, отразившего предыдущий отрезок bool allow - false; разрешение на отражение луча собираем зеркала, которые луч пересекает for (i = 0; i < mirrors.size(); i++) если зеркалу можно отражать и уравнения имеют общую точку it(mirrors 111.prev itGr reflccted && GctlntersectionPoint(i, m)) { m.pos = i; запомнить позицию зеркала в глобальном массиве vm.push back(m); поместить зеркало во временный массив F Main->Canvas->Ellipse(т.х - 4, т.у - 4, т.х + 4, т.у + 4); if (vm.sizeO > 0) { хотя бы одно зеркало пересе- кается прямой луча на следующей итерации это зеркало уже будет доступно mirrors(rem index].prev iter reflected = false; не забываем сбросить значение rem index Idouble delta, d; for (delta = fabs(vm[rem index = i = 0].x - laser.x); i < vm.sizeO; i + + ) ищем ближайшее удовлетворяющее направлению движения луча if ( (d-= fabs(vm[i].x - laser.х)) <= delta && CorrectDirection(vm[i])) delta = d; rem index = i; запоминаем его номер во временном массиве allow = true;/ if (allow) { F Main->Canvas->Pen->Color = clRed; F Main->Canvas->MoveTo(laser.X, laser.у); После того, как стало ясно, от какого зеркала отражать, выясним теперь куда, собственно, отражать. Как уже говорилось, угол отражения луча света в однородной среде равен злу падения. Для горизонтального зеркала формула очевидна: если угол падения равен а, то угол отражения вычисляется по формуле: а = 2?! - а. Для угла mirror angle в диапазоне [О, п/2] угол очередного отрезка луча равен: laser angle = 2*М Р1 - laser angle + 2*mirror angle А для угла в диапазоне [п/2, п]: laser angle = -laser angle - 2*(M PI - mirror angle) Поскольку все зеркала в нашей системе покрыты отражающим слоем с обеих сторон, рассматривать углы наклона в диапазонах [п, Зп/2] и [Зтс/2,2п] не требуется. Таким образом, функция отражения луча будет выглядеть так: void Reflect(Idoublefc laser angle, Idouble mirror angle) { строки /*!*/, 1*2*I, I нужны для того, чтобы математически / / полученные формулы не изменили своего вида из-заразнонаправленности математической и экранной осей ординат /*1*/ laser angle = -laser angle; 1*2*1 mirror angle = -inirror angle; if (inirror angle >= 0 && inirror angle <= M PI 2) laser angle = (2*K PI - laser angle + 2*inirror angle) ; else if (inirror angle >= M PI 2 && mirror angle <= M PI) laser angle = (-laser angle - 2* (]У1 Р1 - mirror angle)) ; else Application->MessageBoxA(He соблюдён диапазон , Неверное значение*, МВ ОК); return; /*3*/ laser angle = -laser angle; } Теперь займёмся основной функцией - вычерчиванием очередного отрезка луча: void fastcall TF Main::B StepClick(TObject *Sender) зеркала, уравнения прямых которых имеют общие точки с прямой лазера vector <TMirror> vm;
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |