Список замечаний
Указывайте тему письма: FIT9201<фамилия>_<задача>
НЕ ПРИНИМАЕТСЯ
НЕОБЯЗАТЕЛЬНО
Алгоритмы
TASK: Альфа-канал не использует билинейную фильтрацию. Правильно: достаточно обрабатывать альфу также как другие каналы.
TASK: Не используются файлы ресурсов для хранения и загрузки изображения.
TASK: Билинейная фильтрация работает неверно. Пример: полосы, рябь
TASK: Текстурирование работает неверно.
TASK: В некоторых случаях перекрестие лемнискаты рисуется неверно - лишние пиксели. В большинстве случаев когда прямая через фокусы параллельна оси абсцисс.
TASK: Толщина влияет на Difference - сдвигает остов. Правильно: либо учитывать всю толщину, либо только остов.
TASK: Маленькие лемнискаты рисуются неверно. Правильно: если ваша реализация не позволяет их рисовать, то запретить их рисовать.
TASK: Использование цикла для рисования лемнискаты WxH неверно. Правильно: не рассматривать лишние пиксели. Алгоритм Брезенхема не рассматривает их.
TASK: Необходимо реализовать целочисленный алгоритм растеризации BresenhamLemniscate, который рассматривает минимум точек изображения для их закраски. Хранить кусочки изображений во временных объектах недопустимо.
TASK: Необходимо перебирать точки кривой с адаптивным шагом так, чтобы не было разрывов, которые нужно заполнять (не должно быть Canvas::drawLine)
TASK: Неверная реализация random(). Пример: return (x = (A * x + C) % M); Правильно: учитывать знаковые биты - если биты 30..16, то return ((x & 7FFF0000) >> 16)
Компиляция
TASK: Ошибка компиляции.
TASK: Не выполнено "Название окна проекта FIT9201SURNAME_<имя проекта>, где SURNAME - ваша фамилия латиницей"
TASK: Не выполнено "5. ... Внутри zip-файла находится папка FIT9201<фамилия>_<задача>" (используются латинские буквы)
TASK: Предупреждение компилятора. Пример: integer constant is too large for 'long' type Правильно: исправлять - например, long long value = 0LL
TASK: Предупреждение компилятора. Пример: warning: 'TriangleAnimationModel::textureTriangle' will be initialized after
TASK: Отсутствует файл изображения. Пример: images\\puzzle.png
TASK: Неверное имя сборки. Пример: TARGET = FIT9201KLIMOV_sinus Правильно: FIT9201<фамилия>_<задача>
DIR: Папка проекта содержит лишние файлы и каталоги: debug, release, Makefile.*, *.pro.user, ui_mainwindow.h
DIR: Нет файла проекта: .pro
Визуализация
TASK: Изображение продолжает изменяться, когда пользователь нажал Stop. Пример: изменения на каждый paintEvent
GUI: Рисуется мусор. Пример: нажал Clear, Draw Sin - кроме графика рисуется мусор. Правильно: следить за инициализацией
GUI: При запуске наблюдается разовое дерганье. Пример: резкое изменение области визуализации. Правильно: устранить.
GUI: Нестандартный масштаб. Пример: GRAPHIC_WIDTH * x / (HALF_CYCLES_NUMBER * Q_PI); Правильно: использовать человеко-ориентированные единицы, по-умолчанию, одинаковые для обоих осей.
TASK: Вращение камеры должно быть относительно центра масс (центра bounding box)
TASK: Неудачная реализация "график функции и оси координат (с делениями - штрихи)" Правильно: нарисовать штрихи с осмысленным масштабом. Например: 10 штрихов соответствует 1.
TASK: Очень большое число изменений между кадрами. Пример: 30000 точек за тик таймера.
GUI: Неудачное обновление изображения. Пример: invalidate() Правильно: если в QImage элементы уже есть, то не надо их перерисовывать
GUI: Неудачное обновление изображения. Пример: paintEvent(QPaintEvent*) Правильно: воспользоваться QImage для записи результата и в paintEvent рисовать готовое изображение.
HINT: Смена изображения через DragNDrop должна подстраивать изображение под размеры исходного, т.е. изображение должно рисоваться в исходных размерах. Т.е. если картинка 64x64, то на экране она должна отображаться также. Для этого вероятно требуется также изменить размеры окна.
HINT: Для задания отображения текстуры на треугольник рекомендуется использовать текстурные координаты.
HINT: Init не должен забывать текущую текстуру.
Интерфейс
TASK: GUI не работает - нажатие любых пользовательских элементов не срабатывает.
GUI: Поворот QDial вручную с большим шагом вызывает подтормаживание. Пример: маленькое окно, вращение вручную с маленьким шагом - плавно, с большим - резкое подтормаживание Правильно: скорость визуализации не должна сильно зависеть от шага анимации.
GUI: Неверная обработка событий ресайза.
TASK: Не требуется запоминать предыдущий выбранный треугольник. Правильно: отображать информацию о треугольнике только если он под курсором
GUI: Неудачное расположение элементов окна - много неиспользуемого пространства.
GUI: Тулбар с управляющими элементами залазит на область рисования.
GUI: Использование терминала в оконном приложении. Пример: CONFIG += console Правильно: не использовать без необходимости окна и терминал одновременно.
GUI: Интерфейс сбивается при изменении размеров окна. Пример: кнопка Maximize Правильно: группировать и использовать layout либо запрещать менять размеры окна.
GUI: Возможно логика размещения элементов может быть упрощена.
GUI: Нецелевое использование элемента. Пример: QLabel для задания размещения объектов. Правильно: использовать компоновщики виджетов
GUI: Кнопка Clear не реагирует на новые размеры, пока не нажата кнопка Start.
GUI: Пользователю стоит показывать выбранные настройки. Пример: QColorDialog(Qt::white, ... Правильно: QColorDialog(selectedColor
GUI: Неиспользуемый элемент интерфейса. Пример: QMenuBar Правильно: не размещать бесполезные элементы интерфейса.
GUI: Нет единиц измерения Speed. Правильно: например, добавить %, чтобы при смещении было понятно направление изменения скорости.
GUI: Большое количество элементов интефейса, которые выполняют вспомогательные функции и завязаны на вшитые в код константы.
GUI: Надпись за пределами кнопки. Пример: Set Clearing color
GUI: Большое количество элементов интефейса, которые выполняют вспомогательные функции и завязаны на вшитые в код константы.
GUI: При переходе по вкладкам прыгают элементы пользовательского интерфейса
HINT: Элементы GUI (например, кнопки QPushButton) можно задавать непосредственно в Qt Designer.
Форматирование
CODE: Стиль кодирования плавает - пропущены пробелы. Пример: setPixel(x, y+i, Qt::black) Правильно: отделять пробелами операторы - пример, setPixel(x, y + i, Qt::black);
CODE: Отстуствует блок {} Пример: if (x+i > width() || x+i < 0) continue;
CODE: Размытие переменных по коду. Пример: double x, y; Правильно: уменьшить расстояние между объявлением и использованием переменной.
CODE: Использование нестандартной табуляции. Пример: отступ 2 пробела. Правильно: 4 пробела.
CODE: Лишние пробелы: static_cast<qint64> (C) ) Правильно: static_cast<qint64>(C))
СODE: Нецелевое использование пустых строк. Пример: }[пустая строка]int half_width = w / 2;[пустая строка]//drawing graduations for x-axis Правильно: исключать пустые строки в реализации методов, при необходимости использовать комментарии
CODE: Много пустых строк. Пример: Curve::calcThird() Правильно: использование комментариев для разделения блоков кода является достаточным.
HINT: Рекомендуется использовать #pragma once вместо include guards
Типы данных
CODE: Неверный тип в выражении. Пример: sinusArea.setPixel(i, sinusArea.height()/2, Qt::black); Правильно: QColor(Qt::black).rgb()
GUI: Использование значений ошибки. Пример: color = QColorDialog::getColor(Qt::white, this); - пользователь закрыл диалог по Cancel. Правильно: не терять выбранный цвет и не использовать значение ошибки для закраски
CODE: Неинициализированный явно элемент перечисления. Пример: MAIN_COLORS_HSV_Red, MAIN_COLORS_HSV_Yellow Правильно: инициализировать значением явно.
CODE: Использование приведения типов в стиле Си. Пример: (double) Правильно: использовать операторы приведения типов (например, static_cast)
CODE: Флажки имеющие общее назначение представлены как отдельные переменные. Пример: isDrawSin, isDrawAxis. Правильно: ввести битовую маску и объявить каждый флаг в enum {eNone = 0, eSin = (1 << 0), eAxis = (1 << 1)}; int m_DrawMask;
CODE: Возможно неправильный диапазон. Пример: for(int y = 0; y <= height(); y++) { Правильно: y < height()
CODE: Неудачный формат. Пример: QImage::Format_RGB555 Правильно: QImage::Format_RGB888
CODE: Возможно требуется приведение к ближайшему значению. Пример: static_cast<int>(middleY - sin(arg)*SCALE); Правильно: static_cast<int>(middleY - sin(arg)*SCALE + 0.5);
CODE: Возможно лишнее приведение типов. Пример: double py = OFFSET + static_cast<int> (GRAPHIC_HEIGHT - (GRAPHIC_HEIGHT / 2 * (y + 1)));
CODE: Лишнее приведение типов. Пример: static_cast<int>(GRAPHIC_HEIGHT / 2 - SCALE_LENGTH / 2) - используются int. Правильно: (GRAPHIC_HEIGHT - SCALE_LENGTH) / 2
СODE: Лишнее предварительное объявление класса. Пример: class QImage; Правильно: #include <QImage>
СODE: Неявное приведение типов. Пример: double xs; double xe; int i0 = -xs*size/(xe-xs); Правильно: int i0 = static_cast<int>(... + 0.5);
HINT: Точности float вполне достаточно для работы с HSV
HINT: Для хранения значений можно использовать контейнеры - вместо стандартных массивов. Пример: int sineValues[PLOT_WIDTH]; Правильно: например, QVector (желательно)
HINT: Для задания константы цвета QColor стоит использовать конструктор QColor(r, g, b, a).
Переменные
СODE: Перекрытие областей видимости одноименных переменных. Пример: int x; for(qreal x = ... Правильно: использовать разные имена
CODE: Наличие в коде глобальных переменных.
CODE: Неудачное именование - наличие слова Flag. Пример: clearFlag. Правильно: isClear или bClear
СODE: Неинициализированные переменные. Пример: double x, y; Правильно: double x = 0.0, y = 0.0;
СODE: Неинициализированная явно в конструкторе переменная. Пример: поле класса pixelPerUnit. Правильно: инициализировать все переменные в конструкторе, чтобы инициализация не зависела от порядка вызова методов класса.
CODE: Неудачное имя аргумента функции - совпадает с именем поля класса. Пример: interval. Правильно: либо поле класса именовать m_Interval, либо аргумент функции _interval
CODE: Использование нечитаемых имен переменных либо именование с опечатками. Пример: MAIN_FRAIM_WIDTH Правильно: MAIN_FRAME_WIDTH
СODE: Неиспользуемые переменные. Пример: float scale Правильно: не перегружать код мусором.
СODE: Не проверяются случаи неверной адресации. Пример: image.setPixel(x, y, ... Правильно: проверять лежат ли x и y в допустимом диапазоне либо assert, если свойство алгоритма
CODE: Использование const для интегрального типа при передаче по значению. Пример: void foo(const int) Правильно: void foo(int) (необязательно)
HINT: Часто применяется указание в имени переменной факта, что она является указателем. Пример: QColor *pColor, QColor **ppColor
Константы
CODE: Имена переменных пересекаются с именами констант. Пример: double XS и static const double XS = 0.5 Правильно: не использовать малоинформативные имена и не писать имена переменных в верхнем регистре.
CODE: Магическая константа - строка. Пример: "Sin(x) graphic". Правильно: static const char LABEL_NAME[] = "Sin(x) graphic";
CODE: Неудачное именование констант. Пример: graduationSize. Правильно: все константы в верхнем регистре + static.
CODE: Неудачное именование констант. Пример: PACE Правильно: DELTA_X, ACCURACY
CODE: Загадочная недокументированная группа констант. Пример: RED_IN_SINUS, GREEN_IN_SINUS Правильно: в случае цвета достаточно объявить одну константу (не выделять каналы)
CODE: Лишние константы в заголовочном файле - протекающая абстракция. Правильно: константы, отвечающиеи за детали реализации класса, не выносить на уровень интерфейса.
CODE: Константы в условии == пишем слева. Пример: if(y % strokeWidth == 0) Правильно: if(0 == (y % strokeWidth))
CODE: Магические число - ноль в контексте сложного типа. Пример: цвет 0x00000000 Правильно: объявить константу или использовать Qt::black
СODE: Использование константы, которая не входит в стандарт. Пример: M_PI. Правильно: static const double Q_PI = 3.14159265358979323846;
CODE: Потенциально неверная константа. Пример: static const int WHITE = 0xFFFFFF; для setPixel Правильно: указывать альфа канал (для 32-битного цвет) либо использовать QColor(Qt::black), чтобы не зависеть от QImage::Format_RGB888
CODE: Выделение всех чисел в качестве констант. Пример: static const qreal REAL_TWO=2.0; Правильно: использовать 2.0 либо 2, поскольку простая константа
CODE: Неудачное именование констант. Пример: static const qint64 MAX_FLOAT = 4294967296; - тип и подразумеваемое именем поведение различны
CODE: Неудачное именование - слишком лаконичное имя глобальной константы. Пример: double XS
HINT: Константные выражения в сравнении отличном от == можно писать справа. Пример: for(int i=0; PLOT_WIDTH>i; ++i) Правильно: i < PLOT_WIDTH (необязательно)
Память и указатели
LEAK: Утечка памяти. Пример: неаккуратное использование QImage *p = new QImage(); (всегда остается живой). Правильно: использовать умные указатели.
TASK: Ломается куча (debug). Пример: HEAP[app.exe]: Invalid Address specified to RtlFreeHeap( 003E0000, 0022FE44 ) - разрушение QImage
CODE: Использование указателей, где это не обязательно. Пример: QImage * Правильно: воспользоваться композицией (поле класса типа QImage)
PERF: Нерациональное выделение памяти. Пример: QImage image(imageWidth,imageHeight, QImage::Format_RGB888); Правильно: воспользоваться компоицией (поместить объект в класс).
PERF: Взаимодействие с кучей можно сократить путем устранения избыточного конструирования контейнеров с последующими аллокациями.
Функции и методы
СODE: Объявление методов без реализации. Пример: QColor setRgbColor(float h,float s,float v); в классе MainWindow (за исключением случая запрета вызова методов - пример, private: CFoo& operator= (const CFoo& );)
CODE: Использование define либо препроцессора там, где нет в этом необходимости. Пример: #define XT(t) Правильно: в данном случае использовать функции
CODE: Многократная реализация одной функции. Пример: drawAxis
CODE: Наличие потенциально бесконечного цикла - явное предположение о завершаемости. Пример: match4Metric Правильно: добавить счетчик итераций, чтобы избежать зацикливания при неверной реализации match4Metric
СODE: Отсутствие квалификатора const, где это возможно. Пример: bool Plotter::isValidPosition(int x, int y); Правильно: bool Plotter::isValidPosition(int x, int y) const;
СODE: Неудачное размещение логики по функциям. Пример: слот clear обнуляет флажки. Правильно: использовать метод clearData(), который использует флажки, и звать его из слота, в котором обнулять эти флажки; в setSize звать clearData();
CODE: Неверный интервал Random либо не документирован. Пример: (qrand() % RANDOM_MAX) / RANDOM_MAX; Правильно: интервал [0.0..1.0] - (qrand() % RANDOM_MAX) / (RANDOM_MAX - 1);
CODE: Пропущен break в switch. Пример: default:
CODE: Реализация метода в заголовочном файле. Пример: bool interSect(const QPoint& point) {
CODE: Потенциально бесконечный цикл. Пример: for(;;){ Правильно: реализовывать ограничивающий счетчик + Q_ASSERT
CODE: Нецелевое использование виртуальных функций. Пример: drawSpecifiedLemn
CODE: Реализация стандартных функций. Пример: abs. Правильно: не изобретать велосипед и воспользоваться qAbs.
CODE: Рекомендуется явно указывать virtual. Пример: void draw(QRgb);
HINT: В большинстве случаев лучше использовать update() вместо repaint() - различие в том, что Qt also tries to speed up painting by merging multiple paint events into one (только для update)
HINT: Обнаружение принадлежности точки может быть упрощено. Пример: areSameSign Правильно: можно воспользоваться знаком векторного произведения
HINT: Рекомендуется передавать изменяемые аргументы по указателю, а не по ссылке. Пример QColor getFilterPixel(float x, float y, QImage &image) Правильно: QColor getFilterPixel(float _x, float _y, QImage *_pImage)
CODE: Лишний this. Пример: this->update(); Правильно: метод класса update(); метод родителя Parent::update(); внешний метод ::update();
CODE: Отсутствие имен аргументов в заголовочных файлах - затрудняет использование классов.
CODE: Реализация методов не шаблонных классов должна быть в cpp
HINT: Необязательно делать все методы слотами. Пример: start().
HINT: Для взятия модуля по степени двойки достаточно битовых операций. Пример: x % 4294967296 Правильно: x & 0xFFFFFFFF
HINT: Хорошим тоном является использование assert. Пример: Q_ASSERT(NULL != pColor);
Комментарии
CODE: Комментарии на русском языке. Пример: //÷èñëî øòðèõîâ Правильно: использовать только английский.
CODE: Полное отсутствие комментариев. Пример: drawGraph - внутренний цикл. Правильно: добавлять однострочные комментарии, где встречаются особенности реализации
CODE: Многократные опечатки в комментариях. Пример: X coordinat, barss
СODE: Дублирование комментариев,. Пример: размещение одинаковых комментариев в .cpp и .h Правильно: только в .h
CODE: Недостаточно документированные методы. Пример: draw4Metric Правильно: документировать методы, поведение которых не очевидно из имени.
CODE: Недостаточно документированные алгоритмы. Пример: // draw sinus, pointsFromLeft Правильно: если реализуется стандартный алгоритм, то указать какой
CODE: Неочевидный усложненный код без комментария: double py = OFFSET + GRAPHIC_HEIGHT - (GRAPHIC_HEIGHT / 2 * (y + 1)); Правильно: OFFSET + GRAPHIC_HEIGHT * (1.0 - y) / 2.0 // [-1..1]->[2..0]
CODE: Неудачное размещение комментария. Пример: class ColorWorker // hsv->rgb,random function Правильно: // hsv->rgb,random function [enter]class ColorWorker {
СODE: Код, назначение которого не ясно. Пример: // int isDrawing; Правильно: убирать неиспользуемый код.
CODE: assert, смысл которого не читается в момент возникновения. Пример: assert(0); Правильно: Q_ASSERT(false && "State of rasterization is invalid.");
CODE: Не хватает комментариев в коде - необходимо пояснять детали реализации.
Производительность
PERF: Передача контейнера по значению. Пример: bool isContainedInLast(int newX, int newY, QVector<QPoint> lastPoints) Правильно: по ссылке на константный объект
PERF: Наличие отладочного вывода - в консоль. Правильно: при необходимости убирать его под define и использовать только в debug сборке.
PERF: Нецелевое использование объекта. Пример: QTimer timer; return 0;
PERF: Лишние вызовы метода. Пример: x+i > width() Правильно: вынести width() из тела цикла.
PERF: Лишний вызов. Пример: image.fill(Qt::red); и далее image.fill(Qt::white);
PERF: Избыточный вызов конструктора. Пример: QImage(width(), height(), QImage::Format_RGB555); Правильно: вызов только когда меняется размер (resizeEvent).
PERF: Избыточное взаимодействие с кучей. Пример: new QImage при изменении размеров. Правильно: воспользоваться композицией.
PERF: Перезапуск таймера, где это не требуется. Пример: timer.start(interval); в обработке события. Правильно: перезапускать, если меняется дельта
Qt
PERF: Нецелевой вызов функции. Пример: installEventFilter.
CODE: Возможно реализованные слоты должны быть private. Пример: void drawPixels(); Правильно: private slots:
HINT: Существуют варианты обрабатывать события без installEventFilter.
HINT: Рекомендуется не вызывать слоты напрямую. Пример: on_actionClear_triggered() вызывается из on_actionSettings_triggered() Правильно: вынести часть функционала в метод Clear() и вызывать его
HINT: Связывать слоты и сигналы можно прямо в Qt Designer
HINT: Не всем объектам обязательно указывать родителя. Пример: QTimer
HINT: Рекомендуется использовать QtCore/qmath.h (желательно) Пример: qSin, qAbs
Проектирование
CODE: Неверное использование исключений - для обработки некритичных случаев. Пример: приложение не должно бросать исключение, если изображение за пределами формы.
CODE: Лишние зависимости от заголовочных файлов. Пример: QLabel, math.h. Правильно: объявлять только там, где они действительно нужны.
CODE: Смешивание двух подходов. Пример: connect в .cpp и кнопки в .ui. Правильно: выбрать для описания интерфейса что-то одно (например, Qt Designer)
СODE: Чрезмерное усложнение класса. Пример: реализация HSV2RGB в классе окна. Правильно: вынести за пределы класса окна
CODE: Вынесение деталей реализации в интерфейс класса. Пример: RandomGenerator Правильно: объекты, используемые только в одной функции, объявлять в ней.
GUI: Неудачная логика элементов GUI. Пример: кнопка Clear. Правильно: применять действие сразу (без хранения флажков) либо использовать вместо кнопок CheckBox для Sin и Axis без Clear
CODE: Размытие по коду функциональных блоков, реализующих одну функцию. Пример: seed в Random(), а Random использует rnd() Правильно: либо seed отдельной функцией (как qseed), либо в основной функции (в данном случае в rnd)
CODE: Логика класса генератора случ чисел может быть упрощена. Пример: вызов getRandomNumber из getNormalizedRandomNumber - убрать зависимость; также можно вынести getFirstRandomNumber() из класса
CODE: Неясно назначение приватного конструктора.
CODE: Возможно лишние методы и применение двух разных подходов. Пример: getFirst, getSecond, ... и getTextureTriangle Правильно: getCurrentTriangle и getTextureTriangle
CODE: Детали реализации либо функционал общего назначения в конкретном классе. Пример: BresenhamTab::eqLemn
СODE: Назначение Observer не ясно, поскольку любой QObject реализует механизм слотов и сигналов, что является вариацией паттерна Наблюдатель. Правильно: не изобретать велосипед.
HINT: Вместо sprintf можно использовать QString("value %1").arg(value)
HINT: Не всегда требуется делать универсальные интерфейсы на простые сущности. Пример: генератор случ. чисел следует реализовывать без виртуальных методов - например, через функтор.
HINT: Вероятно PuzzleView не стоит наследовать TrianglePainter, поскольку это детали реализации обновления изображения. Правильно: например, применить паттерн стратегия (рисование изображения)
HINT: Реализация метода может быть упрощена. Пример: setModelTextureCoordinates
HINT: Логичнее вынести код визуализации в отдельный класс.
HINT: Возможно стоит воспользоваться композицией. Пример: QTimer *m_pTimer; Правильно: QTimer m_Timer
HINT: Рекомендуется использовать qt-контейнеры так, чтобы не конструировать их на каждом вызове функции.