2013년 12월 31일 화요일

java 정규식에서 많이 사용하는 항목들(개인적으로...)

블로그에 글을 기재하기 시작한지 얼마 되지 않지만, 처음과는 다른각도에서 접근하게 되는것 같습니다.

개인적인 정리가 필요해서 시작했고, 지금도 그것이 중요한 이유이지만, 간혹 드물게 접속해서 보시는 분들이 있는 것 같습니다.
제가 검색등을 통해서 도움을 받았듯, 혹시라도 조금이라도 도움이 될 수 있다면 좋겠다는 생각을 아주 가끔(^^)은 하게 되었습니다.
조금은 생각을 정리해서 기재 해야 겠다는 생각이 들기 시작하는 중입니다. 

정규식은 Java 아닌 다른 곳에서도 많이 사용되는 표현 형식입니다.   
문장중의 특정 단어를 찾거나, 일부분을 변경하는 것 등에서도 많이 사용됩니다.  Linux 등의 환경에서는 일반정규식은 말할 것도 없고, 정규식이라 말하긴 어려워도 검색(파일 및 내용)을 위한 많은 명령어가 제공되기도 합니다. (awk, sed 등의 명령어는 정말 강력합니다. find 와 함께 쓰는 xargs 명령어도 파일 내용을 찾는 데에는 더할 나위 없이 강력하고요 ... ), Unix 에서의 egrep 는 정규식 사용할 수 있게 도와 줍니다.

java 에서는 java.util.regex package 정규식 관련한 내용이 담겨 있습니다.
처음 블로그 할 때 기록했던 수치 확인 내용을 다시 한번 기록해 보겠습니다.
[+\\-]?([\\d]+([.][\\d]*)?|[.][\\d]+)([eE][+\\-]?[\\d]+)? 
숫자형으로 넘어온 데이터가 DB 에서 문자열로 가져올 때, 가끔 형변환된 문자열이 다음과 같이 출력되어 나타날 수가 있습니다. -.7265  -1.2E+12 등의 문자열에서 -.으로 -0.7.. 형식이 아닌 데이터가 나타날 때도 수치형 데이터 라고 인식 시켜 주려면 +,- 기호가 사용가능한 
기호라는 표기가 필요합니다. 그게 [+\\-]? 기호 입니다. \\기호는 - 기호가 [] 블럭에서 이미 사용되고 있는 문자열이라서 의미 자체로 '-' 다 라는 것을 인식시켜 주기 위한 부분 입니다.  '?' 는 있을 수도 있고 없을 수도 있다라는 의미로 받아 들이시면 됩니다. 
정규식 API 를 참조하시면서 보시면 쉽게 확인 하실 수 있을 것 같습니다.  참고로 [\\d\\w]+ 라는 의미는 숫자 혹은 문자라는 의미 입니다. 

그럼 위의 내용을 어떻게 사용할까요 ?
위의 내용은 굳이 Patten 객체까지 사용하지 않고 String 객체에서 직접 사용하 실 수 있습니다.   아래와 같은 간단한 코드 입니다.
public static boolean isNumberFormatStr(String str) {
if ( str == null ){
return false;
}
return str.matches("[+\\-]?([\\d]+([.][\\d]*)?|[.][\\d]+)([eE][+\\-]?[\\d]+)?");
}

다음의 코드를 살펴보겠습니다.

public static void testRegex() {
StringBuffer sb = new StringBuffer();
sb.append("<a href=\"www.google.co.kr\" \r\n target=\"_black\"> TEST </a><a onClick=www.naver.com> test2\r\n</a>");

System.out.println( sb );

Pattern pObj = Pattern.compile("(<[^>]*>)",Pattern.MULTILINE);
Matcher mObj = pObj.matcher(sb);
while ( mObj.find()) {
System.out.println (mObj.groupCount() + " : " + mObj.group(0));
}

pObj = Pattern.compile("(<(/)?([\\w]+)([\\s]+[^>]*)?>)",Pattern.MULTILINE);
mObj = pObj.matcher(sb);
while ( mObj.find()) {
int cnt = mObj.groupCount();
for ( int i = 1; i <= cnt; i++ ) {
System.out.print ("\t\t" + i + " : " +  mObj.group(i));
}
}
}

html tag 를 확인하기 위한 코드 입니다.
처음에는 단순히 tag 만을 확인 하기 위해서 '<'가 있고 '>' 이 아닌 모든것인 있을 수도 있고 '>' 이 있는 유형을 찾도록 했습니다. <[^>]*> 가 그런 의미 입니다.
하지만 이렇게 되면 tag 안의 내용을 다시 확인하여야 합니다. 종료 tag인지 아닌지 이름이 뭔지 속성은 있는지 등등을요 그래서 (/)? 으로 종료 태그 여부를 확인합니다. 다시 태그 이름은 '<'다음에 붙어 나오는 단어이기 때문에 [\\w]+로 확인을 합니다.
그 다음은 속성에 해당하는 것이 있다면  확인하는 구문이 들어 있습니다.
()기호와 []기호 + ? * {1,5} 등의 기호로 찾아야할 영역을 지정할 수 있습니다.

아래의 내용을 한번 살펴 보겠습니다.
"ABCDEFGABCDEFGABCDEFG ABCDEFGABCDEFGABCDEFG" 라는 문장이 있습니다.
이 문장에서 EFG 가 포함된 영역과 그 외 영역을 찾고자 한다고 가정해 보겠습니다.
일반적으로 생각해 볼 수 있는 정규식은 (.+)(EFG)라는 문장을 생각해 볼 수 있습니다.
무엇이 되었던 EFG 앞의 내용은 다 그리고 EFG 를 찾아라 하는 구성입니다.
이것을 실행하면 ABCDEFGABCDEFGABCDEFG ABCDEFGABCDEFGABCD EFG 의 두 영역을 찾아 오게 됩니다.  원하는 내용이었다면 다행이지만 의도가 ABCD EFG 의 반복을 원하였다면 위의 정규식에서 (.+?)(EFG)의 구성으로 변경해 주시면 됩니다. +뒤의 ? 의 의미는 EFG 가 포함된 영역을 최소한의 수준에서 찾아 주게 됩니다.
API의 Greedy quantifiers, Reluctant quantifies 의 내용을 확인해 보시면 됩니다.
문장 전체에서 특정 영역을 찾아 오는 Parser를 구성할 때 위의 내용이 빈번하게 사용될 수 있습니다.(제 개인적으론 그렇습니다.)
코드로 구성해 보면 다음과 같습니다.

String str = "ABCDEFGABCDEFGABCDEFG ABCDEFGABCDEFGABCDEFG";
System.out.println ("\n\n" + str );
Pattern pObj = Pattern.compile("(.+?)(efg)",Pattern.CASE_INSENSITIVE);
Matcher mObj = pObj.matcher(str);
while ( mObj.find() ) {
System.out.println (mObj.group(1) + " : " + mObj.group(2) );
}


정규식은 mail 형식 html tag 수치확인 등의 단순한 확인 및 처리에서 부터 좀더 복잡한 파일내용의 분석 등 다양한 영역에서 사용할 수 있습니다.  정규식의 강력함을 접하면서 다 알지는 못하지만, 혹시 정규식을 많이 접하지 않았던 분들에게 조금이라도 도움이 될 까 해서 기록해 보았습니다.






2013년 12월 11일 수요일

R Graph를 이용한 금융계산

R은 통계 툴입니다.
주어진 데이터를 분석 추정하는데 사용하는 강력한 툴입니다.

그렇지만 생활에서 많이 접하는 정기예금, 단리, 복리 수익 및 대출등의 금융계산에 이용할 수도 있습니다.

R의 그래프 기능을 이용하여 아주 간단한 R 프로그램을 구성해 보겠습니다.

먼저 간단한 예로 일정금액을 일정기간동안 예치해 놓는 경우를 가정해 보겠습니다.  기간동안 이자가 발생하는데 첫번째는 금액의 이자가 단순히 기간에 비례하여 증가하는 경우(경우1)와 금액의 이자가 합산되어 증가되는 경우(경우2)를 가정해 보겠습니다.

amount 는 원금입니다.
period는 연단위 기간입니다.
rate는 연단위 이자 입니다.   이자는 월단위로 발생하는 것으로 가정하겠습니다.

경우1)
fnSimple <- function(amount,period,rate) { amount*period*rate + amount }

경우2)
fnCompound <- function(amount,period,rate) { amount*(1+rate/12)^(period*12) }

이제 조건을 정해 보겠습니다.
두 영역의 차이를 크게 하기 위해 기간과 이율을 좀 높혀서 확인해 보겠습니다.
amt 는 원금 입니다. (원 단위 만원단위는 편하게 생각하셔도 됩니다.)
px 는 기간 입니다. 1년에서 100년 까지 설정한 것입니다.
rt 는 이율 입니다.
amt <- 1000;  px <- c(1:100);  rt <- 0.12;
psy <- fnSimple(amt,px,rt)
pcy <- fnCompound(amt,px,rt)
plot(px,pcy,type='o',col='blue',tck=1);
points(px,psy,type='o',col='red');
위의 내용을 보시면 다음과 같습니다.


그래서 y축의 값을 로그로 환산하여 처리해 보았습니다.
plot(px,pcy,type='o',col='blue',log='y',tck=1);
points(px,psy,type='o',col='red');



이제 매월 일정액을 납부하여 만기일에 이자와 원금을 받는 경우를 가정해 보겠습니다.  세금납부는 제외하겠습니다.
먼저 단리의 경우입니다.  단리는 납부원금에 대해서만 발생하는데 매월 일정액이 납부되기때문에 일정한 값만큼 증가하게 됩니다. 예를 들어 10만원에 월 1% 이자 (연 12%)라면. 첫달에 1000원 둘째달에 2000원(200000×0.01) , 셋째달은 3000원이 발생합니다.
등차수열의 합을 구하는 방식을 연상해 보시면 됩니다.  (초항+마지막항)×기간/2 가 이자금액의 합이됩니다.
fnMSimple <- function(amount,period,rate) { rateM <- rate/12; (amount*rateM+amount*rateM*period*12)*period*12/2 + amount*period*12 }

복리는 원금에 이자가 합산되어 이자가 발생하는 방식입니다.
첫달은 원금*연이률/12+원금 입니다. 식으로 표현하면 amount*(1+rate/12) 입니다.
둘째달은 (첫째달 금액 + amount)*(1+rate/12) 입니다. 결국 등비수열의 합 공식으로 유도됩니다.
fnMCompound <- function(amount,period,rate) { rateM <- rate/12; amount*(1+rateM)*((1+rateM)^(period*12)-1)/(rateM) }

100년의 기간은 너무 많은 차이를 나타내기 때문에 이곳에서 20년 정도로 기간을 단축해 보겠습니다.   그리고 금액도 10 정도를 매달 저금하는 것으로 해 보겠습니다.

amt <- 10; px <- c(1:20);
psy <- fnMSimple(amt,px,rt);
pcy <- fnMCompound(amt,px,rt);
plot(px,pcy,type='o',col='blue',tck=1);
points(px,psy,type='o',col='red');
points(px,px*amt*12,type='o');




10년 정도의 기간에서는 큰 차이가 발생하지 않지만 20년 정도에는 2배 가량 차이가 나타나고 있습니다.


대출의 경우는 대개 2가지 방식으로 진행 될 수 있습니다.
매달 원금에서 동일한 금액을 상환하는 방식(원금균등상환)과 매달 동일한 금액을 상환하는 원리금균등상환 방식을 생각해 볼 수 있습니다.
전자(원금균등)의 방식이 총액에서는 이익이지만 초기에 상환해야할 금액이 커질 수 있습니다.
아래는 함수 입니다. 상환이기 때문에 기간은 월단위로 설정해 보겠습니다.

fnSLoan <- function(amount,period,rate) {
    result <- c(1:period);
    mAmt <- amount/period;
    mRate <- rate/12;
    for ( i in c(1:period) ) {
        result[i] <- amount*mRate+mAmt;
        amount <- amount-mAmt;
    }
    result;
}

fnCCalc <- function(amount,period,rate) { (amount*rate/12*(1+rate/12)^period)/((1+rate/12)^period-1) }

fnCRemain <- function(amount,period,rate,mAmt) {
    result <- c(1:period);
    mRate <- rate/12;
    for ( i in c(1:period) ) {
        mAmtR <- mAmt-amount*mRate;
        amount <- amount-mAmtR;
        result[i] <- amount
    }
    result;
}


2013년 12월 9일 월요일

R 설치 및 간략한 소개

R은 프로젝트에서 사용해 보고는 바로 매료되었던 통계 관련툴입니다.
무엇보다도 open source로 개발되어 배포되는 무료 툴입니다.  이런 모듈을 사용하다 보면 외국의 IT 환경이 부럽기도 하고, 부끄러움도 느끼게 되는것 같습니다.

통계 전반을 사용하지는 못하였고, 그 기능 중 일부분인 Graph 기능을 중심으로 사용해 보았습니다.

Windows에는 설치 파일을 실행하는 것으로 거의 모든 과정이 종료 되기 때문에 간단하게 설치할 수 있습니다.

다음의 사이트를 참조하여 파일과 내용을 확인하실 수 있습니다.
http://www.r-project.org/

windows 환경에서는 설치와 동시에 메뉴에서 마우스 클릭으로 접근할 수 있습니다.(설치시 기본으로 하셨다면)
console 에서 사용하고 싶으시다면, path를 잡아 주셔야 합니다.(그래야 사용하기 편합니다.)

cmd 창에서 R 이라고 입력하고 enter를 클릭하면(Path가 잡혀 있을 경우) > 표시가 표현되면서 명령어 입력 창이 나타 납니다.

종료하려면 quit()를 입력하고 enter를 클릭하시면 됩니다.

사용법은 매뉴얼을 참조하시면 되지만, 이 글에서는 그래프중 perspective Plots 을 예제로 기록하겠습니다.

> ?persp 를 입력하고 enter를 클릭하면 새로운 webpage가 구동하면서 상세 내용이 뜨게 됩니다. ?(keyword)로 해당 내용을 확인하실 수 있습니다.
내용중 예제에 나와 있는 부분을 복사해 보았습니다.

x <- seq(-10, 10, length= 30)
y <- x
f <- function(x, y) { r <- sqrt(x^2+y^2); 10 * sin(r)/r }
z <- outer(x, y, f)
z[is.na(z)] <- 1
op <- par(bg = "white")
persp(x, y, z, theta = 30, phi = 30, expand = 0.5, col = "lightblue")
persp(x, y, z, theta = 30, phi = 30, expand = 0.5, col = "lightblue",
      ltheta = 120, shade = 0.75, ticktype = "detailed",
      xlab = "X", ylab = "Y", zlab = "Sinc( r )"
) -> res
round(res, 3)

아래는 그 결과 이미지 입니다.
<- 또는 -> 등은 방향을 가진 할당 (다른 언어의 '=' 해당) 기능을 가지고 있습니다.
변수 선언이 형없이 선언되고 있습니다.  (어떤 면에서는 javascript 와 유사한 문법 구조를 가지고 있습니다.)

그래프에서 대표적인 것은 plot 이지만, 위의 그래프는 직접 구현할 때 많은 노력이 들어가야할 graph 유형까지 지원하고 있는 것을 보여 주는 예인것 같아서 예시해 보았습니다.



2013년 11월 27일 수요일

GWT 설치하기

WEB 개발환경에서 JAVA 를 알고 있는 개발자라면 한번 고려 해 볼만한 UI 툴이 GWT 인것
같습니다.

특화된 디자인이 꼭 필요한 곳이 아니라면, application 같은 Web Page 구성이 필요한 사이트에서 사용하면 좋은 툴이라고 생각합니다.

http://www.gwtproject.org 사이트에 접속해서 GWT SDK를 다운받으면(현재 2.5.1) 사용할 준비가 끝난것과 같습니다.
IDE로 이클립스를 사용하신다면 Eclipse plugin 을 이용해서 개발환경을 구축하실 수도 있습니다.


개인적으로 console로 관리하는게 더 편하기 때문에 cmd 창에서 프로젝트를 생성합니다.
먼저 압축을 푼 디렉토리로 이동합니다.
이동후 확인해 보시면 webAppCreator 이름의 파일이 있습니다. 이 실행파일로 프로젝트를
생성합니다.
webAppCreator 의 기능중 프로젝트 생성에 꼭 필요한 것은 -out 이라는 option 과 moduleName 만 정확히 기록하면 가능합니다.
예를 들어 C:\\GwtTester 라는 디렉토리에 test.abc.GwtTester 라는 모듈이름으로
프로젝트를 생성하고 싶다면 다음과 같이 입력합니다.
webAppCreator -out C:\\GwtTester  test.abc.GwtTester
-out 다음의 경로는 상대경로도 가능합니다.

위와 같이 하여 프로젝트가 생성되었다면, 해당 디렉토리로 이동해 생성된 파일을 확인해 봅니다.
정상적으로 진행되었다면 build.xml 파일이 생성되어 있어야 합니다.
ant 가 설치되어 있다면 바로 ant 라고만 명령을 수행하면 컴파일이 수행됩니다.
이후 and devmode 라고 입력하면 개발된 내용을 확인해 보실 수 있습니다.

개발자 입장에서 보면
GWT로 개발하는 과정은 극단적으로 자바스크립트를 단 한줄도 코딩하지 않고 java 만으로 개발할 수 있습니다.

어떻게 보면 전통적인 web page 구성이 아니라 application 개발과 유사한 모습을 보이기도 합니다.

Page 가 전체 refresh 되지 않고 구성되기 위해서는 AJAX 방식과 DHTML 의 동적인 구성이 가능해야 하는데 gwt는 그것을 가능하게 해 주는 툴입니다.
더 더욱 강력한 기능은 AJAX의 통신을 RPC라는 방식으로 지원해 주고 있고 RPC 방식의 통식 객체는 java serialize 가 가능한 객체와 java.util 의 collection 객체들을 지원해 주고 있습니다.

생성된 소스 디렉토리에는 client, server, shared 가 directory로 생성되어 있습니다.
client 의 UI 소스는 서비스 웹서버에 배포할 때는 필요하지 않습니다. interface인 ..Service.java, ...ServiceAsync.java 와 server디렉토리의 ...ServiceImpl.java 클래스 가 일반적으로 배포시 필요한 파일들 입니다.

새롭게 설치하기 위해 오랜만에 GWT 사이트를 접속해 보았습니다.
디자인도 많이 변했고(너무 오랜만에 들어가 보아서요 ...) 무엇보다 Article중 MVP라는 항목이 눈길을 끌었습니다.
잠깐 살펴보았더니 GWT 개발할 때, 어떻게 할까 망설이던 내용을 잘 정리해 놓은듯 했습니다.   굳이 여러명이 개발하는 환경이 아니라도 하나의 화면에 다양한 기능이 집약되어 있다면 서버와의 통신(RPC 담당객체)을 관리하는 영역과 각 UI의 이벤트 핸들링, 화면 리셋과 다시 로딩하는 문제등을 한번 생각하게 만드는 글로 생각되었습니다.

예전 web이 page 에서 get 혹은 post 호출로 다른 페이지를 호출하고 화면이 갱신되는 구조에서는 사용자 요청을 담당하는 Servlet 이 Controller 역활을 담당하고 command 형태의 명령어에 따라 요청을 처리한 후 그 결과에 따라 페이지를 분할 하여 로딩하도록 하는 형태를 많이 사용하였습니다.(현재도 사용할 수 있을 것 같습니다.) 이때 화면에 필요한 데이터는 프레임웤에 따라 차이가 있으나, 대게 setAttribute의 메소드와 scope 영역을 지정하여 forward 하는 방식으로 모델 객체를 넘겨 화면단에서 처리하도록 하는 방식을 많이 사용하였습니다.

AJAX 환경에서는 단일 Page가 application 처럼 비대해졌고(다양한 기능을 담당해야 하고요) 이 환경에서는  Client 단의 event 처리 및  UI 갱신 서버와의 통신이 한 페이지 내에서 빈번히 발생할 수 밖에 없습니다.   Server 에 있는 Controller 가 그 역활을 담당하기에는 한계가 있을 수 밖에 없는 구조입니다.( 서버의 controller 가 의미가 없다는 것이 아니라 AJAX 페이지 내의 업무를 담당하는 구조가 더 많이 필요하다는 의미입니다.)
gwt mvp 소스에서 RootPanel ( html의 document.body 에 해당 한다고 보셔도 될 듯 합니다.) 을 Presenter에서 갱신하면서 각 Presenter 에서 객체로 보유한 viewer 의 내용을 표현하고 각 Presenter는 처음 생성된 rpc객체(통신객체)를 공유하고, event를 보유한 viewer 에 bind 하고 있습니다.
gwt 개발에는 문서에서 설명하듯 접근방법이 mvc 형태의 접근보다, mvp의 접근 방식이 더 타당해 보입니다.

GWT를 설치하러 들어왔다 잠깐 문서를 보고 검토한 것이라, 생각에 오류가 있을 수도 있습니다.  하지만, 잠깐 보았어도 상당히 매력있는 방식이라는 생각이 들었습니다.

2010 년도에 게시된 내용을 지금 보았다는 것도 많은 자극이 되고요, 설치하다 설치와는 별로 관계 없는 내용이 되어버렸습니다.  조금이나마 도움이 되실까 해서 장황하게 적어 보았습니다.






2013년 11월 19일 화요일

아파치 ant 오픈소스 설치를 시작합니다

개발 환경을 리룩스에 구성하려 하다가, 결국 윈도우에 설치하게 되었습니다.
설치후 매번 다시 내용을 찾아야 하는 번거로움에 설치 내용을 기록하게 되었습니다.

먼저 ant 입니다.
java로 개발 하는 프로젝트에서 ant는 강력한 컴파일 툴로 사용할 수 있습니다.
물론 이클립스에는 기본적으로 포함되어 있기 때문에 별도로 설치하지 않아도 되지만,
콘솔로 프로젝트를 관리하는 경우도 종종 있기 때문에 ant 를 설치합니다.
개인적으로는 직접 하나씩 관리하는 방식을 더 선호하기 때문이기도 합니다.

매번 설치시에만 살펴 보기 때문에 기억하기가 쉽지 않아 많이 쓰는 기능만 추려서 구성해 보려 합니다. ( ant 기능은 manual 을 찾아 보면 정말로 많습니다. )

먼저 사이트 입니다. 다들 아시겠지만  http://ant.apache.org/ 에서 다운 받습니다.
다음은 zip 파일을 구동할 디렉토리에 압축을 해제합니다.

ANT_HOME 패스를 설정합니다.(패스를 전역에 등록해 놓지 않아도 사용할 수 있습니다.
다만 상당히 불편합니다.)

cmd 창에서 ant -version 을 입력하여 정상적으로 출력되면 설치가 된것으로 생각할 수 있습니다.

한글을 주석 등에서 사용하고 싶다면
encoding 항목을 설정하여야만 합니다.
예를 들어 xml 파일 형식이 ansi 라면 encoding="EUC-KR" 을 해 주셔야 합니다.
utf-8 을 사용하신 다면 파일형식도 uft-8로 저장되도록 해야 한글을 사용할 수 있습니다.

그래서 저는  아래와 같은 선언을 합니다.( 하지 않으셔도 됩니다. )

<?xml version="1.0" encoding="EUC-KR" ?>

project가 가장 큰 범주입니다. 이름과 처음시작할 기본 업무 위치 정보 등을 설정합니다.
다음은 property 인데 주로 소스의 위치 라이브러리의 위치 등이 될 것 같습니다.
프로젝트 마다 달라 질 수 있기 때문에 외부에 위치한 파일에서 불러 오도록 하면 ant 파일도
다시 사용이 가능하기 때문에 저는 그렇게 구성합니다.
아래는 아파치에서 가장 기본적인 예제로 제공하는 build.xm 파일을 약간 수정해 놓은
예제 입니다.

file 속성은 properties 를 로딩할 수 있기 때문에 key value 구성이 가능하고
프로젝트 위치나 라이브러리 위치를 프로퍼티 파일에 저장해 필요한 부분만 수정해
사용할 수 있습니다.


<?xml version="1.0" encoding="EUC-KR" ?>
<project name="testAntPjt" default="dist" basedir=".">
    <description>
        simple example build file
    </description>
  <!-- set global properties for this build -->
  <property file="app.properties"/>
  <property name="home" value="${app.home}"/>
  <property name="src" location="${home}/${app.src}"/>
  <property name="build" location="${home}/${app.build}"/>
  <property name="lib" location="${home}/${app.lib}"/>
  <property name="dist" location="${home}/${app.dist}"/>
  <property name="backup"  location="${app.backup}"/>

  <path id="compile.path">
<pathelement path="${classpass}"/>
<fileset dir="${lib}">
<include name="**/*.jar"/>
</fileset>
<pathelement location="${build}"/>
  </path>

  <target name="init">
    <!-- Create the time stamp -->
    <tstamp/>
    <!-- Create the build directory structure used by compile -->
    <mkdir dir="${build}"/>
<echo>${app.home}</echo>
<echo>${TSTAMP}</echo>
  </target>


  <target name="compile" depends="init"
        description="compile the source " >
    <!-- Compile the java code from ${src} into ${build} -->
    <javac srcdir="${src}" destdir="${build}" classpathref="compile.path" includeantruntime="false" encoding="euc-kr"/>
  </target>

  <target name="dist" depends="compile"
        description="generate the distribution" >
    <!-- Create the distribution directory -->
    <mkdir dir="${dist}"/>

    <!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->
    <jar jarfile="${dist}/testAntPjt_${DSTAMP}.jar" basedir="${build}">
<manifest>
<attribute name="Main-Class" value="test.ant.exec.TestExecutor"/>
</manifest>
</jar>
  </target>

  <target name="run" depends="init"
        description="run " >
    <!-- Compile the java code from ${src} into ${build} -->
    <java jar="${dist}/testAntPjt_${DSTAMP}.jar" classpathref="compile.path"  fork="true"/>
  </target>

  <target name="backup" depends="init"
        description="backup" >
    <!-- Compile the java code from ${src} into ${build} -->
    <jar jarfile="${backup}/fullBackup_${DSTAMP}.jar" basedir="${home}"/>
  </target>

  <target name="clean"
        description="clean up" >
    <!-- Delete the ${build} and ${dist} directory trees -->
    <delete dir="${build}"/>
    <delete dir="${dist}"/>
  </target>
</project>










2013년 11월 16일 토요일

최외곽 좌표 찾기 (Convexhull)

최외곽 포인트를 찾는 방법을 이용하면 다양한 분야에 응용될 수 있습니다.
그 방법중 시계반대방향으로 좌표를 찾아 라인의 왼쪽 오른쪽 방향을 판단하여 최외곽 위치를
판단하는 방법을 사용할 수 있습니다.(Graham's scan)
세좌표 가 순서데로 있다고 할때 ((x2-x1)×(y3-y1))-((x3-x1)×(y2-y1)) 의 값이 양수면 왼쪽 음수
면 오른쪽 같으면 직선으로 볼 수 있습니다.
다음의 주소에서 자세한 내용을 확인할 수 있습니다.(http://en.m.wikipedia.org/wiki/Graham_scan)

다음은 실행 해 볼 수 있는 소스입니다.
기본적인 작동 원리만 구현해 보았습니다.(에러를 하나 하나 확인한 소스는 아닙니다.)


function makeRandomPoints(size,maxRadious,startX,startY) {
var pointsArray = [];
for ( var i = 0; i < size; i++ ) {

var pDis = Math.round(Math.random()*maxRadious);
pDis = pDis%maxRadious;
var pAngle = Math.round(Math.random()*360);
pAngle = pAngle%360;
var px = Math.round(Math.cos(pAngle*Math.PI/180.0)*pDis)+startX;
var py = Math.round(-Math.sin(pAngle*Math.PI/180.0)*pDis)+startY;

var ptArray = [];
ptArray.push(px);
ptArray.push(py);
pointsArray.push(ptArray);
}

return pointsArray;
}

function getCounterClockwiseTurn(x1,y1,x2,y2,x3,y3) {
return ( (x2-x1)*(y3-y1) - (x3-x1)*(y2-y1) );
}

function getConvexHullValues(points) {
var fst = -1;
var scd = -1;
var thd = -1;
var size = points.length;

// y값중 최소값 파악
var stdIdx = -1;
var stdX = -1;
var stdY = -1;
for ( var i = 0; i < size; i++ ) {
if ( stdIdx == -1 ) {
stdIdx = i;
stdX = points[i][0];
stdY = points[i][1];
continue;
}
if ( stdY > points[i][1] ) {
stdIdx = i;
stdX = points[i][0];
stdY = points[i][1];
} else if ( stdY == points[i][1] ) {
// 왼쪽 혹은 오른쪽 으로 정렬 ( 동일 선상에 있을 경우 처리를 위함)
// 이 코드에서는 왼쪽 정렬
if ( points[i][0] < stdX ) {
stdIdx = i;
stdX = points[i][0];
stdY = points[i][1];
}
}
}

var sortedArray = [];
// 최소 위치로 부터 시계 반대 방향으로 배열 - 각도를 이용한 소팅
for ( var i = 0; i < size; i++ ) {
var arr = [];
var degree =  (Math.atan2(points[i][1]-stdY,points[i][0]-stdX)*180)/Math.PI;
arr.push(degree);
arr.push(points[i][1]);
arr.push(i);
sortedArray.push(arr);
}

// 정렬 1.각도, 2, Y 값이 작은 수로 선택
// X가 왼쪽 정렬이면 X가 작은 값이 먼저 선택되도록 조치 skip ..
sortedArray.sort( function(a,b) {
var aa = parseFloat(a[0]);
var bb = parseFloat(b[0]);
if ( aa > bb ) {
return 1;
} else if ( aa < bb ) {
return -1;
} else {
var aaa = parseFloat(a[1]);
var bbb = parseFloat(b[1]);
return (aaa<bbb?1:-1);
}
});

// 마지막 항목 다시 계산
sortedArray.push(sortedArray[0]);
//alert ( sortedArray );
var convexPoints = [];
convexPoints.push(sortedArray[0][2]);
for ( var i = 2; i <= size; i++ ) {
fst = convexPoints[convexPoints.length-1];
scd = sortedArray[i-1][2];
thd = sortedArray[i][2];

var dv = getCounterClockwiseTurn(points[fst][0],points[fst][1]
, points[scd][0], points[scd][1], points[thd][0], points[thd][1]);
if ( dv > 0 ) {
convexPoints.push(scd);
} else {
while ( convexPoints.length > 1 ) {
scd = convexPoints.pop();
fst = convexPoints[convexPoints.length-1];
dv = getCounterClockwiseTurn(points[fst][0],points[fst][1]
, points[scd][0], points[scd][1], points[thd][0], points[thd][1]);
if ( dv > 0 ) {
convexPoints.push(scd);
break;
}
}
}
}
return convexPoints;
}

function drawPoint(ownerObj,x,y,w,h,colStr) {
var pObj = document.createElement("DIV");
if ( !colStr ){
colStr = "#FFFFFF";
}
pObj.style.position = "absolute";
pObj.style.margin = "0px";
pObj.style.padding = "0px";
pObj.style.border = "2px solid black";

pObj.style.backgroundColor = colStr;
pObj.style.top = y + "px";
pObj.style.left = x + "px";
pObj.style.width = w+"px";
pObj.style.height = h+"px";
if ( ownerObj ) {
ownerObj.appendChild(pObj);
}
return pObj;
}

window.onload = function() {
var size = 1000;
var maxRadious = 300;
var startX = 350;
var startY = 350;
var points = makeRandomPoints(size,maxRadious,startX,startY);
//alert ( points);
for ( var i = 0; i < size; i++ ) {
drawPoint(document.body,points[i][0], points[i][1],2,2,null);
}

var convexPoints = getConvexHullValues(points);
for ( var i = 0; i < convexPoints.length; i++ ) {
var pt = convexPoints[i];
//alert ( pt );
drawPoint(document.body,points[pt][0], points[pt][1],10,10,"#FF0000");
}
}




2013년 11월 13일 수요일

다익스트라(Dijkstra) 구현하기

매번 정리해야지 하면서 하지 못했던것들을 조금씩 블로그에 기록하고 있습니다.
정리가 우선이기 때문에 보시는 분들에게 설명이 부족할것이라는 생각이 들었습니다.

그래서 간단한 변명을 하려고 합니다.
우연찮게 이 곳을 들리시는 분들이 몇분이라도 드물지만 계시는 것 같습니다.
나중에 시간이 되면 이곳에 올린 내용을 정리해 보겠습니다.
여기 올리는 내용은 알고리즘을 설명하기 보다는 구현 내용에 치중하고 있어서
자세한 설명을 생략하고 있습니다.

최단 거리를 찾는 알고리즘입니다.
결국 시작점에서 부터 연결되는 모든 지점을 찾아 최소거리를 확인하고
확인하는 시점에 경로를 기록해 두는 것이중심이 된 알고리즘입니다.

경로를 구성하는 방법은 Matrix 인데 Random 함수로 거리를 산정했습니다.
그 후는 Dijkstra 알고리즘에 의해 거리를 구하고 경로를 구하는 방법을 구현해 보았습니다.

다음은 소스 입니다.

function makeRandomDistanceMatrix(maxSize,connSize,maxDistance) {
// check arguments ... skip
var result = [];
var seed = Math.round(maxDistance/1000);
for ( var y = 0; y < maxSize; y++ ) {
var arr = [];
for ( var x = 0; x < maxSize; x++ ) {
var cPos = Math.round(x/2);
if ( (y-cPos) >= 0 && (y+cPos) < maxSize ) {
if ( x%2 == 0) {
cPos = y+cPos;
} else {
cPos = y-cPos;
}
} else if ( (y-cPos) < 0 ) {
cPos = x;
} else {
cPos = (maxSize-x-1);
}

if ( cPos == y ) {
arr[cPos] = 0;
} else {
if ( x < connSize ) {
arr[cPos] = Math.round(Math.random()*seed)*x;
} else {
arr[cPos] = maxDistance;
}
}
}
result.push(arr);
//document.write(arr+"<BR>");
}
return result;
}

function dijkstraFn(matrix,start,end,maxDistance) {
// check arguments ... skip
var size = matrix.length;
var distance = [];
var visit = [];
var path = [];
var result = [];
for ( var i = 0; i < size; i++ ) {
distance[i] = maxDistance;
visit[i] = 0;
path[i] = -1;
}
// set the start point..
distance[start] = 0;

for ( var y = 0; y < size; y++ ) {
var minDistance = maxDistance;
var cPos = -1;
for ( var x = 0; x < size; x++ ) {
if ( visit[x] == 0 && distance[x] < minDistance ) {
cPos = x;
minDistance = distance[x];
}
}
if ( cPos == -1 ) {
break;
}
visit[cPos] = 1;
for ( var x = 0; x < size; x++ ) {
//skip start point ..
if ( distance[x] > 0 && distance[x] > ( distance[cPos] + matrix[cPos][x] ) ) {
distance[x] = ( distance[cPos] + matrix[cPos][x] );
// x 접점에 가장 가까운 포인트를 설정
path[x] = cPos;
}
}
}

//alert ( path.join() );

result.push(end);
while( path[end] != -1 ) {
end = path[end];
result.push(end);
}

result.reverse();

var resultMx = [];
var st = result[0];
var sumDist = 0.0;

var arr2 = [];
arr2.push("DIST");
arr2.push(distance.join());
resultMx.push(arr2);
arr2 = [];
arr2.push("PATH");
arr2.push(result.join());
resultMx.push(arr2);

arr2 = [];
arr2.push(st);
arr2.push(0);
resultMx.push(arr2);
for ( var i = 1; i < result.length; i++ ) {
arr2 = [];
var ed = result[i];
arr2.push(ed);
arr2.push(matrix[st][ed]);
sumDist += matrix[st][ed];
st = ed;
resultMx.push(arr2);
}

arr2 = [];
arr2.push("T_DIST");
arr2.push(sumDist);
resultMx.push(arr2);

return resultMx;
}

function drawTableTmp(matrixTbl) {
var tableObj = document.createElement("TABLE");
tableObj.style.backgroundColor = "#000080";
tableObj.style.tableLayout = "fixed";
tableObj.setAttribute("cellSpacing","1");
tableObj.setAttribute("cellPadding","0");
var tBodyObj = document.createElement("TBODY");
tableObj.appendChild(tBodyObj);
var size = matrixTbl.length;
for ( var y = 0; y < size; y++ ) {
var trObj = document.createElement("TR");
trObj.style.height = "22px";
tBodyObj.appendChild(trObj);
for ( var x = 0,xSize=matrixTbl[y].length; x < xSize; x++ ) {
var tdObj = document.createElement("TD");
tdObj.style.width = "100px";
tdObj.style.backgroundColor = "#FFFFFF";
tdObj.innerText = matrixTbl[y][x];
trObj.appendChild(tdObj);
}
}
document.body.appendChild(tableObj);
}

window.onload = function() {
var maxSize = 14;
var connSize = 8;
var maxDistance = 99999999;
var matrix = makeRandomDistanceMatrix(maxSize,connSize,maxDistance);
var start = 1;
var end = maxSize-2;
var realPath = dijkstraFn(matrix,start,end,maxDistance);

drawTableTmp(matrix);

drawTableTmp(realPath);
}


실행해 볼 수 있는 소스입니다.

Random 함수라 그때 그때 달라 지지만 아래는 결과 입니다.
09925673060144105167852429055240384136297999999999999999999999999999999999999999999999999
1629301210303973217588848020018645091546999999999999999999999999999999999999999999999999
249129565830139936339416361645186468256319999999999999999999999999999999999999999999999999
183830838247002065416302336411780613676999999999999999999999999999999999999999999999999
1763934205453843642297011784250384226356999999999999999999999999999999999999999999999999
9999999968555931792517496639370625563147525048349999999999999999999999999999999999999999
99999999999999995590903221052646188924506544035821214307099999999999999999999999999999999
999999999999999999999999136668259060221022311050174768370000298410999999999999999999999999
999999999999999999999999999999992998803607352871422298101477841204483762729999999999999999
999999999999999999999999999999999999999939489112655512399327991017802639837641035899999999
999999999999999999999999999999999999999999999999607012613602226429433011448486744228378
999999999999999999999999999999999999999999999999295722278970196490251391323470589562984
999999999999999999999999999999999999999999999999407680233490215635185468265302344780191114
9999999999999999999999999999999999999999999999994901968292687785138120163947141286601550
DIST16293,0,86734,39732,105148,211896,122651,91546,266314,265721,386762,501246,473506,504230
PATH1,7,8,10,12
10
791546
8174768
10120448
1286744
T_DIST473506

1에서 12까지의 경로는 1->7->8->10->12 로 이어지고 있습니다.

2013년 11월 11일 월요일

베지어 구성하기

파스칼 삼각형을 이용하여 차수에 대한 계수를 구할수 있습니다.
0 ~ 1사이 값으로 t라는 값을 설정하고 1-t 의 값을 tRes라고 설정합니다.
다음은 tRes 와 t의 값을 차수에 따라 교차계산해 값을 구합니다.

예제는 다음과 같습니다.

getPascalTriangleRecursive : function(nDegree,orgArray) {
var result = null;
if (orgArray == null || nDegree < 0) {
return result;
}
var size = orgArray.length;
result = [];
result.push(1);
for ( var i = 1; i < size; i++ ) {
result.push(orgArray[i-1]+orgArray[i]);
}
result.push(1);
if ( size < nDegree ) {
return this.getPascalTriangleRecursive(nDegree,result);
} else {
return result;
}
},
getPascalTriangle : function(nDegree) {
var result = null;
if (nDegree < 0) {
return result;
}
result = [];
if ( nDegree < 2 ) {
for ( var i = 0; i <= nDegree; i++ ) {
result.push(1);
}
return result;
}
for ( var i = 0; i <= 1; i++ ) {
result.push(1);
}

return this.getPascalTriangleRecursive(nDegree,result);
},

getBezierCoef : function(nDegree,t) {
var result = [];
var tRes = (1.0-t);
var pascalArray = this.getPascalTriangle(nDegree);
for ( var i = nDegree; i >=0; i-- ){
var j = nDegree-i;
result.push(Math.pow(tRes,i)*Math.pow(t,j));
}
return result;
}

위의 예에서 구성한데로 계수가 구해지면 t 와 tRes 관계를 차수에 해당하는
위치에서 승수를 구해 각각을 곱해준 값을 더하면 해당 t의 베지어 값을 확일 할 수 있습니다

아래는 호출한 영역입니다.

this.drawBezierLine = function(ownerObj,xArray,yArray) {
// error handler -- check args ...
var nDegree = xArray.length-1;
var minX = xArray[0];
var maxX = xArray[0];
// check x order ... skip
for ( var i = 1; i <= nDegree; i++ ) {
if ( minX > xArray[i] ) {
minX = xArray[i];
}
if ( maxX < xArray[i] ) {
maxX = xArray[i];
}
}

var gap = maxX-minX;
var bx = -1;
var by = -1;
var pArray = getPascalTriangle(nDegree);
//alert( pArray + " : " + gap );

for ( var x = 0; x <= gap; x++ ) {
var t = (x/gap);
var bArray = getBezierCoef(nDegree,t);
//alert ( bArray );
var tX = 0.0;
var tY = 0.0;
for ( var i = 0; i <= nDegree; i++ ) {
var calc = pArray[i]*bArray[i];
tX += xArray[i]*calc;
tY += yArray[i]*calc;
}
if ( x == 0 ) {
bx = tX;
by = tY;
} else {
this.drawLine(ownerObj,bx,by,tX,tY,5,"#000080");
//alert ( bx + ":" + by + " : " + tX + " : " + tY );
bx = tX;
by = tY;
}

if ( x % 100 == 0){
//alert ( bArray + "\n" + bx + " : "+ by );
}
}

....


var xArray = [];
var yArray = [];
xArray.push(100);
yArray.push(100);
xArray.push(200);
yArray.push(250);

xArray.push(300);
yArray.push(50);

xArray.push(400);
yArray.push(100);

graphicObj.drawBezierLine(document.body,xArray,yArray);
for ( var i = 0; i < xArray.length ; i++){
graphicObj.drawPoint(document.body,xArray[i],yArray[i],10,10,"#FF0000");
}




2013년 11월 7일 목요일

파스칼 삼각형 구하기

계수를 구하는 기본적인 내용으로 파스칼 삼각형을 구성해 보았습니다.
자바로 구현한 간단한 코드 예제 입니다.


private static List<Integer> getPascalTriangleRecursive(int nDegree,List<Integer> orgList) {
List<Integer> result = null;
if (orgList == null || nDegree < 0) {
return result;
}
int size = orgList.size();
result = new ArrayList<Integer>();
result.add(1);
for ( int i = 1; i < size; i++ ) {
result.add(orgList.get(i-1)+orgList.get(i));
}
result.add(1);
if ( size < nDegree ) {
return getPascalTriangleRecursive(nDegree,result);
} else {
return result;
}
}

public static List<Integer> getPascalTriangle(int nDegree) {
List<Integer> result = null;
if (nDegree < 0) {
return result;
}
result = new ArrayList<Integer>();
if ( nDegree < 2 ) {
for ( int i = 0; i <= nDegree; i++ ) {
result.add(1);
}
return result;
}
for ( int i = 0; i <= 1; i++ ) {
result.add(1);
}

return getPascalTriangleRecursive(nDegree,result);
}

아래는 출력 결과 입니다. 0 -> 9까지 10개 돌려 보았습니다.
---------- JAVA ----------
0 : [1]
1 : [1, 1]
2 : [1, 2, 1]
3 : [1, 3, 3, 1]
4 : [1, 4, 6, 4, 1]
5 : [1, 5, 10, 10, 5, 1]
6 : [1, 6, 15, 20, 15, 6, 1]
7 : [1, 7, 21, 35, 35, 21, 7, 1]
8 : [1, 8, 28, 56, 70, 56, 28, 8, 1]
9 : [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]

출력 완료 (0초 경과) - 정상 종료

2013년 11월 6일 수요일

스크립트 기초 모듈 구성(삼각형 그리기 테스트)

어떻게 하다보니 먼저 javascript 모듈을 정리하기 시작하게 되었습니다.
조금씩 정리해 갈 생각입니다.

모듈화를 생각하다, 사용자와 상호작용하기 위한 부분이 필요해서 일단 기본적인 내용을
구성해 보고 있습니다.

canvas 없이 (html5 이전) 그리기 기능을 정리하는 중입니다.

다음은 간략한 코드 입니다.( 나중에 다시 정리할 예정 입니다.)

(function(winObj,baseObj) {
var window = winObj;
var document = window.document;
var ksbee = {};
if ( window.ksbee ){
alert("duplicated....");
return;
} else {
window.ksbee = ksbee;
}

ksbee.ui = {};
ksbee.ui.dom = {
createDomObject : function(tagName,clsName,attMap,styleMap) {
var tagObj = document.createElement(tagName);
if ( tagObj ) {
if ( clsName ) {
tagObj.className = clsName;
}

if ( attMap && attMap instanceof ksbee.ui.data.Map ) {
for ( var i = 0,size=attMap.size(); i < size; i++ ) {
tagObj.setAttribute(attMap.getIndexKey(i),attMap.getIndexValue(i));
}
}

if ( styleMap && styleMap instanceof ksbee.ui.data.Map ) {
for ( var i = 0,size=styleMap.size(); i < size; i++ ) {
// alert ( styleMap.getIndexKey(i) );
tagObj.style[styleMap.getIndexKey(i)] = styleMap.getIndexValue(i);
}
//tagObj.style[width] = "200px";
//tagObj.style["height"] ="300px";
}

}
return tagObj;
}
};
ksbee.ui.data = {
Map : function() {
var storage = [];
var isUnique = function(key) {
if ( key == null || key === 'undefined' ){
return false;
}
for ( var i = 0,size=storage.length; i < size; i++ ) {
if ( storage[i][0] === key ) {
return false;
}
}
return true;
};
this.put = function(key,value) {
if ( isUnique(key) ){
storage.push( [key,value] );
}
};
this.get = function(key) {
if ( key == null || key === 'undefined' ){
return null;
}
for ( var i = 0,size=storage.length; i < size; i++ ) {
if ( storage[i][0] === key ) {
return storage[i][1];
}
}
return null;
};
this.getIndexKey = function(index) {
var size = storage.length;
if ( index < 0 || index >= size ) {
return null;
}
return storage[index][0];
};
this.getIndexValue = function(index) {
var size = storage.length;
if ( index < 0 || index >= size ) {
return null;
}
return storage[index][1];
};

this.size = function() {
return storage.length;
};
this.remove = function(key) {
if ( key == null || key === 'undefined' ){
return null;
}
for ( var i = 0,size=storage.length; i < size; i++ ) {
if ( storage[i][0] === key ) {
return storage.splice(i,1);
}
}
return null;
};

this.removeIndex = function(index) {
var size = storage.length;
if ( index < 0 || index >= size ) {
return null;
}
return storage.splice(index,1);
};
this.keySet = function() {
var arr = [];
for ( var i = 0,size = storage.length; i < size; i++ ) {
arr.push(storage[i][0]);
}
return arr;
};
}
};

ksbee.ui.drawings = {
Graphics : function() {
var pointMap = new ksbee.ui.data.Map();
var init = function() {
pointMap.put("position","absolute");
pointMap.put("margin","0px");
pointMap.put("padding","0px");
pointMap.put("border","0px");
};

this.drawPoint = function(ownerObj,x,y,w,h,colStr) {
var pObj = ksbee.ui.dom.createDomObject("DIV",null,null,pointMap);
if ( !colStr ){
colStr = "#000080;";
}
pObj.style.backgroundColor = colStr;
pObj.style.top = y + "px";
pObj.style.left = x + "px";
pObj.style.width = w+"px";
pObj.style.height = h+"px";
if ( ownerObj ) {
ownerObj.appendChild(pObj);
}
return pObj;
};

this.drawLine = function(ownerObj,x1,y1,x2,y2,thick,colStr) {
var a = (x1==x2) ? 0 : (y2-y1)/(x2-x1);
var b = y1 - a*x1;

var minX = x1 <= x2 ? x1 : x2;
var maxX = x1 > x2 ? x1 : x2;

var minY = y1 <= y2 ? y1 : y2;
var maxY = y1 > y2 ? y1 : y2;

var xGap = maxX-minX;
var yGap = maxY-minY;
var pointsArray = [];
if ( xGap >= yGap ) {
var pPos = null;
var duration = 1;
var stPos = minX;
for ( var i = minX; i <= maxX; i++ ) {
var cPos = Math.round(a*i+b);
if ( pPos == null ) {
pPos = cPos;
continue;
}
if ( pPos == cPos ) {
duration++;
continue;
}
pointsArray.push(this.drawPoint(ownerObj,stPos,pPos,duration,thick,colStr));
duration = 1;
stPos = i;
pPos = cPos;
}
pointsArray.push(this.drawPoint(ownerObj,stPos,pPos,duration,thick,colStr));
} else {
var pPos = null;
var duration = 1;
var stPos = minY;
for ( var i = minY; i <= maxY; i++ ) {
var cPos = (a == 0 ? x1 : Math.round((i-b)/a));
if ( pPos == null ) {
pPos = cPos;
continue;
}
if ( pPos == cPos ) {
duration++;
continue;
}
pointsArray.push(this.drawPoint(ownerObj,pPos,stPos,thick,duration,colStr));
duration = 1;
stPos = i;
pPos = cPos;
}
pointsArray.push(this.drawPoint(ownerObj,pPos,stPos,thick,duration,colStr));
}
};

init();
}
};

ksbee.fn = {};
ksbee.fn.util = {

};
})(window,undefined);

window.onload = function() {
var graphicObj = new ksbee.ui.drawings.Graphics();
//alert ( graphicObj);
graphicObj.drawLine(document.body,300,100,400,400,5,"#FF0000");
graphicObj.drawLine(document.body,300,100,100,300,5,"#00FF00");
graphicObj.drawLine(document.body,100,300,400,400,5,"#0000FF");
}


스크립트가 가장 직관적이라서요 ...
아직 검토도 않한 게시물입니다. (다시 정리할 예정입니다.. 언젠가는 ... )
오류가 있을수 있습니다. ( 혹시 우연찮게 조회하실 분들에게 미리 말씀드리는 겁니다. )