20190526のC#に関する記事は19件です。

C#手遊び(DataGridViewの列数制限)

この記事、何?

DataGridViewの制限を調べてみた。
というか、実際に動かしてみたかった。
・・・あちこちからもらった情報の写経なんですけどね。

参考資料

■DataGridViewに列を手動で追加する
https://dobon.net/vb/dotnet/datagridview/addcolumn.html
■DataGridView の描画が遅いときに気をつけること
http://tt195361.hatenablog.com/entry/2015/06/12/084110
■配列⇔List変換、Listのコピー、末尾の改行文字等削除【C#】
http://kan-kikuchi.hatenablog.com/entry/C%23Tip3
■日時、時間の計算をする
https://dobon.net/vb/dotnet/system/datetimecal.html

方針

とりあえず、FillWeightを調整しないといけないらしい。
既定値が100で累積65535までとか。
なので普通に作ると650列ちょっとが上限。
今回、C#のキャパの確認が目的なので・・・といいつつ、さすがに65500列はいらないので、3000列表示可能か試すことにした。
そしたらちょっと調整しないと遅くなるらしいのでその点も含めてちょっと調整。
あと、単に列を表示してもしょうがない。列の中に値を入れるのが目的だから。
なので、10行、すべての行・列のセルに異なる値を入れてみた。
(で、よく考えたら繰り返しってListが扱いやすかったからToArray()を調べてみた)
最後に、ついでに処理時間計測をやってみた。

出来上がり

        private void Button1_Click(object sender, EventArgs e)
        {
            int columnsCount = 3000;

            var started = System.DateTime.Now;

            dataGridView1.AutoGenerateColumns = false;
            dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
            dataGridView1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None;
            dataGridView1.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;

            for (int i = 0; i < columnsCount; i++)
            {
                string colName = "Col" + i.ToString();
                var textColumn = new DataGridViewTextBoxColumn
                {
                    DataPropertyName = colName,
                    Name = colName,
                    HeaderText = colName,
                    FillWeight = 6
                };
                dataGridView1.Columns.Add(textColumn);
            }

            string[] cols = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J" };

            foreach(string col in cols)
            {
                var values = new List<string>();
                for (int i = 0; i < columnsCount; i++)
                {
                    values.Add(col + ":values" + i.ToString());
                }
                dataGridView1.Rows.Add(values.ToArray());
            }

            dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
            dataGridView1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells;
            dataGridView1.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.EnableResizing;

            MessageBox.Show((System.DateTime.Now - started).ToString());

        }

出力

53-01.JPG

こんな感じ。
3000列・10行で30,000種類の値を表示してみた。
5.27秒らしい。

感想

まあ・・・こんなもの?
もうちょっと工夫すると思うけどいったんここまで。
サイトで見てた通りといえばその通り。
ただ、やってみないと見えないことがあるから、ひとまず確認できてよかった。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【競プロ】AtCoder Beginner Contest 128 に参加した話

ABC128に参加しました。
競プロ用リポジトリを今回から非公開と公開用に分けました。
コミットを頻繁にするタイプなのでタイムスタンプを見るとコンテスト中に公開しているように見えて、それを追及されるのが嫌なので。
プッシュはコンテスト後にしているのですが、何分証拠がないことに気づいてしまったのでこうしました。
気にしすぎですかね。
いわゆる誤爆を防ぐ目的でもあります。

今回、AからCの3完でした。辛い。
雑にコードだけ貼っておきます。

A: Apple Pie

a.cs
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;

namespace ABC128
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] s = Console.ReadLine().Split(' ');
            int A = int.Parse(s[0]);
            int P = int.Parse(s[1]);

            Console.WriteLine((A * 3 + P) / 2);
        }
    }
}

B: Guidebook

b.cs
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;

namespace ABC128
{
    class PP: IComparable<PP>
    {
        public string S;
        public int P;
        public int idx;
        public PP(string s, int p, int id)
        {
            S = s;
            P = p;
            idx = id;
        }
        public int CompareTo(PP p)
        {
            if (S.CompareTo(p.S) < 0)
            {
                return 1;
            }
            else if (S.CompareTo(p.S) == 0)
            {
                if (P > p.P)
                {
                    return 1;
                }
                else if (P == p.P)
                {
                    return 0;
                }
                else
                {
                    return -1;
                }
            }
            else
            {
                return -1;
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            int N = int.Parse(Console.ReadLine());
            PP[] SP = new PP[N];

            for (int i = 0; i < N; i++)
            {
                string[] s = Console.ReadLine().Split(' ');
                SP[i] = new PP(s[0], int.Parse(s[1]), i + 1);
            }

            Array.Sort(SP);
            Array.Reverse(SP);

            for (int i = 0; i < N; i++)
            {
                Console.WriteLine(SP[i].idx);
            }
        }
    }
}

C: Switches

c.cs
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;

namespace ABC128
{
    class Program
    {
        static int Search(int[] ss, int num, int M, List<int[]> s, int[] p)
        {
            if (num == ss.Length)
            {
                int res = 0;
                bool flag1 = true;
                for (int i = 0; i < M; i++)
                {
                    int cnt1 = 0;
                    for (int j = 0; j < s[i].Length; j++)
                    {
                        cnt1 += ss[s[i][j] - 1];
                    }
                    if (cnt1 % 2 != p[i]) flag1 = false;
                }
                if (flag1) res++;
                return res;
            }
            int[] ss1 = new int[ss.Length];
            int[] ss2 = new int[ss.Length];
            Array.Copy(ss, ss1, ss.Length);
            Array.Copy(ss, ss2, ss.Length);
            ss1[num] = 0;
            ss2[num] = 1;
            return Search(ss1, num + 1, M, s, p) + Search(ss2, num + 1, M, s, p);
        }
        static void Main(string[] args)
        {
            string[] S = Console.ReadLine().Split(' ');
            int N = int.Parse(S[0]);
            int M = int.Parse(S[1]);

            int[] k = new int[M];
            List<int[]> s = new List<int[]>();

            for (int i = 0; i < M; i++)
            {
                S = Console.ReadLine().Split(' ');
                k[i] = int.Parse(S[0]);
                int[] ss = new int[k[i]];
                for (int j = 0; j < k[i]; j++)
                {
                    ss[j] = int.Parse(S[j + 1]);
                }
                s.Add(ss);
            }
            int[] p = Console.ReadLine().Split(' ').Select(x => int.Parse(x)).ToArray();

            int res = 0;
            res = Search(new int[N], 0, M, s, p);

            Console.WriteLine(res);
        }
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【VisualStudio 2019 for Mac】【C#】Mac用アプリ:ボタンを置く(追加)

手順追記

storyboardとC#の処理の実装はファイルが分離しているのでVisualStudioでも個別に作業を進めることができる。
storyboardを変更してもdesigner.csを変更しても変更が有効になるようなのでルールは決めるべきなのか
※WARNINGに記載されている

storyboard(XCode)

スクリーンショット 2019-05-26 22.25.32.png

C#のコード(VisualStudio)

スクリーンショット 2019-05-26 22.25.53.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【VisualStudio 2019 for Mac】【C#】Mac用アプリ:ボタンを置く

Mac上で動作するGUIアプリケーションの基本的な作り方

Mac上で動作するGUIアプリケーションをC#で書くための手順を、プロジェクト作成から順に追っていく。
VisualStudio for MacではXcodeの標準的なGUI作成方法であるstoryboardに対応している。
storyboardの編集はXcodeを使用するため、XcodeとVisualStudioを行き来して作っていくことになる。
今回は仕組みは置いておいて手順だけのメモ。

手順1:VisualStudio上でMac用のGUIアプリのプロジェクトファイルを作成する

まずはVisualStudioでプロジェクトファイルを作成する。
スクリーンショット 2019-05-25 23.12.15.png

手順2:StoryBoardの編集

プロジェクトにはStoryBoardが追加されており、選択するとStoryBoardを編集するためにXcodeが起動する
スクリーンショット 2019-05-25 23.26.49.png

手順3:配置した部品とC#の処理を接続

StoryBoardに置いたボタンにアクションを繋げる。
※不明な場合、この辺はXCodeの操作になるのでXCode関連の情報を調べる。
スクリーンショット 2019-05-26 20.58.30.png

繋げたアクションでコールバックされる処理処理と同名のメソッドがC#側クラスににも作られているので、独自の処理を書ける。
スクリーンショット 2019-05-26 21.06.53.png

参考

https://docs.microsoft.com/ja-jp/xamarin/mac/get-started/hello-mac

動作環境

Mac OS : 10.14.3(Mojave)
XCode : Version 10.2.1 (10E1001)
VisualStudio Comunity 2019 for Mac

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

FlipViewerで「次=左」を実現する

書籍ビューアー

UWPを使って、書籍ビューアーを作ろうと思っている。
とりあえず最初のステップはImageをひとつ用意し、単にボタンでクリックして、次の画像をそのImageに読み込む、というレベルで、まあ当面本を読むのには、それで足りている。工夫した点は、トリミングで、版面のうちの文字の部分だけをトリミング表示することで、最大限読みやすくしているところ。

FlipViewer

とはいえ、よのなかのブックビューアーの大半は、フリップに対応していて、最近ちょっとフリップを使えるようになってきたので、FlipViewerで書籍ビューアーを作ってみようと思った。

FlipViewerの下に、単純にimageを複数置いた

まず、FlipViewerの下に、単純にimageを複数置いてみた。

<Grid>
    <FlipView x:Name="MainView">
        <Image Source="/Assets/01.jpg" />
        <Image Source="/Assets/02.jpg" />
        <Image Source="/Assets/03.jpg" />
        <Image Source="/Assets/04.jpg" />
    </FlipView>
</Grid>

実行したところ、課題発覚。それは、FlipViewerはマイクロソフトがというか英語圏で開発しているので、右にいくほど大きくなる、「次」は「右」に割り当てられているという点だ。

「次=右」問題

これはもう、日本語の縦書きの書籍を翻訳するときに最初から起きていた問題で、まあいろいろな解決策(裏焼きや次=右を許容を含め)があったわけだが、自作するからには縦書きの本は次=左でなければぜったいにいやだ。これはもう究極のこだわりポイントなので、まず次=左が大前提。これを達成できないのなら、作らないし、電子書籍を読む必要もないとずっと思っていた。

オールタイムベストブックが、1冊もない

そもそも電子書籍なんてほぼ読まないのだが、そういっている場合じゃないんじゃないのと思い始めたきっかけは、大好きなオールタイムベストブックが、ほぼ1冊もスマートホンにはいっていないということをあらためて認識したためだった。たしかに大切な本は紙で読んで紙で保管しているが、それは死蔵ではないのか、大事でないものばかり入っているスマートホンを肌身離さずもっている価値はあるのか、持ち歩いているならそこに大切な本をいれたらいいじゃないか、というような流れである。
そんなわけで、大事な書籍をディジタル化するぞ、ディジタルにして持ち歩くぞ、いつでも読めるぞ、というようなことを試してみたいわけなのだった。

手始めに10冊

大好きなオールタイムベストブックは約40冊程度で、作家でいうとごくわずかなのだが、ともかく好きなものほどディジタルになっていないというあたりはもうどうにも袋小路な感じではあるけれども、最近は中古も手軽に入手できるし、ディジタル化しているものもすこしはあるので、まずはそのあたりから始めようと手始めに10冊ほど入れてみた。それをとりあえず内蔵のイメージビューアーでフリップでスクロールしてみたところ、先の課題に直面したわけである。すなわち「次=右」問題である。

「次=左」を実現する

そこで、FlipViewerを使って、「次=左」を実現するために、次のように考えた。
そもそもFlipViewerじたいを180度回転する。
そのうえで、FlipViewerの子要素として登録するImageも180度回転する。これによって、スクロール方向は左になり、画像も正位置で表示できるはず。
まずはxamlで実装してみた。

<Grid>
    <FlipView x:Name="MainView" RenderTransformOrigin="0.5,0.5">
        <FlipView.RenderTransform>
            <CompositeTransform Rotation="180"/>
        </FlipView.RenderTransform>

        <Image Source="/Assets/01.jpg" RenderTransformOrigin="0.5,0.5">
            <Image.RenderTransform>
                <CompositeTransform Rotation="180"/>
            </Image.RenderTransform>
        </Image>

        <Image Source="/Assets/02.jpg" RenderTransformOrigin="0.5,0.5">
            <Image.RenderTransform>
                <CompositeTransform Rotation="180"/>
            </Image.RenderTransform>
        </Image>
    </FlipView>
</Grid>

Assetsに複数の画像を取り込み、FlipViewerとImageを回転してみた。デバイスに転送して実行したところ、期待通りの動作を行えた。第一ステップは完了。

Next Steps(展望)

次はこれをC#のコードで実装する。Xamlでは複数の書籍をダイナミックに変更しにくいため。
その次のステップは、標準のイメージビューアーを見ていて気づいたのだが、標準のイメージビューアーでは、ファイルを読むとまずひとつファイルを読み、そのあと非同期で2枚目以降の画像を読んでいるようだ。2枚目以降は先読みをしているらしい。
なるほど。とすると、FlipPiewerのManipurationEndとかで次の画像を読めばよいのかもしれない。このあたりの挙動を調査する必要がありそう。

200pages over

あと、そもそもMSDNを読んだら、FlipViewerは25枚程度の画像を表示するのに適したコントローラーという記事があって、なるほど、書籍は200枚とかいま読んでいるのだと600ページ=600枚とか、ということはざらにあって、たぶんFlipViewerで読んでいくと、重くなる可能性がある。

先読み、ページジャンプのためのClass

とすると、前に行くときには先読みするとして、規定のページ数に達したら、FilpViewerからいちばん不要な画像を捨てていく処理が必要そうだ。ところてんみたいに押し出し処理が必要。
さらにさらに、これまで作ってきた書籍では、しおり機能を実現していて、ページジャンプできるようになっている。とすると、そのページをFlipViewerのページのところに挿入する必要があるということだ。

Image+Pageのクラスを設計する

かりにいま10ページの本を読んでいるとする。Viewer内部で保持しているのが、456の3ページ分とする。
ここにブックマークしていたページ1を追加するときには4を捨てて156とする。
8を追加するときには、568とするということだ。
さらに次のページにいったときには5を捨てて678を保持するわけだ。
じっさいにはページは200ページ以上を想定しているので、保持するページは飛び飛びになる。とすると画像でもつだけではダメで、その画像が何ページであるかを保持するクラスが必要そうだ。
既存のjpegではページ数はもっていないから、ページ数に相当する順番をファイルの順番で作る必要がありそう。
まだまだけっこうかかるな~。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Autodesk Inventor API Hacking (だいたい一度はハマること)

0. はじめに

一度は誰もがハマる失敗を、まとめてみました。今後もハマる度に、追加していきます。
明らかに直ぐに判明するbugではなく、ある時突然発病する系のものが中心です。

1. SelectSet

通常はActiveEditDocumentを起点にdocumentにアクセスしますが、SelectSetだけは例外です。
ActiveDocument.SelectSetにアクセスしないと、失敗します。
だいたい、インプレース編集するまで気づきません。

2. Proxy

これは奥が深いので、簡単にしか触れませんが、AssemblyDocument内で、Sub Assemblyをインプレース編集中にoccurrenceにアクセスする時は、注意が必要です。
テスト項目に、インプレース編集中の動作は必ず入れるべきです。

3. ユーザーは自由に選択する

例えば、次のようなコマンドを開発するとします。

  • 2Dスケッチ環境で
  • 選択されたSketchEntityの
  • 色を変える。

この場合、ユーザーがアクティブではないスケッチのSketchEntityを選択する可能性を考慮する必要があります。

4. 何もない場合がある

  • 開いているDocumentがない。(ActiveEditDocument == null)
  • PartDocumentにFeatureが1つもない。
  • AssemblyDocumentにOccuurenceが1つもない。
  • DrawingDocumentにViewが1つもない。
  • DrawingDocumentに図面枠、表題欄がない。

5. オブジェクト編集中

インプレース編集中でない時にしか動作させないコマンドは、現在インプレース編集中でないかを判断してから実行する必要があります。
PartDocument, AssemblyDocumentでは、document.ActivatedObject == nullかどうか、
DrawingDocumentでは、document.ActivatedObject is Sheetかどうかで判断します。

6. コマンド実行中にDocumentをスイッチ

コマンドでインタラクティブな操作を実行中に、ユーザーがActiveEditDocumetを変更する可能性があります。
場合によっては、コンテキスト(データ)をDoument毎に用意する必要があります。

7. コマンド実行中にコマンドを実行

コマンドを実行中に、ユーザーにコマンドを起動されることがあります。
OnExecute()で、いわゆる再入が発生します。

8. インタラクティブな操作をする時は、一旦OnExecute()を抜けるべき

インタラクティブな操作をする時は、InteractionEvents.Start()して、即座にOnExecute()を終了すべきです。
OnExecute()から抜けずに処理していると、色々と微妙な問題が発生します。

9. 新規ドキュメント

新規に作成されたDocumentで、一度も保存されていないものは、一部のPropertyにアクセスすると例外を発生します。(ファイルの実体が無いので)

10. 解決していないドキュメント

Inventorは柔軟なので、一部のDocumentファイルがなくても、親Documentを開くことが出来ます。
その場合、リンク切れのOccurrenceの一部のPropertyにアクセスすると、例外が発生します。

11. BrowserNode.NativeObjectに触れると例外発生

例えばDrawingDocumentで、BrowserNode.NativeObjectに触れると例外が発生することがあります。
他には、インポートしたソリッドなども同様です。
ですので、以下のcodeは、そのうち例外を送出します。

BrowserNodesEnumerator browserNodes = document.BrowserPanes["PmDefault"].TopNode.BrowserNodes;
foreach (BrowserNode node in browserNodes)
{
    if (node.NativeObject is EndOfFeatures)
    {
        break;
    }
    //
    // 何らかの処理
    //
}

12. ファイル名問題

これはInventorのAddIn開発に限った話ではなく、Windows全般の問題ですが、あるpath(ファイル名)が、他のpathと同じファイルを指しているかどうかを判断するのが、非常に難しいです。

  • 大文字、小文字
  • ネットワークドライブ
  • Symbolic link
  • などなど・・・

path文字列同士の比較では、恐らく判断しきれないので、わざと実行時例外を発生させるようなcodeを書いて判断するしかないのかなと思います。
大文字・小文字問題はToUpper()すればいいじゃないか、という人もいますが、それ、本当に全言語のWindowsで成り立ちますか?

13. SelectEvents.RemoveFromSelectedEntities()してますか?

これはやれば気付く部類ですが、SelectEventsを実行しっぱしで、選択したものを次々処理するような場合は、処理が終わったobjectはRemoveFromSelectedEntities()SelectEventsから除去する必要があります。
(でないと、HighLightしたままになる)

99. 親の記事に戻る

Autodesk Inventor API Hacking (概略)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Autodesk Inventor API Hacking (AddIn/Command名前情報について)

0. はじめに

AddInを開発していると、名前や情報を設定する箇所が多々あるのに気づきます。
同じようなものが色々あるので、何が違うのかをまとめました。

1. AddInとCommand

まずはその前に、AddInとCommandの名前情報は、それぞれ別に設定するということを理解してください。
単機能のAddInでは、AddIn ≒ Commandかもしれませんが、多機能なAddInでは、1つのAddInの中に複数のCommandを内包します。例えば、シートメタルのようなAddInを作ろうとすると、そのAddInには多数のRibbon上のButtonが含まれます。

2. AddInの名前情報

AddInの名前情報は、2つのファイルに分散しています。

AssemblyInfo.cs

AssemblyInfo.csで指定する各情報は、AddInのdllを右クリック → プロパティー詳細 で表示される情報です。
直接的にはInventorで使われません。

Autodesk.<AddIn名>.Inventor.addin

本ファイル中の

<DisplayName>SampleInventorAddIn</DisplayName>
<Description>SampleInventorAddIn</Description>

が、アドインマネージャ使用可能なアドイン(DisplayName)と説明(Description)に対応します。

3. Commandの名前情報

Commandの名前情報は、AddButtonDefinitionの引数で指示します。

ButtonDefinition Inventor.Application.CommandManager.ControlDefinitions.AddButtonDefinition(
    string DisplayName,                 // Ribbonのbuttonに表示されます。
                                        //   また、カスタマイズ ダイアログのコマンド名にも使用されます。
    string InternalName,                // Commandへのアクセスに使われるユニークな名前です。
    CommandTypesEnum Classification,
    object ClientId,
    string DescriptionText = "",        // Ribbonのボタンにポインタを合わせたときにStatusBarに表示されます(現在は無視される?)
    string ToolTipText = "",            // Ribbonのbuttonにポインタを合わせた時に表示されます。
    object StandardIcon = null,
    object LargeIcon = null,
    ButtonDisplayEnum ButtonDisplay = ButtonDisplayEnum.kDisplayTextInLearningMode
    );

99. 親の記事に戻る

Autodesk Inventor API Hacking (概略)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Unity] カスタムTextコンポーネントで省略表示をする

はじめに

Unityでテキストが表示範囲内に収まらない場合に文字をトリミングして省略文字を表示するカスタムTextコンポーネントを作成しました。
Image01_20190526.png

環境

Unity 2018.2.18.f1

ソースコード

using System;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;

public class TrimmingText : Text
{
    // 省略時に表示する文字列
    private string ellipse = "...";

    // テキスト
    private string _text;

    public new string text
    {
        get { return _text; }
        set
        {
            _text = value;
            UpdateText(_text);
        }
    }

    // Text の RectTransform
    private RectTransform _rectTransform;

    public RectTransform _RectTransform
    {
        get
        {
            if (_rectTransform == null)
                _rectTransform = GetComponent<RectTransform>();
            return _rectTransform;
        }
    }

    // RectTransformのサイズ変更時に呼び出される
    protected override void OnRectTransformDimensionsChange()
    {
        base.OnRectTransformDimensionsChange();
        UpdateText(_text);
    }

    // ロードされた時やインスペクターの値が変更されたときに呼び出される
    protected override void OnValidate()
    {
        base.OnValidate();
        _text = m_Text;
        UpdateText(_text);
    }

    // テキストの更新
    private void UpdateText(string str)
    {

        // RichTextはタグの解析をしていないため未対応(タグがトリムされてしまう)
        if (supportRichText)
        {
            throw new Exception("Richtext is not supported");
        }
        else
        {
            // 文字列がない場合は以降の処理をしない
            if (string.IsNullOrWhiteSpace(str))
            {
                return;
            }

            // 設定されたテキスト情報を元にジェネレータを作成する
            var generator = new TextGenerator();
            var settings = this.GetGenerationSettings(_RectTransform.rect.size);
            generator.Populate(str, settings);

            // 0行の場合は、以降の処理を行わない
            if (generator.lineCount <= 0)
            {
                return;
            }

            var updatedText = str;

            // horizontalOverflow = warp
            if (HorizontalWrapMode.Wrap.Equals(horizontalOverflow))
            {
                var height = generator.GetPreferredHeight(updatedText, settings) / settings.scaleFactor;

                // generatorで設定されたテキスト領域高が、RectTransform高を超える場合はトリムを行う
                if (_RectTransform.rect.size.y < height)
                {
                    updatedText += ellipse;
                    height = generator.GetPreferredHeight(updatedText, settings) / settings.scaleFactor;

                    // generatorで設定されたテキスト領域高が、RectTransform高を超えないようになるまで文字を削除する
                    while (_RectTransform.rect.size.y < height && ellipse.Length < updatedText.Length)
                    {
                        updatedText = updatedText.Remove(updatedText.Length - ellipse.Length - 1, 1);
                        height = generator.GetPreferredHeight(updatedText, settings) / settings.scaleFactor;
                    }
                }
            }

            // horizontalOverflow = overflow
            else
            {
                var width = generator.GetPreferredWidth(updatedText, settings) / settings.scaleFactor;

                // generatorで設定されたテキスト領域幅が、RectTransform幅を超える場合はトリムを行う
                if (_RectTransform.rect.size.x < width)
                {
                    updatedText += ellipse;
                    width = generator.GetPreferredWidth(updatedText, settings) / settings.scaleFactor;

                    // generatorで設定されたテキスト領域幅が、RectTransform幅を超えないようになるまで文字を削除する
                    while (_RectTransform.rect.size.x < width && ellipse.Length < updatedText.Length)
                    {
                        updatedText = updatedText.Remove(updatedText.Length - ellipse.Length - 1, 1);
                        width = generator.GetPreferredWidth(updatedText, settings) / settings.scaleFactor;
                    }
                }
            }
            m_Text = updatedText;
        }
    }
}

使い方

Textコンポーネントの代わりに「TrimmingText」をアタッチしてください。
Image02_20190526.png

解説

  • 「TextGenerator」で生成された文字列の表示範囲がRectTransformの範囲を超える場合、文字列をトリミングして「...」を表示します。
  • 「HorizontalOverflow」の「Overflow」「Warp」の両方に対応させています。「Overflow」の場合は、文字列の幅を監視し、「Warp」の場合は高さを監視してトリミング位置を判定します。
  • RichTextなどの一部の機能が正常動作しないためご注意ください。また表示幅が非常に狭いなど特定の条件下では正しく動作しません。

免責事項

  • 本記事に掲載された内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。また本記事に記載されているリンクなどによって他のサイトに移動した場合、移動先サイトで提供される情報、サービス等について一切の責任を負いません。
  • 本記事に記載されているソースコードの利用は自己責任でお願い致します。プログラムの利用で生じるすべての損害に関して、利用者がその一切の責任を負うものとし、当方は一切の責任を負いません。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Autodesk Inventor API Hacking (RibbonにIconを登録したい~IPictureDisp~)

1. Ribbonに登録するIconの画像形式について

Ribbonに表示されるIcon画像を表示するには、

ButtonDefinition Inventor.Application.CommandManager.ControlDefinitions.AddButtonDefinition(
    string DisplayName,
    string InternalName,
    CommandTypesEnum Classification,
    object ClientId,
    string DescriptionText = "",
    string ToolTipText = "",
    object StandardIcon = null,
    object LargeIcon = null,
    ButtonDisplayEnum ButtonDisplay = ButtonDisplayEnum.kDisplayTextInLearningMode
    );

StandardIconLargeIconに画像を指定するわけですが、その形式で戸惑うかもしれません。
これらには、Picture (IPictureDisp)形式のオブジェクトで、それをどう用意すれば良いのかが、なかなかと難しいのです。
VB.netだと別の方法があるそうなので、C#だと次の方法をとります。

class AxHostConverter : AxHost
{
    private AxHostConverter() : base("") { }

    static public IPictureDisp ImageToPictureDisp(Image image)
    {
        return (IPictureDisp)GetIPictureDispFromPicture(image);
    }
    static public Image PictureDispToImage(IPictureDisp pictureDisp)
    {
        return GetPictureFromIPicture(pictureDisp);
    }
}

System.Windows.Forms.AxHostには、ImageとIPictureDispを相互変換するstaticな関数GetIPictureDispFromPictureGetPictureFromIPictureが用意されているので、単にそれを呼べば済みそうなものです。
しかし、残念ながらこれらの関数はprotectedが指定されているので、AxHostを継承してpublicなwrapper関数を持ったclassを作る必要があるわけです。

99. 親の記事に戻る

Autodesk Inventor API Hacking (概略)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

スクレイピングでの改行コードの処理

フィールドに\rが仕込まれている....!?

改行コードが混在するテキストファイルから,\r\nだけ残し,\n\rは削除したい(別の文字に置き換えたい)時があります(下参照).

あるテキストファイル
// 元の文
Hello.\r\nToday\nis\rMay.

// こんな感じにしたい
Hello.
Today is May.

解決策

以下のC#のコードで実現しました.

RemoveCRandLF.cs
var text = @"test.txt";

var result = System.Text.RegularExpressions.Regex.Replace(
    input: File.ReadAllText( text ),
    pattern: @"(?<!\r)\n|\r(?!\n)",
    replacement: " "
);

Console.WriteLine( result );

ここで,正規表現(?<!\r)\nは,前に\rが無い\nで,\r\nにはマッチしません.
同様に\r(?!\n)は,後ろに\nが無い\rで,これも\r\nにマッチしません.

細かくテストしてないので,間違ってたらごめんね.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Autodesk Inventor API Hacking (Versionあれこれ)

0. はじめに

本稿では、Autodesk InventorのAddIn開発における、様々なVersionにまつわる情報をまとめます。
時折、作ったAddInがInventorのVersionによって動作したり、しなかったりということが起きますが、大抵はこれらのVersionの不整合が原因です。

1. InventorのVersion

まずは、Inventor自体のVersionです。現時点での最新Versionは、Inventor 2020です。
この2020とは別に、内部的には連番で管理されている番号があります。
Version番号は、Inventor.Application.SoftwareVersionからアクセスできるので、VBAのイミディエイトで確認してみます。

? ThisApplication.SoftwareVersion.DisplayVersion
2020
? ThisApplication.SoftwareVersion.Major
 24 
? ThisApplication.SoftwareVersion.Minor
 0 

内部的には、Inventor 2020のVersionは24で、今後updateが入ると、Minorの数字が上がっていきます。
また、必ずしも保証されているわけではありませんが、Inventorは1年に1回のMajor updateがあり、その度にMajorの数字が1つ増えます。

つまり、

Major = DisplayVersion - 1996;

だということですね。
以後はInventorのVersionと言えば、2020(DisplayVersion)ではなく、24(Major)の方を指すので、注意してください。

2. InteropのVersion

AddInは、直接Inventorとやり取りするわけではなく、途中に仲介者がいます。それが、Interopです。
なぜ間に仲介者(Interop)を挟むのかは色々な事情があるのでしょうが、確かなことは、InventorのVersionが上がっても、AddInをrecompileすることなく使えるということです。
InteropVersion.png
この場合、InteropのVersionは24.0.0.0です。ピリオドで区切られた1つ目の数字がMajorで、2つ目がMinorです。3つ目以降は常に0という認識で問題ないです。

さて、このVersionの意味するところですが、これはAddInがlinkされるVersionであり、AddInはこのVersion以前のInventorには対応しなくなります。
今回ですと、Majorが24ですので、このままcompileすると、そのAddInはInventor 2019では使えないというわけです。

でしたら、大は小を兼ねる的な発想で(数字の大小は逆だが)、出来るだけ古いInteropを使えば良いのでしょうか?
その場合は、新しいVersionで追加されたAPIが使えません。Major24で追加されたAPIを使いたければ、InteropもVersion24.x.x.xを使わないといけないのです。

さて、先ほど、

確かなことはInventorのVersionが上がっても、AddInをrecompileすることなく使えるということです。

と書きましたが、実は極まれに例外があります。
Major Versionが上がった直後に、1つ前のVersionのMinorが上がり、そのInteropを使った場合です。
実際にあったのは、Inventor 2020のリリースとほぼ同時に、Inventor 2019に対しUpdateがかかり、その際にInteropのMinorの数字が上がったのです(23.2.0.0になった)。
Inventor 2020にしてみれば、自分よりも古いVersionのはずですが、全く見知らぬInterop23.2.0.0からのアクセスなので、拒否されてしまいました。

3. addinファイルに指定するVersion

さて、InteropのVersionと、AddInが動作するInventorのVersionの関係を説明しましたが、もう1つ別の要素があります。
それは、拡張子がaddinのファイルです。templateで自動作成されたaddinファイルを見てみましょう。

<Addin Type="Standard">
  <!--Created for Autodesk Inventor Version 24.0-->
  <ClassId>{dad03fdd-e24c-4cd9-af01-631e3e11fb0a}</ClassId>
  <ClientId>{dad03fdd-e24c-4cd9-af01-631e3e11fb0a}</ClientId>
  <DisplayName>SampleInventorAddIn</DisplayName>
  <Description>SampleInventorAddIn</Description>
  <Assembly>SampleInventorAddIn.dll</Assembly>
  <LoadOnStartUp>1</LoadOnStartUp>
  <UserUnloadable>1</UserUnloadable>
  <Hidden>0</Hidden>
  <SupportedSoftwareVersionGreaterThan>23..</SupportedSoftwareVersionGreaterThan>
  <DataVersion>1</DataVersion>
  <UserInterfaceVersion>1</UserInterfaceVersion>
</Addin>

ここに記載されている、<SupportedSoftwareVersionGreaterThan>のキーより新しいVersionのInventorでないと、動作しません。
少しややこしいのですが、Greater Thanですので、この場合は23よりも新しいということで、24以降(つまり、Inventor 2020以降)が動作対象バージョンとなります。
ちなみに、2行目の<!--Created for Autodesk Inventor Version 24.0-->は、コメントなので無視して大丈夫です。

なお、このaddinファイルはAddIn自体のdllと同じディレクトリに配置され、Inventorはこのファイルを見てAddInを読み込むかどうかを判断します。

4. AddIn自体のVersion

AddIn自体のVersionは、Inventorからは無視されますが、ソースに変更を加えたら数値を上げると良いでしょう。
設定個所は、2か所あります。

  • AssemblyInfo.cs
  • <AddIn名>.X.manifest

それぞれに別の値を設定することが出来ますが、特別な理由がない合わせておいた方が良いでしょう。
また、ピリオドで区切られた4つの数字には、それぞれに意味がありますが、ここでは割愛します。(事実上、勝手につけて問題ない)

5. Visual StudioのVersion

現時点で最新のInventor 2020のSDKがサポートするVisual StudioのVersionは2013, 2015, 2017ですが、別の記事に書いた通りVisual Studio 2019でも開発できます。
その場合は、記事に従ってtemplateを自分でコピーする必要があります。
既にtemplateを必要としない場合は、もちろんコピーする必要すらありません。

6. .NET FrameworkのVersion

templateからprojectを作ると、.NET Framework 4.5が選択されるようです。
最近のInventorのVersionと、動作環境に上げられている.NET FrameworkのVersionをまとめてみましょう。

Inventor .NET Framework
2017 4.6以降
2018 4.6以降
2019 4.7以降
2020 4.7以降

では、どのVersionの.NET Frameworkを対象のフレームワークのとし選択すれば良いかですが、別に4.5のままでもInventor 2020で動作します。
もちろん、AddIn自体が4.7に依存する機能を使っているのでしたら、それを選択する必要があります。注意が必要なのは、全てのWindowsに4.7がinstallされている保証がないということです。例えば、Windows 8でInventor 2018が動作している環境では、4.7が無いかもしれません。

7. ExcelのVersion

関係はないですが、ついでにExcelのVersionもまとめておきます。
近年のInventorは、一部の動作に64 bit版を要求しているので、注意してください。大抵の作業は、32 bit版でも動作すると思います。

Inventor Excel
2017 2010, 2013, 2016
2018 2010, 2013, 2016
2019 2010, 2013, 2016
2020 2010, 2013, 2016

99. 親の記事に戻る

Autodesk Inventor API Hacking (概略)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity(C#)】Animationとパラメータを連動させる方法

デモ

タイトルの内容について書きますが、百聞は一見に如かずということでご覧ください。

Gif_Squat.gif

膝の角度に合わせて数値及び、青い角度計が変化しています。

これは膝の角度を読み取っているわけではないです。

アニメーション(スクワット)の再生時間に応じてパラメータを変化させています。

GetCurrentAnimatorStateInfo

Animatorを作ると複数のステートを作成できます。

ステートはアニメーションを遷移させるときに便利です。

今回は再生中のアニメーションを調べる必要があるので、
GetCurrentAnimatorStateInfo(0)を使って調べます。

   [SerializeField]
   Animator squatAnimeController;

   void Update()
    {
       AnimatorStateInfo animeStateInfo = squatAnimeController.GetCurrentAnimatorStateInfo(0);
    }

GetCurrentAnimatorStateInfo(0)を使うことで、再生中のアニメーションを取得できます。
引数はアニメーションのLayerですが、複雑なアニメーションを作成してLayerを使用した場合を除いて、
基本的にデフォルトの0で大丈夫です。

AnimatorStateInfo.normalizedTime

normalizedTimeを使用すればアニメーションの再生時間を0~1の値に正規化することができます。

正規化された時間を利用して他のパラメータの値を操作すれば
デモのようにアニメーションに合わせて数値を変化させることができます。

Mathf.Lerp

先程の正規化された時間とMathf.Lerpを利用して数値を連動して変化させます。
Mathf.Lerpの引数は3つ用意されており、Lerp(float a, float b, float t)となっています。

それぞれの引数は
a:開始値
b:終了値
t:補完値(0~1)
となっています。aの値からbの値に向けてtで補完します。
と言われてもいまいちわからないと思います。(私は使ってみるまでさっぱりでした)

なので一例を示します。

まずはコードです。

   [SerializeField]
   Animator squatAnimeController;

   [SerializeField]
   Text degreesText;

   void Update()
    {
       AnimatorStateInfo animeStateInfo = squatAnimeController.GetCurrentAnimatorStateInfo(0);

       float degrees = Mathf.Lerp(180, 0, animeStateInfo.normalizedTime); 
       degreesText.text = ((int)degrees).ToString() + "°";
    }

このように、アニメーションの再生時間(正規化されているので0~1)に合わせて
角度が180~0°で変化しているのがわかるかと思います。(GIFが汚いのはご容赦ください)
Gif_Squat_Explanation.gif

これが先程のaの値からbの値に向けてtで補完するという意味です。

a:開始値(180)
b:終了値(0)
t:補完値(正規化したアニメーションの再生時間0~1)

Mathf.Repeat

先程のGIF画像ではステートを二つ用意して、同じアニメーションを交互に遷移させていました。
その理由としてはアニメーションのループ設定がうまくいかなかったからです。

ループ設定してしまうと、アニメーションの正規化がうまくできないようです。
0~1収まるはずの正規化した値が1を超えて無限に増えていきます。(ループに終了時間という概念が存在しないから?)

もし、なんらかの事情でアニメーションのループ設定を外せない場合は、
Mathf.Repeatで正規化した値が1を超えないようにします。

Mathf.Repeat(float t, float length)
指定値(length)を超えると0に戻り、再度 指定値まで循環します。

   [SerializeField]
   Animator squatAnimeController;

   [SerializeField]
   Text degreesText;

   void Update()
    {
       AnimatorStateInfo animeStateInfo = squatAnimeController.GetCurrentAnimatorStateInfo(0);

       float degrees = Mathf.Lerp(180, 0, Mathf.Repeat(animeStateInfo.normalizedTime, 1)); 
       degreesText.text = ((int)degrees).ToString() + "°";
    }

これでループ設定を外せないとしても安心です。
Gif_Squat_Explanation_2.gif

fillAmount

角度計のアニメーションはこちらのサイトを参考にしました。

アニメーションに応じて、FillAmountの値を変化させています。
ImageFillAmount.PNG

最終的なコード

当方、マジックナンバーおじさんですが、ご容赦ください。

作成した画像(色付き)にアタッチ
using UnityEngine.UI;
using UnityEngine;

public class AngleVisualController : MonoBehaviour
{
    [SerializeField]
    Animator squatAnimeController;

    AnimatorStateInfo animeStateInfo;

    [SerializeField]
    Text degreesText;

    Image circle_Image;

    void Start()
    {
        circle_Image = this.gameObject.GetComponent<Image>();
    }

    void Update()
    {
        animeStateInfo = squatAnimeController.GetCurrentAnimatorStateInfo(0);

        float squatVertical = 25 / 60.0f;
        float degrees = 0;

        if (animeStateInfo.normalizedTime < squatVertical)
        {
            circle_Image.fillAmount = Mathf.Lerp(0.48f, 0.25f, animeStateInfo.normalizedTime * (60.0f / 25));

            degrees = Mathf.Lerp(170, 90, animeStateInfo.normalizedTime * (60.0f / 25));
            degreesText.text = ((int)degrees).ToString() + "°";
        }
        else
        {
            circle_Image.fillAmount = Mathf.Lerp(0.25f, 0.48f, (animeStateInfo.normalizedTime-(25.0f/60)) * (60.0f / 25));

            degrees = Mathf.Lerp(90, 170, (animeStateInfo.normalizedTime - (25.0f / 60)) * (60.0f / 25));
            degreesText.text = ((int)degrees).ToString() + "°";
        }
    }
}

なぜこんなわけのわからない計算をLerpの中に仕込ませているかというと、
・スクワットのアニメーションの折り返し地点(正規化した値でいうと0.5)がしゃがんだ状態ではない
・角度の値が、直立(170)→しゃがむ(90)→直立(170)を取る
・アニメーションの再生フレームを取得できない
からです。

なので、もっとシンプルに使うには
・しゃがむ、立ち上がる の二つのアニメーションを用意する
なのかな~と思います。そうすれば、変な計算しなくても正規化した値をそのまま使えるかと思います。

本当は、アニメーションの再生フレームを取得して
0~25フレームはこの処理をして、26~60フレームは別の処理を...
みたいなことができれば便利なのですが、見つかりませんでした。(ありましたら教えてください)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Autodesk Inventor API Hacking (firstTimeとは何ぞや)

1. fistTimeとは何ぞや

AddInが有効化される時に呼ばれる関数の引数についてです。

public void Activate(Inventor.ApplicationAddInSite addInSiteObject, bool firstTime)

この第2引数のfirstTimeですが、どのように扱えば良いのか、という話しです。

結論を言うと、firstTimeは常にtrueです。

API Helpなどを見ると、firstTime == trueの場合はコントロールを登録して、falseの場合はしないとあります。
これは、Ribbonインターフェースより昔のインターフェース(ボタンとパネルのやつ)での話で、このインターフェースは既に廃止されています。
将来、Ribbonに代わるインターフェースが出てきたときに、このfirstTimeに意味が出てくることがあるかもしれませんが、現時点ではfistTime == falseで呼ばれることはありません。

99. 親の記事に戻る

Autodesk Inventor API Hacking (概略)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Autodesk Inventor API Hacking (Dockable Windowでのキー入力)

0. はじめに

近年のInventorでは簡単にDockable Windowを作成できるのですが、EnterキーとESCキーが入力できずにハマるというパターンがあると思います。
これについて、回避方法を説明します。

1. そもそも、この問題は何故発生する?

WinFormsを使って開発すると、それぞれのControlが特殊キー(EnterESC)を受け取った時に、親Formにお伺いを立てるわけですが、その近辺の処理がうまく出来ないと、この問題が発生するようです。

2. 回避策 ~ Formに配置しよう

この問題が発生するのは、DocableWindowにControlを直接登録した場合です。
逆を言えば、ControlをいったんFormに配置して、そのFormを登録すれば、問題をひとまずは回避できます。

public void Activate(Inventor.ApplicationAddInSite addInSiteObject, bool firstTime)
{
    DockableWindow dockWindow;

    // (中略)

    Form1 frm = new Form1();

    dockWindow.AddChild(frm.Handle);
    dockWindow.Visible = true;
    dockWindow.SetMinimumSize(200, 300);

    frm.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
    frm.Show();
}

しかし、この方法では、DocableWindowがInventorとは別のWindowとして振舞おうとするので、微妙にフォーカスがらみで挙動がおかしくなります。
具体的には、InventorのWindowが例えばメモ帳の後ろにある場合、このDocableWindowをクリックしてもInventorが前面に来ません。

3. 回避策 ~ WM_GETDLGCODEに対し、DLGC_WANTALLKEYSを返そう

RichTextBoxでしか試していませんが、殆どの場合はこれで回避できると思います。

MyRichTextBox.cs
using System;

namespace InvAddIn
{
    class MyRichTextBox : System.Windows.Forms.RichTextBox
    {
        private const int WM_GETDLGCODE = 0x0087;
        private const int DLGC_WANTALLKEYS = 0x0004;

        protected override void WndProc(ref System.Windows.Forms.Message m)
        {
            switch (m.Msg)
            {
                case WM_GETDLGCODE:
                    if (m.LParam != null)
                        m.Result = (IntPtr)DLGC_WANTALLKEYS;
                    break;
                default:
                    base.WndProc(ref m);
                    break;
            }
        }
    }
}

技術的な詳細は割愛しますが、RichTextBoxの代わりに上記のMyRichTextBoxを使うと、Enterキーをキャプチャーできるようになります。
実際の場面では、UserControlに配置して使うのでしょうから、そのUserControl自体にこの方法を適用すれば良いのでは? と思い試してみましたが、駄目でした。
ですので、UserControl内の全てのControlは、それぞれに対策が必要です。(もちろん、必要がある場合は、ですけど)

4. 回避策 ~ WindowsのEventをHookしよう

WM_GETDLGCODEに対応することで解決したかに思えたのですが、DataGridViewではうまく行きませんでした。
追いかけてみると、DataGridViewは、編集時にそのセルに重ねるように別のTextBoxを配置していたのです。
このTextBoxのWndProcに介入できないので、DataGridViewについては、大掛かりになりますがWindows自体のKey EventをHookします。
少しcodeが長いですが、お付き合いください。

MyDataGridView.cs
class MyDataGridView : System.Windows.Forms.DataGridView
{
#region Win32 API declare
    private static class NativeMethods
    {
        [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
        internal static extern System.IntPtr SetWindowsHookEx(int hookType, HookObject.HookHandler hookDelegate, System.IntPtr module, uint threadId);
        [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
        internal static extern bool UnhookWindowsHookEx(System.IntPtr hook);

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        internal static extern int CallNextHookEx(System.IntPtr hook, int code, System.IntPtr wParam, System.IntPtr lParam);
        [System.Runtime.InteropServices.DllImport("kernel32.dll")]
        internal static extern uint GetCurrentThreadId();
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        internal static extern System.IntPtr GetFocus();
        [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
        internal static extern System.IntPtr SendMessage(System.IntPtr hWnd, int Msg, System.IntPtr wParam, System.IntPtr lParam);
    }
    private const int WH_KEYBOARD = 2;
    private const int VK_CANCEL = 0x03;
    private const int VK_RETURN = 0x0d;
    private const int VK_ESCAPE = 0x1b;
    private const int WM_KEYDOWN = 0x0100;
#endregion
#region Key Hook
    private class HookObject
    {
        public delegate int HookHandler(int code, System.IntPtr wParam, System.IntPtr lParam);
        public HookHandler hookDelegate;
        public System.IntPtr hook;

        public void SetHook(HookHandler onHook)
        {
            // Check this control is in Visual Studio.
            bool inDesignMode;
            if (System.ComponentModel.LicenseManager.UsageMode == System.ComponentModel.LicenseUsageMode.Designtime)
            {
                inDesignMode = true;
            }
            else
            {
                using (var p = System.Diagnostics.Process.GetCurrentProcess())
                {
                    inDesignMode = (
                        p.ProcessName.Equals("DEVENV", System.StringComparison.OrdinalIgnoreCase) ||
                        p.ProcessName.Equals("XDesProc", System.StringComparison.OrdinalIgnoreCase)
                    );
                }
            }
            // Execute only outside of Visual Studio.
            if (!inDesignMode)
            {
                if (hook == System.IntPtr.Zero)
                {
                    hookDelegate = new HookHandler(onHook);
                    hook = NativeMethods.SetWindowsHookEx(WH_KEYBOARD, hookDelegate, System.IntPtr.Zero, NativeMethods.GetCurrentThreadId());
                    if (hook == System.IntPtr.Zero)
                    {
                        int errorCode = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
                        throw new System.ComponentModel.Win32Exception(errorCode);
                    }
                }
            }
        }
        public void Unhook()
        {
            if (hook != System.IntPtr.Zero)
            {
                NativeMethods.UnhookWindowsHookEx(hook);
            }
        }
        public HookObject(HookHandler onHook)
        {
            SetHook(onHook);
        }

        ~HookObject()
        {
            Unhook();
        }

    }
    private static readonly HookObject hookObject = new HookObject(OnHookKey);

    private static System.IntPtr hwndKeyHook = System.IntPtr.Zero;

    private static int OnHookKey(int nCode, System.IntPtr wParam, System.IntPtr lParam)
    {
        if (-1 < nCode)
        {
            int vKey = (int)wParam;
            ulong keyFlag = (ulong)lParam;
            switch (vKey)
            {
                case VK_RETURN:
                case VK_CANCEL:
                case VK_ESCAPE:
                    if ((keyFlag & 0xc0000000) == 0 && hwndKeyHook == NativeMethods.GetFocus())
                    {
                        NativeMethods.SendMessage(hwndKeyHook, WM_KEYDOWN, wParam, lParam);
                        return 1;
                    }
                    break;
            }
        }
        return NativeMethods.CallNextHookEx(hookObject.hook, nCode, wParam, lParam);
    }
#endregion
#region Event
    private static void Controls_GotFocus(object sender, System.EventArgs e)
    {
        if (sender is System.Windows.Forms.Control control)
        {
            hwndKeyHook = control.Handle;
        }
    }

    private static void Controls_LostFocus(object sender, System.EventArgs e)
    {
        hwndKeyHook = System.IntPtr.Zero;
    }

    protected override void OnEditingControlShowing(System.Windows.Forms.DataGridViewEditingControlShowingEventArgs e)
    {
        if (e.Control is System.Windows.Forms.DataGridViewTextBoxEditingControl textBox)
        {
            textBox.GotFocus -= Controls_GotFocus;
            textBox.GotFocus += Controls_GotFocus;
            textBox.LostFocus -= Controls_LostFocus;
            textBox.LostFocus += Controls_LostFocus;
        }
        base.OnEditingControlShowing(e);
    }
#endregion
}

この対策をUserControlに適用すれば、全て解決するのでは? と思ったのですが、DataGridViewに対しては良かったものの、今度はRichTextBoxに対してうまく動作しませんでした。
もう少し突っ込んでいけば、UserControlだけの対応ですむ方法が見つかったのかもしれません。しかし、上述の通りRichTextBoxには既に回避策が見つかっていたので、個別対応することにしました。(この時点で、既に結構な日数を消費していたので、力尽きたのです)

上記code内で、AddInがunloadされるときにHookを解放しようとしています。(HookObjectのデストラクター)
しかし、実際にはこの解放するcodeは呼ばれていないようです。Inventorが正しい手順を踏んでAddInのtheradを終了させていないのかもしれません。
現時点では何の問題も発生していないので、WindowsがAddInのtheradが終了するときにunhookしてくれているようです。

5. 自作Controlを使う場合の注意点 (x32/x64問題)

通常は気にすることが無いのですが、今回の解決策のように標準のWinFormsではない自作Controlを使って開発しようとする場合は、対象プラットフォームAny CPUではなくx64を指定していると、ハマります。
具体的には、Visual StudioのデザイナーでControlを配置できなくなります。
これは、デザイナーが実際にControlをロード(実行)して画面上に配置するからで、x32アプリケーションであるVisual Studioはx64のカスタムControlを読み込めないからです。
解決策としては、デザイナーで作業する前にAny CPUにしてリビルドすると良いです。デザイナーで作業しない限りは、x64で作業して問題ありません。

99. 親の記事に戻る

Autodesk Inventor API Hacking (概略)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Autodesk Inventor API Hacking(APIのbug)

0. はじめに

私が知っているだけでも、片手以上のAPIのbugがあるのですが、明らかに気付くbugが除外して、気づきにくいbug、つまりははまりやすいbugについて記載します。

1. ReferenceKeyManager.BindKeyToObjectの第3引数がnullでないと落ちる

object dummy;
byte[] byteArray1, byteArray2;

// (中略)

// ↓ これは通る(場合がある)
someObuject.ReferenceKeyManager.BindKeyToObject(ref byteArray1, 0, out dummy);
// ↓ ここで落ちる
anotherObuject.ReferenceKeyManager.BindKeyToObject(ref byteArray2, 0, out dummy);

結論を言うと、都度dummyにnullをセットすると良い。

object dummy;
byte[] byteArray1, byteArray2;

// (中略)

// ↓ OK
dummy = null;
someObuject.ReferenceKeyManager.BindKeyToObject(ref byteArray1, 0, out dummy);
// ↓ OK
dummy = null;
anotherObuject.ReferenceKeyManager.BindKeyToObject(ref byteArray2, 0, out dummy);

これはVBAにおけるValiant型のハンドリングについて、inventor.interop.dll内にbugがあるんだろうけど、とにかく事前にnullを入れておけば大丈夫。

実際に私がはまった時の形はこれ。

someObuject.ReferenceKeyManager.BindKeyToObject(ref byteArray1, 0, out _);
anotherObuject.ReferenceKeyManager.BindKeyToObject(ref byteArray2, 0, out _);

これの2行目で落ちられたら、byteArrayの方を疑ってしまって、時間がかかったよ。

2. ApplicationEvents_OnNewEditObjectと3DスケッチとActiveEditDocument.ActivatedObjectの関係

下のコメントにある通り、特定の条件においてActivatedObjectの値が信用できない。

private void ApplicationEvents_OnNewEditObject(object EditObject,
                                               EventTimingEnum BeforeOrAfter,
                                               NameValueMap Context,
                                               out HandlingCodeEnum HandlingCode)
{
    HandlingCode = HandlingCodeEnum.kEventNotHandled;

    if (BeforeOrAfter == EventTimingEnum.kAfter)
    {
        if (EditObject is PartDocument) {
            object obj = m_Application.ActiveEditDocument.ActivatedObject;

            // ここで、objは常にnullのはずだが、3Dスケッチから抜けたときだけは、nullではない。
            // フィーチャーもしくはスケッチが編集中かどうかを、
            // ActiveEditDocument.ActivatedObject == nullで判断すると、バグる。
            // 代わりに、EditObjectがDocumentかどうかで判断することで、回避する。
        }
    }
}

99. 親の記事に戻る

Autodesk Inventor API Hacking(概略)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Autodesk Inventor API Hacking (APIのbug)

0. はじめに

私が知っているだけでも、片手以上のAPIのbugがあるのですが、明らかに気付くbugは除外して、気づきにくいbug、つまりははまりやすいbugについて記載します。

1. ReferenceKeyManager.BindKeyToObjectの第3引数がnullでないと落ちる

object dummy;
byte[] byteArray1, byteArray2;

// (中略)

// ↓ これは通る(場合がある)
someObuject.ReferenceKeyManager.BindKeyToObject(ref byteArray1, 0, out dummy);
// ↓ ここで落ちる
anotherObuject.ReferenceKeyManager.BindKeyToObject(ref byteArray2, 0, out dummy);

結論を言うと、都度dummyにnullをセットすると良い。

object dummy;
byte[] byteArray1, byteArray2;

// (中略)

// ↓ OK
dummy = null;
someObuject.ReferenceKeyManager.BindKeyToObject(ref byteArray1, 0, out dummy);
// ↓ OK
dummy = null;
anotherObuject.ReferenceKeyManager.BindKeyToObject(ref byteArray2, 0, out dummy);

これはVBAにおけるValiant型のハンドリングについて、inventor.interop.dll内にbugがあるんだろうけど、とにかく事前にnullを入れておけば大丈夫。

実際に私がはまった時の形はこれ。

someObuject.ReferenceKeyManager.BindKeyToObject(ref byteArray1, 0, out _);
anotherObuject.ReferenceKeyManager.BindKeyToObject(ref byteArray2, 0, out _);

これの2行目で落ちられたら、byteArrayの方を疑ってしまって、時間がかかったよ。

2. ApplicationEvents_OnNewEditObjectと3DスケッチとActiveEditDocument.ActivatedObjectの関係

下のコメントにある通り、特定の条件においてActivatedObjectの値が信用できない。

private void ApplicationEvents_OnNewEditObject(object EditObject,
                                               EventTimingEnum BeforeOrAfter,
                                               NameValueMap Context,
                                               out HandlingCodeEnum HandlingCode)
{
    HandlingCode = HandlingCodeEnum.kEventNotHandled;

    if (BeforeOrAfter == EventTimingEnum.kAfter)
    {
        if (EditObject is PartDocument) {
            object obj = m_Application.ActiveEditDocument.ActivatedObject;

            // ここで、objは常にnullのはずだが、3Dスケッチから抜けたときだけは、nullではない。
            // フィーチャーもしくはスケッチが編集中かどうかを、
            // ActiveEditDocument.ActivatedObject == nullで判断すると、バグる。
            // 代わりに、EditObjectがDocumentかどうかで判断することで、回避する。
        }
    }
}

98. Invenorのbugを見つけたら、どうすれば良いの?

APIに限らず、Inventorに関するbugを見つけた場合に、ユーザーはどうすれば良いのでしょう?
信じがたいことに、Autodeskには公のbug報告の窓口がありません

  • サブスクリプション契約に含まれるQA窓口から報告する。
  • 商社経由で問題報告をしてもらう。
  • 何らかのコネクションで、日本のAutodesk社員に訴える。

これらの方法は、どうも効果がないように見えます。

今、一番効果があるのは、Autodesk Feedback Communityに登録して、Inventorのベータ登録をし、そこでbugレポートを上げることです。
当然、英語での報告になるのでハードルは高いのですが、Autodeskとして不具合を認識し(Defect Accepted)、開発チームのデータベースに載ったかまでは、確実に分かります。
Defect Acceptedが、必ずしもbug修正と同義ではない(放置されることも多々ある)のですが、現時点では一番有効な方法だと思います。

99. 親の記事に戻る

Autodesk Inventor API Hacking (概略)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Autodesk Inventor API Hacking(概略)

0. はじめに

ミドルレンジの3D CADであるAutodesk InventorのAddinをC#で開発する記事です。
日本語での情報が限られていて、基本的には英語の資料を漁ることになります。
せっかく調べたのに埋もれさせるのは勿体ないので、自分用メモも兼ねて、書いていこうと思います。

1. 想定する対象者

基本的には自分用メモなので、自分と同等レベルのスキルを持ったユーザーを対象にします。
つまりは、

  • CADとしてのInventorに、中程度以上の理解がある。
  • VBAでInventorのマクロを書いたことがある。
  • C#はカタコトで喋られる。

といった、ユーザーが対象です。
C#の文法や、Inventorのオブジェクトツリーについての細かい解説は、しない予定です。

2. 情報ソース

日本語API Help

日本語では、Technology Perspective from Japanが参考になります。というか、ここしかありません。
ここで、新しいInventorがリリースされる度にAPI Helpの日本語訳が公開されるので、それをダウンロードしましょう。
現時点では、Inventor 2020 APIの日本語プログラミング用ヘルプが公開されています。
日本語訳は誤訳もありますが、原文を想像しながら読めば、十分わかります。
新リリースで追加されたAPIも、その年のHelpに記載されているので、毎年確認するのが良いと思います。

APIトレーニングマテリアル

また、Technology Perspective from Japanでは、APIトレーニングマテリアルという、Addin開発の勉強に役立つ資料が公開されています。
現時点では、Autodesk Inventor 2019 の APIトレーニングマテリアルが公開されています。

英語のリソース

日本語に拘らなければ(英語で良ければ)、Google先生に聞くのが一番早いです。
キーワードは、Autodesk+Inventor+API+[知りたいこと]で良いです。
検索した結果、だいたいはこのどちらかの中にある記事に案内されます。

どうしても分からないことは、悩むより上記のInventor Customization Forumで聞く方が早いです。
大よそ24時間以内に、誰かが返答してくれます。
時差があるので、昼間に質問をポストして、夜中に返答が来ることが多いです。

3. Visual Studioについて

Autodeskは正式にサポートしていませんが、無料のVisual Studio Community Editionで十分開発できます。ただし、あなたがCummunity Editionの要件を満たしているかどうかは、各自判断してください。
現時点では、Visual Studio 2019が公開されており、これまたAutodeskは正式にはサポートしていませんが、使えます。但し、新規プロジェクト用のtemplateがinstallされないと思うので、手動でInstallする必要があります。

developertools.msiからTemplateをぶっこ抜く方法

Inventorをinstallすると、developertools.msiもPublicなDocumentsに配置されます。
しかし、配置されているだけで、installは実行されていません。Inventorが対応しているバージョンのVisual Studioだと、これをinstallすることでTemplateがVisual Studioにinstallされるわけです。
しかし、今はまだサポートされていないVisual Studio 2019を使おうとしているので、手動でコピーする必要があります。
まずは、developertools.msiから必要なファイルを抜き出します。

C:>msiexec /a "C:\Users\Public\Documents\Autodesk\Inventor <バージョン>\SDK\developertools.msi" /qb TARGETDIR="<フォルダ名>"

そうして抜き出したファイル、例えば

VCSInventorAddInTemplate2020.zip

を、例えば、以下にコピーすれば完了です。

C:\Users<ユーザー名>\Documents\Visual Studio 2019\Templates\ProjectTemplates

mt.exeが無いんですけど・・・

プロジェクトプロパティービルドイベントビルド後のイベントのコマンドラインに色々書いてあるのですが、まずマトモに動きません。Visual StudioがCommunity Editionだからなのだと思います。
そこで、苦労しながらscriptを編集するのですが、まず最初に立ちはだかるのが、mt.exeがどこにも無いということでしょう。
結論を言うと、Windows SDKをinstallすれば、この中に含まれています。
もし、mt.exeだけをcopyして、Windows SDKをuninstallするのならば、その前にsn.exeもcopyしておいた方が良いです。たぶん。
今使っているscriptを参考までに貼っておきます。

call "%vsappiddir%..\..\Common7\Tools\VsDevCmd.bat"
call mt.exe -manifest "$(ProjectDir)$(TargetName).X.manifest" -outputresource:"$(TargetPath)";#2
XCopy "$(TargetPath)" "%AppData%\Autodesk\ApplicationPlugins\$(TargetName)\" /Y /R
XCopy "$(ProjectDir)Autodesk.$(TargetName).Inventor.addin" "%AppData%\Autodesk\ApplicationPlugins\$(TargetName)\" /Y /R   
mkdir "%AppData%\Autodesk\ApplicationPlugins\$(TargetName)\ja"
XCopy "$(TargetDir)ja" "%AppData%\Autodesk\ApplicationPlugins\$(TargetName)\ja\" /S /Y /R

Debuggerは使えますか?

使えます。
プロジェクトプロパティーデバッグ動作条件外部プログラムの開始 → Inventorのexeを指定します。
但し、Programを修正する(= Compileする)度に、Inventorを再起動することになります。
この辺りはVBAのルーズさに比べると大きなデメリットなので、API Hackしている間はVBAでテストコードを書いて、目途が付いたらC#へ移行するのが良いと思います。

4. 言語(日本語/英語)について

将来的にAUTODESK APP STOREで公開したいのであれば、英語のサポートは必須となります。なぜなら、事実上、日本語のみのソフト(≒英語をサポートしないソフト)は受け付けてもらえないからです。
そこで、日本語と英語の両対応にするわけですが、デフォルト言語を英語にしてください。
つまり、

× ベース:日本語 + 追加:英語
〇 ベース:英語 + 追加:日本語

ということです。なぜなら、英語でも日本語でもないInventor、例えばフランス語版のInventorで実行した時に、デフォルト言語で表示されるからです。
ちなみに、Addinで使われる言語は、Windowsの言語ではなく、Inventorの言語です。ですので、Inventorに英語のLanguage Packをinstallすれば、英語環境でのテストができます。

5. 言語(iLogic/VBA/VB.net/C#)について

必ずしも、C#でAddinにする必要はありません。誤解を恐れず言うならば、iLogic/VBA/VB.net/C#の4つで出来ることに違いはありません。
しかし、開発効率は全く違います。一度、Visual Studioでの開発に慣れてしまうと、VBAで組むのが辛くなります。
VB.netとC#のいずれか、というと、VBAからの繋がりでVB.netを選択する人が多いのかもしれませんが、これを機会にC#を選択することをお勧めします。
あまり強く推すと、宗教論争に発展するので、この辺りで留めておきます。

お気楽さならば、iLogicが良いのかもしれません(この記事の存在意味を否定してしまいますが)。
プロパティーや変数にも簡単にアクセスできます。

99. リンク

Autodesk Inventor API Hacking(APIのbug)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Autodesk Inventor API Hacking (概略)

0. はじめに

ミドルレンジの3D CADであるAutodesk InventorのAddinをC#で開発する記事です。
日本語での情報が限られていて、基本的には英語の資料を漁ることになります。
せっかく調べたのに埋もれさせるのは勿体ないので、自分用メモも兼ねて、書いていこうと思います。
ページの最後に、他の記事へのリンクを貼っています。

1. 想定する対象者

基本的には自分用メモなので、自分と同等レベルのスキルを持ったユーザーを対象にします。
つまりは、

  • CADとしてのInventorに、中程度以上の理解がある。
  • VBAでInventorのマクロを書いたことがある。
  • C#はカタコトで喋られる。

といった、ユーザーが対象です。
C#の文法や、Inventor APIのオブジェクトモデルについての細かい仕様解説はしません。

2. 情報ソース

日本語の情報ソースでは、Technology Perspective from Japanが参考になります。というか、ここしかありません。
(後は、今まさに見ているこのページですね!)

日本語API Help

ここで、新しいInventorがリリースされる度にAPI Helpの日本語訳が公開されるので、それをダウンロードしましょう。
現時点では、Inventor 2020 APIの日本語プログラミング用ヘルプが公開されています。
日本語訳は誤訳もありますが、原文を想像しながら読めば、十分わかります。
新リリースで追加されたAPIも、その年のHelpに記載されているので、毎年確認すると良いでしょう。

APIトレーニングマテリアル

また、Technology Perspective from Japanでは、APIトレーニングマテリアルという、AddIn開発の勉強に役立つ資料が公開されています。
現時点では、Autodesk Inventor 2019 の APIトレーニングマテリアルが公開されています。

英語のリソース

日本語に拘らなければ(英語で良ければ)、Google先生に聞くのが一番早いです。
キーワードは、Autodesk+Inventor+API+[知りたいこと]で良いです。
検索した結果、だいたいはこのどちらかの中にある記事に案内されます。

どうしても分からないことは、悩むより上記のInventor Customization Forumで聞く方が早いです。
大よそ24時間以内に、誰かが返答してくれます。
時差があるので、昼間に質問をポストして、夜中に返答が来ることが多いです。

3. Visual Studioについて

Autodeskは正式にサポートしていませんが、無料のVisual Studio Community Editionでも開発できます。ただし、あなたがCummunity Editionの要件を満たしているかどうかは、各自判断してください。
現時点では、Visual Studio 2019が公開されており、これまたAutodeskは正式にはサポートしていませんが、使えます。但し、新規プロジェクト用のtemplateがinstallされないと思うので、手動でInstallする必要があります。

developertools.msiからTemplateをぶっこ抜く方法

Inventorをinstallすると、developertools.msiもPublicなDocumentsに配置されます。
しかし、installerが配置されているだけで、installされていません。InventorのSDKが対応しているバージョンのVisual Studioだと、これをinstallすることでTemplateがVisual Studioにinstallされます。
しかし、サポート外のVisual Studio 2019を使おうとしているので、手動でコピーする必要があります。
まずは、developertools.msiから必要なファイルを抜き出します。

C:>msiexec /a "C:\Users\Public\Documents\Autodesk\Inventor <バージョン>\SDK\developertools.msi" /qb TARGETDIR="<フォルダ名>"

そうして抜き出したファイル、例えば

VCSInventorAddInTemplate2020.zip

を、例えば、以下にコピーすれば完了です。

C:\Users\<ユーザー名>\Documents\Visual Studio 2019\Templates\ProjectTemplates

mt.exeが無いんですけど・・・

Visual Studioプロジェクトプロパティービルドイベントビルド後のイベントのコマンドラインにscriptが書いてあるのですが、まずマトモに動きません。Visual StudioがCommunity Editionだからなのでしょう。
そこで、苦労しながらscriptを編集するのですが、まず最初に立ちはだかるのが、mt.exeがどこにも無いということでしょう。
結論を言うと、Windows SDKをinstallすれば、この中に含まれています。
もし、mt.exeだけをcopyして、Windows SDKをuninstallするのならば、その前にsn.exeもcopyしておいた方が良いです。たぶん。
参考までに今使っているscriptを貼っておきます。

call "%vsappiddir%..\..\Common7\Tools\VsDevCmd.bat"
call mt.exe -manifest "$(ProjectDir)$(TargetName).X.manifest" -outputresource:"$(TargetPath)";#2
XCopy "$(TargetPath)" "%AppData%\Autodesk\ApplicationPlugins\$(TargetName)\" /Y /R
XCopy "$(ProjectDir)Autodesk.$(TargetName).Inventor.addin" "%AppData%\Autodesk\ApplicationPlugins\$(TargetName)\" /Y /R   
mkdir "%AppData%\Autodesk\ApplicationPlugins\$(TargetName)\ja"
XCopy "$(TargetDir)ja" "%AppData%\Autodesk\ApplicationPlugins\$(TargetName)\ja\" /S /Y /R

Debuggerは使えますか?

使えます。
プロジェクトプロパティーデバッグ動作条件外部プログラムの開始 → Inventorのexeを指定します。
但し、Programを修正する(= Compileする)度に、Inventorを再起動する必要があります。
この辺りはVBAのルーズさに比べると大きなデメリットなので、API Hackしている間はVBAでテストコードを書いて、目途が付いたらC#へ移行すると良いです。

なお、外部プログラムの開始構成別に設定できるので、逆を言えば、DebugReleaseの双方に設定するのを忘れないでください。

4. 言語(日本語/英語)について

将来的にAUTODESK APP STOREで公開したいのであれば、英語のサポートは必須となります。なぜなら、事実上、日本語のみのソフト(≒英語をサポートしないソフト)は受け付けてもらえないからです。
そこで、日本語と英語の両対応にするわけですが、デフォルト言語を英語にしてください。
つまり、

〇 デフォルト : 英語 + 追加 : 日本語
× デフォルト : 日本語 + 追加 : 英語

ということです。なぜなら、英語でも日本語でもないInventor、例えばフランス語版のInventorで実行した時に、デフォルト言語で表示されるからです。
ちなみに、AddInで使われる言語は、Windowsの言語ではなく、Inventorの言語です。ですので、Inventorに英語のLanguage Packをinstallすれば、英語環境でのテストができます。

5. 言語(iLogic/VBA/VB.net/C#)について

必ずしも、C#でAddInにする必要はありません。誤解を恐れず言うならば、iLogic/VBA/VB.net/C#の4つで出来ることに違いはありません。
しかし、開発効率は全く違います。一度、Visual Studioでの開発に慣れてしまうと、VBAで組むのが辛くなります。
では、VB.netとC#のどちら? というと、VBAからの繋がりでVB.netを選択する人が多いのかもしれませんが、これを機会にC#を選択することをお勧めします。
あまり強く推すと、宗教論争に発展するので、この辺りに留めておきます。

お気楽さならば、iLogicが良いのかもしれません(この記事の存在意味を否定してしまいますが)。
プロパティーや変数にも簡単にアクセスできます。

99. リンク

Autodesk Inventor API Hacking (Versionあれこれ)
Autodesk Inventor API Hacking (firstTimeとは何ぞや)
Autodesk Inventor API Hacking (AddIn/Command名前情報について)
Autodesk Inventor API Hacking (RibbonにIconを登録したい~IPictureDisp~)
Autodesk Inventor API Hacking (Dockable Windowでのキー入力)
Autodesk Inventor API Hacking (だいたい一度はハマること)
Autodesk Inventor API Hacking (APIのbug)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【競プロ】AtCoder Beginner Contest 127 に参加した話

今週もABCに参加しました。MonotreaでAtCoderやってます。
AからDの4完でした。
ratedにちゃんとなって嬉しかったですね。
まだまだ初心者なのでもっと精進します。

A: Ferris Wheel

三項演算子を使って分岐して出力しました。

a.cs
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;

namespace ABC127
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] s = Console.ReadLine().Split(' ');
            int A = int.Parse(s[0]);
            int B = int.Parse(s[1]);

            Console.WriteLine((A <= 5)? 0:
                              (A < 13)? B / 2: B);
        }
    }
}

B: Algae

$x$に$r$かけて$D$を引いたのを再び$x$に入れる度に$x$を10回出力します。

b.cs
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;

namespace ABC127
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] s = Console.ReadLine().Split(' ');
            int r = int.Parse(s[0]);
            int D = int.Parse(s[1]);
            int x = int.Parse(s[2]);

            for (int i = 0; i < 10; i++)
            {
                x =  r * x - D;
                Console.WriteLine(x);
            }
        }
    }
}

C: Prison

ABC126のままにしてしまった。つらい。
$L$から$R$の範囲を入力に伴って狭めていって、もし$L \leq R$だったら$R - L + 1$を出力し、それ以外は$0$を出力します。

c.cs
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;

namespace ABC126
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] s = Console.ReadLine().Split(' ');
            int N = int.Parse(s[0]);
            int M = int.Parse(s[1]);
            int[] L = new int[M];
            int[] R = new int[M];

            int Lres = 1;
            int Rres = N;

            for (int i = 0; i < M; i++)
            {
                s = Console.ReadLine().Split(' ');
                L[i] = int.Parse(s[0]);
                R[i] = int.Parse(s[1]);
                if (Lres < L[i])
                {
                    Lres = L[i];
                }
                if (R[i] < Rres)
                {
                    Rres = R[i];
                }
            }

            int res = (Lres <= Rres)? Rres - Lres + 1: 0;
            Console.WriteLine(res);
        }
    }
}

D: Integer Cards

Aを降順にソート。(1)
B, CのペアをCの値に準じて降順にソート。(2)
(1)(2)の先頭から順に大きいものをN個とっていきます。
その和を出力します。

d.cs
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;

namespace ABC127
{
    class P
    {
        public int B;
        public long C;
        public P(int a, long b)
        {
            B = a;
            C = b;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            string[] s = Console.ReadLine().Split(' ');
            int N = int.Parse(s[0]);
            int M = int.Parse(s[1]);
            long[] A = Console.ReadLine().Split(' ').Select(x => long.Parse(x)).OrderByDescending(x => x).ToArray();
            P[] BC = new P[M];

            for (int i = 0; i < M; i++)
            {
                s = Console.ReadLine().Split(' ');
                int B = int.Parse(s[0]);
                long C = long.Parse(s[1]);
                BC[i] = new P(B, C);
            }

            BC = BC.OrderByDescending(x => x.C).ToArray();

            long res = 0;
            int Acount = 0;
            int Bcount = 0;

            for (int i = 0; i < N; i++)
            {
                if (Acount < N && Bcount < M)
                {
                    if (A[Acount] < BC[Bcount].C)
                    {
                        res += BC[Bcount].C;
                        BC[Bcount].B--;
                        if (BC[Bcount].B == 0)
                        {
                            Bcount++;
                        }
                    }
                    else
                    {
                        res += A[Acount];
                        Acount++;
                    }
                }
                else if (Acount < N)
                {
                    res += A[Acount];
                    Acount++;
                }
                else if (Bcount < M)
                {
                    res += BC[Bcount].C;
                    BC[Bcount].B--;
                    if (BC[Bcount].B == 0)
                    {
                        Bcount++;
                    }
                }
            }

            Console.WriteLine(res);
        }
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む