<iframe>内コンテンツのサイズ変更

お久しぶりです、旧:ふくともです

先日webサイトに別のページを取り込み、その取り込んだページのサイズを指定サイズに変更したいなぁ、と思ったところ割と手こずったので書き出しておこうと思った次第。

※CSS3でないとうまく行きません。

HTML

<div class="parent">

  <div class="child">

    <iframe id="external" src="SITE URL" scrolling="no"></iframe>

  </div>

</div>

CSS

#external{

  width:578px;

  height:771px;

  transform:scale(0.65); transform-origin:0 0;

  -o-transform:scale(0.65);-o-transform-origin:0 0;

  -webkit-transform:scale(0.65); -webkit-transform-origin:0 0;

  -moz-transform:scale(0.65); -moz-transform-origin:0 0;

  -ms-transform:scale(0.65); -ms-transform-origin:0 0;"

}

.parent{

  width:375px;

  height:400px;

  overflow-x:hidden;

}

.child{

  width:375px;

  height:450px;

  overflow:hidden;

}

 

〜解説〜

#external

<iframe>のサイズは内部コンテンツと同じに設定(私が使ったものは578×771だったのでこの値、適宜変更してください)

構造上、上の階層の<div>からはみ出してしまうのでscrolling="no"

transform:scale(N);は縦横比を維持し内部コンテンツをN倍にするので、横幅を表示したい領域(後述のparent)に合わせて375/578≒0.65

transform-origin:0 0;は縮小の起点座標ををコンテンツの左上に設定する(デフォルトではコンテンツの中心)

 

.parent

表示したいサイズを設定

横幅がはみ出してしまうためoverflow-x:hidden;を指定

 

.child

横幅に合わせたコンテンツのサイズを設定

縦幅がはみ出すのでoverflow:hidden;を指定

横幅はぴったり合わせてあるのでoverflowにしたが、明示的にoverflow-yでも問題ない。

 

transformの仕様上、コンテンツの上下左右に余計なマージンが発生するので、コンテンツサイズに合わせた<div class="child">を<div class="parent">上でスクロールさせて実装している。

多次元配列の動的確保

やや、どーもどーも、ふくともです。

今回は宣言通り多次元配列の動的確保についてまとめますです。

 

・そもそも多次元配列って何ぞ?

正直うまい例が思いつかないんだけど、

複数の軸を持つ座標ってところかな

一次元配列はX座標だけで

二次元配列はX座標、Y座標で

三次元配列はX座標、Y座標、Z座標で、という風に表現されるの。

二次元配列ならint array[y][x];

三次元配列ならint aray[z][y][x];

って感じで、型 配列名の後に各次元の要素数を書いていく。

 

(使用可能な)全体の要素数は各次元の要素数の積になってて

int array[3][3][3];

って宣言すると3*3*3で全部で27要素で

array[0][0][0] array[0][0][1] array[0][0][2]

array[0][1][1] array[0][1][1] array[0][1][2]

array[0][2][0] array[0][2][1] array[0][2][2]

array[1][0][0] array[1][0][1] array[1][0][2]

array[1][1][1] array[1][1][1] array[1][1][2]

array[1][2][0] array[1][2][1] array[1][2][2]

array[2][0][0] array[2][0][1] array[2][0][2]

array[2][1][1] array[2][1][1] array[2][1][2]

array[2][2][0] array[2][2][1] array[2][2][2]

が使用可能

 

配列の掛け算になってる、くらいの理解でだいたいOK

素数は各次元で違っても問題なくて int array[3][5]; みたいな宣言も問題無いよ

 

で、その多次元配列(説明を簡略化するために二次元配列)の動的確保は前回を元にすると

int *array = new int[y][x];

と書きたくなるんだけどこれは間違い。

 

多次元配列は配列の配列なんです。

前回の記事を参照してもらえれば分かるかと思いますが、int * はint型配列を示すから配列の配列は示せないのです。

 

じゃあ、どうするかというとint  *が int型配列を示すのでint型配列の配列は

int **array = new int[y][x];

 

......惜しい。

newは指定された要素数配列を確保するためのものだからこれも間違い

でも配列の配列もint型の配列の配列 (だんだんわけがわからなくなってきたかな?)

「int型の配列」型の配列として考えると

int **array = new int*[y];

for(int i=0; i<x; ++I)

    *array[i] = new int[x];

と書くことで解決できる。

 

これは

「int型の配列」型の配列を動的に確保する

→その要素(つまりint型の配列)をそれぞれ動的に確保する

という仕組みになっているわけです。

 

三次元配列なら

int ***array = new int**[z];

for(int i=0; i<y; ++i){

    **array[i] = new int*[y];

    for(int j=0; j<x; ++j)

        *array[i][j]  = new int[x];

}

と入れ子にしてあげれば大丈夫。

 

delete[ ]は内側(とここでは表現する。 宣言時の右側の次元)から順に行う

for(int i=0; i<x; ++i)

    delete[ ] array[i];

delete[ ] array;

三次元以上の配列も同様に、適宜delete[ ]を入れ子にしてあげてくださいな。

 

前に書こうって言ってから一週間くらい経ってしまったけど一応こんな感じです。

前回致命的な記述ミスがあったことをもう一度お詫びします、申し訳ございませんでした。

今回も何かミス等があれば報告していただけると助かります。

報告は記事へのコメントでも構いませんし、下記のいずれかでも大丈夫です。

http://ask.fm/kata_tsuki

https://twitter.com/kata_tsuki

katatsuki52☆gmail.com (☆は@(半角)に置き換えてください)

2014/12/20 Sat.

配列の初期化(+配列の動的確保)について

どーもどーも、久々の更新です。

今回はJOI予選中にハマってしまったことについて書かせていただこうかと。

 

問題文は省略。

回答作成中にint配列の全要素を-1で初期化したかったから

int array[n] = {-1};

と書いたわけですが、これが間違い。

n==5;

とすると

イメージ

array[0] == -1;

array[1] == -1;

array[2] == -1;

array[3] == -1;

array[4] == -1;

現実

array[0] == -1;

array[1] == 0;

array[2] == 0;

array[3] == 0;

array[4] == 0;

ってなるらしい。

 

それを知らなかったために色々と余計な部分を確認した。

入力データも処理も確認したけどおかしくはない、となると他の部分?

と思い変数の値をすべて出力するという暴挙に。(デバッグツールっておいしいの?)

その結果、

array[0] == -1;

array[1] == 0;

array[2] == 0;

array[3] == 0;

array[4] == 0;

......えー、何々どゆこと?

これは入力データに依存しないはずだぞ?

代入(初期化)ミス?

 

という訳で検索した結果。

初期化子リストの値の数が集約型より少ない場合、集約型の残りのメンバーまたは要素は 0 に初期化されます。

 

お、おぅふ......。

いやぁ、これ知らなかったせいでループ内の処理とか入力データを延々と睨み続けるハメに......。

普段一律の値で配列初期化するときは0ばっかりで

int array[n]={0};

って書いてて、てっきり値を1つしか書かなければその値が全部代入されるのかと思ってたんです(汗)

 

なのでここのコードを

int array[n];

for(int i=0; i<n; ++i)

    array[1]=−1;

と書き換えた結果、狙い通り動いてくれた(全要素に代入してるんだから当たり前)

 

あ、あと配列ついでにもう一個、いわゆる配列の動的確保。

動的確保ってなんぞ?って人のために(雑に)説明すると、

・配列使いたい!!

・要素数は入力された値使いたい!!→コンパイル時には要素数未定、実行時に確定させたい!!

って時に使う技術。

(実際には入力以外の要因で使いたい場面もいっぱいあるだろうけど割愛、そういう場面でも使えるから安心してね!!)

 

じゃあ、ソース書くぜ!!

// array-sample1.cpp

#include <iostream>

using namespace std;

 

int main(){

    int n;

    cin >> n;

    int array[n];

    for(int i=0; i<n; ++i)

        array[i]=0;

    for(int i=0; i<n; ++i)

        cout << "array[" << i << "] ==" << array[i] << endl;

    return 0;

}

と書いたら(開発環境にも依るっぽいけど)コンパイルエラー、または実行時にエラーが出る。

環境によって動作の変わるプログラム→不安定のプログラム、を組むということでよろしくない。

なのでここのコードを

int array[n];

から

int *array = new int[n];

と書き換えちゃいます!!

int *arrayint型の配列を「示す」よ〜(配列そのものじゃないよ!!)ってやつで、

そのあとのnew int[n];int型の領域をn個確保(つまり動的確保!!)を代入するよ!!ってやつ。

......え?動的確保ってことは結局未定のまんまじゃん、って思うだろうけどそれでいいの。

newは動的確保を可能にするためのものだからこれでいいんです!!

 

で、注意としてはnewで確保した領域はdeleteで解放しなきゃいけないっていうのがあって、それの書き方は

delete array[ ];

これだけです笑

newの時と違ってarrayのサイズが決まってるから要素数は書かなくていいの。

というわけでさっきのコードを修正すると

// array-sample2.cpp

#include <iostream>

using namespace std;

 

int main(){

    int n;

    cin >> n;

    int *array = new int[n];

    for(int i=0; i<n; ++i)

        array[i]=0;

    for(int i=0; i<n; ++i)

        cout << "array[" << i << "] ==" << array[i] << endl;

    delete[ ] array;

    return 0;

}

これでOK

 

......なんか初期化より動的確保がメインになっちゃったかも(汗)

取り敢えずここまで!!

もっと詳しく知りたいって人はここ見るといいかも→C++編(言語解説) 第12章 new/delete

次回は多次元配列の動的確保について書いてみようかな

 

2014/12/14 Sun.

 

2014/12/18 Thu.

一部修正。

new[ ]で確保した領域はdeleteではなくdelete[ ]で開放しなければなりませんでした。

私のせいで勘違いしてしまった方がいらっしゃいましたら申し訳ありませんでした。

Windows10TechnicalPreview

お久しぶりです、旧:ふくともです。


Windows Technical Preview - Windows Insider Program - Microsoft Windows

 

β版と聞けば気になっちゃうよね

というわけでインストールしてみた。

(と言っても機体はMBA Mid 2012だから純正じゃなくてVirtualBoxなんだけど。)

 

取り敢えずOSイメージください、というわけでDLしに行くと......

f:id:q-fukutomo:20141202193041p:plain

そ、想定内だもんね!!

イメージさえ有れば俺の勝ちですし......(震え声)

Windows Technical Preview ISO のダウンロード - Microsoft Windows

f:id:q-fukutomo:20141202193618p:plain

......え?

\ページの言語は日本語あるのに日本語OSは無い!!/

 

仕方なく英語版の64bitを導入することに。

設定は以下の通り。

f:id:q-fukutomo:20141202194432p:plain

一番の注意点はOSの種類をWindows8.1にすることかな。

その他はDirectXを導入する予定だからビデオメモリを最大にしたことと、ある程度動作に余裕を持たせるためプロセッサを4つ(使用率制限100%)、メインメモリを4GBに設定した、くらい。

ネットワーク設定はVirtualBoxの基本(らしい)のNATに設定し、ホストOSと同じものを利用する。

仮想ハードディスクのサイズは任意で。

俺は取り敢えず100GBにしておいた。

 

いざ起動。

最初はOSイメージ(今回はWindowsTechnicalPreview-x64-EN-US)から起動して仮想ハードディスクにインストール。

 

アカウント設定については省略。

 

OSのインストールが終わったら一旦再起動し、VirtualBoxのお約束Guest Additionsのインストール。

何のためのどういうものなのかについてはこちらを参照

→ Guest Additionsのインストール | VirtualBox Mania

※前述のDirectXはここでのDirect3Dのこと

 

基本的にはこれでOK

あとはアプリケーションインストールしたり、壁紙とかの外観変えたりお好きな様に。

 

取り敢えずこれでも自称エンジニア(見習い)なので開発環境は整える。

Windowsを最新のにしたついでに環境も最新のにしてみる。

Microsoft Visual Studio Express 2013 for Windows Desktop をインストール!!

 

......実はこれのインストールに時間かかりすぎて暇だったからこの記事書いてます。

というわけで今回はここまで!!(雑)

環境構築まで気になる方はこちらを参考にするとよろしいかと。

Windows 8.1 で作る 無料の VC++ 開発環境 - Planetleaf.com Lab.

Windows8.1ってなってるけど、気にしなくて大丈夫。

 

次回は環境整え終わってからそれでも晒そうかなぁ......

2014/12/02 Tue.

配列の宣言について。

お久しぶりです。

今回は配列の宣言について。

 

例によって例のごとくノベルゲームの(ryを組んでた際にエラーが発生

.exe の 0x00ac4bc4 でハンドルされていない例外が発生しました: 0xC0000005: 場所 0xdadad240 に書き込み中にアクセス違反が発生しました。

エラー発生時に実行されていた関数がこちら

void setScript(Script* pScript, const char* str) {
    int i, start = 1, size;
    const char* pStr;
    for (i = 0; i < 3; ++i){
        // ","を探す
        pStr = strstr(&str[start], ",");
        if (pStr == NULL ) // ,がない場合、行末(改行)を探す
            pStr = strstr(&str[start], "\n");
        size = pStr - &str[start];
        char Buf[ ] = {0};
        switch(i){
            case 0:
                strncpy(Buf, &str[start-1], size+1);
                Buf[size+1] = '\0';
                pScript->Name = Buf;
                break;
            case 1:
                strncpy(Buf, &str[start-1], size+1);  // ここでエラー発生
                Buf[size+1] = '\0';
                pScript->Message = Buf;
                break;
            case 2:

                // キーワードに合わせて読み込む画像を変える

                strncpy(Buf, &str[start-1], size+1);

                Buf[size+1] = '\0';
                if(strcmp(Buf, "A"))
                    pScript->bgGraph = LoadGraph(FILENAME_A);
                else if(strcmp(Buf, "B"))
                    pScript->bgGraph = LoadGraph(FILENAME_B);
                else if(strcmp(Buf, "C"))
                    pScript->bgGraph = LoadGraph(FILENAME_C);
                else if(strcmp(Buf, "D"))
                    pScript->bgGraph = LoadGraph(FILENAME_D);
                break;
            default:
                break;
        }
        start = start + size + 2;
    }
}

 

pScriptは以下の通り

struct Script{

    char* Name,Message;

    int bgGraph;

}script;

Script *pScript = &script;

 

FILENAME_◯は画像ファイルのパス(文字列)で定義済。

 

 

switch文のcase 1:とcase 2:の違いなんて代入先だけじゃないか!!と思いつつ色々調べてみる。

 

すると、問題があったのは......

\ switch文の前のBufの宣言!! /

char Buf[ ] = {0};

素数の動的確保、こいつが穴だった。

 

素数を指定しなかった場合、配列の利用時に要素数が確定される。

→case 1:で確定される。==  case 2:以降では既に確定している。

 

文字列の長さがNameに代入したい値よりも、Messageに代入したい値の方が長かった場合、配列の最終要素(書き込み禁止!!)にはみ出た分の最初の文字を代入しようとし、エラーを吐いた、ということだったようだ。

 

動的確保は便利だけど長さが未定で何度も使い回す場合には最初っから多めに確保したほうが良さそう。

動的に再定義とかできたら無駄がなくていいんだろうけど、そこまでの知識は俺には無いからなぁ......(´-ω-`)

 

これもまた調べたいことに追加して今回も終了、かな。

PS.冬コミまで日が無くなってきて焦ってます。ダレカタスケテー

2014/11/22 Sat.

【C++】ノベルゲームのスクリプト - その2.5

前回に引き続きスクリプトについて。

といっても今回はスクリプトそのものではなくその扱い方についてなんだが。

 

前回の記事を振り返って、無駄に気付いた。

構造体の配列を利用するということは、それだけメモリを必要としてしまうため単純計算でいくと原稿が2倍になれば必要なメモリも2倍になってしまう。

 

例えばDVD-ROM3枚組の某型月さんのFate/stay night

もしその原稿を前回のに読み込ませようとした場合どれだけのメモリが必要になることやら......

 

というわけで

配列に詰めるのはやめよう!!

 

では、どうするか。

今回も検証してみないことにはわからないけども案として、

 

構造体を1つ生成して一回の発言分をメンバ変数にセット

描画

 

これでループすればいいのでは無いだろうか。

また、以前にも書いたがDxLibの利用を前提に考えているのだが、

LoadGraph()関数で画像をメモリに読み込むとDeleteGraph()関数で削除するまでメモリに残留してしまうらしい。

【C/C++】画像描画について【DxLib】 • C言語交流フォーラム ~ mixC++ ~

そこでメッセージを描画し終わったら構造体を破棄し、デストラクタでDeleteGraph関数を呼んであげればメモリ圧迫はしなくて済むのでは無いだろうか。

 

ただし、この手法では描画ループ内で毎回

・構造体の生成

・構造体に代入(画像や音声ロード含む)

・構造体を破棄

を行うので処理速度が落ちる。

 

この処理速度の落ち具合によっては構造体配列を要素数を決めて生成して、ある程度まとめて行ったほうが良いかもしれない。

 

いずれにしても実際に走らせてみないことにはなんとも言えないねw

時間ができれば実装して検証しようと思います。

 

もうひとつこれに関して気になることがあるけど、それは下のリンクで質問中

【C++/DxLib】LoadGraph()関数について|teratail

 

色んなとこで質問してる無知っぷりを晒しつつ、今回はこの辺で。

2014/11/15 Sat.

【C++】ノベルゲームのスクリプト - その2

前回に引き続きスクリプトについて考えてみる。

 

前回はデータを受け取る構造体だけを考えたので、今回はそれにデータを受け渡す方法を考えようと思う。(いちいち構造体のデータを記述するのは面倒なため)

 

まず初めに思いついたのはExcelファイルを読み込むこと。

横で要素数、縦でデータ内容を統一すれば管理が楽だからだ。

問題は、Microsoftさんのライセンス品であるExcelのデータを読み込むのは骨が折れそうなのと、完成後に一般公開しようと思っているため利用するときに何かの有料アプリケーションが必須という事態は避けたいというという方針に反すること。

 

そこでまた色々調べてみるとCSVファイルなるものがあるらしい。

曰く、Excelで言うところの横列が行・縦列はその行の中で「,」で区切って表現されるらしい。

これを利用すればデータの入力は楽になるのではないだろうか。

 

前回のコードを再掲

struct Script{

    char* Name;
    char* Message;
    int BGIHandle;
    int PersonHandle;
    int BGMHandle;

};

Script *p = new Script;

メンバ変数は5つ。

 

// CSVファイルから読み込んだデータをScript構造体に設定
void SetScript(struct Script* pScript, const char* Str) {
    int i = 0, Start = 1, Size;
    char* pStr,pResource;
    char Buff[100];

    for (i = 0; i < 5; ++i) {
        // ","を探す
        pStr = strstr(&Str[start], "\",\"");
        if (pStr == NULL )
            // ","がない場合、最後の"を探す
            pStr = strstr(&str[start], "\"\n");
        size = pStr - &str[start];

        switch (i) {
            case 0:
                // 発言者
                strncpy(pScript->Name, &str[start], size);
                break;
            case 1:
                // メッセージ
                strncpy(pScript->Message, &str[start], size);
                break;
            case 2:
                // 背景画像
                strncpy(pResource, &str[start], size);
                pScript->BGIHandle = LoadGraph(pResource);
                break;
            case 3:
                // キャラ画像
                strncpy(pResource, &str[start], size);
                pScript->PersonHandle = LoadGraph(pResource);
                break;
            case 4:
                // BGM
                strncpy(pResource, &str[start], size);
                pScript->BGMHandle = LoadSoundMem(pResource);
                break;
            default :
                break;
        }
        start = start + size + 3;
    }
}

という関数を組んで、引数に構造体配列の要素とCSVファイルを1行づつ渡して、EOFまでループしてあげればいいはず。
ファイル読み込みにはfgets()関数を使えばいいかな。
fgets()に渡すバッファサイズとかは適宜調整してくださいな。

 

これでCSVファイルで書かれた原稿を渡すだけでScript構造体が使えるはず。(検証自体は未済、あくまでもアイデアのメモですので。)

毎回CSVの原稿を読み込むのは面倒だし、構造体のデータを書き出せるようにして、書きだしたデータをエンジン側で読み込めればいいのかなぁ。

 

次回はその構造体のファイル書き出しによるデータ保持について纏めようと思う。

 

......ということは、これはスクリプト生成用のプログラムであってエンジンそのものには搭載されない気ががががが

まぁ、一緒にパッケージングして配布とかになるかなぁ、(俺主観でだけど)需要はありそうだし。

 

2014/11/14 Fri.

 

以下、参考サイト様

CSVとは 〔 カンマ区切り 〕 【 Comma-Separated Values 】 - 意味/解説/説明/定義 : IT用語辞典
ちゃぱてぃ商店IT部 @ ウィキ - C言語/サンプル/CSVファイルの読込ソート出力