📏
フリーエッジを取得する(C#)
フリーエッジとは?
- シェル(2次元の面)の端で、他のシェルのエッジ(1次元の辺)と共有していないもの
- この形状の
- この部分
フリーエッジが取得できると何がうれしいのか
- 例えば、剛性解析で固定点に依存しない全体剛性を見たいときに、部品形状の外周を拘束条件にする必要があります。この時、外周のセットノードをHyperMesh等のプリソフトで選択していくのは、面倒だしミスすることもあります。
- フリーエッジの中に外周の節点(ノード)は含まれているので、そこからいらない節点を省いていくほうが簡単です。
今回やること
- 1:メッシュモデルファイル(今回は、keywordの標準表記)を読み込み、
節点、シェルの内容を記録する。 - 2:指定したPIDのすべてのシェルから構成するエッジを作成する。
- 3:エッジの内一つも重複していないエッジ(フリーエッジ)を取り出す
プログラムの説明
- まず節点、エッジ、シェルとデータを保存するクラスを作成します。
class NODE//節点クラス
{
public Vector3 Crd { get; set; }
public NODE(Vector3 _Crd)
{
this.Crd = _Crd;
}
}
class EAGE//エッジクラス、重複が判定できるようにEquals等をオーバーライド
{
public Vector3 Center { get; set; }
public List<int> Nid { get; set; }
public EAGE(Vector3 _Center,List<int> _Nid)
{
this.Center = _Center;
this.Nid = _Nid;
}
public override bool Equals(object other)
{
if (other == null || this.GetType() != other.GetType())
{
return false;
}
var _other = other as EAGE;
return this.Center.X - _other.Center.X < 0.001 &&
this.Center.Y - _other.Center.Y < 0.001 &&
this.Center.Z - _other.Center.Z < 0.001;
}
public override int GetHashCode()
{
return this.Center.X.GetHashCode() ^
this.Center.Y.GetHashCode() ^
this.Center.Z.GetHashCode();
}
public static bool operator == (EAGE lhs, EAGE rhs)
{
if (lhs is null)
{
if (rhs is null)
{
return true;
}
// Only the left side is null.
return false;
}
// Equals handles case of null on right side.
return lhs.Equals(rhs);
}
public static bool operator !=(EAGE lhs, EAGE rhs) => !(lhs == rhs);
}
class SHELL//シェルクラス
{
public int Pid { get; set; }
public List<int> Nid { get; set; }
public SHELL(int _Pid, List<int> _Nid)
{
this.Pid = _Pid;
this.Nid = _Nid;
}
}
class CAE_DATA//データの保存用
{
public Dictionary<int, NODE> Node { get; set; }
public Dictionary<int, SHELL> Shell { get; set; }
public CAE_DATA()
{
this.Node = new Dictionary<int, NODE>();
this.Shell = new Dictionary<int, SHELL>();
}
}
- メッシュファイルを読み込んで保存する。
class FILE_CAE
{
public CAE_DATA from_Key_File(string FileName)
{
var DATA = new CAE_DATA();
//ファイルの内容で特定のキーワード(今回は"$")が書かれていない行を
//一行ずつリストにする
var Base = Read_File_Make_All_Line_without_keyword(FileName, "$");
var NodeLine = Sepalete_Line(Base, "*NODE");//節点の行だけにする
var ShellLine = Sepalete_Line(Base, "*ELEMENT_SHELL");//シェルだけの行にする
DATA.Node = NodeList_make(NodeLine);//節点を保存する
DATA.Shell = ShellList_make(ShellLine);//シェルを保存する
return DATA;
}
private List<string> Read_File_Make_All_Line_without_keyword(string FileName, string Without_Keyword)
{
return File.ReadAllLines(FileName).Where(i=>!i.Contains(Without_Keyword)).ToList();
}
private List<string> Sepalete_Line(List<string>BaseLine,string Sepalate_keyword)
{
var temp = new List<string>();
var JUG = false;
foreach (var i in BaseLine)
{
if (i.Contains(Sepalate_keyword)){ JUG = true; }
if (JUG)
{
temp.Add(i);
if (i.Contains("*") && !i.Contains(Sepalate_keyword)) { break; }
}
}
temp.RemoveAt(0);
temp.RemoveAt(temp.Count-1);
return temp;
}
private List<List<string>> Sepalete_Cloum(List<string> ORG, int Sepalate_num)
{
//文字列を文字数(Sepalate_num)で分割する。
//SubstringAtCountは拡張メソッドです。下に内容を記載します。
return ORG.Select(i => i.SubstringAtCount(Sepalate_num)).ToList();
}
private Dictionary<int,NODE> NodeList_make(List<string> ORG)
{
var temp = new Dictionary<int, NODE>();
foreach (var i in Sepalete_Cloum(ORG,8))
{
var ID = int.Parse(i[0]);
var X = float.Parse(i[1] + i[2]);
var Y = float.Parse(i[3] + i[4]);
var Z = float.Parse(i[5] + i[6]);
var Crd = new Vector3(X, Y, Z);
temp.Add(ID,new NODE(Crd));
}
return temp;
}
private Dictionary<int, SHELL> ShellList_make(List<string> ORG)
{
var temp = new Dictionary<int, SHELL>();
foreach (var i in Sepalete_Cloum(ORG, 8))
{
var ID = int.Parse(i[0]);
var PID = int.Parse(i[1]);
i.RemoveRange(0, 2);
var NID=i.Select(j => int.Parse(j)).ToList();
temp.Add(ID, new SHELL(PID,NID));
}
return temp;
}
}
//拡張メソッド
public static class String_Expension
{
public static List<string> SubstringAtCount(this string self, int count)
{
// 拡張メソッド 文字列を文字数で分割する 例: String.SubstringAtCount(5) 戻り値:String型配列
var result = new List<string>();
var length = (int)Math.Ceiling((double)self.Length / count);
for (int i = 0; i < length; i++)
{
int start = count * i;
if (self.Length <= start)
{
break;
}
if (self.Length < start + count)
{
result.Add(self.Substring(start));
}
else
{
result.Add(self.Substring(start, count));
}
}
return result;
}
}
- 保存したデータからエッジを作成し、そこからフリーエッジを取得する
class FreeEage
{
public List<EAGE> FreeEage_Make(CAE_DATA DATA,int TargetPID)
{
//TargetPIDのシェルからエッジを作成する
var EageList = EageList_make(DATA,TargetPID);
//処理速度を上げるため、重複したエッジを検索する範囲を分割する
var sepalete_List = Sepalete_Area(EageList,1000);
return Get_Free_Eage(sepalete_List);//フリーエッジを取得する
}
private List< EAGE> EageList_make(CAE_DATA DATA,int TargetPID)
{
return DATA.Shell.Where(i=>i.Value.Pid==TargetPID).SelectMany(i => Eage_Nid_Make(DATA, i.Value)).ToList();
}
private List<EAGE> Eage_Nid_Make(CAE_DATA DATA,SHELL E)
{
var temp = new List<EAGE>();
//keyword形式では、三角形シェルの節点IDの4番目は、3番目と同じ値になっているため
//下の方法でシェルが三角か四角かが判別できる
var num = E.Nid[2]==E.Nid[3] ? 3 : 4;
var E_NID = E.Nid.GetRange(0, num);
E_NID.Add(E.Nid[0]);
for (var i=0;i<E_NID.Count()-1;i++)
{
var NID=new List<int> { E_NID[i], E_NID[i+1] };
var Crd1 = DATA.Node[NID[0]].Crd;
var Crd2 = DATA.Node[NID[1]].Crd;
var Center = Vector3.Divide(Vector3.Add(Crd1, Crd2), 2);
temp.Add(new EAGE(Center, NID));
}
return temp;
}
private List<EAGE> Get_Free_Eage(List<List<EAGE>> temp)
{
var Free_Eage = new List<EAGE>();
foreach (var i in temp)
{
var distinct = i.Distinct();
foreach (var q in distinct)
{
var temp_List = i.Where(j => j==q).ToList();
if (temp_List.Count() == 1)
{
Free_Eage.Add(temp_List[0]);
}
}
}
return Free_Eage;
}
private List<List<EAGE>> Sepalete_Area(List<EAGE> temp,int num)
{
var Max = temp.Max(i => i.Center.Y)+1;
var Min = temp.Min(i => i.Center.Y)-1;
var dis = (Max - Min) / num;
var temp2 = new List<List<EAGE>>();
for (var i = 0; i <= num; i++)
{
temp2.Add(temp.Where(j => j.Center.Y >= Min + i * dis &&
j.Center.Y < Min + (i + 1) * dis).ToList());
}
return temp2;
}
}
使い方
static void Main(string[] args)
{
var Infile = @"C:/Users/test.key";
var DATA=new FILE_CAE().from_Key_File(Infile);
var TargetPID=1;
//List<EAGE>でフリーエッジが取得できます
var FreeEage=new FreeEage().FreeEage_Make(DATA,TargetPID);
}
今後
- フリーエッジが書けたので次は、フリーの面(表面シェル)を書いていこうと思います
Discussion