以下の、Clone or downloadからVCG Libraryをダウンロードする。
https://github.com/cnr-isti-vclab/vcglib/
//基本的なデータタイプ #include <vcg/complex/complex.h> // PLYファイルの入出力 #include <wrap/io_trimesh/import_ply.h> #include <wrap/io_trimesh/export_ply.h>
vcg::Vertex,vcg::Faceなどの既存型があるが、それらを継承した型を使うのが方針らしい。
class MyFace; class MyVertex; class MyEdge; struct MyUsedTypes : public vcg::UsedTypes< vcg::Use<MyVertex>::AsVertexType, vcg::Use<MyFace>::AsFaceType, vcg::Use<MyEdge>::AsEdgeType> {}; //頂点型 class MyVertex : public vcg::Vertex< MyUsedTypes, vcg::vertex::Coord3f, vcg::vertex::Color4b, vcg::vertex::Normal3f, vcg::vertex::VFAdj, vcg::vertex::VEAdj, vcg::vertex::Qualityf, vcg::vertex::BitFlags, vcg::vertex::Mark> { //ここにメンバ変数を追加できる }; //面型 class MyFace : public vcg::Face < MyUsedTypes, vcg::face::VertexRef, vcg::face::Normal3f, vcg::face::FFAdj, vcg::face::EFAdj, vcg::face::Mark, vcg::face::VFAdj, vcg::face::BitFlags > { }; //エッジ型 class MyEdge : public vcg::Edge< MyUsedTypes, vcg::edge::VertexRef, vcg::edge::BitFlags, vcg::edge::EVAdj, vcg::edge::EFAdj> { }; //3Dモデル型 class MyMesh : public vcg::tri::TriMesh< std::vector<MyVertex>, std::vector<MyFace>, std::vector<MyEdge> > { };
エッジ情報などはデフォルトでは計算してくれないので、UpdateTopologyのAllocateEdgeなどで作成する
//メッシュデータオブジェクト MyMesh mesh; int main(int argc, char **argv) { //plyファイル読み込み vcg::tri::io::ImporterPLY<MyMesh>::Open(mesh, "data.ply"); //面法線計算 vcg::tri::UpdateNormal<MyMesh>::PerFace(mesh); //Triangleのエッジ情報を作成 vcg::tri::UpdateTopology<MyMesh>::AllocateEdge(mesh); //縮尺 // meshオブジェクトがbboxメンバ(Bounding Box)を持っているので、 // モデルの最大・最小の座標がわかる double scalex = 1.0 / (mesh.bbox.max.X() - mesh.bbox.min.X()); double scaley = 1.0 / (mesh.bbox.max.Y() - mesh.bbox.min.Y()); double scalez = 1.0 / (mesh.bbox.max.Z() - mesh.bbox.min.Z()); double scale = (std::min)((std::min)(scalex, scaley), scalez); //センタリング double offsx = mesh.bbox.min.X() + (mesh.bbox.max.X() - mesh.bbox.min.X()) / 2; double offsy = mesh.bbox.min.Y() + (mesh.bbox.max.Y() - mesh.bbox.min.Y()) / 2; double offsz = mesh.bbox.min.Z() + (mesh.bbox.max.Z() - mesh.bbox.min.Z()) / 2;
全ての三角形はmeshオブジェクト内のface配列に格納されている。
//三角形で表示 void disp_triangles() { //マテリアルの設定 glMaterialfv(GL_FRONT, GL_AMBIENT, material_ambient); glMaterialfv(GL_FRONT, GL_SPECULAR, material_specular); glEnable(GL_LIGHTING); glMaterialfv(GL_FRONT, GL_DIFFUSE, material_diffuse_w); //描画処理本体 // mesh.face[0].N で面法線 // mesh.face[0].V(0)->C.X() で三角形の第一頂点のR // mesh.face[0].V(0)->P().X() で三角形の第一頂点のx座標 // * cがついているのはconstの意味 glBegin(GL_TRIANGLES); for (const auto& f : mesh.face) { glNormal3d(f.cN().X(), f.cN().Y(), f.cN().Z()); for (size_t i = 0; i < 3; i++) { glColor3ub(f.cV(i)->cC().X(), f.cV(i)->cC().Y(), f.cV(i)->cC().Z()); glVertex3d(f.cV(i)->cP().X(), f.cV(i)->cP().Y(), f.cV(i)->cP().Z()); } } glEnd(); } //三角形のエッジを表示 void disp_edge() { glDisable(GL_LIGHTING); glColor3d(1, 0, 0); glBegin(GL_LINES); for (const auto& e : mesh.edge) { glVertex3d(e.cV(0)->cP().X(), e.cV(0)->cP().Y(), e.cV(0)->cP().Z()); glVertex3d(e.cV(1)->cP().X(), e.cV(1)->cP().Y(), e.cV(1)->cP().Z()); } glEnd(); }
VCG LibraryのBall Pivotingでメッシュ生成
VCG Library ターゲットの周辺のオブジェクトを取得
Vray for Blender、Sun(Direct)のDecayの変化を一括して動画にしました。
Decayは減衰を表します。ライトの距離を離していくと、以下のように違いが現れます。
Point Cloud Library (PCL) と言うのを使っているのですが、その中でstlの入出力をしたくなりました。
#include <pcl/io/vtk_lib_io.h>
をしてから
pcl::io::loadPolygonFileSTL ()
を使ってみましたが、リンクエラーでvtkDebugLeaksManagerが見つからないといわれます。
答えを言うとvtkCommonCore-8.0.lib をリンクすればいいのですが、調べてみてもCMakeすればいいよみたいな情報しかなくて結構困りました。
vtkとついているんだからvtkのlibディレクトリを探せばいいことまではわかっています。ある程度目星がついているのに他を探し回るのは嫌だし、だからと言って200個あるlibを一つ一つリンクしてみるのも自分がかわいそうになるので、もう少しましな方法を考えます。
以下のようなBATファイルを作成しました(linksearch.bat)
使い方は、第一引数にライブラリディレクトリのパス、第二引数に検索したいシンボルを指定します。
linksearch "C:\Program Files\PCL 1.8.1\3rdParty\VTK\lib\*" vtkDebugLeaksManager@@QEAA@XZ
この出力結果(flistresult.txt)は、このようになります。
... (以下略)
※行が長くなるので中身を.\libx\に移しています
検索対象のvtkDebugLeaksManagerが表れているなら、その上のlib名で定義されています
本当は、必要なファイルだけを表示するなどしたかったのですが、Windowsのbatがよくわからずに断念しました。
Microsoft COFF Binary File Dumper (DUMPBIN.EXE) は、COFF (Common Object File Format) 形式のバイナリ ファイルに関する情報を出力します。 DUMPBIN を使うと、COFF オブジェクト ファイル、COFF オブジェクトの標準ライブラリ、実行可能ファイル、およびダイナミック リンク ライブラリ (DLL) の内容を確認できます。
ということで、dumpbinはexe,dll,libの内容を確認するためのツールです。
dumpbinはVisual Studioに入っています。Visual Studioのコマンドプロンプト、例えば「VS 2017用 x64 Native Tools コマンドプロンプト」から起動します。
そして、/LINKERMEMBER オプションをつけます。
このオプションは、ライブラリで定義されているパブリック シンボルを出力します。
ただしこれだけだと、必要のない情報も全部出力されてしまいます。テキストエディタの検索機能を使ってもいいのですが、せっかくなのでfindstrへパイプします。
ファイルの中からテキスト文字列を検索する。検索対象のパス指定が無ければ、プロンプトで入力されたテキストまたは別のコマンドからパイプ処理で渡されたテキストを検索する。正規表現の使用ができる。
即ち、目星のついているライブラリに対して、以下のように実行して、標準出力があればよいという事になります。
注意として、findstrに渡すシンボル名は大抵の場合、(C++でコンパイルされているなら)C++の命名規則で渡した方が確実のようです。@@とかついたものです。これはリンクエラーに書いてあるものをそのままコピペすればよいという意味です。
あとはこの内容をbatで適切に変えていけば良いと言うことになります。
Windowsのbatは(本当に)よくわからないので、解説出来ません。
Direct Light Parameters (Google翻訳)
Direct - a simple directional light
直接 - 単純な指向性のある光
ということで、Directは太陽光というよりも指向性のライトという扱いです。
とどのつまりはライトの強さです。
ライトの径です
Radius=10の時のhotspotの変化。
半径内の最も光の強い領域のサイズです
別ページで掲載
記事に画像などをアップロード出来ないことがあります。Wordpressの管理画面の「メディア」→「新規追加」の「最大アップロードサイズ」を上回るファイルはアップロード出来ません。
これが初期状態では2M等小さい値になっています。上限を上げる方法です。
php.iniか.htaccessを変更します。巷ではphp.iniのほうが多いですがそれでは効かない場合があります。やってみて動かなかったら.htaccessを編集しましょう。
ちなみに私はさくらレンタルサーバーを使っていますがphp.iniが効きませんでした。
次のいずれかを行います。
追加内容:
追加内容:
追加内容:
functions.phpの場合、ini_set関数を使います。
ini_setはphp.iniの設定を変える関数です。
@はエラーを出力しないためにつけます。
http://php.net/manual/ja/function.ini-set.php
意味は、
で、設定するサイズは以下の関係である必要があります。
C/C++で、ある三次元ベクトルに垂直なベクトルを求めます。
内積・外積を使うので、それらも定義する必要があります。
外積を求める式は、このようになります。
//! @brief 三次元ベクトルの外積を求める //! @param [out] dvec 求めたベクトル //! @param [in] svec1 一つ目の三次元ベクトル //! @param [in] svec2 二つ目の三次元ベクトル //! @return なし void outer(double * const dvec, double const * const svec1, double const * const svec2) { const double& xa = svec1[0]; const double& ya = svec1[1]; const double& za = svec1[2]; const double& xb = svec2[0]; const double& yb = svec2[1]; const double& zb = svec2[2]; dvec[0] = ya * zb - za * yb; dvec[1] = za * xb - xa * zb; dvec[2] = xa * yb - ya * xb; }
内積を求める式は、このようになります。
//! @brief ベクトルの内積を求める //! @param [in] vec1 一つ目の三次元ベクトル //! @param [in] vec2 二つ目の三次元ベクトル //! @return 内積 double inner(double const * const vec1, double const * const vec2) { const double& xa = vec1[0]; const double& ya = vec1[1]; const double& za = vec1[2]; const double& xb = vec2[0]; const double& yb = vec2[1]; const double& zb = vec2[2]; return (xa*xb + ya*yb + za*zb); }
//! @brief ベクトルの長さを求める関数 //! @param [in] vec 三次元ベクトル //! @return 長さ double vlen(double const * const vec) { const double& x = vec[0]; const double& y = vec[1]; const double& z = vec[2]; return sqrt(x * x + y * y + z * z); }
//! @brief 二つのベクトルの角度をラジアンで求める関数 //! @param [in] vec1 一つ目の三次元ベクトル //! @param [in] vec2 二つ目の三次元ベクトル //! @return 二つのベクトルのなす角(ラジアン) double vangle(double const * const vec1, double const * const vec2) { return acos(inner(vec1, vec2) / (vlen(vec1) * vlen(vec2))); }
上記コードでは省いていますが、acosの引数の範囲は-1~1なので、この範囲を超えると定義域エラーとなります。実用する場合は何らかのエラー処理が必要になります。
//! @brief ある三次元ベクトルに垂直な三次元ベクトルを求める //! @param [out] dst3 結果の格納先 //! @param [in] src3 三次元ベクトル void rightvec(double* dst3, double* src3) { double tmp[3] = { 1,0,0 }; //tmpとsrc3の角度0またはそれに近いなら、別なベクトルを用意 if (vangle(tmp, src3) < 0.00174533) { //0.00174533rad=1degree tmp[0] = 0; tmp[1] = 1; tmp[2] = 0; } //外積を求める outer(dst3, tmp, src3); }
ベクトルa,bの外積は、必ずa,b両方に対して垂直になります。
従って、あるベクトルuと、何でも良いからuとは違うベクトルvを用意し、その二つの外積wを求めると、必ずu⊥wとなります。計算が終わったらvは捨てて構いません。
ただし、u≠vでなければなりません。そこで上記プログラムでは、まずv(1,0,0)を用意します。もしuとvの角度が極めて0に近ければ、同じベクトルと考え、vを(0,1,0)に変更した後、外積を求めます
#include <Windows.h> #include <gl/GL.h> #include <gl/freeglut.h> #include <math.h> #pragma comment(lib,"opengl32.lib") #pragma comment(lib,"freeglut.lib")
void display(void) { glClear(GL_COLOR_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslated(0.0, 0.0, -5.0); glPushMatrix(); static double deg = 0; deg = deg + 1; glRotated(deg, 0, 1, 0); glColor3d(1, 0, 0); //一本目のベクトルを表示 double src3[3] = { 2,-3,4 }; glBegin(GL_LINES); glVertex3d(0, 0, 0); glVertex3dv(src3); glEnd(); glColor3d(0, 1, 0); double dst3[3]; rightvec(dst3, src3); //二本目のベクトルを表示 glBegin(GL_LINES); glVertex3d(0, 0, 0); glVertex3dv(dst3); glEnd(); glPopMatrix(); glFlush(); } void timer(int value) { glutPostRedisplay(); glutTimerFunc(50, timer, 0); } void init(void) { glClearColor(0.0, 0.0, 1.0, 1.0); } void resize(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(30.0, (double)w / (double)h, 1.0, 100.0); } int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA); glutCreateWindow(argv[0]); glutDisplayFunc(display); glutReshapeFunc(resize); glutTimerFunc(100, timer, 0); init(); glutMainLoop(); return 0; }
C++11以降、ハッシュマップ(hash map)の機能が追加されました。
しかしhash mapの名前の既存ライブラリがたくさんあったので、衝突しないようにunordered_mapという名前にしました。
で、このunorderd_mapは基本型ならそのまま使えるんですが、自作型の場合にはクラスのインスタンスからハッシュ値を作る機能も作ってやる必要があります。
#include <string> #include <unordered_map> #include <iostream> //////////////////////////////////////////////////////////////// //ハッシュ値を統合する ( 汎用的に使用できる関数 ) // seed in:既存のハッシュ値 out:元のseedとvから作成したハッシュ値を統合した値 // v 新たにハッシュ値を作成する値 template<typename T> void hash_combine(size_t & seed, T const& v) { //基本型に関するハッシュ生成は標準ライブラリが提供している std::hash<T> primitive_type_hash; //生成したハッシュを合成する。このコードはboostものを使用する seed ^= primitive_type_hash(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); } //////////////////////////////////////////////////////////////// // 自作データ class MyData { public: std::string m_v_str; int m_v_int; double m_v_double; }; //////////////////////////////////////////////////////////////// // 自作データの比較演算子 bool operator==(const MyData& v1, const MyData& v2) { return v1.m_v_str == v2.m_v_str && v1.m_v_int == v2.m_v_int && v1.m_v_double == v2.m_v_double;//本当はdoubleを==比較してはいけません } //////////////////////////////////////////////////////////////// //自作データのハッシュを作成 namespace std { // 標準ライブラリは struct hash<>というデータをハッシュ化する関数オブジェクトのテンプレートを提供している // これは最初から基本型に対しては特殊化されているのでそのまま使える(上記hash_combine内で使用) // 自作クラスの場合は自分で特殊化し、operator()を定義してやる。 template<> struct hash<MyData> { public: size_t operator()(const MyData& data)const { //クラスのメンバの値それぞれについてハッシュ生成して、それらを結合して一つのハッシュ値にする std::size_t seed = 0; hash_combine(seed, data.m_v_str); hash_combine(seed, data.m_v_int); hash_combine(seed, data.m_v_double); return seed; } }; } //////////////////////////////////////////////////////////////// // エントリポイント int main() { std::unordered_map<MyData, int> map; map[ { "hello" , 111 , 6.0e3 } ] = 0; map[ { "goodbye" , 111 , 6.0e3 } ] = 0; map[ { "hello" , 222 , 6.0e3 } ] = 0; map[ { "goodbye" , 222 , 6.0e3 } ] = 0; map[ { "hello" , 222 , 2.0e4 } ] = 0; map[ { "goodbye" , 222 , 3.0e4 } ] = 0; for (int i = 0; i < 3; i++) { map[{"hello" , 111, 6.0e3 }]++; map[{"goodbye", 111, 6.0e3 }]++; } using namespace std; //結果出力 for (auto& data : map) { cout << "( " << data.first.m_v_str << " , " << data.first.m_v_int << " , " << data.first.m_v_double << " ) = " << data.second << std::endl; } int i; cin >> i; return 0; }
unorderd_mapのキーとなる値が
std::hashはハッシュを生成するクラス(コード上はstruct)のテンプレートです。
これはintやfloat等の基本型や、std::stringやstd::shared_ptr型等の(標準)ライブラリ型に対して特殊化がされており、それらの型のハッシュ値を作成できます。
boost Reference l- 1.55.0 Header <boost/functional/hash.hpp>
これはboostにあった関数です。この関数は、与えられたハッシュ値と、与えられたデータから作成した別のハッシュ値の二つを統合し、一つのハッシュ値を作成します。boostではハッシュ生成にboost::hash_valueという関数が使われています。自前実装するときはstd::hashで作成します。
import bpy #頂点定義 verts = [] verts.append( [0.2,1.5,1.5] ) verts.append( [1.5,1.5,1.5] ) verts.append( [1.5,0.2,0.2] )
#面を、それを構成する頂点番号で定義 faces =[] faces.append( [0,1,2] )
#頂点と頂点番号からメッシュを作成 mymesh = bpy.data.meshes.new("mymesh") mymesh.from_pydata(verts,[],faces) #作成部分 mymesh.update()
#オブジェクトを作成し、メッシュを登録 obj = bpy.data.objects.new("My_Object", mymesh)
#オブジェクトのマテリアルを作成 mat = bpy.data.materials.new("Mat1") mat.diffuse_color = (0.3,0.7,0.5) obj.active_material = mat
#オブジェクトを登録 scene = bpy.context.scene scene.objects.link(obj)
#作成部分を以下のように変更すると、頂点の登録のみになります。
mymesh.from_pydata(verts,[],[])
エッジ用の頂点リストを作成したうえで、#作成部分を以下のように変更すると、エッジの登録のみになります。
edges = [] edges.append( [0,2] ) # エッジをなす二点の頂点番号 mymesh = bpy.data.meshes.new("mymesh") mymesh.from_pydata(verts,edges,[]) mymesh.update()
<?php /////////////////////////////////////////////////////////////// //カスタムフィールドの入力欄の中のデータをデータベースへ保存する function hook_func_for_save_customfield( $post_id ) { $areaname = "field_1"; $key = "_key_for_field_1"; ///// 正しい手段で送信されたデータであることを確認 //// //① nonceがセットされていなければ不正 if ( ! isset( $_POST[ 'nonce_for_field_1' ] ) ) return $post_id; //② nonceがセットされていても、自分で作成した値でないなら不正 if ( ! wp_verify_nonce( $_POST['nonce_for_field_1'] , 'send_field1' )) return $post_id; if ( ! current_user_can( 'edit_post', $post_id ) ) // ③ 書き込み権限がなければ不正 return $post_id; ///// カスタムフィールドの値を更新 /// $data = $_POST[$areaname]; if(get_post_meta($post_id, $key) == ""){ //新しいデータならデータを作成 add_post_meta($post_id, $key, $data, true); } elseif($data != get_post_meta($post_id, $key, true)){ //既存にあるデータで内容が異なるなら更新 update_post_meta($post_id, $key, $data); }elseif($data == ""){ //入力内容が空ならデータの削除 delete_post_meta($post_id, $key, get_post_meta($post_id, $key, true)); } return $post_id; } add_action( 'save_post', 'hook_func_for_save_customfield' ); ?>
save_postアクションフック
save_post は投稿や固定ページが作成または更新されたとき実行されるアクションです。インポート、投稿や固定ページの編集画面、XMLRPC、メールによる投稿などがきっかけになります。投稿データは投稿の編集方法に応じて $_POST, $_GET またはグローバルの $post_data に保存されます。例えばクイック編集は $_POST を使います。
これはWordpressではなくPHPの世界の話です。
<input type="hidden" id="nonce_for_field_1" name="nonce_for_field_1" value="81b29b57b2" />
というフィールドをPOSTした場合、PHP側では
として受け取ります。issetは'nonce_for_field_1'という変数が送信されてきたかを確認します。nonceが正しいかを調べる前に、そもそも送られてきているかを調べるわけです。
変数がセットされており、それが
NULL
でないことを調べます。
wp_nonce_fieldあるいはwp_create_nonceで生成したnonceと、送られてきたnonceが等しいかを検証する関数です。
nonce が正しいもので有効期限が切れていないことを、指定されたアクションとの関係も含めて確かめます。
似た関数にcheck_admin_refereがありますが、現在は非推奨です。
バージョン 2.0.1 以降、リファラーがチェックされるのは $action 引数を省略(またはデフォルトの -1 をセット)したときだけですが、これは nonce を使わない後方互換性のためです。
つまりリファラーをチェックするときはnonceがチェックされず、nonceがチェックされるときはリファラーがチェックされない・・・ということでしょうか。
まとめると、wp_verify_noceとwp_nonce_fieldは、以下のように対応しています。
<?php //送信時 wp_nonce_field( 'send_field1' , 'nonce_for_field_1' ); ?>
<?php //受信時 wp_verify_nonce( $_POST['nonce_for_field_1'] , 'send_field1' ) ?>
ユーザーが権限を所有しているかどうかを調べます。
ページの編集の権利を持っているかどうかを調べるときは、'edit_pages'を指定します。
権限の一覧はこちらが詳しいです:
WordPress私的マニュアル current_user_can
get_post_meta関数で該当する価を取得します。get_post_meta関数の第三引数がfalseまたは省略した場合、カスタムフィールドの配列が返ります。trueを指定した場合は、配列の内容が全て一つの文字列となって返ります。
その結果に応じて、次の各関数を呼び出し追加・削除・更新を行います。
add_post_meta ($post_id, $key, $data, true);
add_post_meta() は、指定した投稿や固定ページへカスタムフィールド(「メタデータ」とも呼ばれます)を追加します。どんな投稿タイプの投稿でも構いません。ひとつのカスタムフィールドは、実際にはキーと値の組です。
update_post_meta($post_id, $key, $data);
update_post_meta() は、指定した投稿に存在するカスタムフィールドの値を更新します。add_post_meta() の代わりとしても使うことができます。この関数はまず、$post_id で ID を指定した投稿に $meta_key を持つカスタムフィールドが存在することを確認します。もし存在しなければ代わりに
add_post_meta( $post_id, $meta_key, $meta_value )
を実行し、その結果を返します。
delete_post_meta($post_id, $key, get_post_meta($post_id, $key, true));
この関数は、投稿から指定したキー(もしくはキーと値)を持つカスタムフィールドをすべて削除します。update_post_meta()、get_post_meta()、add_post_meta() も見てください。
自分でカスタムフィールドを作成する場合、大きく分けて
の二つが必要になります。
まず、メタボックスを表示します。functions.phpに以下を記述します。
<?php //////////////////////////////////////////////////////////////////// //カスタムフィールドの見た目(入力欄等)を追加する function hook_func_for_metabox(){ // ① メタボックスの特性を定義する add_meta_box( 'metabox_div_id',//メタボックスのdivに指定されるID 'メタボックス1', //タイトル 'html_for_metabox_1_func', //表示用のHTMLを出力するphp関数(下で定義) 'post', //どのタイプの記事入力画面で表示するか 'normal', 'high' ); } // ② メタボックスの中身を実装する function html_for_metabox_1_func($post){ $areaname = "field_1"; $key = "_key_for_field_1"; echo '<textarea name="' . $areaname . '" rows="8" cols="60">' . get_post_meta($post->ID,$key,true) . '</textarea>'; // ③ nonceを作成し、hiddenフィールドとして書き込む wp_nonce_field( 'send_field1' , 'nonce_for_field_1' ); } // ④ カスタムフィールド用のメタボックスを追加する add_action('admin_menu', 'hook_func_for_metabox'); ?>
動作としては、
1.admin_menuアクションフックにより、管理画面が表示されたときにhook_func_for_metaboxが実行されます。
2.hook_func_for_metaboxの中で、メタボックスを追加するadd_meta_box関数が呼び出されます。そしてこの関数から、メタボックスの中身を描画するhtml_for_metabox_1_func関数が呼び出されます。
3.html_for_metabox_1_func関数内で、<textarea></textare>タグをechoし、入力欄を表示します。
これにより、投稿画面に「メタボックス1」というタイトルの、テキスト入力フィールドを一つ持つ領域(通称メタボックス)が作成されます。
admin_menuアクションフックは、
管理画面メニューの基本構造が配置された後に実行する。
とのことです。
ちなみに、
この一行により、管理画面メニューが配置された後に、hook_func_for_metaboxが実行されるようになります。
ちなみに、add_meta_boxesアクションフックというのもあり、
こちらでもメタボックスを追加出来ます。この場合、下記add_meta_boxの$screenが'dashboard'など、引数によってはメタボックスが追加出来なくなります。
add_meta_boxはメタボックスを追加する関数です。
引数名 | 今回の例 | 意味 |
$id | metabox_div_id |
例えばこのように出力されます: <div id="metabox_div_id"> ... </div> |
$title | メタボックス1 |
タイトルです |
$callback | html_for_metabox_1_func | このメタボックスの中身を表示するための関数を指定します(後述) |
$screen | post |
|
$context | normal |
メタボックスの表示位置を指定します。 |
$priority | high | (※) |
$callback_args |
(※) 表示順と思われます。
html_for_metabox_1_funcの中ではHTMLをechoしているだけですが、重要な点がいくつかあります。
<?php //メタボックスの中身を実装する function html_for_field1_func($post){ $areaname = "field_1"; $key = "_key_for_field_1"; echo '<textarea name="' . $areaname . '" rows="3" cols="40">' . get_post_meta($post->ID,$key,true) . '</textarea>'; //nonceを作成し、hiddenフィールドとして書き込む wp_nonce_field( 'send_field1' , 'nonce_for_field_1' ); } ?>
最も重要なのは_key_for_field_1という値で、これがいわゆる、カスタムフィールドの値が保存されている変数名です。
html_for_field1_funcでは<textarea>...</textarea>を表示しますが、その際に既に値が設定されていた場合はその値を表示したいので、get_post_meta関数で、現在のpostの_key_for_field_1の項目を取得して表示しています。
生成されるHTMLの例:
<textarea name="field_1" rows="3" cols="40"> この記事のカスタムフィールドの内容 </textarea>
注意として、_key_for_field_1のように、キー名は_ (アンダースコア)から始めた方が格好良いです。必須ではないですが、_から始まるキー名でないと、自分で追加したメタボックスの中だけでなく、投稿編集ページに最初からある「カスタムフィールド」の領域にもこのキーのカスタムフィールドの編集欄が出来てしまいます。害はないですが同じ物が二つあるのは気持ちが悪いので避けた方が良いでしょう。
参考 Codex 関数リファレンス/add post meta 見えない カスタムフィールドを作る
最後にwp_noce_fieldとwp_create_nonceですが、これらはセキュリティ的に必要なのでカスタムフィールドの書き込みと読み出しには直接関係はありません。従って省略可能ですが、普通セキュリティ対策は必須なので書きます。
nonce(ノンス)は、ある種の誤使用や悪意のある操作から URL やフォームを守るための「一度だけ使われる数値」です。
私はセキュリティの専門家ではないのですが、もの凄くざっくりとした説明をすると以下のようになります。
wp_nonce_fieldは、内部で wp_create_nonce 関数を呼び出しています。
wp_create_nonce
nonce を生成して返します。nonce は現在の時刻、$action 引数、現在のユーザー ID に基づいて生成されます。
つまり時間やユーザーや引数を元に、81b29b57b2のような文字列を作成します。そしてHTMLのhiddenフィールドとして出力します。
表記
出力
<input type="hidden" id="nonce_for_field_1" name="nonce_for_field_1" value="81b29b57b2" />
ここで、HTMLのformからPOSTでSubmitした場合、PHP側では
<?php if(isset($_POST[‘nonce_for_field_1’])){ $nonce = $_POST[‘nonce_for_field_1’]; echo $comment; //この結果は81b29b57b2 } ?>
と、$_POSTを使って受け取ることができます。
これで表示が終わったので、次回にはこのカスタムフィールドの値が「投稿」ボタンや「下書き」ボタンが押されたときにデータベースに保存するコードを記述します。