Physicist, Programmer. What you eat, how you think, and most importantly what you have done become who you are. Who are you? and who will you be?
[Recoeve.net]
Career Portfolio - 이강수
kipid2024. 8. 27. 01:21
728x90
반응형
# Career Portfolio - 이강수
Physicist, Programmer.
최근의 좌우명은 "What you eat, how you think, and most importantly what you have done become who you are. Who are you? and who will you be?"
## PH
57% 내향형|Introversion (외향형|Extroversion 43%, 내향형|Introversion 57%)
내향형의 사람들은 소수의 사람들과 깊고 의미 있는 관계를 맺는 일을 선호하며, 차분한 환경을 원할 때가 많습니다.
78% 직관형|iNtuition (직관형|iNtuition 78%, 현실주의형|Sensing 22%)
직관형의 사람들은 매우 상상력이 뛰어나고 개방적이며 호기심이 많습니다. 이들은 독창성을 중시하며 어떤 것에 숨은 의미와 막연한 가능성에 대해 집중하곤 합니다.
54% 사고형|Thinking (사고형|Thinking 54%, 감정형|Feeling 46%)
사고형의 사람들은 객관성과 합리성을 중시하며 논리에 집중하느라 감정을 간과할 때가 많습니다. 이들은 사회적 조화보다는 효율성이 더 중요하다고 생각하는 경향이 있습니다.
78% 계획형|Judging (계획형|Judging 78%, 탐색형|Perceiving 22%)
계획형의 사람들은 결단력이 높고 철저하며 계획적인 성격을 지니고 있습니다. 이들은 명확성과 예측 가능성을 중시하고 어떤 일을 제대로 끝내고자 하며 즉흥적인 일보다는 체계적으로 계획을 세우는 일을 선호합니다.
90% 확신형|Assertive (확신형|Assertive 90%, 민감형|Turbulent 10%)
확신형의 사람들은 스스로를 믿고 평정심을 잘 유지하며 스트레스에 크게 영향을 받지 않습니다. 이들은 걱정을 자주 하지 않으며 목표를 달성하기 위해 노력하는 과정에서 높은 자신감을 보이는 경향이 있습니다.
### Description of my MBTI (성격 유형: 전략가 INTJ-A(Assertive) / INTJ-T(Turbulent))
성격 유형: 전략가 INTJ-A(Assertive) / INTJ-T(Turbulent)
사고 능력은 인간의 위대한 점 중 하나이다. 인간은 갈대처럼 연약하지만 생각하는 갈대이다. --by 블레즈 파스칼--
최고가 되는 것은 외로운 일입니다. 매우 희귀한 성격이면서도 뛰어난 능력을 지닌 전략가(INTJ)는 이러한 말의 의미를 잘 알고 있습니다. 전략가는 이성적이면서도 두뇌 회전이 빠른 성격으로, 자신의 뛰어난 사고 능력을 자랑스러워하며 거짓말과 위선을 꿰뚫어 보는 능력이 있습니다. 하지만 이로 인해 끊임없이 생각하고 주변의 모든 것을 분석하려는 자신의 성향을 이해할 수 있는 사람을 찾는 데 어려움을 겪기도 합니다.
#### 개척자 정신
전략가는 모든 것에 의문을 제기합니다. 다른 많은 성격은 현재 상태를 유지하고 일반적인 통념과 다른 사람의 전문 지식에 의존해 살아가곤 합니다. 하지만 비판적인 성향을 지닌 전략가는 자신만의 방식을 찾아내기를 원하며, 일을 진행하는 더 나은 방식을 찾기 위해 규칙을 깨거나 다른 사람의 반대를 무릅쓰는 일도 마다하지 않습니다. 사실 오히려 이러한 과정을 즐기는 편입니다.
전략가는 실제로 활용할 수 있는 아이디어만이 가치가 있다고 생각하며, 단순히 새로운 아이디어를 내는 데 그치는 것이 아니라 아이디어를 이용해 성공을 쟁취하고자 합니다. 이들은 업무에 자신의 모든 통찰력과 논리력과 의지를 쏟아부으며, 불필요한 규칙을 설정하거나 쓸모없는 비판을 제기하면서 자신을 방해하는 사람에게는 가차 없는 모습을 보입니다.
전략가는 매우 독립적인 성격으로 다른 사람의 기대를 따르기보다는 자신만의 아이디어를 추구합니다.
전략가는 독립성이 매우 강하며 혼자서 행동하는 일을 두려워하지 않습니다. 아마 다른 사람을 기다리는 일을 좋아하지 않기 때문일 수도 있습니다. 또한 일반적으로 다른 사람의 의견을 묻지 않고 결정을 내리는 편입니다. 이렇게 혼자서 행동하려는 성향으로 인해 다른 사람의 의견과 욕구와 계획을 무시함에 따라 무신경한 사람처럼 보일 수도 있습니다.
하지만 전략가가 남에게 무심하다는 생각은 사실이 아닙니다. 감정이 풍부하지 않고 지적인 성격이라는 편견이 있기는 하지만 사실 전략가는 예민한 감성을 지니고 있기 때문입니다. 전략가는 일이 잘못되거나 남에게 상처를 주게 되었을 때 슬픔과 후회를 느끼며, 왜 그런 일이 발생했는지 파악하는 데 많은 시간과 에너지를 투자합니다. 결정을 내릴 때 감정을 중시하지 않는다고 해서 감정을 느끼지 못하는 것은 아니기 때문입니다.
#### 지식에 대한 갈망
전략가는 대담한 몽상가인 동시에 극심한 비관주의자이기도 합니다. 이들은 의지와 지적 능력이 있다면 어떠한 목표라도 성취할 수 있다고 믿지만, 동시에 대부분의 사람이 게으르고 상상력이 부족하고 특별할 것이 없다고 냉소적으로 생각하기도 합니다.
전략가의 자존감은 대부분 자신의 지식과 지적 능력에 기반을 두고 있습니다. 학교에서 '책벌레'나 '범생이'라는 소리를 듣기도 하지만, 이러한 말을 모욕이 아닌 자신의 특징으로 받아들입니다. 또한 자신에게 코딩, 무술, 클래식 음악 등 관심이 있는 분야라면 어떤 분야든지 최고가 될 수 있는 능력이 있다는 사실을 알고 있습니다.
전략가가 새로운 것을 배우는 이유는 남에게 보여주기 위해서가 아니라 자신의 지식을 확장하는 일 자체를 즐기기 때문입니다.
전략가는 완고할 때가 있으며 주의가 산만한 환경이나 불필요한 잡담 등 시시한 일을 참지 못합니다. 하지만 그렇다고 이들이 지루하거나 재미없는 성격이라는 의미는 아닙니다. 진지해 보이는 모습과 달리 재치가 넘치며. 날카롭게 비꼬면서도 재미있는 유머 감각을 지니고 있는 경우가 많기 때문입니다.
#### 상대적으로 취약한 사교 능력
일반적으로 전략가가 따뜻하고 부드러운 성격은 아닙니다. 겸손함과 인사치레보다는 이성과 성공을 중시하며, 입바른 말을 하기보다는 솔직하게 이야기하는 성격이기 때문입니다. 소설이나 영화의 악당이 전략가의 성격을 지닌 것으로 표현되는 이유도 바로 이러한 점 때문일 것입니다.
솔직함을 중시하고 핵심만을 이야기하려는 전략가는 잡담과 빈말 등 일반적인 사교 활동이 무의미하거나 멍청하다고 생각할 수 있습니다. 이로 인해 솔직함에만 집중하느라 무례하거나 공격적인 사람처럼 보일 수도 있습니다.
전략가는 가끔 다른 사람을 대하는 일 자체가 불필요한 것은 아닌지 생각할 때가 있습니다.
하지만 전략가도 다른 성격과 마찬가지로 다른 사람과의 소통을 원합니다. 다만 자신과 가치관이 비슷한 사람을 만나고 싶어하며, 그럴 수 없다면 차라리 혼자 있는 것을 선택할 뿐입니다. 이들은 자신의 관심사에 집중할 때 자신감을 발산하는 성격으로, 이러한 자신감은 직장 동료와 관계를 맺거나 친구나 연인을 사귈 때 도움이 되기도 합니다.
#### 체스 경기와 같은 삶
전략가는 모순이 가득한 성격입니다. 상상력이 넘치면서도 결단력이 강하고, 야망이 넘치면서도 차분하고, 호기심이 많으면서도 집중력이 높은 성격이기 때문입니다. 다른 사람은 모순적인 전략가의 성격을 이해하기 힘들다고 생각할 수도 있지만, 전략가의 사고방식을 생각하면 이러한 모순도 이해할 수 있습니다.
전략가에게 삶은 거대한 체스 경기와 같습니다. 이들은 운보다는 전략에 의존하며 결정을 내릴 때마다 결정으로 인한 장단점을 심사숙고합니다. 또한 아무리 힘든 일이 생기더라도 지적 능력과 통찰력을 이용하면 승리할 방법을 찾을 수 있다고 믿습니다.
#### 전략가형 인물들
이름 옆 괄호에 있는건 영화 제목. 영화 내의 인물의 성격이 INTJ 인거 일듯.
Friedrich Nietzsche, Michelle Obama, Elon Musk, Christopher Nolan, Arnold Schwarzenegger, Colin Powell, Samantha Power, Walter White ("Heisenberg", Breaking Bad), Petyr Baelish ("Littlefinger", Game of Thrones), Tywin Lannister (Game of Thrones), Yennefer of Vengerberg (The Witcher series), Gandalf the Grey (The Lord of the Rings), Professor Moriarty (Sherlock Holmes series), Katniss Everdeen (The Hunger Games), Seven of Nine (Star Trek: Voyager
), Jay Gatsby (The Great Gatsby)
##[.no-sec-N#sec-CV] Curriculum vitae (이력서)
1984-11-08: Born in Ansan, Republic of Korea.
2002-03 ~ 2006-02: Diploma studies in Physics at KAIST, Korea.
// Simple Research on Quantum Dot and Quantum Information theory.
// Graduation thesis on ‘Electro-Magnetism with General Relativity’; Crude try on explanation of electro-magnetic forces from gravity only. See .
2007-09 ~ 2013-12: Entered Seoul National University.
// Combined master’s and doctorate course in Experimental Lab and Theoritical Lab .
// But dropped at 2013 since I don't care about insignificant certificate of Doctorial degree.
// The proudest program which I coded alone is the one to correct inaccurate location measured by GPS with wifi RSSI (Received Signal Strength Indicatior) .
2024-07-25 ~ 2025-02-25: 국비지원 부트캠프 (Government-assisted bootcamp) Codeit FullStack 2기
### Some other remarks
Feb. 2008 ~ Spring 2010: Programmed plenty of Labview, C++ programs. Almost of them analyze image files taken from microscope with vector calculus. Some of them compile statistics on properties of magnetic domain.
Jan. 2010: Organized ‘3rd BK21 Young Physicists Workshop’. A committee of arrangements.
###[#subsec-papers] Papers Published (and Selected ones which I am proud of.)
#### Truncated many-body dynamics of interacting bosons: A variational principle with error monitoring
Read More : http://www.worldscientific.com/doi/abs/10.1142/S0217979215500216
This is one of my favorite papers published, although many would not understand this well enough. See it in docuK html form in .
#### Emergence of a new pair-coherent phase in many-body quenches of repulsive bosons
#### Universality Classes of Magnetic Domain Wall Motion
#### Long-Range Domain Wall Tension in Pt/Co/Pt Films with Perpendicular Magnetic Anisotropy
## Recoeve.net
최근까지 개발중인 서비스. Fuzzy search, Multi-Categorized Records 등이 핵심 포인트. Vert.x JAVA server + MySQL JDBC (Java DataBase Connector) + Javascript, jQuery, AJAX, React 등 이용하여 개발. 개발에 쓰인 코드들은 github.com/kipid/Recoeve 에서 열람 가능합니다. history.pushState 를 이용해서 페이지를 새로 띄우지 않고 내부적으로 이동하도록 디자인 했습니다. 개발하면서 정리했던 Recoeve database setup 도 참고하시기 바랍니다.
Machine learning 을 이용한 user-collaborative recommendation 이 핵심.
Self-customizing + Multi-Categorized Records, or Scrapbook, for Everything, and Personalized Recommendation for Everything at each Category, which can be called Computer-Assisted Cloud/Crowd Curating.
The below page is [Music/Break]--K-Pop of kipid's Recoeve.net.
### Fuzzy search
거의 모든 것들에 Fuzzy search 를 적용해 자신이 reco 했던 것들을 쉽게 다시 찾을 수 있도록, 카테고리 선택이 쉽도록, 카테고리 이동이 쉽도록 만들었습니다.
### Structured and Multi Categorization
Tag 기능과 비슷하지만 상하위 구조를 가지는 (structured) categorization 이 가능하도록 만들었습니다. Tag 처럼 한 reco 에 여러 category 를 구분자 ";"를 통해 지정할 수 있습니다. Category 를 입력할때도 fuzzy search 의 도움을 받을 수 있도록 구현하였습니다.
Reco 의 category 부분 link 를 통해서도 category jump 가 가능하도록 만들었습니다.
### Change orders of category list
Category list 의 위치를 user 가 쉽게 바꿀 수 있도록 디자인하고 구현하였습니다. 제 페이지 https://recoeve.net/user/kipid 에서 테스트 해보실 수 있습니다. (본인 페이지가 아니더라도 category list 위치를 수정할 수 있게 디자인하였습니다. 자기 페이지가 아닌만큼 이 순서는 페이지 새로고침 전까지만 유지됩니다. 새로고침하면 원래 user 의 category list 순서대로 다시 돌아옵니다.)
### Pattern replace 를 통한 다국어 지원
Pattern replace 를 통해 다국어 지원이 쉽도록 디자인 하였습니다.
[--Pattern--] 형태를 찾아 다국어 replace. 아래는 코드 일부분.
```[.linenums.scrollable.lang-java]
public static final Pattern ptnReplacer=Pattern.compile("\\[--[\\s\\S]+?--\\]");
public static final Pattern ptnVariable=Pattern.compile("\\{--[\\s\\S]+?--\\}");
static {
fileMap=new HashMap<String, Map<String, ArrayList<String>>>(fileMapSize);
File file=null;
String fileStr=null;
file=new File(filePath+"lang.txt");
if (file.exists()) { try {
StringBuilder sb=new StringBuilder();
int ch;
FileReader reader=new FileReader(file);
while((ch=reader.read())!=-1) {
sb.append((char)ch);
}
reader.close();
fileStr=sb.toString();
} catch (IOException e) {
System.out.println(e);
} finally {
file=null;
} }
StrArray langMap=new StrArray(fileStr, true, true);
// System.out.println(langMap);
fileStr=null;
for (String fileName: fileNames) {
file=new File(filePath+fileName);
if (file.exists()) { try {
StringBuilder sb=new StringBuilder();
int ch;
FileReader reader=new FileReader(file);
while((ch=reader.read())!=-1) {
sb.append((char)ch);
}
reader.close();
fileStr=sb.toString();
} catch (IOException e) {
System.out.println(e);
} finally {
file=null;
} }
if (fileStr!=null) {
// System.out.println("\nfileName : "+fileName);
fileMap.put(fileName, new HashMap<String, ArrayList<String>>(fileLangMapSize));
Map<String, ArrayList<String>> fileLangMap=fileMap.get(fileName);
ArrayList<String> strListVars=new ArrayList<String>();
Matcher matchVariable=ptnVariable.matcher(fileStr); // default
int start=0;
while (start<fileStr.length()) {
if (matchVariable.find(start)) {
strListVars.add(fileStr.substring(start, matchVariable.start()));
strListVars.add(matchVariable.group());
start=matchVariable.end();
} else {
strListVars.add(fileStr.substring(start));
start=fileStr.length();
}
}
fileLangMap.put("df", strListVars); // default.
ArrayList<String> strList=new ArrayList<String>();
Matcher matchReplacer=ptnReplacer.matcher(fileStr);
start=0;
while (start<fileStr.length()) {
if (matchReplacer.find(start)) {
strList.add(fileStr.substring(start, matchReplacer.start()));
strList.add(matchReplacer.group());
start=matchReplacer.end();
} else {
strList.add(fileStr.substring(start));
start=fileStr.length();
}
}
if (strList.size()>1) {
int colSize=langMap.getColSizeAtRow(0);
for (int k=2;k<colSize;k++) {
String lang=langMap.get(0,k);
if (!lang.equals("desc")) {
String strReplaced="";
String replaced=null;
for (int i=0;i<strList.size();i++) {
if (i%2==0) {
strReplaced+=strList.get(i);
} else {
replaced=langMap.get(strList.get(i), lang);
if (replaced==null||replaced.isEmpty()||replaced.equals("-")) {
replaced=langMap.get(strList.get(i), "en"); // "en" is default lang.
}
if (replaced==null) {
replaced=strList.get(i);
}
strReplaced+=replaced;
}
}
strListVars=new ArrayList<String>();
matchVariable=ptnVariable.matcher(strReplaced); // [--lang--] replaced
start=0;
while (start<strReplaced.length()) {
if (matchVariable.find(start)) {
strListVars.add(strReplaced.substring(start, matchVariable.start()));
strListVars.add(matchVariable.group());
start=matchVariable.end();
} else {
strListVars.add(strReplaced.substring(start));
start=strReplaced.length();
}
}
fileLangMap.put(lang, strListVars); // after replacing langMap.
}
}
}
fileStr=null;
}
}
}
public FileMapWithVar() {}
public static String get(String fileName, String lang, Map<String,String> varMap) {
Map<String, ArrayList<String>> fileLangMap=fileMap.get(fileName);
if (fileLangMap==null) {return null;}
ArrayList<String> strList=fileLangMap.get(lang);
if (strList==null) {
strList=fileLangMap.get("df");
}
String res="";
String replaced=null;
for (int i=0;i<strList.size();i++) {
if (i%2==0) {
res+=strList.get(i);
} else {
replaced=varMap.get(strList.get(i));
if (replaced==null) { replaced=strList.get(i); }
res+=replaced;
}
}
return res;
}
```/
### Recoeve.net as a playlist of YouTubes and videos
Youtube API 를 이용해서 자신이 reco 한 youtube 영상들을 연속으로 재생할 수 있게 해놓았습니다. 1개의 동영상만 반복해서 재생할수도 있고, 전체 영상을 반복해서 재생할수도 있게 디자인했고, Shuffle 기능도 구현해 놨습니다. Reco 해 놓은 youtube 영상이 재생될 수 없는 경우에는 1초뒤 다음 영상으로 재생되도록 error handling 도 해 놓았습니다.
테스트는 https://recoeve.net/user/kipid?cat=[Music/Break]--K-Pop 에서 해보실 수 있습니다. "Toggle reco list play" 버튼을 눌러보세요.
### Reco example :: Reco Youtube Video.
### Recoeve widget (Recoeve 로 내보내기)
Recoeve widget 을 만들어서 twitter 나 facebook 처럼 SNS 내보내기 가 가능하도록 만들었습니다.
### 동영상과 가사 함께보기
Reco description 에 #lyrics 를 통해 가사를 입력하면 Toggle lyrics 버튼과 함께 가사를 접었다 펼 수 있게 디자인했습니다. 동영상은 stick to the left top 버튼을 통해 왼쪽 위에 붙일 수 있도록 만들었습니다.
### Scalable design
스마트폰에서도 잘 동작하도록 CSS media query 를 통해 Responsible/Scalable 하게 디자인 하였습니다. 사이트를 방문하셔서 직접 테스트 해보세요.
아래는 사용된 CSS.
```[.linenums.scalable.lang-css]
@media all and (min-width:701px) {
#container {margin-left:14.9em}
#sidebar {margin:0; position:fixed; left:0; top:0; bottom:0; width:15em}
#sidebar-dragger {display:none}
#sidebar-exit {display:none}
.side2 {padding:.5em}
.rC.fixed {width:646px; border-right:.15em solid black}
.lyrics {text-align:right}
::-webkit-scrollbar {width:11px; height:11px}
}
@media all and (min-width:901px) {
#container {margin-left:19.9em}
#sidebar {width:20em}
}
@media all and (min-height:500px) {
#fuzzy-search-list {max-height:430px;}
}
```/
### 로그인 암호 해킹 방지 시스템
Iteration 이 들어가는 단방향 암호화 (Hash) 를 이용해서 네트워크 중간에 가로채는 해킹에 대해서도 어느정도 면역이 생기게 디자인하였습니다.
User 쪽 javascript 단에서 처음에는 10000 iteration 해서 보내고. 다음부터는 9999 iteration 해서 보내고 나머지는 server 에서 iteration 통해 암호가 일치하는지 확인하는 방법. 사용자 암호가 털리지 않는한, 네트워크 중간에서 가로챈 암호화 된 암호 가지고도 해킹이 불가능하게 디자인. 단 user 쪽 iteration 은 항상 줄어들어야 함. 안그러면 hash function 이 javascript 에 노출되므로 뒷쪽 iteration 을 유추가능. Server 의 user data 에는 iteration 정보를 저장하고 있어야 하고, user 가 로그인할때 id/e-mail 만 AJAX 로 먼저 보내서 암호 iteraion 을 몇번할 것인지 받아낸 다음 iteration 으로 암호화 후 전체 데이터 전송.
아래는 코드 일부분.
```[.linenums.scrollable.lang-javascript]
eve.encrypt=function(salt, pwd, iter) {
iter=pwd.length+131+((iter&&iter.constructor==Number&&iter>=0)?iter:0);;
pwd=salt+pwd;
var h1=eve.hash1(pwd);
var h2=eve.hash2(pwd);
var h3=eve.hash3(pwd);
var h4=eve.hash4(pwd);
var h5=eve.hash5(pwd);
var h6=eve.hash6(pwd);
var h7=eve.hash7(pwd);
var h8=eve.hash8(pwd);
var h9=eve.hash9(pwd);
var h10=eve.hash10(pwd);
var h11=eve.hash11(pwd);
var h12=eve.hash12(pwd);
var h13=eve.hash13(pwd);
var tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13;
for (var i=0;i<iter;i++) {
tmp1=h13+h12+h11+h10+h9+salt+h8+h7+h6+h5+h4+h3+h2+h1;
tmp2=h1+h3+salt+h2;
tmp3=salt+h2+h8+h1+h3;
tmp4=h7+salt+h5;
tmp5=h4+salt+h8;
tmp6=h10+h13+salt+h6;
tmp7=h6+h1+h9+salt;
tmp8=h9+salt+h10;
tmp9=h7+salt+h12;
tmp10=h11+salt+h5;
tmp11=h4+salt+h13+h2;
tmp12=h11+salt+h6;
tmp13=h4+h12+salt+h8;
h1=eve.hash1(tmp1);
h2=eve.hash2(tmp2);
h3=eve.hash3(tmp3);
h4=eve.hash4(tmp4);
h5=eve.hash5(tmp5);
h6=eve.hash6(tmp6);
h7=eve.hash7(tmp7);
h8=eve.hash8(tmp8);
h9=eve.hash9(tmp9);
h10=eve.hash10(tmp10);
h11=eve.hash11(tmp11);
h12=eve.hash12(tmp12);
h13=eve.hash13(tmp13);
}
return h1+h2+h3+h4+h5+h6+h7+h8+h9+h10+h11+h12+h13;
};
eve.iterFull=10000;
eve.logIn=function(elem) {
var $elem=$(elem).eq(0);
$elem[0].disabled=true;
var $form=$elem.parent("form").eq(0);
var idType="id";
var userId=$form.find(".user-id")[0].value;
var userIdLength=eve.byteCount(userId);
var userPwd=$form.find(".user-pwd")[0].value;
var err="";
var valid=false;
if (userIdLength==0) {
err+="[--ID or E-mail is empty.--]";
} else if (userIdLength<eve.minIdLength) {
err+="[--ID or E-mail is too short.--]";
} else if (userPwd.length==0) {
err+="[--Password is empty.--]";
} else if (userPwd.length<eve.minPwdLength) {
err+="[--Password is too short.--]";
} else {
if (eve.regExId.test(userId)) {
$form.find("#input-idType")[0].value=idType="id"; valid=true;
} else if (eve.regExEmail.test(userId)) {
$form.find("#input-idType")[0].value=idType="email"; valid=true;
} else {
err+="[--ID--]/[--E-mail--] '"+userId+"' [--is of invalid form.--]";
}
}
if (valid) {
var form_data="log\tiehack\tscreenWidth\tscreenHeight\tidType\tuserId\trememberMe\tuserPwd\n"
+"web\t☠\t"+sW+"\t"+sH+"\t"+idType+"\t"+userId+"\t"+($("input:checkbox[name='rememberMe']").is(":checked")?"yes":"no")+"\t";
$elem.before( eve.encloseErr("[--Checking ID/E-mail--] : [--Please wait.--]") );
$.ajax("./pwd_iteration", {
type: "POST"
, data: idType+"\t"+userId
}).fail(function() {
$elem.before( eve.encloseErr("[--Request times out.--] [--Please click the Log-in button again.--]") );
$elem[0].disabled=false;
}).done(function(resp) { // resp: pwd_iteration or "No such id" salt
var res=resp.split('\t');
var iter=Number(res[0]);
if (isNaN(iter)) {
$elem.before( eve.encloseErr("Error : "+res[0]) );
$elem[0].disabled=false;
} else {
$elem.before( eve.encloseErr("[--Password is being encrypted.--]") );
setTimeout(function() {
form_data+=eve.encrypt(res[1], userPwd, iter); userPwd="";
$elem.before( eve.encloseErr("[--Logging in--] : [--Please wait.--]") );
$.ajax("./log-in.do", {
type: "POST"
, data: form_data
}).done((resp) => {
$elem.before( eve.encloseErr(resp) );
if (resp==="log-in success") {
window.location.pathname="/";
} else {
$elem[0].disabled=false;
}
});
}, 500);
}
});
} else {
$elem.before( eve.encloseErr(err) );
$elem[0].disabled=false;
}
var errorMsgs=$form.find(".error-msg");
for (var i=errorMsgs.length-6;i>=0;i--) {
errorMsgs.eq(i).css({display:"none"});
};
};
```/
아래는 동작하는 장면.
### Delayed loading 을 통한 high performance 구현
Reco 들이 많아지면 많아질수록 페이지 다운로드 속도가 느려질 수 있는데 click event 나 scroll event 를 통해 delayed loading 이 trigger 되도록 구현해 high performance 를 구현하였습니다. 또한 페이지 reload 없이 history.pushState 를 이용 페이지를 새로 띄우지 않고 내부적으로 category 를 이동하도록 디자인했습니다.
한번 방문했던 category 의 data 들은 javascript 에 저장해 다시 같은 category 를 방문했을때에는 http request 없이 (즉 통신없이) 이전에 다운로드 받았던 reco list 를 출력하도록 구현했습니다.
### 단어 외우기 앱 구현 in Recoeve.net
토이 앱으로 단어 외우기 앱도 Recoeve.net 내에 구현해 놨습니다. Spreadsheet 형태로 된 단어장을 #dictionary 태그를 달아 description 에 저장하면 외우기 창이 활성화 되도록 만들었습니다.
## Markdown Language : SEE (Super Easy Edit) and docuK
인터넷에서 글쓸때, 매번 html 로 작성하기는 너무 많은 공을 필요로 해서 만들게 된 Markdown Language. 해당 코드들은 github.com/kipid/docuK 에서 확인 가능합니다.
This is an HTML document format named docuK which is rendered by JavaScript, jQuery, MathJax, and google code prettifier.
Specific features are
Changable mode, font-family.
Resizable font-size, line-height.
Table of contents.
Numbering of sections/figures/equations.
Citing references in bubble-shape pop up.
Refering figures and equations.
Refering anything with id and element with class="number".
Delayed(lazy)-loading of figures (images, iframes).
Delayed(lazy)-rendering of maths (MathJax).
Auto code printing from <codeprint id="code-id"></codeprint> to <pre id="pre-code-id"></pre>.
Quite similar to LaTeX or Wiki document, but extended a little bit.
This document is also made up by using docuK+SEE.
### Citing references and Refering anything inside docuK
You can cite references like <cite class="ref-some"></cite>.
\sum_i x_i ...
Refering the above equation .
### Delayed(lazy)-loading and rendering
Delayed(lazy)-loading of images, videos, html element with src attribute. And delayed(lazy)-rendering of MathJax.
\begin{aligned}
\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\ \nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\
\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\
\nabla \cdot \vec{\mathbf{B}} & = 0
\end{aligned}
## LaTeX to HTML and "docuK" format
"LaTeX to HTML"는 LaTeX 형태로 만들어진 문서를 HTML로 바꿔주는 프로그램입니다. (이 converter는 아주 간단한 형태의 LaTeX 문서만 convert 되고 저 개인에 맞춰진 감이 크긴 합니다.) "docuK"는 document designed by kipid란 뜻으로 개인적으로 만들고 있는 LaTeX 비슷한 HTML document format입니다 . 이 문서도 docuK format을 이용해서 만들었습니다.
LaTeX는 수식이 많이 들어가있는 문서나 구조화 (상호참조(cite, refer), 목차(table of contents, section, subsection), 참고문헌(references) 등) 되어있는 문서의 작성에 용이한 문서 편집 방식입니다 . 하지만 대부분 결과물을 출력이 용이한 pdf 파일형태로 내놓기 때문에 인터넷 상에서 공유하고, 문서의 내용 또한 웹 검색엔진에서 검색이 되도록 하는데에는 불편한 점이 많습니다. 물리 연구를 하면서 몇가지 물리전문지식들에 대한 설명들을 LaTeX 문서로 정리하게 되었는데 , 인터넷 상에서도 공유하고 싶어서 만들어 보게 된 프로그램입니다.
많은 책들과 논문들이 LaTeX 형태로 편집되고 작성되기 때문에 E-book과도 관련이 있을듯했고, HTML(css, javascript 등)도 더 공부해볼겸 수작업을 많이 거치면서 어떤식으로 LaTeX 문서를 HTML 형태로 바꿀지를 고민하면서 만든 프로그램 입니다. 최근에는 HTML을 더 공부하면서 docuK format을 만들고 있기도 합니다 .
궁극적으로는 WYSIWYG (What you see is What you get) 형태의 편집과 text 형태의 명령어 편집의 장점들만 모아놓은 편집기를 개발해보고 싶은 욕심도 있습니다.
기본적으로 수식이 들어가는 HTML 문서는 이미지 형태의 인터넷 수식을 제공하는 codecogs.com나 javascript를 이용해 문자위치를 적절히 배치해 수식으로 나타내주는 MathJax를 사용하여 만듬 .
문서가 긴 경우 스크롤이 너무 길 수 있고, 문서를 읽는 사람이 어디쯤을 읽고 있는지도 파악이 어렵기 때문에 “▼ Show/Hide, ▲ Hide” 기능을 javascript/jQuery로 구현해서 첨부. 클릭시 각 section, subsection을 보여주거나 감춤. (감출때는 window.scrollBy()를 이용해서 문서를 읽는 사용자의 혼란을 줄였음.)
전체적으로 LaTeX 명령어들을 분석하여 그에 상응하는 HTML 형태의 명령어들로 바꾸어 줌. (특수문자들도 HTML 형태로: ex] \"o -> ö -> ö)
\newcommand 형태로 명렁어를 간단하게 만들어 쓰는 LaTeX 문법을 분석하여 바꾸는 기능 + ref, label, cite 등 상호참조나 목차 자동만들기를 분석하여 HTML로 만드는 기능 등이 있음.
반응형 웹 (Responsible web) 으로 스마트폰, 타블렛 PC 등 다양한 화면크기에서도 제대로 보이도록 design.
아래는 docuK format에 쓰인 javascript/jQuery 일부분.
```[.linenums.scrollable]
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
$.fn.exists=function() {
return this.length!==0;
}
var sec=$(".docuK>.sec"), subsec, subsubsec, secContents="";
var secIH2, subsecIH3, subsubsecIH4;
var i, j, k, secI=0, secITxt="", subsecI=0, subsubsecI=0, tocHtml="", txt="", secId="", secPreTxt="";
var eqqs, eqN="", eqC="", figs;
function fTocHtml () {
return "<h"+hN+"><a class='jump' id='toc-"+secId+"' href='#sec-"+secId+"'><span class=\"secN\">"+secPreTxt+".</span>"+txt+"</a></h"+hN+">";
}
function fSecHtml () {
return "<a class='jump' id='sec-"+secId+"' href='#toc-"+secId+"'><span class=\"secN\">"+secPreTxt+".</span></a>"+txt;
}
function fEqqHtml () {
return '<div class="eqCC"><div class="eqN"><span class="number">('+eqN+')</span></div><div class="eqC">\\[ '+eqC+' \\]</div></div>';
}
for (i=0;i<sec.length;i++) {
secIH2=sec.eq(i).find("h2:first-child");
if (secIH2.exists() && !secIH2.is(".notsec")) { // exclude .sec>h1 and .sec>h2.notsec in ToC
hN="2"; txt=secIH2.html();
if (secIH2.is(".no-sec-N")) {
secPreTxt=secId=secITxt=secIH2.attr('id');
tocHtml+="<h"+hN+"><a class='jump' id='toc-"+secId+"' href='#sec-"+secId+"'>"+txt+"</a></h"+hN+">";
secIH2.html(function(ith,origText){return "<a class='jump' id='sec-"+secId+"' href='#toc-"+secId+"'>"+origText+"</a>";});
} else {
secI++;
secPreTxt=secId=secITxt=secI.toString();
tocHtml+=fTocHtml();
secIH2.html(fSecHtml());
}
if (!sec.eq(i).is(".noToggleUI")) {
secContents="sec-"+secITxt+"-contents";
sec.eq(i).append("<div class=\"Hide\" onclick=\"Hide('"+secContents+"')\">▲ Hide</div><div class=\"cBoth\"></div>");
sec.eq(i).find(">*:not(:first-child)").wrapAll("<div class=\"sec-contents\" id=\""+secContents+"\"></div>");
sec.eq(i).append("<div class=\"cBoth\"></div>");
secIH2.after("<div class=\"ShowHide\" onclick=\"ShowHide('"+secContents+"')\">▼ Show/Hide</div>");
}
subsec=sec.eq(i).find(".subsec"); subsecI=0;
for (j=0;j<subsec.length;j++) {
subsecIH3=subsec.eq(j).find("h3:first-child");
hN="3"; subsecI++; secId=secITxt+"-"+subsecI; secPreTxt=secITxt+"."+subsecI; txt=subsecIH3.html();
tocHtml+=fTocHtml();
subsecIH3.html(fSecHtml());
subsubsec=subsec.eq(j).find(".subsubsec"); subsubsecI=0;
for (k=0;k<subsubsec.length;k++) {
subsubsecIH4=subsubsec.eq(k).find("h4:first-child");
hN="4"; subsubsecI++; secId=secITxt+"-"+subsecI+"-"+subsubsecI; secPreTxt=secITxt+"."+subsecI+"."+subsubsecI; txt=subsubsecIH4.html();
tocHtml+=fTocHtml();
subsubsecIH4.html(fSecHtml());
}
}
} else {
secITxt="x";
}
eqqs=sec.eq(i).find("eqq");
for(j=0;j<eqqs.length;j++){
eqN=secITxt+"-"+(j+1).toString();
eqC=eqqs.eq(j).html().trim();
eqqs.eq(j).html(fEqqHtml());
}
figs=sec.eq(i).find("figure");
for(j=0;j<figs.length;j++){
figN=secITxt+"-"+(j+1).toString();
figs.eq(j).find(".caption").html(function(ith,orgTxt){return "Fig. <span class=\"number\">("+figN+")</span>: "+orgTxt.trim();});
}
}
$(".docuK>.sec>.toc").html(tocHtml);
var eqs=$(".docuK eq");
for (i=0;i<eqs.length;i++){
eqs.eq(i).html(function(ith,orgTxt){return "\\( "+orgTxt.trim()+" \\)";});
}
</script>
<script>
function ShowHide (divId) {
$("#"+divId).toggle();
}
function Hide (divId) {
var div=$("#"+divId);
window.scrollBy(0,-div.outerHeight());
div.hide();
}
var timerHide;
function ShowBubbleRef (divId) {
clearTimeout(timerHide);
$(".docuK .bubbleRef").hide();
$(".docuK .bubbleRef#"+divId).show();
}
function HideBubbleRef (divId) {
timerHide = setTimeout(function(){$(".docuK .bubbleRef#"+divId).hide();}, 1000);
}
function pad (str, max) {
str=str.toString();
return str.length<max?pad("0"+str,max):str;
}
$(".docuK cite").html('<span class="emph">[No ref]</span>');
var refs=$(".docuK ol.refs>li"), i, refId, refN="", refHtml="";
var cites, j, citeN="";
function fCiteHtml () {
return '<div class="inRef" onmouseover="ShowBubbleRef(\'bRef-'+citeN+'\')" onmouseout="HideBubbleRef(\'bRef-'+citeN+'\')">['+refN+']<div id="bRef-'+citeN+'" class="bubbleRef"><div class="content">Ref. ['+citeN+'] '+refHtml+'</div><div class="arrow"></div></div></div>';
}
for(i=0;i<refs.length;i++){ // ref [i+1] with id
if (refs.eq(i).is("[id]")){
refN=pad(i+1,2);
refHtml=refs.eq(i).html().trim();
refId=refs.eq(i).attr("id");
cites=$(".docuK cite."+refId);
for(j=0;j<cites.length;j++){ // (j+1)th cite of [i+1] reference.
citeN=refN+"-"+(j+1).toString();
cites.eq(j).html(fCiteHtml());
}
}
}
</script>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]}
});
</script>
<script src="https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script>
$(".docuK refer").html('<span class="emph">[No eq or fig]</span>');
var refers=$(".docuK refer"), i, referI, refered, citeN="", refN="", refHtml="";
function fReferHtml () {
return '<div class="inRef" onmouseover="ShowBubbleRef(\'bRef-'+citeN+'\')" onmouseout="HideBubbleRef(\'bRef-'+citeN+'\')">'+refN+'<div id="bRef-'+citeN+'" class="bubbleRef"><div class="content">'+refHtml+'</div><div class="arrow"></div></div></div>';
}
for(i=0;i<refers.length;i++){
referI=refers.eq(i);
citeN=(i+1).toString()+"-"+referI.attr("class");
refered=$(".docuK #"+referI.attr("class"));
refHtml=refered.html();
refN=refered.find(".number").html();
referI.html(fReferHtml());
}
</script>
<script>
var docuKSec,
fontSize=10,
lineHeight=16,
fontStyle="맑은 고딕",
mode="dark";
function Cmode (modeI) {
if (modeI==mode) {
return;
}
var docuKSec=$(".docuK>.sec");
if (modeI=="dark") {
docuKSec.css({"background":"rgb(0,0,0)","color":"rgb(255,255,255)"});
} else if (modeI=="bright") {
docuKSec.css({"background":"rgb(255,255,255)","color":"rgb(0,0,0)"});
} else {
return;
}
mode=modeI;
}
function CfontSize (increment) {
fontSize+=increment;
if (fontSize<8) {
fontSize=8;
} else if (fontSize>12) {
fontSize=12;
}
$(".docuK").css({"font-size":fontSize+"px"});
$(".docuK .TFontSize").html(fontSize.toString()+"px > 1.8em="+(fontSize*1.8).toString()+"px");
}
function ClineHeight (increment) {
lineHeight+=increment;
if (lineHeight<13) {
lineHeight=13;
} else if (lineHeight>20) {
lineHeight=20;
}
$(".docuK").css({"line-height":(lineHeight/10).toString()});
$(".docuK .TLineHeight").html((lineHeight/10).toString());
}
</script>
```/
## Memorizer App
LabVIEW 프로그램과 안드로이드 앱 두가지로 구현.
주로 단어를 외우거나 할 때 체계적으로 암기를 도와주는 프로그램.
TXT파일로 A, B 두 열의 데이터만 저장해 놓는다. (꼭 2열일 필요는 없고, A와 B에 해당하는 열이 어떤것인지만 명시되면 됨.)
파일 선택 뒤, 몇번째 행부터(From) 몇개의 단어를 외울것인지(count) 결정한다. Cutoff ratio를 설정하고 Auto Select 옵션을 키면 최근 10개의 성적(Recent 10 Scores)이 cutoff를 넘긴 단어는 제외하고 외울단어들을 count(예: 20개)만큼 선택한다.
프로그램을 시작하면 랜덤하게 문제에 해당하는 A를 보여주고 답 B를 맞추도록 한다. (A를 보고 B를 맞출수도 있고, B를 문제로 내고 A를 맞추게 할수도 있다.)
현재 얼마나 진행되었고, 몇개를 맞추고 틀렸는지를 막대 바 형태로 출력해준다. (맞추었는지 틀렸는지의 판단은 사용자가 직접 클릭을 통해 함. 문자 입력을 받은 뒤 자동처리도)
한번의 사이클이 다 지나면, 틀린문제만 모아서 다시 순서를 섞은 뒤 다 맞출때까지 반복시킨다. (잘 외워지지 않는 것들만 반복적으로 더 보여줌으로써 암기 효율을 높인다.)
암기가 다 끝났다면, 이번에 외운 단어들의 성적을 색을 이용하여 시각적으로 보여준다. (성적 표시는 HTML을 이용.)
여러 사이트들에서 제공되는 인터넷 사전 대부분은 사용자가 검색했던 단어들의 리스트를 모아서 내 단어장을 만들어줍니다. 기본적인 단어들만 모아놓은 공개단어장들을 제공하기도 합니다. 이런 파일들을 다운받아 txt 형태로 저장하고 잘 외워지지 않는 단어들을 더 반복적으로 외울 수 있게 만들었습니다.
사진을 보여주고 답을 맞추게 하는 기능 등도 추가로 생각 중 입니다. 개인적으로 영어공부를 할 때 필요로해서 만든 앱이라 저 개인에 맞춰진 감이 많습니다. 또한 요새는 굳이 뭔가를 외울일이 없어서 방치해두고 있는 앱이기도 합니다. (그냥 무료로 배포할까 생각했었는데, 아직 만족할만큼 완성된 것도 아니고 마켓에 올리는 절차가 복잡한거 같아서 계속 미뤄지고 있는 중입니다.)
## Designed and Produced Lab Homepage and Blog page
### Lab Homepage
대학원에서 연구를 하면서 교수님께서 Lab Homepage가 있으면 좋을거 같다는 말씀에 간단하게나마 만들어 드렸습니다 . HTML이나 CSS를 계속 배워나가면서 지금 다시보니 이것저것 이상한 부분도 고치고 싶은 부분도 많긴 합니다.
### Publications
### Blog page (kipid's blog)
## 음력/양력 변환 및 D-day, D+day 계산기
음력과 양력 변환. 이 외에도 다른 달력으로의 변환이 어떻게 이루어지는지 분석하여 프로그램을 만들어 보았습니다. 음력 달력의 경우 달의 공전을 기준으로 날짜를 정하기 때문에 정확한 규칙이란것이 없지만, 각 달의 정보를 데이터베이스화 놓은뒤 대략적인 규칙을 이용해 빠르게 원하는 데이터를 접근하고 상호 변환이 가능하도록 프로그래밍 하였습니다.
기준일(0번째 일)을 "양력 1900년 01월 01일 = 음력 1899년 12월 01일"로 잡고, 이 기준일로부터 매월 첫째일이 몇번째 날인지 그 달의 총 일수는 몇인지를 데이터베이스화 해놓음.
달력마다 각 달의 평균일수가 정해져 있으므로 이 평균일수로 기준일로부터의 일수를 나누어 1~2번내에 바로 해당일의 달력 데이터에 접근할 수 있도록 프로그래밍.
대략 200년 정도의 음력 데이터베이스는 20KB, 3000년간의 양력 데이터베이스는 282KB를 차지. 입력된 날짜의 유효성에 따라 Error는 “Out of convertible years”, “Out of month”, “Out of day”, “Wrong leap option A/B”등을 반환.
날짜를 기준으로한 그래프를 그릴때나 시간 개념이 들어간 대규모 데이터를 처리할 때, 날짜를 숫자로 바꿔주는 이러한 코드 (최적화 되어 속도도 빨라야 하는) 를 필요로 하여 만들어 보았습니다. (이미 배포된 여러가지 코드들이 있는것 같았지만, 속도나 확장성 면에서 마음에 드는게 없었어서...)
## Canvas: 프로그램화 된 그림/그래프
시중에 그래프를 그려주는 다양한 프로그램들이 있지만, text 기반의 명령어로 프로그램화해서 수학적인 그림들 (line, rectangular, polygon and so on) 과 그래프를 그려주는 프로그램은 찾지 못했습니다. 이런 프로그램이 있다면 조금 더 개인 취향에 맞게, 내 임의대로 많은것을 편집하고 조정할 수 있는 그래프를 그릴 수 있을것 같다는 생각, 한번 공들여 그린 그래프 포멧을 쉽게 재사용 할수는 없을까하는 생각이 들어서 만들어보고 있는 프로그램입니다. 또한 그래프 프로그램들이 어떻게 돌아가는지도 bottom up으로 공부하고 싶기도 했고, 짜면서 프로그래밍 적으로 배우는것도 많고, 그래프 외에도 여러가지로 활용할 수 있을거 같아서 만들어 봤습니다. (최근에는 HTML과 javascript를 배우면서 그림이 아닌 사용자와 반응하는 그래프들 (interactive graph) 을 어떻게 구현할 수 있을까도 공부하고 있습니다 .)
기본적으로 구상하고 구현한 명령어들은 다음과 같습니다. Text 명령어를 분석하여 RGB로 구성된 24bit color image array의 값들을 조정하는 방식입니다.
```[.linenums.scrollable]
/* multi-line
comments */
// single line comment
// Trim whitespaces always automatically.
// Command [\s\t\n\r] ";" End-marker
// 속성(Attributes) ":(" delimiter
// 인수값(Arguments) ")" delimiter
// "/" color delimiter "//" comments랑 겹치는데...
// If an error occurs, stop and display remaining commands.
// CanvasLoad, Canvas8, Canvas24
// First command must be "Canvas24" or "Canvas8".
Canvas24
right,bottom:(400,300)
background:(255/255/255); // with opacity: (255/255/255,1.0)
// Default right,bottom:(0,0)
// Default background:(255/255/255);
Canvas8
right,bottom:(400,300)
background:(255);
// Default right,bottom:(0,0)
// Default background:(255);
CanvasLoad
file:(C:\Folders\file.bmp);
// Absolute path: "[A-Z]:"
// Relative path:
// "a.bmp" under base folder
// "..\a.bmp" upper folder
// "..\..\a.bmp" upper-upper folder and so on.
// Save, #Define
// line1s, lines, lline1s, llines, cline1s, clines
// rectangles, circles, circleLines, polygon
// texts
Save
file:(C:\Folders\file.bmp);
#Define
var:(00/00/00)
var2:(5px);
// makes '"var"' into '00/00/00'
// affects only afterwards.
line1s
color:(255/0/0)
offset:(100.5,10.5)
x0y0 to x1y1:( // x0y0 toward dxdy:(
10 10 100 100
100 100 10 100
);
lline1s // linked line1s : cline1s // closed line1s
color:(255/0/0)
offset:(100.5,10.5)
xn yn:( // x0y0 dxdys:(
0 0
100 0
100 100
0 100
);
lines, llines, clines
line_width:(2.0)
rectangles
color:(0/0/0)
offset:(100.5,10.5)
x0y0 to x1y1:( // x0y0 toward dxdy:(
10 10 100 100
);
```/
Figure 는 Canvas에 추가적으로 프로그램을 짜서 이용했고 사용한 명령어는 다음과 같습니다.
```[.linenums]
CanvasLoad file:(image\촛불 큰사진.jpg);
Count color:(250/250/200)
around:(150)
min&max domain:(1, 900) // min보다 미만인 애들과 max보다 초과인 domain은 제거.
font&format:(굴림, 10:%#p) // font, font-size:format
per:(10);
Save file:(image\촛불 큰사진 counted.jpg);
```/
이미지를 제대로 분석하려면 고화질 풀샷을 필요로 했는데 구할수가 없어서 대충만 분석한 결과입니다. 결과값에 대한 오해가 있을수 있어 첨언하자면, 멀리서 찍힌 촛불 부분에서의 노이즈 및 촛불을 들지 않고 있는 사람을 못센 다는 점 등이 있어 넓이와 인구밀집도로도 추산하였었습니다. (이건 제가 프로그램한게 아니라서… 포트폴리오에 넣는게 적절하지는 않지만.)
## 기타: 물리연구 및 잡다한 일들 (개인적인 관심사 통계분석 등) 을 하면서 만들었던 프로그램들.
가장 많은 시간을 투자하고 복잡한 구성을 가진 프로그램들은 물리연구를 하면서 만든 것들이지만, 다른곳에 활용도가 떨어지고 그래픽적으로 보여줄 부분은 많지 않아서 일부만 캡쳐했습니다. 대부분 이미지 데이터를 처리하고 분석하는 프로그램들, 수치계산 프로그램들 입니다.
현미경을 통해 PMA (Perpendicular Magnetic Anisotropy) thin film의 자성변화를 MOKE (Magneto-Optical Kerr Effect) 현미경을 통해 관측/기록. 이 과정에서 외부에서 가해주는 자기장 등 내외부 요인으로 자성박막이 미세하게 ($1\sim10 \mu m$ 정도) 떨림. 이를 자성박막에 고정되어 있는 black spot(불순물)을 이용해 기판이 움직인것을 보정해서 background로 찍은 여러장의 사진을 average하고 raw image를 x-y로 shift해서 뺌. (이 과정은 프로그래밍적으로 돌아감. 선택된 black spot 부분이 가장 일치하는 shift 값을 찾아가서 image를 처리하도록 프로그래밍. Shift는 정수(integer) pixel로만 움직이는 것이 아니라 double 값으로 이동. Black spot 부분의 standard deviation 값이 minimum이 되는 부분을 찾아가는 프로그램. 기본적으로 method of the steepest descent 및 속도 향상을 위해 Hessian matrix (다차원에서의 2차 근사) 등을 이용.) 이후 contrast와 offset을 조정해 Black&White로 변환. Domain 분석을 통해 크기가 noise 이하이면 주변(surrounding) color로 변환.
이미지를 분석하는 프로그래밍에 C++의 class 기능이 유용해서 C++와 LabVIEW (그래프 및 데이터 입력/출력 컨트롤에 용이) 를 DLL (Win32 Dynamic-Link Library) 를 연계하여 프로그램을 만들었음. ▼ 아래는 사용한 C++ 코드 예시. (너무 길어져서 ImageProcess.cpp 파일 중 일부만.)
```[.linenums.scrollable]
#include <iostream> // input, output stream. (cin << ; cout <<;)
#include <fstream> // file input, output stream. (fout <<; f.seekg();)
#include <cmath> // c math. (sqrt)
#include <cstdlib> // c standard lib.
using namespace std;
// class Vector_d2D (Vector double 2-Dimensional) : start
class Vector_d2D{
public:
double x;
double y;
public:
// Constructor
Vector_d2D(double x0=0, double y0=0){ x = x0; y = y0; };
~Vector_d2D(){ x = 0; y = 0; };
// Operators
Vector_d2D& Set(const double x0, const double y0){ x = x0; y = y0; return *this; };
bool operator==(const Vector_d2D& v) { return ( x==v.x && y==v.y ); };
bool operator!=(const Vector_d2D& v) { return ( x!=v.x || y!=v.y ); };
Vector_d2D& operator= (const Vector_d2D v) { x = v.x; y = v.y; return *this; };
Vector_d2D& operator+=(const Vector_d2D v) { x += v.x; y += v.y; return *this; };
Vector_d2D& operator-=(const Vector_d2D v) { x -= v.x; y -= v.y; return *this; };
Vector_d2D& operator*=(const double rH) { x *= rH; y *= rH; return *this; };
Vector_d2D& operator/=(const double rH) { x /= rH; y /= rH; return *this; };
Vector_d2D operator-()const{ Vector_d2D res; res.x=-x; res.y=-y; return res; };
Vector_d2D operator+(const Vector_d2D rH) const{ Vector_d2D res; res.x=x+rH.x; res.y=y+rH.y; return res; };
Vector_d2D operator-(const Vector_d2D rH) const{ Vector_d2D res; res.x=x-rH.x; res.y=y-rH.y; return res; };
// inner product
double operator*(const Vector_d2D rH)const{ double res; res=x*rH.x + y*rH.y; return res; };
Vector_d2D operator*(const double rH)const{ Vector_d2D res; res.x=x*rH; res.y=y*rH; return res; };
friend Vector_d2D operator*(const double lH, const Vector_d2D rH)
{ Vector_d2D res; res.x=lH*rH.x; res.y=lH*rH.y; return res; };
Vector_d2D operator/(const double rH)const{ Vector_d2D res; res.x=x/rH; res.y=y/rH; return res; };
const Vector_d2D Crossz(const double z){ Vector_d2D res; res.x=y*z; res.y=-x*z; return res; };
const double norm(){ return sqrt(x*x + y*y); };
friend ostream& operator<<(ostream& os, const Vector_d2D& v){ os<<v.x<<"\t"<<v.y; return os; };
};
double norm(const Vector_d2D v){
return sqrt(v.x*v.x + v.y*v.y);
}
double norm(const double x, const double y){
return sqrt(x*x + y*y);
}
double Cross(const Vector_d2D v1, const Vector_d2D v2){
double res;
res = v1.x*v2.y - v1.y*v2.x;
return res;
}
Vector_d2D Cross(const double z, const Vector_d2D v){
Vector_d2D res;
res.x = -z*v.y;
res.y = z*v.x;
return res;
}
Vector_d2D Cross(const Vector_d2D v, const double z){
Vector_d2D res;
res.x = v.y*z;
res.y = -v.x*z;
return res;
}
// class Vector_d2D (Vector double 2-Dimensional) : end
// class Vector_i2D (Vector int 2-Dimensional) : start
class Vector_i2D{
public:
int x;
int y;
public:
// Constructor
Vector_i2D(int x0=0, int y0=0){ x = x0; y = y0; };
~Vector_i2D(){ x = 0; y = 0; };
// Operators
Vector_i2D& Set(const int x0, const int y0){ x = x0; y = y0; return *this; };
bool operator==(const Vector_i2D v) { return ( x==v.x && y==v.y ); };
bool operator!=(const Vector_i2D v) { return ( x!=v.x || y!=v.y ); };
Vector_i2D& operator= (const Vector_i2D v) { x = v.x; y = v.y; return *this; };
Vector_i2D& operator+=(const Vector_i2D v) { x += v.x; y += v.y; return *this; };
Vector_i2D& operator-=(const Vector_i2D v) { x -= v.x; y -= v.y; return *this; };
Vector_i2D& operator*=(const int rH) { x *= rH; y *= rH; return *this; };
Vector_i2D& operator/=(const int rH) { x /= rH; y /= rH; return *this; };
Vector_i2D operator-()const{ Vector_i2D res; res.x=-x; res.y=-y; return res; };
Vector_i2D operator+(const Vector_i2D rH)const{ Vector_i2D res; res.x=x+rH.x; res.y=y+rH.y; return res; };
Vector_i2D operator-(const Vector_i2D rH)const{ Vector_i2D res; res.x=x-rH.x; res.y=y-rH.y; return res; };
// inner product
int operator*(const Vector_i2D rH)const{ int res; res=x*rH.x + y*rH.y; return res; };
Vector_i2D operator*(const int rH)const{ Vector_i2D res; res.x=x*rH; res.y=y*rH; return res; };
friend Vector_i2D operator*(const int lH, const Vector_i2D rH)
{ Vector_i2D res; res.x=lH*rH.x; res.y=lH*rH.y; return res; };
Vector_i2D operator/(const int rH)const{ Vector_i2D res; res.x=x/rH; res.y=y/rH; return res; };
const Vector_i2D Crossz(const int z){ Vector_i2D res; res.x=y*z; res.y=-x*z; return res; };
const double norm(){ return sqrt(x*x + y*y); };
friend ostream& operator<<(ostream& os, const Vector_i2D& v){ os<<v.x<<"\t"<<v.y; return os; };
};
double norm(const Vector_i2D v){
return sqrt(v.x*v.x + v.y*v.y);
}
double norm(const int x, const int y){
return sqrt(x*x + y*y);
}
int Cross(const Vector_i2D v1, const Vector_i2D v2){
int res;
res = v1.x*v2.y - v1.y*v2.x;
return res;
}
Vector_i2D Cross(const int z, const Vector_i2D v){
Vector_i2D res;
res.x = -z*v.y;
res.y = z*v.x;
return res;
}
Vector_i2D Cross(const Vector_i2D v, const int z){
Vector_i2D res;
res.x = v.y*z;
res.y = -v.x*z;
return res;
}
// class Vector_i2D (Vector int 2-Dimensional) : end
// class Image : start
class Image{
public:
int right;
int bottom;
unsigned char *img;
int *domain;
int N_domain; // number of domains
public:
// Constructors
Image();
Image(unsigned char* im, int ri, int bo);
// Destructor
~Image();
// Operators
void Domain();
int FillDomain(int x, int y, unsigned char color);
Vector_d2D CenterofMass(int x, int y);
double Length_to_Edge(double x0, double y0, double dx, double dy);
void RemoveNoise(int noise_size);
};
Image::Image(){
right = 1;
bottom = 1;
img = new unsigned char[1];
img[0] = 0;
domain = new int[1];
domain[0] = 0;
}
Image::Image(unsigned char *im, int ri, int bo){
int i, img_max=ri*bo;
right = ri;
bottom = bo;
img = new unsigned char[img_max];
domain = new int[img_max];
for(i=0;i<img_max;i++){
img[i] = im[i];
domain[i] = i;
}
//this->Domain();
}
Image::~Image(){
delete[] img;
delete[] domain;
}
void Image::Domain(){
int i, j, w, a, s; // w
// a s
int p, img_max=right*bottom;
int *ref;
ref = new int[img_max];
for(i=0;i<img_max;i++){
ref[i] = i;
}
// 1st row: start
domain[0] = p = 0;
for(i=1;i<right;i++){
if( img[i]==img[i-1] ){
domain[i]=p;
}
else{
p++;
domain[i]=p;
}
}
// 1st row: end
// 2nd~ row: start
for(j=1;j<bottom;j++){
w = (j-1)*right;
s = j*right;
if( img[w]==img[s] ){
domain[s]=ref[domain[w]];
}
else{
p++;
domain[s]=p;
}
for(i=1;i<right;i++){
w = i + (j-1)*right;
a = i-1 + j*right;
s = i + j*right;
if( img[a]==img[s] ){
if( img[w]==img[s] ){
if( ref[domain[a]]<ref[domain[w]] ){ // following minimum domain#
domain[s] = ref[domain[w]] = ref[domain[a]];
}
else{
domain[s] = ref[domain[a]] = ref[domain[w]];
}
}
else{
domain[s]=ref[domain[a]];
}
}
else if( img[w]==img[s] ){
domain[s]=ref[domain[w]];
}
else{
p++;
domain[s]=p;
}
}
}
// 2nd~ row: end
bool *exist;
int n;
exist = new bool[p+1];
for(i=0;i<=p;i++){
exist[i] = false;
}
for(i=0;i<=p;i++){
ref[i]=ref[ref[i]];
exist[ref[i]]=true;
}
n = 0;
for(i=0;i<=p;i++){
if( exist[i]==true ) n++;
}
N_domain = n;
for(i=0;i<=p;i++){
n = 0;
for(j=ref[i];j>=0;j--){
if(exist[j]==false) n++;
}
ref[i]-= n;
}
for(i=0;i<img_max;i++){
domain[i]=ref[domain[i]];
}
delete[] ref;
delete[] exist;
}// void Image::Domain
int Image::FillDomain(int x, int y, unsigned char color){
int i, img_max=right*bottom, a, p, n=0;
if( x>=0 && x<right && y>=0 && y<bottom ){
a = x + y*right;
if(img[a] != color){
p = domain[a];
for(i=0;i<img_max;i++){
if( domain[i]==p ){ img[i]=color; n++; }
}
}
}
return n;
}// int Image::FillDomain
Vector_d2D Image::CenterofMass(int x, int y){
int i, j, a, p, n=0;
Vector_d2D res(0,0), k;
if( x>=0 && x<right && y>=0 && y<bottom ){
a = x + y*right;
p = domain[a];
for(j=0;j<bottom;j++){
for(i=0;i<right;i++){
a = i+j*right;
if( domain[a]==p ){
res += k.Set(i,j); n++;
}
}
}
}
res /= n;
return res;
}// Vector_d2D Image::CenterofMass
double Image::Length_to_Edge(double x0, double y0, double dx, double dy){ // Length from (x0,y0) to the direction (dx,dy)
int x, y, i, p, k, kmax=1000;
double z;
Vector_d2D r(x0,y0), dr(dx,dy);
dr /= norm(dr);
x = int(x0); y = int(y0);
if( x>=0 && x<right && y>=0 && y<bottom ){
i = x + y*right;
p = domain[i];
for(k=0;k<kmax;k++){
r += dr;
x = int(r.x); y = int(r.y);
if( x>=0 && x<right && y>=0 && y<bottom){
i = x + y*right;
if( domain[i]!=p ){
for(r-=dr/2.,z=4.;z<11;z*=2.){
i = int(r.x) + int(r.y)*right;
if( domain[i]!=p ){r-=dr/z;}
else {r+=dr/z;}
}
break;
}
}
else{
if( r.x<0 ){
r.y+=-r.x/dx*dy; r.x=0;}
else if( r.x>right ){
r.y+=(right-r.x)/dx*dy; r.x=right;}
if( r.y<0 ){
r.x+=-r.y/dy*dx; r.y=0;}
else if( r.y>bottom ){
r.x+=(bottom-r.y)/dy*dx; r.y=bottom;}
break;
}
}
}
return norm(r.x-x0,r.y-y0);
}// double Image::Length_to_Edge
void Image::RemoveNoise(int noise_size){ // only for Black and White images, otherwise it will malfunction.
int *size_Domain; // size of domain (number of points on domain)
int img_max=right*bottom, i, j, p;
size_Domain = new int[N_domain];
for(i=0;i<N_domain;i++){
size_Domain[i] = 0;
}
for(i=0;i<img_max;i++){
size_Domain[domain[i]]++;
}
// (0,0)-point: start
i = 0; j = 0;
p = i + j*right;
if(size_Domain[domain[p]]<=noise_size){
do{
if( i==0 ){
i=j+1; j=0;
}
else{
j++; i--;
}
p = i + j*right;
}while( p<img_max && size_Domain[domain[p]]<=noise_size );
img[0] = img[p];
}
// (0,0)-point: end
// 1st row: start
j = 0;
for(i=1;i<right;i++){
p = i;
if(size_Domain[domain[p]]<=noise_size){
img[p]=img[p-1];
}
}
// 1st row: end
// 2nd~ row: start
for(j=1;j<bottom;j++){
p = j*right;
if(size_Domain[domain[p]]<=noise_size){
img[p]=img[p-right];
}
for(i=1;i<right;i++){
p = i + j*right;
if(size_Domain[domain[p]]<=noise_size){
img[p]=img[p-1];
}
}
}
delete[] size_Domain;
// 2nd~ row: end
}// void Image::RemoveNoise
// class Image : end
int Fill_Domain(unsigned char* image, int right, int bottom, // image info
int x, int y, unsigned char color) // FillDomain(x,y) by color
{
int area, i, img_max=right*bottom;
Image img(image, right, bottom);
img.Domain();
area = img.FillDomain(x,y,color);
for(i=0;i<img_max;i++){ image[i] = img.img[i]; }
return area;
}
void Center_of_Mass(unsigned char* image, int right, int bottom, // image info
int x, int y, double* x_cm, double* y_cm) // Domain(x,y) with Center(x_cm, y_cm)
{
Vector_d2D r_cm;
Image img(image, right, bottom);
img.Domain();
r_cm = img.CenterofMass(x,y);
*x_cm = r_cm.x + 0.5;
*y_cm = r_cm.y + 0.5;
}
double Length_to_the_Edge(unsigned char* image, int right, int bottom, // image info
double x0, double y0, double dx, double dy) // Length of Domain from (x0,y0) to the direction (dx,dy)
{
double l;
Image img(image, right, bottom);
img.Domain();
l = img.Length_to_Edge(x0, y0, dx, dy);
return l;
}
void Noise_Remove(unsigned char* image, int right, int bottom, // image info
int noise_size)
{
int i, img_max=right*bottom;
Image img(image, right, bottom);
img.Domain();
img.RemoveNoise(noise_size);
for(i=0;i<img_max;i++){ image[i] = img.img[i]; }
}
```/
데이터를 분석하면서 잘못된 것(프로그램의 오류나 측정시의 실수 등)이 없는지 여러가지로 확인을 해봐야 하기 때문에, 측정할 때 기록된 여러가지 데이터들(시간, 자기장값, 이미지)을 한눈에 보기위해 만든 프로그램 .
## RRA
Truncated many-body dynamics of interacting bosons: A variational principle with error monitoring, Kang-Soo Lee and Uwe R. Fischer, arXiv:1301.2199 [cond-mat.quant-gas] (2013).
Long-Range Domain Wall Tension in Pt/Co/Pt Films with Perpendicular Magnetic Anisotropy, Kyoung-Woong Moon, Jae-Chul Lee, Soong-Geun Je, Kang-Soo Lee, Kyung-Ho Shin, and Sug-Bong Choe, Appl. Phys. Express 4, 043004 (2011).
Fractal Dimension of Magnetic Domain Walls in CoFe/Pt Multilayers, Kang-Soo Lee, Dong-Hyun Kim, Sug-Bong Choe, J. Magnetics 15(3), 99-102 (2010).
Roughness Exponent of Domain Interface in CoFe/Pt Multilayer Film, Kang-soo Lee, Chang-won Lee, Young-jin Cho, Sunae Seo, Dong-Hyun Kim, Sug-Bong Choe, IEEE Trans. Magn. 45(6), 2548 (2009).
Interdimensional Universality of Dynamic Interfaces, Kab-Jin Kim, Jae-Chul Lee, Sung-Min Ahn, Kang-Soo Lee, Chang-Won Lee, Young Jin Cho, Sunae Seo, Kyung-Ho Shin, Sug-Bong Choe & Hyun-Woo Lee, Nature 458, 740-742 (2009).