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");
}


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