Сообщество - Лига программистов C/C++
27 постов 2 907 подписчиков
1927

Стилистические Войны программистов

Когда-то давным давно я склепал и залил в инет несколько юмористических картинок касательно стилистики написания кода на C++, которые мгновенно разлетелись и вызвали тогда много срача жарких дискуссий на эту тему... И вот перерывая свои старые и пыльные архивы я сейчас снова на них случайно наткнулся. Поэтому решил поделиться с вами полной подборкой. :)

Не сочтите за баян, надеюсь, что кого-нибудь вдохновит и он выложит в комментариях свои достойные варианты на эту тему :))

// [0]

Стилистические Войны программистов Программирование, C++, Юмор, Длиннопост

// [1]

Стилистические Войны программистов Программирование, C++, Юмор, Длиннопост

// [2]

Стилистические Войны программистов Программирование, C++, Юмор, Длиннопост

// [3]

Стилистические Войны программистов Программирование, C++, Юмор, Длиннопост

А вот вариант от некоего пользователя под ником Ges( если ты есть на Пикабу, то респект тебе - долго смеялся с твоей картинки :)) )

Стилистические Войны программистов Программирование, C++, Юмор, Длиннопост

P.S. Баянометр ругался на одну отдельно выложенную картинку :)

Показать полностью 4
605

Язык программирования C иностранным языком не признали.

В продолжении вялотекущего судебного разбирательства  http://pikabu.ru/story/programmistyi_na_c__samyiy_bolshoy_ra...

Думаю некоторым будет интересно, что же там происходит:)

Язык программирования C иностранным языком не признали. C++, Программирование, Суд

По итогу 02.06.2016 суд решил признать незаконным решение ФАС.


Из приведенных заключений экспертов следует, что изображение, размещенное обществом на рекламном баннере, не является надписью или текстом на иностранном языке, а представляет собой фрагмент кода компьютерной программы, выполненной на языке программирования С++


С полным текстом решения можно ознакомится тут http://kad.arbitr.ru/PdfDocument/0235635f-01ce-4589-aaff-fe8...


Но антимонопольная служба так просто не сдается и подала апелляцию.  Следующее заседание назначено на 03.08.2016.

381

Советы падаванам C++ ( Часть 1 )

///////////////////////////////////////////////////

// Начальный курс создания компьютерных игр на C/C++

[0][1][2][2.5][3][4][5][5.5][6][6.2][7][7.2][8][9][9.2][10]


Всем привет! :]


Количество подписчиков в группе перевалило за отметку 256, а значит нужно их чем-то порадовать. :) На полноценные посты об играх у меня сейчас к сожалению нет времени, поэтому попробую себя в новом формате. Встречайте новую рубрику мини-постов "Советы падаванам C++"! :]

Советы падаванам C++ ( Часть 1 ) Программирование, Курсы программирования, Обучение, C++, Dansken, Длиннопост
Советы падаванам C++ ( Часть 1 ) Программирование, Курсы программирования, Обучение, C++, Dansken, Длиннопост
Советы падаванам C++ ( Часть 1 ) Программирование, Курсы программирования, Обучение, C++, Dansken, Длиннопост
Советы падаванам C++ ( Часть 1 ) Программирование, Курсы программирования, Обучение, C++, Dansken, Длиннопост

P.S. Первый блин может быть комом, поэтому напишите пожалуйста в комментариях ваши мнения, пожелания и жалобы. Cya! :)

Показать полностью 3
162

Рисуем в 3d на c++ поверхность, заданную формулой

Всем привет, меня зовут TaHk и по ночам я программирую на c++.

Недавно я столкнулся с задачей (не столько про c++, сколько математической), решение которой показалось мне красивым, и я хочу им поделиться.

Рисуем в 3d на c++ поверхность, заданную формулой C++, Программирование, 3d, Визуализация, Рендер, Геометрия, Алгебра, Вектор, Длиннопост

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


Итак. Задача -- нарисовать в 3d поверхность, которая задана не набором полигонов, а формулой.

Алгоритм был выбран следующий:

0. Исходные данные.

*Координаты нулевой точки поверхности (точка в глобальной системе координат, являющаяся центром координат для формулы, которой задана поверхность);

*Вектор нормали поверхности в нулевой точке (показывает, как наклонена поверхность относительно глобальных координат);

*Размеры поверхности по x, y и z (чтобы она просчитывалась не по бесконечность во все стороны, а в рамках определенного объема);

*Координаты "глаза";

*Вектор направления взгляда;

*Вектор, показывающий, где у "глаза" верх;

1. Формируем лучи из каждого пикселя экрана и проверяем на пересечение с поверхностью.

2. Если луч не пересекается с поверхностью -- окрашиваем пиксель в черный.

3. Если пересекается -- определяем освещенность точки. Для этого определяем нормаль к поверхности плоскости, и чем меньше угол между нормалью и лучом из камеры -- тем ярче точка.


Давайте разбираться с каждой задачей в отдельности.

1. Лучи из каждого пикселя экрана.

Ну, тут все просто. Чтобы задать луч -- нужна точка и вектор. Точка у нас есть -- это координаты "глаза". И направление взгляда есть. Соответственно, луч для координат [0,0] у нас уже есть. Что же делать с остальными координатами? Ответ на этот вопрос даст (или нет) эта картинка:

Рисуем в 3d на c++ поверхность, заданную формулой C++, Программирование, 3d, Визуализация, Рендер, Геометрия, Алгебра, Вектор, Длиннопост

Для начала, нам нужны единичные векторы dx и dy, перпендикулярные друг другу и вектору взгляда, чтобы построить на них плоскую систему координат, которая будет для нас экраном. Вектор dy у нас уже есть («Вектор, показывающий, где у "глаза" верх» в исходных данных). Что же на счет dx? А тут приходит на помощь векторная алгебра, из которой мы помним (или нет), что результатом векторного произведения двух векторов является вектор, перпендикулярный векторам-множителям, с нормой равной площади параллелограмма, построенного на этих векторах. Соответственно, если исходные векторы перпендикулярны друг другу и оба с длиной равной единице – длина результата тоже будет равен единице. А это прекрасно, ведь нам нужен именно единичный вектор.

Таким образом,

dX[0] = tv[1]*dY[2] - tv[2]*dY[1];
dX[1] = tv[2]*dY[0] - tv[0]*dY[2];
dX[2] = tv[0]*dY[1] - tv[1]*dY[0];

где tv — вектор направления взгляда, а dY вектор, показывающий где у взгляда верх.

Дальше, было бы неплохо определиться с длиной векторов dx и dy, определив таким образом размер экрана (по сути, это управление углом обзора). Мне оптимальным показался вариант, когда у экрана размер равен единице

dX[0] /= max_x;
dX[1] /= max_x;
dX[2] /= max_x;
dY[0] /= max_x;
dY[1] /= max_x;
dY[2] /= max_x;

Где max_x – разрешение экрана по x. Да, dy тоже делится на max_x, чтобы длина вектора dx была равна вектору dy, так как иначе изображение будет растянуто по одной из осей.

Рисуем в 3d на c++ поверхность, заданную формулой C++, Программирование, 3d, Визуализация, Рендер, Геометрия, Алгебра, Вектор, Длиннопост

Итак, у нас есть:

dX, dY – векторы, определяющие плоскость экрана.

dZ – вектор направления взгляда.

for (int y = 0; y <= max_y; ++y) {
for (int x = 0; x <= max_x; ++x) {
tv[0] = dZ[0]+x*dX[0]+y*dY[0];
tv[1] = dZ[1]+x*dX[1]+y*dY[1];
tv[2] = dZ[2]+x*dX[2]+y*dY[2];
// tv – вектор, показывающий направление луча из точки [x,y] экрана
// ...
};
};

В рамках этого цикла мы и будем работать дальше.


2. Пересечение луча с поверхностью.

Обычно лучший способ найти в пространстве точку пересечения луча с поверхностью, которая задана формулой – это решить систему уравнений, состоящую из уравнения плоскости и уравнения луча. Но если в уравнение плоскости проникает тригонометрия – решений становится бесконечное множество (буквально бесконечное, вида x = 0 ± 2πn, где n∈R ), и что делать программно с этой бесконечностью – не ясно. Поэтому мы будем искать точку пересечения итеративно:

Рисуем в 3d на c++ поверхность, заданную формулой C++, Программирование, 3d, Визуализация, Рендер, Геометрия, Алгебра, Вектор, Длиннопост

1. Определяем параллелепипед, ограничивающий нашу поверхность (мы помним, что по условиям ограничили ее в размерах).

2. Ищем точки пересечения луча и параллелепипеда. Получаем 2 точки – «вход» и «выход» в пределы параллелепипеда.

3. В точке «входа» переводим координаты из глобальной системы координат в локальную для параллелепипеда.

4. В локальных x и у находим по формуле поверхности координату z. Если найденная координата z больше, чем у точки «входа», фиксируем, что мы «под» поверхностью. Иначе – фиксируем, что мы «над» поверхностью.

5. От точки «входа» до «выхода» проверяем аналогичным образом все точки на луче с заданным шагом. Как только на следующем шаге состояние отличается от того, что было на предыдущем – значит, нашли точку пересечения.

6. Если до точки «выхода» состояние не менялось – значит пересечения не было.


Теперь по пунктам.

Существует простой и понятный алгоритм быстрого поиска пересечения прямоугольника с лучом на плоскости. Для его понимания нужно ввести пару терминов:

Точка входа – точка пересечения луча с прямой, в случае если нормаль этой прямой направлена навстречу лучу.

Точка выхода – точка пересечения луча с прямой, в случае если нормаль этой прямой направлена навстречу лучу.

Рисуем в 3d на c++ поверхность, заданную формулой C++, Программирование, 3d, Визуализация, Рендер, Геометрия, Алгебра, Вектор, Длиннопост

Алгоритм такой:

Проверяем каждую прямую на пересечение с лучом и определяем для каждой точку входа или выхода. Если самая дальняя из найденных точек входа ближе, чем самая ближняя из найденных точек выхода, то это значит, что луч пересекает прямоугольник в этих точках. Если самая дальняя из найденных точек входа дальше, чем самая ближняя из найденных точек выхода, то это значит, что луч не пересекает прямоугольник.

К чему это я? А вот к чему. Этот подход отлично работает и в пространстве, если заменить 4 прямые на 6 плоскостей.

Рисуем в 3d на c++ поверхность, заданную формулой C++, Программирование, 3d, Визуализация, Рендер, Геометрия, Алгебра, Вектор, Длиннопост

Как мы помним, поверхность задана точкой и нормалью и ограничена в размерах по x, y и z. Причем эти x y и z – в локальных координатах. То есть наш параллелепипед, ограничивающий поверхность, должен уметь вращаться вместе с поверхностью.

Поэтому для начала определим эти плоскости с учетом поворота параллелепипеда с поверхностью. Для этого нам понадобятся 3 вектора:

ddX[3] = {0,0,0}; // единичный вектор, который направлен вдоль оси X нашей поверхности
ddY[3] = {0,0,0}; // единичный вектор, который направлен вдоль оси Y нашей поверхности
ddZ[3] = {0,0,0}; // единичный вектор, который направлен вдоль оси Z нашей поверхности

с ddZ все понятно – это нормаль к поверхности.

С ddX и ddY сложнее. Одной только точкой и нормалью можно задать в пространстве только бесконечную поверхность. А тут нам нужно зафиксировать поворот этой плоскости вокруг оси z. Это можно сделать разными способами – я беру еще одну точку на плоскости и фиксирую ее координаты в глобальной системе координат. Допустим, это точка point2, а нулевая точка поверхности — point. Тогда

ddX[0] = point2[0] - point[0];
ddX[1] = point2[1] - point[1];
ddX[2] = point2[2] - point[2];
len = sqrt(ddX[0]*ddX[0]+ddX[1]*ddX[1]+ddX[2] *ddX[2]) ;
ddX[0]/=len;
ddX[1]/=len;
ddX[2]/=len;

Как мы видим, я не забыл, что нам нужен именно единичный вектор (с длиной равной единице), поэтому я посчитал длину вектора от точки point к точке point2 и разделил каждую координату на длину. Эта процедура называется нормализация вектора – приведение его к единичной длине с сохранением направления.

Ну и ddY по старой схеме (как в пункте 1, где мы экран рисовали) определяем как векторное произведение ddX на ddZ:

ddY[0] = ddX[1]*ddZ[2] - ddX[2]*ddZ[1];
ddY[1] = ddX[2]*ddZ[0] - ddX[0]*ddZ[2];
ddY[2] = ddX[0]*ddZ[1] - ddX[1]*ddZ[0];

Таким образом, мы можем задать все 6 плоскостей. Например, плоскость, ограничивающая параллелепипед «снизу» определяется так:

spoint[0] = point[0] - z * ddZ[0];
spoint[1] = point[1] - z * ddZ[1];
spoint[2] = point[2] - z * ddZ[2];
n[0] = -ddZ[0];
n[1] = -ddZ[1];
n[2] = -ddZ[2];

где point — нулевая точка нашей поверхности, а z - половина высоты параллелепипеда.

Остальные – по аналогии, меняем только - на + и буквы координат.


Дальше нужно в общем виде понимать, как мы будем искать точки пересечения луча и плоскости. Допустим, плоскость задана точкой point[3] и нормалью n[3]. Луч задан точкой tp[3] и вектором tv[3]. Еще одна картинка в стиле «я у мамы paint master»:

Рисуем в 3d на c++ поверхность, заданную формулой C++, Программирование, 3d, Визуализация, Рендер, Геометрия, Алгебра, Вектор, Длиннопост

Несложно догадаться, что если векторы n и tv имеют единичную длину, то расстояние от точки tp до точки пересечения с плоскостью будет равно -h/p. Шучу, сложно догадаться. Но при желании вы можете это проверить. Даже так – если вы сейчас в школе проходите векторную алгебру – вы должны это проверить и выложить в комментариях доказательство. Иначе вам поставят двойку. Или что там сейчас ставят в школах…

Как же найти эти h и p? Тут на помощь снова приходят определения из векторной алгебры. Скалярное произведение векторов численно равно длине проекции одного из этих векторов на другой. Как определить скалярное произведение? А примерно так:

float MulVecSc(float *Vec1, float *Vec2){
return Vec1[0]*Vec2[0] + Vec1[1]*Vec2[1] + Vec1[2]*Vec2[2];
};

Как мы видим из рисунка, h – это проекция на нормаль вектора от точки tp до точки point, принадлежащей плоскости, а p – это проекция на нормаль вектора tv.

vp[0] = point[0] - tp[0];
vp[1] = point[1] - tp[1];
vp[2] = point[2] - tp[2];
p = MulVecSc(n,tv);
h = MulVecSc(n,vp);
t = - h/p;

Знак p покажет нам, с какой стороны плоскости мы находимся (а значит покажет, нашли мы точку «входа» или «выхода»).


Резюмируем. В пространстве точки пересечения луча с параллелепипедом находятся так:

float ddY[3] = {point2[0],point2[1],point2[2]};
float ddX[3] = {0,0,0};
float ddZ[3] = {n[0],n[1],n[2]};
tmMulVec(n,ddY,ddX);
// 6 плоскостей. Ищем самый поздний "вход" и самый ранний "выход"
// Плоскость 1
spoint[0] = point[0] + x * ddX[0];
spoint[1] = point[1] + x * ddX[1];
spoint[2] = point[2] + x * ddX[2];
vp[0] = tp[0]-spoint[0];
vp[1] = tp[1]-spoint[1];
vp[2] = tp[2]-spoint[2];
n[0] = ddX[0];
n[1] = ddX[1];
n[2] = ddX[2];
p = tmMulVecSc(lv_n,tv);
t = - tmMulVecSc(lv_n,lv_vp)/p;
if(p>0.0f){ // выход
if(t<t_out)
t_out = t;
}else{ // вход
if(t>t_in)
t_in = t;
};
// Плоскость 2
spoint[0] = point[0] - x * ddX[0];
spoint[1] = point[1] - x * ddX[1];
spoint[2] = point[2] - x * ddX[2];
vp[0] = tp[0]-spoint[0];
vp[1] = tp[1]-spoint[1];
vp[2] = tp[2]-spoint[2];
n[0] = -ddX[0];
n[1] = -ddX[1];
n[2] = -ddX[2];
p = tmMulVecSc(lv_n,tv);
t = - tmMulVecSc(lv_n,lv_vp)/p;
if(p>0.0f){ // выход
if(t<t_out)
t_out = t;
}else{ // вход
if(t>t_in)
t_in = t;
};
// Плоскость 3
// ...
// Плоскость 4
// …
// Плоскость 5
// …
// Плоскость 6
// ...
if(t_in<t_out&&t_out>0){
// есть пересечение
};
Так. Теперь мы знаем, что в промежутке от t_in до t_out мы внутри параллелепипеда.
Дальше, как и планировали, будем проверять каждую точку с неким шагом h на то, находится она выше или ниже поверхности в локальных координатах.
Что такое локальные координаты?
Рисуем в 3d на c++ поверхность, заданную формулой C++, Программирование, 3d, Визуализация, Рендер, Геометрия, Алгебра, Вектор, Длиннопост

Это система координат, образованная теми самыми ddX, ddY, ddZ которые мы определили выше. Как очевидно из рисунка (на этот раз действительно очевидно), если у нас есть точка p, то вектор, ведущий от нулевой точки нашей поверхности до точки p будет определяться векторной суммой x*ddX+y*ddY+z*ddZ, где x y и z локальные координаты этой точки. Что ж, в таком случае, зная глобальные координаты точки p мы можем получить локальные таким образом:

vtp[0] = p[0]-point[0];
vtp[1] = p[1]-point[1];
vtp[2] = p[2]-point[2];
x = MulVecSc(ddX,vtp);
y = MulVecSc(ddY,vtp);
z = MulVecSc(ddZ,vtp);

Дальше, проверяем, находимся мы в этой точке под поверхностью или над.

F = F_xy; // тут у нас определение значения функции, описывающей плоскость. Его мы рассмотрим позже, в 3й части.
if(z<F){ // определяем, находимся мы в точке входа «над» или «под» поверхностью
in = true;
}else{
in = false;
};

Дальше – просто делаем это в цикле, перемещая точку p:

for(t = t_in+h;t<=t_out;t+=h){
pt[0] = tp[0] + tv[0]*t;
pt[1] = tp[1] + tv[1]*t;
pt[2] = tp[2] + tv[2]*t;
vtp[0] = point[0]-pt[0];
vtp[1] = point[1]-pt[1];
vtp[2] = point[2]-pt[2];
x = -tmMulVecSc(ddX,vtp);
y = -tmMulVecSc(ddY,vtp);
z = -tmMulVecSc(ddZ,vtp);
F = F_xy;
if(z<F){ // сейчас внутри
if(!in){ // были снаружи
break;
};// были снаружи
}else{ // сейчас снаружи
if(in){ //были внутри
break;
};//были внутри
}; // внутри/снаружи
}; // for t_in ... t_out
if(t>=t_out){
break; // никакого пересечения не было
};
// на данный момент t – это расстояние от точки tp до точки пересечения
// фактически точка на расстоянии t уже после пересечения с плоскостью, поэтому
// в качестве точки пересечения берем t-h
t-=h
collision_pt[0] = tp[0] + tv[0]*t;
collision_pt[1] = tp[1] + tv[1]*t;
collision_pt[2] = tp[2] + tv[2]*t;

Вот мы и нашли, где луч пересек поверхность. Запихнув это решение в цикл, который мы получили в первой части, мы получим картинку, на которой будут видны очертания поверхности. Но на 3d картинку это не будет похоже – чтобы одна точка поверхности отличалась от другой (что создаст объем) нужно определить освещенность.


3. Определение освещенности точки.

В рамках этой задачи мы не будем заморачиваться с источниками света – будем считать, что у нас один источник и он расположен непосредственно за «глазом». В таком случае расчет освещенности сводится к простому определению нормали к поверхности. Чем меньше значение проекции – тем темнее точка, так как свет падает под большим углом. Поскольку векторы у нас единичные, значение проекции будет изменяться в диапазоне [0..1], что позволяет просто домножать значение цвета на проекцию и получать нужную картинку.

Итак, вернемся к «простому определению нормали».

Рисуем в 3d на c++ поверхность, заданную формулой C++, Программирование, 3d, Визуализация, Рендер, Геометрия, Алгебра, Вектор, Длиннопост

Пора наконец определиться с тем, какую поверхность мы рисуем. В коде ранее была конструкция F = F_x; Тут F_x это макрос, в который я подставляю формулу, которую хочу рисовать. Разумеется, в последствии в код можно будет подставить любую формулу, но для примера нужно взять что-то конкретное. Я выбрал такое:

#define F_xy cos((x*x+y*y)/20.0)/(1+(x*x+y*y)/100.0)

Чтобы понимать, что мы хотим увидеть в программе, построим этот график в каком-нибудь инструменте, который это уже умеет:

Рисуем в 3d на c++ поверхность, заданную формулой C++, Программирование, 3d, Визуализация, Рендер, Геометрия, Алгебра, Вектор, Длиннопост

Так как же определить нормаль в какой-нибудь точке поверхности? Нормаль – это вектор перпендикулярный касательной плоскости к поверхности в заданной точке, правильно? А коэффициент угла наклона касательной к графику функции в точке – это ни что иное как производная функции в этой точке. Таким образом, нужно просто взять с обратным знаком производные исходной функции по трем координатам – это и будет вектор нормали.

Рисуем в 3d на c++ поверхность, заданную формулой C++, Программирование, 3d, Визуализация, Рендер, Геометрия, Алгебра, Вектор, Длиннопост

Для примера возьмем двумерный график:

Рисуем в 3d на c++ поверхность, заданную формулой C++, Программирование, 3d, Визуализация, Рендер, Геометрия, Алгебра, Вектор, Длиннопост

(графики касательных смещены для наглядности)

Функция Y=x^2. Её производная Y'=2x. Соответственно, в точке X=0.5 коэффициент угла наклона равен единице, а в точке 2 – четырем. Из графика видно, что коэффициент угла наклона с обратным знаком показывает угол наклона линии, перпендикулярной касательной. Эта же логика работает и для более сложных графиков. Так же она работает и в пространстве.

Поэтому запишем уравнение поверхности в виде cos((x*x+y*y)/20.0)/(1+(x*x+y*y)/100.0) - z = 0 и возьмем производные по трем координатам:

F'x = ((sin((x*x+y*y)/20.0)*x/10.0)*(1+(x*x+y*y)/100.0)-(x/50.0)*cos((x*x+y*y)/20.0))/((1+(x*x+y*y)/100.0)*(1+(x*x+y*y)/100.0))

F'y = ((sin((x*x+y*y)/20.0)*y/10.0)*(1+(x*x+y*y)/100.0)-(y/50.0)*cos((x*x+y*y)/20.0))/((1+(x*x+y*y)/100.0)*(1+(x*x+y*y)/100.0))

F'z = -1

Вернемся к коду. Теперь зная точку пересечения и ее локальные координаты – мы можем найти нормаль. Она будет определяться как N[3] = {-F'x,-F'y,-F'z}. Но нужно помнить, что это ее значения в локальных координатах. Чтобы перевести в глобальные – нужно каждый компонент умножить на значение вектора той оси, которой он соответствует:

#define F_x ((sin((x*x+y*y)/20.0)*x/10.0)*(1+(x*x+y*y)/100.0)-(x/50.0)*cos((x*x+y*y)/20.0))/((1+(x*x+y*y)/100.0)*(1+(x*x+y*y)/100.0))
#define F_y ((sin((x*x+y*y)/20.0)*y/10.0)*(1+(x*x+y*y)/100.0)-(y/50.0)*cos((x*x+y*y)/20.0))/((1+(x*x+y*y)/100.0)*(1+(x*x+y*y)/100.0))
#define F_z 1.0
float Fx = F_x;
float Fy = F_y;
float Fz = F_z;
collision_normal[0] = ddX[0]*Fx+ddY[0]*Fy+ddZ[0]*Fz;
collision_normal[1] = ddX[1]*Fx+ddY[1]*Fy+ddZ[1]*Fz;
collision_normal[2] = ddX[2]*Fx+ddY[2]*Fy+ddZ[2]*Fz;

Теперь мы готовы определять компоненты цвета в заданной точке. Допустим, базовый цвет поверхности у нас лежит в переменной rgb:

Shade = 255.0f * MulVecSc(collision_normal,tv);
if(Shade < 0)
Shade = -Shade;
r = Shade * rgb[0];
g = Shade * rgb[1];
b = Shade * rgb[2];

Оценим результат:

Рисуем в 3d на c++ поверхность, заданную формулой C++, Программирование, 3d, Визуализация, Рендер, Геометрия, Алгебра, Вектор, Длиннопост

Сменим формулы:

#define F_xy 0.3*(sin(x)*cos(y/2.0)+cos(x/2.5)*sin(y/1.5))
#define F_x -0.3*(cos(x)*cos(y/2.0)-sin(x/2.5)*sin(y/1.5))
#define F_y -0.3*(-sin(x)*sin(y/2.0)+cos(x/2.5)*cos(y/1.5))
Рисуем в 3d на c++ поверхность, заданную формулой C++, Программирование, 3d, Визуализация, Рендер, Геометрия, Алгебра, Вектор, Длиннопост

Ок.

Рисуем в 3d на c++ поверхность, заданную формулой C++, Программирование, 3d, Визуализация, Рендер, Геометрия, Алгебра, Вектор, Длиннопост

Если кого-нибудь заинтересовало – подписывайтесь. Если будет достаточно желающих – освещу другие моменты и задачи, с которыми я столкнулся в ходе разработки: преломление, рассеивание света. Картинка для затравки (на ней чуть менее чем всё, что сейчас умеет программка, в ходе реализации которой я столкнулся с описанной выше задачей):

Рисуем в 3d на c++ поверхность, заданную формулой C++, Программирование, 3d, Визуализация, Рендер, Геометрия, Алгебра, Вектор, Длиннопост
Показать полностью 15
217

Советы падаванам C++ ( Часть 2 )

///////////////////////////////////////////////////

// Начальный курс создания компьютерных игр на C/C++

[0][1][2][2.5][3][4][5][5.5][6][6.2][7][7.2][8][9][9.2][10]

///////////////////////////////////////////////////

// Советы падаванам C++

[1]



Всем привет! :]


После первого поста количество подписчиков резко увеличилось в два раза, поэтому я решил не тянуть со вторым постом. :]

Сегодня в рубрике "Советы падаванам C++" у нас будут советы-привычки - те вещи, к которым нужно привыкнуть как можно раньше и использовать каждый день :]

Советы падаванам C++ ( Часть 2 ) Программирование, Курсы программирования, Обучение, C++, Dansken, Длиннопост
Советы падаванам C++ ( Часть 2 ) Программирование, Курсы программирования, Обучение, C++, Dansken, Длиннопост
Советы падаванам C++ ( Часть 2 ) Программирование, Курсы программирования, Обучение, C++, Dansken, Длиннопост
Советы падаванам C++ ( Часть 2 ) Программирование, Курсы программирования, Обучение, C++, Dansken, Длиннопост

Жду ваши отзывы в комментариях :]


И вот вам в тему пару весёлых картинок напоследок:

Советы падаванам C++ ( Часть 2 ) Программирование, Курсы программирования, Обучение, C++, Dansken, Длиннопост
Советы падаванам C++ ( Часть 2 ) Программирование, Курсы программирования, Обучение, C++, Dansken, Длиннопост
Показать полностью 5
192

Стилистические Войны программистов ( Часть 2 )

Мой предыдущий пост посвященный стилистическим войнам программистов к большому удивлению был очень тепло встречен пикабушниками и привел к немалому числу оживленных обсуждений в комментариях. :)

Поэтому я решил воплотить в жизнь еще пару-тройку жизненных картинок в этой тематике :)


// [0]

Стилистические Войны программистов ( Часть 2 ) Программирование, C++, Юмор, Длиннопост

// [1]

Стилистические Войны программистов ( Часть 2 ) Программирование, C++, Юмор, Длиннопост

// [2]

Стилистические Войны программистов ( Часть 2 ) Программирование, C++, Юмор, Длиннопост

// [3]

Стилистические Войны программистов ( Часть 2 ) Программирование, C++, Юмор, Длиннопост

И бонусная версия для особых почитателей моды:

Стилистические Войны программистов ( Часть 2 ) Программирование, C++, Юмор, Длиннопост
Показать полностью 4
72

Реализация кортежа на C++11

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

В языке Python кортеж является стандартным типом данных и позволяет делать вот так:

Реализация кортежа на C++11 Программирование, Языки программирования, C++, Длиннопост

Но Python использует динамическую типизацию, а в C++ типизация статическая. Как же создать подобную структуру и не сломать систему типов? Используем C++11 и одну и главных особенностей этого стандарта — шаблоны с переменным числом аргументов (variadic templates).

Обычные шаблоны используются в C++ повсеместно, большинству они знакомы. Шаблоны с переменным числом аргументов позволяют передавать произвольное число параметров в шаблон. Вот небольшой пример, демонстрирующий эту возможность языка:

Реализация кортежа на C++11 Программирование, Языки программирования, C++, Длиннопост

Теперь, собственно, реализация кортежа. Для этого мы воспользуемся рекурсивным наследованием структур. Так выглядит объявление структуры:

Реализация кортежа на C++11 Программирование, Языки программирования, C++, Длиннопост

Также нам понадобится вспомогательная структура, позволяющая получить тип элемента по его индексу и тип оставшейся части кортежа:

Реализация кортежа на C++11 Программирование, Языки программирования, C++, Длиннопост

Теперь мы можем написать функцию доступа к элементу по его индексу:

Реализация кортежа на C++11 Программирование, Языки программирования, C++, Длиннопост

Конечно, сам кортеж и доступ — это хорошо, но мы же хотим его создавать! Для этого сперва реализуем заполнение кортежа.

Реализация кортежа на C++11 Программирование, Языки программирования, C++, Длиннопост

Теперь легко реализовать и функцию создания кортежа. Почему не конструктор? В нем не работает автоматическое выведение типов, а мы же не хотим писать огромный список параметров ;)

Реализация кортежа на C++11 Программирование, Языки программирования, C++, Длиннопост

Ну и напоследок демонстрация того, что у нас получилось. Распаковку и групповое присваивание пока не демонстрирую, и так достаточно кода :P

Реализация кортежа на C++11 Программирование, Языки программирования, C++, Длиннопост

P.S. Не судите строго — это мой первый пост. Возможно тема покажется сложной, я не привожу здесь подробное объяснение. Пишите в комментарии что не понятно, постараюсь ответить.

Показать полностью 5
63

Как начать понимать указатели

Тема указателей для многих является той точкой, на которой многие новички спотыкаются. Секрет такого ступора объясняется довольно просто, из мира языка С++ Вы погружаетесь в мир архитектуры компьютера и выполнения программ. Поскольку Вы его не знаете и почему-то авторы книг/курсов/статей не хотят Вас с ним знакомить, этот процесс может проходить довольно болезненно. В данном статье мы попытаемся устранить этот недостаток.

Любая программа перед тем как начать исполняться полностью загружает свой код в ОЗУ или проще говоря оперативную память. Оперативная память в урощенной схеме представляет из себя таблицу адресов и значений.


Есть такой термин как разрядность и применяется он для ОС, шины, адресного пространства. Он означает, сколько одновременно бит может обработать устройство. Вот один из примеров оперативной памяти с адресной разрядностью в 8 бит(2^8 = 256 адресов) и разрядностью шины 16 бит.

Как начать понимать указатели Указатель, Память, Программирование, C++, Длиннопост

Давайте посмотрим на примере простой программы, как это может работать в упрощенной схеме.

Как начать понимать указатели Указатель, Память, Программирование, C++, Длиннопост

После того, как компилятор пройдется по вашему коду, он заменит все ваши имена переменных на адреса и заполнит как-то так оперативную память

Как начать понимать указатели Указатель, Память, Программирование, C++, Длиннопост

Соответсвенно адрес у переменной не меняется и если мы присвоим ей новое значение, то оно изменится только в столбце "значение по адресу".

Вот мы и подошли к самому главному - указатель, это совершенно обычная переменная которая хранит АДРЕС. У нас есть переменные, которые хранят числа, которые хранят буквы, которые хранят строки. А теперь просто появились переменные, которые хранят адрес.


Что бы ОБЪЯВИТЬ указатель, достаточно указать перед именем переменной звездочку. Это даст понять, что в этой переменной вы будете хранить адрес определенного типа. На самом деле хранение в памяти значений намного сложнее, поэтому компилятору надо знать тип хранимого адреса, что бы правильно его получить из памяти.


Для работы с указателем используются две новые операции.

& - взятие адреса у какой-либо переменной

* - получение значение по адресу в указателе

Как начать понимать указатели Указатель, Память, Программирование, C++, Длиннопост

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


Удачи в обучении.

Показать полностью 1
62

Используем low-level возможности языка C

Итак, у нас есть какая-то задачка. Качество решения определяется временем выполнения: меньше - лучше. В первую очередь нам нужен рациональный алгоритм, но не только. Процесс работы программы зачастую сильно затягивается из-за "узких мест". Типичный случай - простой процессора в ожидании результата обращения к памяти. С этим можно бороться по следующей аналогии.


Представьте, что вы делаете ручную работу, сидя за просторным деревянным столом. Инструменты и материалы разложены по полкам в шкафу в 3-х метрах от вас. Если всякий раз выкладывать ту или иную вещь из рук на полку и брать с полки, большая часть времени уйдет на хождение от стула к шкафу и обратно. Умный человек довольно скоро додумается разместить активно используемые инструменты прямо на столе, чтоб они были под рукой.


Вернемся к компьютеру. В компьютере, по грубой аналогии, роль стола играет процессор, роль шкафа - оперативная память, а жесткий диск, который нас, впрочем, не интересует, представляет большой склад в отдаленном помещении. Нас интересуют регистры - это такая штука в процессоре, которая в нашем случае играет роль места "под рукой" для размещения активно используемых вещей, в т.ч. и тех, что обрабатываются в текущий момент. Не буду вдаваться в подробности, какое особое назначение у RAX, RBX, RCX, RDX, RSI, RDI.. Скажу лишь, что они всегда используются, в худшем случае (тупой компилятор) только для счетчика комманд, указателя стека и прочих технически важных вещей, а также для передачи некоторых аргументов функции, возвращаемого значения функции и для переменных, над которыми идет операция в текущий момент, после чего каждый раз производится перезапись в память. На языке высокого уровня этого не видно, видны лишь имена переменных, а реальный (машинный) код создается компилятором. Assembly же, где соотношение того, что вы пишете, и того, что получится - один к одному, отпускает полный контроль под ваши руки.


Перейдем к делу. Возьмем простенький итерационный алгоритм нахождения n-го числа Фибоначчи.

Используем low-level возможности языка C C++, Программирование, Длиннопост

Если запустить цикл while на несколько миллирадов итераций, такое количество обращений к памяти (f,f1,f2) займет немало времени. Можно написать функцию fibo(n) на ASM.

Используем low-level возможности языка C C++, Программирование, Длиннопост

Но подобная экзотика не всем по вкусу. К счастью, такая прекрасная вещь, как C/++, позволяет во многих случаях обходиться и без Assembly. Можно "попросить" компилятор держать, по возможности, те или иные переменные по возможности держал в регистрах. Делается это очень просто: перед объявлением переменной пишется ключевое слово register.

Используем low-level возможности языка C C++, Программирование, Длиннопост

Но надо еще все протестировать. Запустим это дело на 4 миллиарда. С такими входными данными, разумеется, ни о каком корректном результате речи не идет, но главное, чтоб не вылетало (благо не Java, под 64 бита срежет и промолчит) и чтоб обе memslow и regfast дали одинаковый результат, этого более чем достаточно для честной проверки быстродействия.

Используем low-level возможности языка C C++, Программирование, Длиннопост

Скриншот говорит сам за себя. 5-кратная разница в результате. А вот разница в причине такого результата.

Используем low-level возможности языка C C++, Программирование, Длиннопост

Выделена та часть, где вводятся f, f1, f2.

Используем low-level возможности языка C C++, Программирование, Длиннопост

Первое - медленный вариант, последнее - быстрый. Обратите внимание, что в последнем случае за весь цикл (все что между jmp и jne) - ни разу не обращаемся к памяти.


Ассемблерную часть изложенной инфы не обязательно всем понимать, достаточно было узнать про register-фичу языка C. Не вижу смысла расписывать ASM, кто шарит тот шарит, а кто не шарит, но хочет, тому могу посоветовать хорошую книжку PC Assembly Language чувака по имени Paul A. Carter (ссылку оставлю в комментах).


Сомневаюсь, что будут плюсы, поскольку не занимаюсь регулярно постами на какую-либо тематику, и вообще сам учусь, но чувствую необходимость где-то это зафиксировать, чтоб хоть сокурснику в удобном виде (ссылка Pikabu) кинуть. Грубо говоря, воспользовался сайтом в качестве удобного средства оформления информации с такими ништяками, как картинки и хранение в сети. Да простят меня за это.


Гавнокод мой - тег моё.

Показать полностью 5
96

Несколько советов по оптимизации кода на C++

1) Вместо использования цикла for() быстрее использовать цикл while(). По той простой причине, что while() был придуман раньше, чем for(), то следовательно while() более низкоуровневый, а следовательно за абстрагирование мы расплачиваемся меньше, что ускоряет наш код.

Несколько советов по оптимизации кода на C++ Программирование, C++, Оптимизация, 1 апреля, Длиннопост

2) если честно, то данной код можно ускорить ещё больше. поскольку оператор ++ является неоптимальным, лучше использовать его аналог =.

Так делали ещё наши деды на Паскале и Фортране, получая код, который просто летал. Я вам больше скажу, при программировании процессоров, работавших на советских космических станциях использовали именно этот оператор. И ведь где, а там нужно высокая эффективность кода.

Несколько советов по оптимизации кода на C++ Программирование, C++, Оптимизация, 1 апреля, Длиннопост

3) Если вам нужно сделать вечный цикл, например как при поиски в ширину, лучше писать while(!false) вместо обычного while(true). поскольку процессор быстрее воспринимает команды с нулями, то и отрицание нуля он будет обрабатывать быстрее, нежели просто единицу. Это сильно ускоряет ваш проход по циклу.

Несколько советов по оптимизации кода на C++ Программирование, C++, Оптимизация, 1 апреля, Длиннопост

4) Описание типа данных для каждой отдельной переменной отдельно также уменьшает затраты процессорного времени. Поскольку вы перекладываете часть работы процессора на себя. Как бы расписывая всё для каждой переменной в отдельности, тем самым "разжёвывая" код, процессору приходиться декодировать значительно меньше данных.

Несколько советов по оптимизации кода на C++ Программирование, C++, Оптимизация, 1 апреля, Длиннопост

5) Ну и последний совет по оптимизации заключается в том, чтобы сократить потребление оперативной памяти при исполнении программы. ОБЯЗАТЕЛЬНО удаляйте все лишние пробелы, табуляции, переходы на новые строки и комментарии. Именно они зачастую являются причиной потребления большей части оперативки и кеша процессора. Вы сами можете увидеть, как код стал более компактным и более читабельным. Если ваш код будет читать другой программист, то на его экранном пространстве также он будет занимать меньше места, соответственно полезного кода будет помещаться больше.

Несколько советов по оптимизации кода на C++ Программирование, C++, Оптимизация, 1 апреля, Длиннопост
Несколько советов по оптимизации кода на C++ Программирование, C++, Оптимизация, 1 апреля, Длиннопост
Показать полностью 3
29

С++ - [Марио];

В прошлом посте про тетрис, многие кто хотел бы в этом разобраться или просто учится писать на С++, использовать SFML, или просто интересуется для себя, жаловались, что видео ряд сильно ускорен, музыка достала, да и вообще...

В общем, это видео - должно всё исправить.

33

C++ constexpr

C++ 11 добавил много всего интересного, но сегодня речь пойдет о constexpr. Концептуально constexpr указывает значение, которое не просто является константой, но и известно во время компиляции. Поэтому если требуется, чтобы переменная была известна на этапе компиляции, то смело помечаем ее constexpr, потому что простой const таких гарантий не дает.

Теперь поговорим про функции. Данное ключевое слово можно применять к функциям, например:

C++ constexpr C++, Constexpr, Факториал, Длиннопост

Однако, constexpr-функция не обязана(!) давать константный результат или результат, известный во время компиляции. Все зависит от входных аргументов - если они известны на этапе компиляции, то функция работает как задумано, иначе она работает как обычная функция (ошибки компиляции нет). Это следует помнить. Благодаря этому можем писать код наподобие следующего:

C++ constexpr C++, Constexpr, Факториал, Длиннопост

По-моему круто! Тем самым можно посчитать что-то на этапе компиляции, например, факториал. Да - да, изъезженный пример, но что поделаешь - это классика. Вот как их можно реализовать:

C++ constexpr C++, Constexpr, Факториал, Длиннопост

Почему две разные реализации? Потому что в constexpr в C++11 имеет больше ограничений по сравнению с C++14. Подробнее уже можно почитать на cppreference.com, не буду делать copy-paste.

Ну и напоследок. Нам заполнить массив некоторыми значениями на этапе компиляции. Как будем это делать? Для наглядности возьму тот же факториал.

C++ constexpr C++, Constexpr, Факториал, Длиннопост

Темная уличная шаблонная магия). На самом деле ничего сложного здесь нет, разве что variadic templates, но они хорошо описаны у С. Прата. Проверить работу кода можно здесь: http://rextester.com/KGUP58147.


Наверняка читатель может задать вопрос - зачем мне это? Тут я скажу, что вы сами должны

решать использовать такие возможности языка или нет. Я лишь рассказал о такой штуке - переносе части вычислений из run-time в compile-time.


Для подготовки текста использовались материалы из книги замечательного автора S. Meyers "Effective Modern C++".

Показать полностью 1
17

Как искать работу джуном c++?

Собственно, сабж. Ищу советов и дельных комментариев к ситуации.

Итак.

Некоторое время назад решил сменить сферу деятельности и перейти в IT, благо, небольшой багаж был. В процессе просмотра вакансий и разъездов по интервью выяснил, кто непосредственно позиций для джунов нигде нет. Под джуновским уровнем я понимаю учебник Прата + Программирование Под Win95 + Программирование на С++ для профессионалов + всякие базы вроде алгоритмов и с.д. Везде требуется дополнительный стек, и часто уникальный (от подразделов буста, до всячески рестов и т.д.)

Что в таком случае делать? Есть ли стратегия прокачки?

И вообще - актуально ли сейчас искать по c++? Или лучше уж переходить на java?

Спасибо.

15

Язык программирования C иностранным языком не признали. Окончание.

Для жаждущих продолжения =)

В продолжении http://pikabu.ru/story/programmistyi_na_c__samyiy_bolshoy_ra...

и http://pikabu.ru/story/yazyik_programmirovaniya_c_inostranny...


В конечном итоге сегодня суд постановил:

"Оставить решение суда без изменения, жалобу без удовлетворения".

Антимонопольная служба с апелляцией обломалась, суд остался на стороне организации и ЯП "С++" иностранным языком не признал, а многие надеялись ;)

Язык программирования C иностранным языком не признали. Окончание. C++, Программирование, Суд
11

Динамическая библиотека структур данных для C с интерфейсом похожим на C++

Всем привет. Я хочу поделиться с вами библиотекой структур данных для языка C. Интерфейс библиотеки напоминает интерфейс стандартной библиотеки C++. Буду рад любому фитбэку.


Ссылка на публичный репозиторий: https://github.com/maksimandrianov/cdcontainers


Ниже на картинке представлен аналог vector из C++.

Динамическая библиотека структур данных для C с интерфейсом похожим на C++ C, Структуры Данных, Библиотека, Контейнер
11

Статические переменные вместо глобальных на Си/Си++

Использование глобальных переменных считается дурным тоном, потому что создает много проблем. Избежать их можно с помощью ключевого слова static, которое создает статические переменные.

Есть простой способ избавиться от большинства глобальных переменных.

Статические переменные вместо глобальных на Си/Си++ Си++, Си, C++, Программирование

Переменная A располагается в глобальной области памяти, но имеет локальную область видимости (в пределах функции fun). То есть переменная сохраняет свое значение между вызовами функции. Начальное значение переменной равно 10.

Этот способ не годится, если функциям нужно обмениваться данными, например, обработчику прерывания с основной программой. Это решается созданием глобальной переменной в отдельном файле. Ниже приведен код такого файла.

Статические переменные вместо глобальных на Си/Си++ Си++, Си, C++, Программирование

Область видимости переменной A — только этот файл. Никаким способом к ней нельзя получить доступ из других файлов. Функции setA и getA позволяют получить и установить значение переменной.

На Си++ переменную дополнительно можно обернуть классом.

11

Реализуем числа Фибоначчи на Си, по простому о сложном...               Машинное Обучение.

Внимание: Видео на английском языке, но изъяснения вполне понятны и ясны.

Начинаем с простых правил и заканчиваем более сложными моментами.

8

С++ лишится* указателей!

Писать отдельный пост не вижу смысла так как на хабре инфу хорошо разжевали, считаю информацию достаточно важной чтобы иметь возможность "набрать материал и переучиться, а сделать это придется каким бы профи вы не были" [это заявление одного из членов собрания по стандартизации языка в Джексонвиле]

Source: https://m.habrahabr.ru/post/352570/

return 0;
}

6

Заметка Неуча "О циклах в C++"

Циклы, это такая крайне полезная фиговина, дающая возможность упорно двигаться к результату.

PS: Применительно не только к C++.

Заметка Неуча "О циклах в C++" C++, Заметки, Учимся, Для чайников

Цикл FOR - когда вы наверняка сколько раз надо "сделать дело", чтобы "гулять смело".

Что делает: объявляет старт счетчика (в примере и сам "счетчик"), условие его остановки, и шаг с которым он тикает, а уже после выполняет "ВАШИ БЕЗУМНЫЕ КОМАНДЫ".


Его можно использовать, например, для чтения или наполнения массива типа char. Ну или просто заставить эту тупую железяку сосчитать до 1000.

Заметка Неуча "О циклах в C++" C++, Заметки, Учимся, Для чайников

Цикл WHILE - когда вы сначала ставите цель, а потом упорно к ней стремитесь.

Что делает: объявляет условие для остановки и начинает выполнять "ВАШИ БЕЗУМНЫЕ КОМАНДЫ" до тех пор пока это условие не будет достигнут.


Тут надо очень ясно понимать, что пока условие не достигнуто - цикл не окончится. В примере, все упирается в счетчик, и он увеличивается на 1 в конце каждого прогона цикла.

Заметка Неуча "О циклах в C++" C++, Заметки, Учимся, Для чайников

Цикл DO...WHILE - когда вы сначала делаете, потом думаете, и опять делаете, пока не выясняется, что хватит.

Что делает: сначала выполняет "ВАШИ БЕЗУМНЫЕ КОМАНДЫ", потом сверяет их с ожидаемым результатом, и если не сходится, то вновь выполняет "ВАШИ БЕЗУМНЫЕ КОМАНДЫ".


Ключевое отличие, что в отличии от обычного while, тут ваши команды в любом случае выполняться хотя бы раз.

С уважением, Ваш Неуч.

Отличная работа, все прочитано!