Архив Январь 2010
Проблемы проектирования
Внезапно обнаружилась такая небольшая деталь. Мы знаем, что мы должны сделать (функциональность), но не знаем, как оно должно выглядеть. В смысле интерфейс еще не определен.
Например, можно инкапсулировать все внутрь класса механизма и максимально упростить его интерфейс. Скажем, будет что-то типа такого:
Mechanism M;
M.AddKPair("O", KP_TYPE_SLIDING);
M.AddKPair("A", KP_TYPE_SLIDING);
M.AddSegment("OA",13.0);
M.GetKPairByName("O").ConnectTo( M.GetSegmentByName("OA") );
M.GetKPairByName("A").ConnectTo( M.GetSegmentByName("OA") );
Или же можно предоставить пользователю всю иерархию классов, чтобы он сам создавал кинематические пары и звенья, когда ему будет нужно.
Короче говоря, при желании пишите в комментах любые идеи по поводу того, как мог бы выглядеть результат, а точнее – что должно получиться в результате.
Еще одна проблема состоит в следующем. Для проведения динамического анализа механизма, нужно рассмотреть его состояния во времени. Состояние механизма в общем-то определяется положением звеньев и кинематических пар. Вопрос: стоит ли хранить все состояния внутри одного объекта или лучше сделать массив объектов?
class Something
{
//...
SomeData Data[N];
//...
};
V.S.
class Something
{
//...
SomeData Data;
//...
};
//...
Something Things[N];
Как по мне – второй подход является наилучшим. Для меня это настолько очевидно, что я даже не знаю, как это объяснить. Немного подумав, можно привести и список плюсов и минусов второго подхода.
“+”:
- На основе второго варианта можно реализовать первый, при необходимости
- Состояние механизма будет представляться отдельным объектом, так что получится массив состояний. А если пытаться перенести массив внутрь – то в результате надо будет создавать классы GPoint, KPair и Segment с массивами внутри. Оно надо?
“-”:
- При создании массива из N объектов, конструктор будет вызван N раз. Хотя я не думаю, что это как-то серьезно может повлиять на скорость программы при N<1000
И еще парочка проблем. Звенья хранят ссылки на кинематические пары, кинематические пары – ссылки на звенья. Получаются классы, ссылающиеся друг на друга. Здесь я вижу два выхода.
Первый состоит в том, чтобы создать базовый класс и все классы наследовать от него. При этом в базовом классе реализовать некоторые функции, которые потом позволят идентифицировать класс объекта в runtime. Что-то типа такого:
#include <iostream>
using namespace std;
class TObject
{
public:
virtual string GetName();
};
class TPoint: public TObject
{
string GetName();
};
string TObject::GetName()
{
return "TObject";
};
string TPoint::GetName()
{
return "TPoint";
};
int main()
{
TObject *b, *p;
b = new TObject;
p = new TPoint;
cout << b->GetName() << endl;
cout << p->GetName() << endl;
delete b;
delete p;
return 0;
}
Думаю, результат работы программы комментировать не стоит.
Второй вариант – да просто взять и написать:
Первый класс:
//file A.h
#ifndef A_H_INCLUDED
#define A_H_INCLUDED
#include "B.h"
class B;
class A
{
public:
int data;
int GetData();
B *objB;
};
#endif // A_H_INCLUDED
//file A.cpp
#include "A.h"
int A::GetData()
{
return data;
}
Второй класс:
//file B.h
#ifndef B_H_INCLUDED
#define B_H_INCLUDED
#include "A.h"
class A;
class B
{
public:
int data;
int GetData();
A *objA;
};
#endif // B_H_INCLUDED
//file B.cpp
#include "B.h"
int B::GetData()
{
return data;
}
И их использование:
//file main.cpp
#include <iostream>
#include "A.h"
#include "B.h"
using namespace std;
int main()
{
A *a = new A;
B *b = new B;
a->data = 1;
a->objB = b;
b->data = 2;
b->objA = a;
cout << a->objB->objA->objB->GetData() << endl;
cout << b->objA->objB->objA->GetData() << endl;
return 0;
}
Вторая проблема состоит в том, стоит ли различать точки, геометрически имеющие одни и те же координаты (причем в любой момент времени), но логически принадлежащие разным звеньям?
Мое личное мнение – не стоит, но ArcSin почему-то настаивает. Посмотрим, что получится в итоге.
Ariel
Пока есть свободное время (сессия все-таки) и нечем заняться, решил перевести еще одну песню.
Rainbow – Ariel (с альбома Stranger In Us All 1995)
(Ritchie Blackmore, Candice Night)
Послушать можно здесь.
|
I search for her in the dead of night A silhouette lit by candle light In a whispered word she is gone Familiar stranger without a name So far away In the mirror you can see her face Just an image lost in fantasy Oh, Ariel Lost in a distant dream I’m so lost in a distant dream Oh, Ariel I’m so lost in a distant dream Lost, I’m so lost |
Я ищу ее в темноте ночи - Силуэт в свете свечи. Шепнув слово, она ушла. Близкая незнакомка без имени… Так далеко… В зеркале видишь ее лицо, Просто образ, затерянный в памяти, О, Ариэль! Потерянного в отдаленных мечтах, Я потерян в отдаленных мечтах, О, Ариэль! Я потерян в отдаленных мечтах, Потерян, да я потерян, |
Теперь про особенности перевода.
Во-первых, я не мог найти нормальный оригинальный текст. Я точно слышал “she’s an angel”, но в большинстве источников было просто “an angel”. Здесь помогла AltaVista, с ее помощью был найден офигенный сайт о Rainbow. Вывод: Google не панацея, не стоит забывать о других поисковиках.
Familiar Stranger – буквально “знакомый незнакомец”. Я был уверен, что профессиональные переводчики уже перевели эту фразу, но похоже, что нет.
В википедии написано: “Familiar stranger, an individual that is recognized, but not known”, впрочем, как и ожидалось. Еще в инете я наткнулся на это, где написано :
The Familiar Stranger is a social phenomenon first addressed by the psychologist Stanley Milgram in his 1972 essay on the subject. Familiar Strangers are individuals that we regularly observe but do not interact with. By definition a Familiar Stranger (1) must be observed, (2) repeatedly, and (3) without any interaction. The claim is that the relationship we have with these Familiar Strangers is indeed a real relationship in which both parties agree to mutually ignore each other, without any implications of hostility. A good example is a person that one sees on the subway every morning. If that person fails to appear, we notice.
Familiar Strangers form a border zone between people we know and the completely unknown strangers we encounter once and never see again. While we are bound to the people we know by a circle of social reciprocity, no such bond exists between us and complete strangers. Familiar Strangers buffer the middle ground between these two relationships. Because we encounter them regularly in familiar settings, they establish our connection to individual places.
Т.е. familiar stranger – это люди, которых мы регулярно видим, но в контакт с ними не вступаем. Например, мой лектор по дифурам.
Darkened room – тоже ожидал, что это какое-то клише и имеет устоявшийся перевод. В процессе поиска выяснил, что есть такой 8-минутный фильм (хм, надо посмотреть), но никакого стандартного перевода не нашел.
С остальным вроде все понятно, только мне интересно, кто же это такая – Ариэль.
Выяснилось, что имя может иметь множество подразумевающихся значений. В том числе:
- Арабская газель, очень выносливая и живучая кстати
- Не то мексиканка, не то американка, модель и девушка-рестлер Shelly Martinez, есть много сходств с арабской тезкой
- Архангел Самуил, покровитель музыкантов, поэтов и писателей
Искренне надеюсь, что Candice Night не имела в виду ничего из вышеперечисленного… Короче, был бы рад узнать, кто такая Ариэль.
Sail away
Все-таки, знание английского языка – важная штука. Особенно, если ты хочешь:
- … стать хакером
- … или хотя бы понимать, о чем говорят хакеры
- … или слушать заграничную музыку
- … и еще много много пунктов
Так что я начинаю потихоньку приобщаться к зарубежной культуре и буду переводить тексты любимых групп.
Deep Purple – Sail away (с альбома Burn 1974)
(Ritchie Blackmore, David Coverdale)
Послушать можно здесь.
| If you’re driftin’ on an empty ocean With no wind to fill your sail, The future, your horizon, It’s like searchin’ for the Holy Grail. You feel there’s no tomorrow Time will show, Sail away tomorrow, Oh, woman, I keep returnin Feel like I’m goin’ to surrender, Time will show, Sail away tomorrow, |
Если ты дрейфуешь в пустом океане И нет ветра в твоих парусах, То твое будущее – это твой горизонт. Это как поиски Святого Грааля. Ты чувствуешь, что завтра не настанет, Время покажет, Я уплываю завтра, О, женщина, я продолжаю возвращаться, Такое чувство, что я собираюсь сдаться, Время покажет, Я уплываю завтра, |
Критика приветствуется.
Из последнего
Во-первых, поздравляю всех с прошедшим праздником. 19 января обычно принято купаться, что, собственно, я и сделал
Жаль, что Днепр не замерзает, потому что купаться в проруби в Донце было куда более захватывающим мероприятием.
Во-вторых, надо учить математику. Столкнулся с такой задачкой: вывести формулу для суммы квадратов натуральных чисел от 1 до n. Самое очевидное решение основано на том, что данная сумма квадратов (интуитивно) является многочленом степени 3. Т.е. формула будет вида A*x3 + B*x2 + C*x + D, теперь осталось вручную посчитать сумму для первых четырех чисел и найти коэффициенты.
Есть еще один способ, а именно: составить рекуррентную последовательность fn+1 = fn + (n+1)2 и задать условие f0=0. После этого можно найти функцию f используя знания, полученные на 1-ом курсе по дискретке. Честно говоря, так и не решил. Зато вспомнил производящие функции.
В-третьих, есть классный сайт Project Euler.net, где можно проверить свои знания и умения в программинге и матике. Больше математики. Интересный момент в том, что решать задачу можно как угодно. Главное – решить. Полученный на своей машине (или листочке) ответ надо ввести в поле ввода, после чего обрадоваться или решать задачу дальше.
В-четвертых, у нас будет экзамен по матану. Внезапно и неожиданно наш препод нас обломал. Хм…
Значит, до 30-го надо осилить 2 семестра практики по матану. Аминь.
В-пятых, с каждым днем все больше и больше кайфую от Deep Purple. Надо перевести их песни, хотя смысл и так понятен.
Структурный анализ
Продолжаем конкретизировать задачи, цели и методы их достижения.
Предположим, все звенья, кинематические пары и связи между ними уже хранятся в памяти, пока что не важно, каким образом.
Для дальнейшего исследования механизма необходимо сначала провести структурный анализ.
Один из возможных вариантов – делать это с помощью графа.
На самом деле это единственный нормальный вариант, до которого я смог додуматься
.
Радует то, что в какой-то статье (ссылку дам позже) работники какой-то кафедры СПбГУ ИТМО описывали свою программу, созданную только для структурного анализа, причем тоже использовали графы.
Итак, в чем суть. Построим граф, вершинами которого будут звенья механизма, а ребрами – кинематические пары.
Сначала надо указать, что приводит механизм в движение. Выбранные пользователем стартовая КП и стартовое звено образуют группу Ассура первого класса.
Далее начинается собственно анализ.
Пока что будем реализовывать поиск групп Ассура 2-го класса (используемый алгоритм легко модифицировать для более сложных задач, мы оставляем это на будущее). Группа Ассура 2-го класса – это набор из 2 КП и 3 звеньев, которые соединяются этими КП. Искать эти группы надо по правилу: 2 вершины должны быть уже просчитаны на данный момент, тогда мы можем просчитать третью.
Для примера, пусть есть следующий механизм:
Качество рисунков в этой статье не ахти, но это потом поправлю.
Этому механизму соответствует следующий граф:
Числа в вершинах графа – это номера соответствующих звеньев механизма. Введена фиктивная вершина “0″, которая обозначает “землю”. Это сделано для упрощения дальнейшего анализа. “Землей” будем называть любую просчитанную вершину. Буквы рядом с ребрами – это имена соответствующих кинематических пар механизма.
“Земляные” вершины будем красить в зеленый темный. Значит, граф превращается в такой:
С учетом того, что пользователь указал 1-ую группу Ассура, имеем следующее:
Звенья 0, 1 и точка O принадлежат теперь 1-ой группе Ассура, 1-го класса.
Далее алгоритм похож на обход графа в ширину. На каждой итерации вершины, соседние с темными зелеными, окрашиваются в светлый зеленый. Теперь задача состоит в том, чтобы найти путь из темной зеленой вершины в другую темную зеленую. По построению, этот путь будет содержать две светлых зеленых. Три ребра и две светлых зеленых вершины выделяем в отдельную группу Ассура. По-моему можно обойтись и без перекрашивания вершин в светлые зеленые, но я подумаю и вспомню, зачем же это все-таки понадобилось…
Алгоритм в действии:
- Вершины 2, 3 и 5 – соседние с темными зелеными. Красим их в светлый зеленый.

- Нашли путь 1 A 2 B 3 C 0. Выделяем в отдельную группу Ассура второго класса ребра A, B, C и вершины 2, 3.

- Вершины 4 и 7 теперь соседние с темными зелеными. Красим их в светлый зеленый.

- Нашли путь 0 D3 5 D2 6 D1 3 (или в другую сторону, не важно). Выделяем вершины 5, 6 и ребра D1, D2, D3 в отдельную группу Ассура.

- Вершина 6 теперь стала соседней с темной зеленой. Перекрашиваем ее в светлый зеленый:

- И последнее: вершины 6, 7 и ребра E, F1, F2 выделяем в группу Ассура.

Надо еще раз обратить внимание на то, что ребра графа – это кинематические пары механизма, а вершины графа – это его ребра.
Каждый раз, когда какая-то часть механизма обосабливается в отдельную группу Ассура, создаем объект класса AssurGroup, ставим ему соответствующие значения свойств и добавляем в указатель на этот объект в список.
Полученный список позволяет провести кинематический анализ.
Если просчитаны все группы Ассура с 1-й по i-ую в этом списке, то есть все данные для вычисления (i+1)-ой. Таким образом, провести кинематический анализ можно за 1 проход списка.
Проект
Сейчас я хочу рассказать об одном проекте, который для меня станет чем-то вроде темного коридора с большим количеством граблей, через который мне все равно когда-либо придется пройти.
Это печально, но занимаясь программированием больше 2 лет, я не успел написать ни одного серьезного проекта. Участвовал в олимпиадах, решал задачки на acm.timus.ru, даже использовал OpenGL. Но ничего большого и красивого не написал.
Итак, мы с другом решили написать программу, связанную с ТММ (теория механизмов и машин).
Основные функции программы:
- Дать пользователю возможность собрать механизм путем создания списка кинематических пар и звеньев, а также задания связей между ними.
- Структурный анализ механизма
- Кинематический анализ механизма
- Динамический анализ механизма
Теперь по порядку.
Что такое механизм? Будем считать, что это набор твердых тел, которые совершают некоторые движения и взаимодействуют друг с другом. Механизмы широко используются в технике для многих целей. Например, передача движения, точное позиционирование и так далее. Звено – это часть механизма. Звенья соединяются кинематическими парами. Кинематические пары могут быть вращательными или поступательными, вообще говоря – кинематическая пара это нечто, что устанавливает ограничение на движения звеньев друг относительно друга. В физических движках это называется joints.
Структурный анализ механизма – это логическое разделение механизма на отдельные части, которые называются группами Ассура. Это делается для упрощения кинематического анализа.

На картинке: слева – механизм, который будет собирать пользователь, все остальное – это части, на которые будет разбиваться этот механизм для анализа.
Кинематический анализ – это, по сути, решение прямой задачи кинематики. Зная угол поворота начального звена и структуру механизма, нужно определить положение всех кинематических пар и параметры звеньев. Также сюда входит определение скоростей и ускорений для каждой кинематической пары в заданные моменты времени.
Динамический анализ – определение сил, дейсвтующих на звенья в данные моменты времени. Здесь должны учитываться масса звена, возможно – сила сопротивления воздуха.
Все вышесказанное касается ядра программы. Кроме этого, хотелось бы иметь красивенький интерфейс с редакторами объекта, красивую прорисовку и так далее. Я так же не отрицаю возможность добавления в будущем возможности работы с 3d-механизмами.
Надеюсь, проект будет завершен и результаты обрадуют меня и всех, кто сможет их наблюдать.
Какую пользу должен принести проект? Что интересно мне:
- Командная разработка. Хотя, учитывая, что нас двое, можно попробовать что-то вроде eXtreme Programming
- Проектирование и ООП. На данный момент имеются некоторые наброски классов и связей между ними
- Возможно, применение юнит-тестов, чего я раньше никогда не делал.
- Практика использования C++
Кроме этого, программу кто-то сможет использовать (судя по тому, что есть аналогичные).
Подобные проекты:
- Список программ на сайте ДонНТУ, там есть “ТММ (курсовой проект)” (windows-программа)
- Mechanics Studio .NETНевероятно красивый проект, но, судя по всему, мертвый. Скриншоты впечатляют.
Чуть-чуть о классах
Как известно, в университетах надо делать лабораторные работы.
Если верить старшекурам, то реакция нашего теперешнего препода по проге на несвоевременную сдачу лабок весьма негативная. В конце концов, рискуешь быть нахрен выпиленым из университета.
Даже учитывая всю бредовость заданий, все равно хочется поебать моск найти в них что-то интересное.
Вот, например, такой фрагмент:
“Побудувати та візуалізувати список файлів, що є у каталозі (із підкаталогами включно) Каталог обирається візуально. Усі дії над файлами заносити у протокол роботи, що можна візуалізувати. Для відібраного файлу із зображенням знайти усі HTML-файли, що посилаються на нього. ” (это конкретно мой вариант).
Как же быть? Чуть-чуть подумав, я сотворил новый класс Task4() для решения этой задачи, который имеет свои параметры в конструкторе, собственно после завершения работы конструктора объект и удаляется. Может, это идиотский стиль, но сейчас не об этом.
Ситуация такова. На форме/фрейме/_еще_чем_нибудь (лично я использовал C++/wxWidgets ) есть батон.
В обработчике клика по нему – вызывается метод Analyze(). Наконец, в методе Analyze() создается объект класса Task4() с передачей определенных параметров. Изначально список был примерно таким: string path_name, string file_name, list &L.
И вроде все нормально, НО. Вдруг я понял, что хочу, чтобы все было красивенько. Я хочу, чтобы юзер видел информацию типа “Просмотрено ххх файлов, из них ууу содежрат нужную ссылку”.
И почему-то мне в голову пришла ну очень плохая идея. А именно – я решил передавать в конструкторе указатель на callback, принимающий и возвращающий какие-то параметры!
Т.е. я передаю указатель на функцию, имеющую сигнатуру, скажем int f(int*, int*).
Зачем так сложно? Просто потому, что я не хотел пихать цикл обработки файлов в каталоге+подкаталогах в обратотчик нажатия на кнопочку или даже в Analyze.
В общем-то и цикл там не такой уж простой, чтобы делать такую начинку.
В общем, две крайности одной и той же сущности, как оказывается.
1) Выносить цикл в обработчик нажатия – некрасиво
2) Делать с колбэками.. слишком монструозно.
Спустя некоторое время (а правда ведь, что мозг подсознательно ищет решения!), где-то около двух недель, до меня дошло! Оказывается, можно себе упростить жизнь, если в Task4() реализовать что-то наподобие итераторов.
И применять єто как-нибудь вроде:
while (Task4.GetNextFile())
{
//
if (Task4.ProcessCurrentFile())
{
//
cntAll++;
cntSucc++;
//
}
//
}
Кавайненько!



