суббота, 11 июля 2015 г.

Сцены OpenGL в Mac OS X

Ну раз говорится про Mac OS, значит программировать будем в cocoa на языке Objective-C. Cоздадим простейшее приложение OpenGL, рисующее на "сцене" куб. Откроем среду разработки XCode и в меню выберем создать новый проект. В списке предложений находим Cocoa Application. Жмем Next.
Выбираем именем рашего проекта CocoaOpenGL и языком разработки объектный си. Далее произвольно заполняем остальные пустые поля. Жмем Next.

Видим "пустой" каркас нашего будущего приложения: 

Во вкладке Build Settings находим поле Linked Frameworks and Libraries и в появившемся списке выбираем OpenGL.framework . Фреймворк, как вы догадались, это заголовочные файлы и бинарные библиотеки для OpenGL .
 Далее создаем пустые заголовочный и реализующий класс Сube файлы из меню "создать новый файл". Класс Cube и является классом, реализующим OpenGL сцену.
Задаем имя CocoaOpenGL.h . Жмем Next:
Это мы создали заголовочный файл. Теперь аналогично создаем файл реализации(эти оба файла пока пусты):
Жмем Next:
 Пишем в поле имени CocoaOpenGL . Жмем Next:
 И, наконец, create, мы увидим пустой файл реализации:
 Новый заголовочный файл заполняем таким содержимым:
@interface Cube : NSOpenGLView
{
    bool        initialized;
    float        rotation[3];
}
+ (NSOpenGLPixelFormat*) basicPixelFormat;
- (void) drawRect: (NSRect) bounds;
- (void) resizeGL:(NSRect)rect;
- (id)initWithFrame:(NSRect)frameRect;
@end
Файл реализации делаем пока таким:
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#import "CocoaOpenGL.h"
#import <OpenGL/gl.h>
#import <OpenGL/glu.h>
@implementation Cube
+ (NSOpenGLPixelFormat*) basicPixelFormat{
}
- (void) resizeGL:(NSRect)rect{
}
- (id)initWithFrame:(NSRect)frameRect{
}
- (void)drawRect:(NSRect)rect {
[super drawRect:rect];
// Здесь будет рисующий код
}
@end
Далее заполняем пустые тела функций следующим содержанием. basicPixelFormat :
    static NSOpenGLPixelFormatAttribute    attributes[] =   
    {
        //NSOpenGLPFAWindow,
        NSOpenGLPFADoubleBuffer,
        NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)32,
        (NSOpenGLPixelFormatAttribute)nil
    };
    return ([[[NSOpenGLPixelFormat alloc] initWithAttributes:attributes] autorelease]);
initWithFrame :
     NSOpenGLPixelFormat    *pf;
     pf   = [Cube basicPixelFormat];
     self = [super initWithFrame: frameRect pixelFormat: pf];  
     return (self);
drawRect :
    int            width, height;
    int            i, j;       
    float         aspectRatio;

    if (!initialized)
    {
        rotation[0] = 45.0f;
        rotation[1] = 45.0f;
        rotation[2] = 45.0f;
        initialized = true;
    }

    width  = rect.size.width;
    height = rect.size.height;
 
    glViewport(0, 0, width, height);
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    aspectRatio = (GLfloat)width/(GLfloat)height;
    //  gluPerspective(100.0f, aspectRatio, 1, 2);
    if(width <= height)
    {
        glOrtho(-2.0f, 2.0f, -2/aspectRatio, 2/aspectRatio, 2.0f, -2.0f);
    }
    else
    {
        glOrtho(-2*aspectRatio, 2*aspectRatio, -2.0f, 2.0f, 2.0f, -2.0f);
    }
    
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glRotatef(rotation[0], 1.0f, 0.0f, 0.0f);
    glRotatef(rotation[1], 0.0f, 1.0f, 0.0f);
    glRotatef(rotation[2], 0.0f, 0.0f, 1.0f);
    // рисуем куб
    glEnable(GL_DEPTH_TEST); 
    glBegin(GL_QUADS);
    for (i = 0; i < 6; i ++)
    {
        glColor3fv(colors[i]);
        for (j = 0; j < 4; j ++)
            glVertex3fv(corners[sides[i][j]]);
    }
    glEnd();  
  
    glFlush();
    //[[self openGLContext]flushBuffer];

Обратите внимание, что функция resizeGL - пуста. Код, который реагирует на изменение собственно сцены (не так в Линуксе и я Windows), находится теле основной рисующей функции drawRect. 
Также после директив импорта в файле реализации вставляем такие строки:
static const GLfloat    corners[8][3] =   
{
    {  1.0f,  1.0f,  1.0f },    // Front top right
    {  1.0f, -1.0f,  1.0f },    // Front bottom right
    { -1.0f, -1.0f,  1.0f },    // Front bottom left
    { -1.0f,  1.0f,  1.0f },    // Front top left
    {  1.0f,  1.0f, -1.0f },    // Back top right
    {  1.0f, -1.0f, -1.0f },    // Back bottom right
    { -1.0f, -1.0f, -1.0f },    // Back bottom left
    { -1.0f,  1.0f, -1.0f }     // Back top left
};
static const int    sides[6][4] =   
{
    { 0, 1, 2, 3 },                // Front
    { 4, 5, 6, 7 },                // Back
    { 0, 1, 5, 4 },                // Right
    { 2, 3, 7, 6 },                // Left
    { 0, 3, 7, 4 },                // Top
    { 1, 2, 6, 5 }                 // Bottom
};
static const GLfloat    colors[6][3] = 
{
    { 1.0f, 0.0f, 0.0f },        // Red
    { 0.0f, 1.0f, 0.0f },        // Green
    { 1.0f, 1.0f, 0.0f },        // Yellow
    { 0.0f, 0.0f, 1.0f },        // Blue
    { 1.0f, 0.0f, 1.0f },        // Magenta
    { 0.0f, 1.0f, 1.0f }         // Cyan
};
Далее идем в Main.storyboard:
 Добавляем OpenGL View c хранилища элементов управления главный вид приложения. В правом верхнем углу среды разработки в поле Custom Class указываем на класс Cube (при выделенном элементе управления OpenGL View)
 Далее на вкладке Build Settings поле Objective-C Automatic Reference Counting в значение No
 Для нашего OpenGL View элемента надо установить поле Depth в любое не-none значение:
 Компилируем, запускаем и видим:

понедельник, 29 июня 2015 г.

Как обменять значения двух переменных без использования третей в языке си

Приведу пять способов, без комментариев:
switch2val(int *a, int* b){
    *a=*b+*a;
    *b=*a-*b;
    *a=*a-*b;
}

switch2val(int *a, int *b){
     *a=*a+*b-(*b=*a);
}

switch2val(int *a, int *b){
     *a=*a^*b;
     *b=*a^*b;
     *a=*b^*a;
}

switch2val(int *a, int *b){
     *a=*b-~*a-1;
     *b=*a+~*b+1;
     *a=*a+~*b+1;
}

switch2val(int *a, int *b){
      *a=*b+*a,*b=*a-*b,*a=*a-*b;
}

вторник, 23 июня 2015 г.

Objective-C шпоргалка, часть 1

  • Члены класса называются инвариантами;
  • Создаваемые в Objective-С объекты классов размещаются в динамической памяти;
  • Тип id является указателем на объект любого класса (как void *);
  • Если тип, возвращаемы методом, не указан, то считается, что возвращается значение типа id;
  • Нулевой указатель именуется константой nil;
  • Инвариант isa присутствует в любом объекте класса, унаследовавшего класс NSObject;
  • Все классы должны наследовать класс NSObject;
  • Каждый класс сам по себе тоже является объектом;
  • Каждый объект Objective-C содержит в себе атрибут (инвариант) isa - указатель на объект класса данного объекта (экземпляра). Объект класса для данного объекта (экземпляра) автоматически создается компилятором и существует как один экземпляр, на который через isa ссылаются все экземпляры данного класса;
  • Все зарезервированные слова Objective-C, отличающиеся от зарезервированных слов языка С, начинаются с символа @;
  • Строковая константа класса NSString записывается как @”my string”;
  • Директива @protected стоит по умолчанию. Ограничивает область видимости инвариантов класса методами класса и методами производных классов;
  • Тип BOOL (на самом деле это unsigned char) принимает только константные значения YES и NO;
  • Объект выполняет метод если ему приходит сообщение, именуемое так же, как и требуемый метод. Такое сообщение называется селектор метода;
  • Директива @selector(name_of_method) возвращает скомпилированный селектор для имени метода name_of_method;
  • При посылке сообщения в nil оно исчезает;
  • Если в начале прототипа метода поставить знак '+', то такой метод будет считаться методом класса;
  • Такой метод не принимает неявный параметр self, соответственно указатель super тоже работать не будет;
  • Если в начале прототипа метода поставить знак '-', то такой метод называется методом объекта (или экземпляра);
  • Указатель на данные экземпляра текущего(данного) объекта доступен посредством зарезервированного слова self, а указатель на данные экземпляра базового класса – через super;
  • Все методы объектов в Objective-C являются виртуальными и всегда следуют динамическому полиморфизму;
  • В метод объекта также передается неявный параметр _cmd – селектор этого метода из глобальной таблицы селекторов;
  • Примеры описания (объявления) методов - метода объекта класса, метода класса без аргументов, с одним, с двумя и с переменным числом аргументов, соответственно:
    + (id)method1;
    - (void) method2;
    - (void) method3: (float) the_value1;
    - (void) method4: (float) the_value2 second_part_of_name_method4: (float) the_value3;
    - method: (id) object, ...;
  • Синтаксис вызова метода без параметров, с фиксированным числом параметров, с переменным числом параметров, соответственно, таков:
    [receiver method];
    [receiver method_arg1: 10 arg2: 10] - в данном случае имя метода является таким - method_arg1:arg2:  Этот момент наиболее трудно воспринимаем;
    [receiver variable_number_parameters: var1, var2, var3]; 
  • При посылке сообщения объекту, который принадлежит классу, не реализовавшему заказанный метод, возникает исключение, которое приводит к незапланированному завершению;
  • Поэтому, когда такая ситуация возможна, лучше провести соответствующую проверку:
    if ([object respondsToSelector: @selector(method::)])
    {
    [object method: 10 : 10];
    }
    else
    {

    }
    respondsToSelector - это метод класса NSObject;
  • Сообщение [receiver method] преобразуется в Си функцию с прототипом:
    id objc_msgSend(id receiver, SEL method, ...);
  • Тип SEL определен как char const*;
  • Во время выполнения все селекторы индексируются целыми значениями согласно глобальной таблице селекторов;
  • Глобальная таблица селекторов - это такая таблица, в которой некоему уникальному целому числу (селектору) соответствует своя строка (имя метода);
  • Функция objc_msgSend пользуясь инварианом isa oбъекта просматривает локальный список селекторов класса, чтобы определить, отвечает ли объект данного класса на сообщение method. Если у объекта-приемника данный селектор отсутствует, функция objc_msgSend() просматривает список селекторов его базового класса и т.д.;

пятница, 19 июня 2015 г.

Комбинации клавиш необходимые для комфортной работы в Mac OS X

  • Вызвать mission control: ctrl+"стрелка вверх";
  • Перейти на рабочий стол слева (если он создан в mission control):                                    ctrl+"стрелка влево";
  • Перейти на рабочий стол справа (если он создан в mission control):ctrl+"стрелка вправо";
  • Показать рабочий стол: F11;
  • Вызвать dashboard: F12;
  • Переместить фокус в меню: ctrl+F2;
  • Переместить фокус в dock: ctrl+F3;
  • Перейти к следующему несвернутому окну данного рабочего стола: ctrl+F4;
  • Перейти к  следующему несвернутому окну данного приложения: command+` ;
  • Переключиться между запущенными приложениями: command+tab;
  • Переместить фокус в меню статуса: ctrl+F8;
  • Поменять раскладку клавиатуры: command+space;
  • Вызвать spotlight: ctrl+space;
  • Убить текущее зависшее приложение: option+command+shift+esc;
Конечно вы можете поменять эти комбинации клавиш в system preferences. Здесь представлены установки по умолчанию. Также по умолчанию отсутствуют горячие клавиши для следующих операций: 
  • Вызвать launch pad;
  • Вызвать notification center;
  • Усыпить дисплей;
Я бы рекомендовал их определить для этих трех последних пунктов.Также очень удобной является комбинация:  
  • "Перейти в директорию" - shift+command+g;
По умолчанию в finder дерево файловой структуры частично невидно. Иногда это удобно, иногда - нет. На тот случай, когда вам надо переместиться в директорию, которая "невидима", эта комбинация клавиш незаменима.
Уверен - твердое знание этих комбинаций ускорит и сделает более комфортной вашу работу.
Кстати, обратите внимание, что клавиша ctrl более "крута", чем command.

понедельник, 15 июня 2015 г.

Программа "Hello, World" в питоне

Создаем текстовый файл hello.py следующего содержания:
#!/usr/bin/env python
import sys

def main():
    print 'Hello, ', sys.argv[1]

if __name__ == '__main__':
    main()
Далее из консоли делаем:
#chmod +x hello.py
Теперь из этой же директории можно сделать:
#hello World
В результате увидим в выводе следующее:
Hello, World

среда, 10 июня 2015 г.

Как послать почтовое сообщение из командной строки UNIX

Существует много способов сделать это, конечно. Приведем примеры с использованием утилит mail и mutt:
#echo "message" | mutt -a /path/to/file/with/attachment -s "subject" 
        -- my@e.mail.address
#mutt -a /path/to/file/with/attachment -s "subject" -- my@e.mail.address 
        < /path/to/file/with/text
#echo "message" | mail -s "subject" my@e.mail.address
#mail -s "subject" my@e.mail.address < /path/to/file/with/text

понедельник, 1 июня 2015 г.

Python-программа с интерфесом Cocoa

Рассмотрим простейшую программу, написанную на питоне, но использующую интерфейс Cocoa. Достоинство питона, как известно, заключается в легкости его изучения и кроссплатформенности. Однако, если писать программу на питоне, то встает вопрос - какую оконную систему использовать. Если мы используем MacOS, то естественно, предпочтительно использовать Cocoa. Здесь рассматривается как соединить мощь питона и "внешний вид" Cocoa. Примеры компилировались на OS X Yosemite, Version 10.10.3.
Для начала нам необходимо установить последнюю версию питона. Для этого вытираем из файловой системы текущую версию питона.
#rm -rf /Library/Frameworks/Python.framework
#rm -rf /Applications/Python*
#rm -rf /usr/local/bin/python*
Устанавливать питон предпочтительно утилитой brew. В принципе конечно можно устанавливать и другими путями: "вручную" - выкачивая прямо с домашней страницы с распаковкой по нужному пути; используя утилиту fink; или используя порты. Я предпочитаю brew. Если она еще не установлена, устанавливаем ее, нужно соединение с интернетом:
#ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
Далее делаем следующую последовательность команд:
#brew doctor
#brew update
#brew install python
#brew install python3
Следим, чтобы значение переменной PATH (в нашем случае это /usr/local/bin) указывало на интерпретатор питона. Если нет - то делаем это вручную - редактируем .bash_profile (или .bash_login или .profile) - добавляем строку 
export PATH=/usr/local/bin:/usr/local/sbin:$PATH
- и перелогиниваемся:
#echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/texbin
#which python

/usr/local/bin/python
#which python3
/usr/local/bin/python3

Также интерпретатор находится по пути /usr/bin/python
brew проинсталлирует полезные для работы со средой питона утилиты pip и setuptools. 
pip - это менеджер пакетов питона;
setuptools - это набор утилит для установки пакетов питона через сеть. Наиболее полезная из них - easy_install.
Обновим версию pip и setuptools до актуальной (версии, установленные brew могут отставать):
#pip install --upgrade pip setuptools
#pip3 install --upgrade pip setuptools
или:
#easy_install3 pip
#brew linkapps python
#brew linkapps python3
Теперь, когда у нас установлен питон, нам надо "провести мост" для взаимодействия между ним и "родным" для MacOS языком objective-c. Это осуществляется модулем питона PyObjC. С помощью этого модуля мы имеем функции питона, вызывающие соответствующие функции objective-c. Устанавливаем:
#pip2 install -U pyobjc-core
#pip2 install -U pyobjc
Теперь мы имеем интерпретатор питона обоих версий (2-й и 3-й) по пути /usr/local/bin и директорию site-packages по пути /usr/local/lib/python[2.7-3.4]/site-packages со всеми необходимыми для нашей задачи модулями питона. Питон реально расположен по пути: /usr/local/Cellar/python[3]
Наконец мы можем приступить к нашей главной цели.
Открываем XCode и создаем новый пустой проект PyObjC.
Обобщенная схема наших действий будет следующая:
  • Создаем файл с питоновским кодом PythonCode.py c реализацией класса PythonClassForCocoa;
  • Создаем файл setup.py;
  • Создаем в XCode файл интерфейса CocoaWindow.xib;
  • Создаем наше целевое приложение PythonCode.app;
Теперь немного более подробно и в картинках:

Создаем новый файл:



Код файла PythonCode.py:
from Cocoa import *
from Foundation import NSObject

class PythonClassForCocoa(NSWindowController):
    counterTextField = objc.IBOutlet()
  
    def windowDidLoad(self):
        NSWindowController.windowDidLoad(self)
      
        # Start the counter
        self.count = 0
  
    @objc.IBAction
    def increment_(self, sender):
        self.count += 1
        self.updateDisplay()
  
    @objc.IBAction
    def decrement_(self, sender):
        self.count -= 1
        self.updateDisplay()
  
    def updateDisplay(self):
        self.counterTextField.setStringValue_(self.count)

if __name__ == "__main__":
    app = NSApplication.sharedApplication()
  
    # Initiate the contrller with a XIB
    viewController = PythonClassForCocoa.alloc().initWithWindowNibName_("CocoaWindow")
  
    # Show the window
    viewController.showWindow_(viewController)
  
    # Bring app to top
    NSApp.activateIgnoringOtherApps_(True)
  
    from PyObjCTools import AppHelper
    AppHelper.runEventLoop()
Обратите внимание на выделенные красным цветом участки кода.
Опять создаем новый файл:

Это файл с графическим интерфейсом. В пустое окно ложим три кнопки и текстовое поле. Две кнопки соответствуют методам нашего питоновского класса, текстовая метка соответствует полю нашего класса, а третья кнопка будет для выхода из приложения. Для установки данного соответствия методом drag&drop "соединяем" эти кнопки с иконкой File's Owner, кнопку выхода с методом terminate в иконке First Responder. Поле класса с меткой связываем используя drag&drop из иконки File's Owner в эту метку в диалоге. Вышеупомянутые элементы видны на следующем рисунке. Также необходимо при "активном" File's Owner наисать имя нашего питоновского класса в "Custom Class":
Обратите внимание, что XCode используется только для построения интерфейса. Питоновские файлы мы можем писать используя другие среды разработки. Здесь же в проете мы имеем только два файла: CocoaWindow.xib и PythonCode.py.
Теперь сохдадим файл setup.py:
В директории проекта из командной строки запускаем следующую команду:
#py2applet --make-setup PythonCode.py
Создается файл setup.py, который надо отредактировать, как обозначено цветом:
#vim setup.py
"""
This is a setup.py script generated by py2applet

Usage:
    python setup.py py2app
"""

from setuptools import setup

APP = ['PythonCode.py']
DATA_FILES = ['CocoaWindow.xib']
OPTIONS = {здесь пробел или пусто}

setup(
    app=APP,
    data_files=DATA_FILES,
    options={'py2app': OPTIONS},
    setup_requires=['py2app'],
)

Последний шаг:
#python setup.py py2app -A
Если все сделано аккуратно, то появиться директория dist c нашим целевым приложением PythonCode.app, при запуске которого появится диалог с текстовым полем, где отображается цифра, которую можно менять, нажимая на две кнопки:
Признаю, что для чтения этот текст тяжел. Несмотря на то, что долго думал, как описать все эти действия. Особенно место, где обясняется работа с графическим интерфейсом, не  очень удачно.  Посмотрите англоязычное решение описанной задачи.

воскресенье, 4 января 2015 г.

Лямбда выражения и алгоритмы STL transform и for_each

Анонимная лямбда функция в С++ в наиболее абстрактном виде выглядит так:
[](){}
или так:
[]() mutable -> T { }
где
[] - список внешних переменных передаваемых внутрь лямбда функции;
() - список аргументов лямбда функции;
T - явно задаваемый тип возвращаемой лямбда функцией значения;
{} - тело лямбда функции.
Рассмотрим простой пример использования алгоритмов for_each и transform:
#include <algorithm>
#include <vector>
#include <iostream>
class functorType1{
public:
        void operator()(double i){
                std::cout << i << std::endl;
        }
};     
class functorType2{
public:
        double operator()(double i){
                return ++i;
        }
};     
int main(int argc, char* argv[]){
        std::vector<double> v;
        functorType1 functorObject1;
        functorType2 functorObject2;
        v.push_back(1);
        v.push_back(2);
        v.push_back(3);
        std::for_each(v.begin(), v.end(), functorObject1);
        std::transform(v.begin(), v.end(), v.begin(), functorObject2);
        std::for_each(v.begin(), v.end(), functorObject1);
        return 0;
}

А теперь то же самое, но с использованием лямбда выражения (они обозначены жирным шрифтом):
#include <algorithm>
#include <vector>
#include <iostream>
int main(int argc, char* argv[]){
        std::vector<double> v;      
        v.push_back(1);
        v.push_back(2);
        v.push_back(3);
        std::for_each(v.begin(), v.end(), [](double i){std::cout << i << std::endl;});
        std::transform(v.begin(), v.end(), v.begin(),  [](double i) ->double {return ++i;});
        std::for_each(v.begin(), v.end(), [](double i){std::cout << i << std::endl;});
        return 0;
}
Это более удобно для использования маленькой функции. Не нужно ее определять где-то. Используется прямо на месте. Получается более удобно читаемый код.
Теперь чуть модифицируем этот код для иллюстрации использования внешних к лямбда функции переменных:
#include <algorithm>
#include <vector>
#include <iostream>
int main(int argc, char* argv[]){
        double var1=10;
        double var2=100;
        std::vector<double> v;
        v.push_back(1);
        v.push_back(2);
        v.push_back(3);
        std::for_each(v.begin(), v.end(), [](double i){std::cout << i << std::endl;});
        std::transform(v.begin(), v.end(), v.begin(),  [var1, var2](double i) ->double {return ++i/var1*var2;});
        std::for_each(v.begin(), v.end(), [](double i){std::cout << i << std::endl;});
        return 0;
}
Все примеры компилировались командой g++ ourSample.cpp -std=c++11на системе Дебиан.

суббота, 3 января 2015 г.

Строковые типы в Windows, часть 2

Если мы работаем со строками STL в Windows и хотим быть независимыми от значения переменной _UNICODE (то есть писать код независимый от того работаем ли мы со строками UNICODE или нет), то тогда можно воспользоваться таким куском кода:
#ifdef _UNICODE
#define tstring wstring
#else
#define tstring string
#endif
Если воспользоваться фактом, что std::string и std::wstring являются typedef шаблона класса std::basic_string, то можно сделать так:
namespace std
{
 typedef basic_string<TCHAR> tstring; 
}

Наиболее общее решение выглядит так:

#include  <tchar.h>
#include  <string>
#include  <fstream>
#include  <sstream>
namespace std
{
typedef basic_string<TCHAR> tstring;
typedef basic_ostream<TCHAR> tostream;
typedef basic_istream<TCHAR> tistream;
typedef basic_iostream<TCHAR> tiostream;
typedef basic_ifstream<TCHAR> tifstream;
typedef basic_ofstream<TCHAR> tofstream;
typedef basic_fstream<TCHAR> tfstream;
typedef basic_stringstream<TCHAR> tstringstream;
}
Взято отсюда.

Графическое представление системы ввода-вывода в STL