iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど
前回の続き。
エラーの原因がわかった。glVertexPointer()渡している列挙子が間違えていた。GL_FLATではなくGL_FLOATだ。
#import <iostream>
#import <sstream>
#import <Foundation/Foundation.h>
#import <OpenGL/gl.h>
#import <OpenGL/glu.h>
#import <GLUT/glut.h>
int gWidth = 600;
int gHeight = 500;
const int QUIT_VALUE(99);
GLuint gListID; /* ディスプレイリストID */
void display(void)
{
/* カラーバッファの初期化 */
glClear(GL_COLOR_BUFFER_BIT);
/* モデリング変換、z軸の負の方向に幾何形状を4単位移動する。 */
glLoadIdentity(); /* 単位行列 */
glTranslatef(0.0f, 0.0f, -4.0f);
/* 幾何形状を描画する。 */
glCallList(gListID);
/* バッファの入れ替え */
glutSwapBuffers();
assert(glGetError() == GL_NO_ERROR);
}
void resize(int w, int h)
{
/* ウィンドウ・サイズとOpenGLの座標を対応づける */
glViewport(0, 0, w, h);
/* 投影行列とアスペクト比を更新する */
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(50.0, (GLdouble)w / (GLdouble)h, 1.0, 10.0);
/* 表示ルーチン用にモデルビューモードに設定する */
glMatrixMode(GL_MODELVIEW);
assert(glGetError() == GL_NO_ERROR);
gWidth = w;
gHeight = h;
}
void keyboard(unsigned char key, int x, int y)
{
DBGMSG(@"%s", __func__);
}
void special(int key, int x, int y)
{
DBGMSG(@"%s", __func__);
}
void mouse(int button, int state, int x, int y)
{
DBGMSG(@"%s", __func__);
}
void motion(int x, int y)
{
DBGMSG(@"%s", __func__);
}
void idle(void)
{
glutPostRedisplay();
}
void main_menu_callback(int value)
{
if (value == QUIT_VALUE)
exit(EXIT_SUCCESS);
}
void init(void)
{
DBGMSG(@"%s", __func__);
/* ディザ処理を無効にする */
glDisable(GL_DITHER);
std::string ver((const char*)glGetString(GL_VERSION));
assert(! ver.empty());
std::istringstream verStream(ver);
int major, minor;
char dummySep;
verStream >> major >> dummySep >> minor;
const bool useVertexArrays = ((major >= 1) && (minor >= 1));
const GLfloat data[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
if (useVertexArrays) {
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, data);
}
/* ディスプレイリストを作成する。 */
gListID = glGenLists(1);
glNewList(gListID, GL_COMPILE);
if (useVertexArrays) {
glDrawArrays(GL_TRIANGLES, 0, 3);
//glDisableClientState(GL_VERTEX_ARRAY);
}
else {
glBegin(GL_TRIANGLES);
glVertex3fv(&data[0]);
glVertex3fv(&data[3]);
glVertex3fv(&data[6]);
glEnd();
}
glEndList();
assert(glGetError() == GL_NO_ERROR);
/* 描画 */
glutDisplayFunc(display);
/* リサイズ処理 */
glutReshapeFunc(resize);
/* キーボード */
glutKeyboardFunc(keyboard);
/* 特殊キー */
glutSpecialFunc(special);
/* マウス */
glutMouseFunc(mouse);
/* ドラッグ */
glutMotionFunc(motion);
/* バックグランド処理 */
glutIdleFunc(idle);
/* コンテキスト・メニュー */
glutCreateMenu(main_menu_callback);
glutAddMenuEntry("Quit", QUIT_VALUE);
glutAttachMenu(GLUT_RIGHT_BUTTON);
}
int main(int argc, const char * argv[])
{
@autoreleasepool {
glutInit(&argc, (char **)argv);
/* RGBカラーモード ダブルバッファ */
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
/* 初期ウィンドウ・サイズ */
glutInitWindowSize(gWidth, gHeight);
/* 初期ウィンドウ位置 */
glutInitWindowPosition(500, 100);
/* タイトルバー */
glutCreateWindow("IRIS GL");
/* 初期化 */
init();
/* 主ループ(イベント駆動) */
glutMainLoop();
}
return 0;
}
Combineはイベント処理演算子を組み合わせて、非同期でイベントを処理するSwiftのAPIだ。
既存のNotificationCenterやAppKitとUIKitのUI部品はCombineに対応しているので、Combineのイベント処理演算子の使ったイベント処理が行える。
例えば、テキスト入力フィールドとデバッグ出力用テキスト・ビューがあるNSViewControllerの派生クラスがあるとする。
import Cocoa
import Combine
class ViewController: NSViewController {
@IBOutlet var inputField: NSTextField!
@IBOutlet var debugMessageTextView: NSTextView!
var sub: AnyCancellable? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
inputFieldにテキストが入力されたら、そのイベントを受け取りたい場合は、以下のコードとなる。
override func viewDidLoad() {
super.viewDidLoad()
sub = NotificationCenter.default
.publisher(for: NSControl.textDidChangeNotification, object: inputField)
.sink(receiveCompletion: { print ($0) },
receiveValue: { print ($0) })
}
sink() はイベントを受け取るもので、こんな感じでイベント処理演算子をつなげていることになる。
override func viewDidLoad() {
super.viewDidLoad()
sub = NotificationCenter.default
.publisher(for: NSControl.textDidChangeNotification, object: inputField)
.map( { ($0.object as! NSTextField).stringValue } )
.filter( { $0.unicodeScalars.allSatisfy({CharacterSet.alphanumerics.contains($0)}) } )
.debounce(for: .milliseconds(500), scheduler: RunLoop.main)
.receive(on: RunLoop.main)
.assign(to:\NSTextView.string, on: debugMessageTextView)
}
上の例のコードを説明する。
map() で文字列に変換している。
filter() で英数字のみとしている。
debounce() でイベント発行の頻度を調整している。
receive() でメイン・スレッドで受け取るようにしている。
assign() で入力内容をデバッグ出力用テキスト・ビューに設定している。