Development of Fashion CAD System 4. Lines Sungmin Kim SEOUL NATIONAL UNIVERSITY Lines Topics Line 정의 및 화면 표시 여러 개의 점을 선택해서 선을 정의 연결된 여러 개의 직선 또는 하나의 곡선을 정의 곡선의 표시 Bezier Curve 사용하기 각종 요소의 표시하기/숨기기 사용자와의 상호작용 마우스를 써서 선과 점을 동시에 선택하기 선의 속성 설정하기 굵기, 색상, 모양 등 선과 점을 선택해서 새로운 점을 만들기 2
Line Definition 선정의하기 여러개의점을선택해서선을만들기 ChildForm 에다음메뉴를추가 연결된직선 (Polyline) 또는 Bezier Curve 를정의 3 선정의하기 Bezier Curve 역사 1962 년프랑스르노자동차의 Piere Bezier 가발표 오래전부터알려진 Bernstein polynomial 을곡선제도에응용 장점 계산이간단하여구현이쉽다 단점 곡선이조정점을지나지않음 조정점을하나만변경해도전체곡선의형상이변화됨 Line Definition 4
Line Definition 선정의하기 변수선언 TChildForm.h 에 Line 을정의하는데필요한변수를정의한다 int ptpoint PointNum; Point[1000]; int LineNum; // 선의갯수 int LinePointNum[100]; // 선의점갯수 (2, 3, 4, ) int LinePoint[100][100]; // 점의번호 5 Line Definition 선정의하기 변수선언 Struct 를써서다시정의하기 struct ptline int PointNum; int Point[4]; ; int ptline LineNum; Line[100]; TChildForm.cpp 에서변수값초기화 fastcall TChildForm::TChildForm(TComponent* Owner,AnsiString C) : TForm(Owner) PointNum=0; LineNum=0; 6
Line Definition 선정의하기 Polyline 메뉴핸들러작성 void fastcall TChildForm::Polyline1Click(TObject *Sender) if (SelPointNum<2) Application->MessageBox("Select more than two points","caution",mb_iconexclamation MB_OK); return; for(i=0;i<selpointnum-1;i++) Line[LineNum].PointNum=2; Line[LineNum].Point[0]=SelPoint[i]; Line[LineNum].Point[1]=SelPoint[i+1]; LineNum++; 7 Line Definition 선정의하기 Bezier Curve 메뉴핸들러작성 void fastcall TChildForm::BezierCurve1Click(TObject *Sender) if (SelPointNum!=4) Application->MessageBox("Select four points","caution",mb_iconexclamation MB_OK); return; Line[LineNum].PointNum=4; for(i=0;i<4;i++) Line[LineNum].Point[i]=SelPoint[i]; LineNum++; 8
Line Drawing 선표시하기 ChildForm 의 OnPaint 이벤트핸들러수정 직선그리기 void fastcall TChildForm::FormPaint(TObject *Sender) for(i=0;i<pointnum;i++) P=Screen(Point[i].x,Point[i].y); Canvas->Ellipse(P.x-3,P.y-3,P.x+3,P.y+3); for(i=0;i<linenum;i++) if (Line[i].PointNum==2) // straight line P=Screen(Point[Line[i].Point[0]].x,Point[Line[i].Point[0]].y); Canvas->MoveTo(P.x,P.y); P=Screen(Point[Line[i].Point[1]].x,Point[Line[i].Point[1]].y); Canvas->LineTo(P.x,P.y); else Canvas->Pen->Color=clRed; 9 Line Drawing 선표시하기 ChildForm 의 OnPaint 이벤트핸들러수정 곡선그리기 Hull 도함께표시 else TPoint pt[4]; int j; Canvas->Pen->Color=clLime; for(j=0;j<4;j++) P=Screen(Point[Line[i].Point[j]].x,Point[Line[i].Point[j]].y); pt[j].x=(int)p.x; pt[j].y=(int)p.y; if (!j) Canvas->MoveTo(P.x,P.y); else Canvas->LineTo(P.x,P.y); Canvas->Pen->Color=clBlack; Canvas->PolyBezier(pt,3); 10
Line Drawing 선표시하기 결과비교 Polyline Bezier Curve 11 Line Drawing 선표시하기 각종요소의보이기 / 숨기기 ChildForm 에다음메뉴를추가하고변수정의를추가 TChildForm.h int ViewPoint,ViewHull TChildForm.cpp ViewPoint=ViewHull=1; 12
Line Drawing 선표시하기 각종요소의보이기 / 숨기기 Toggle 메뉴의이벤트핸들러작성 void fastcall TChildForm::TogglePoint1Click(TObject *Sender) ViewPoint=1-ViewPoint; void fastcall TChildForm::ToggleHull1Click(TObject *Sender) ViewHull=1-ViewHull; 13 Line Drawing 선표시하기 각종요소의보이기 / 숨기기 OnPaint 이벤트핸들러수정 if (ViewPoint==1) for(i=0;i<pointnum;i++) P=Screen(Point[i].x,Point[i].y); Canvas->Ellipse(P.x-3,P.y-3,P.x+3,P.y+3); for(i=0;i<linenum;i++) if (Line[i].PointNum==2) // straight line else for(j=0;j<4;j++) if (ViewHull==1) if (!j) Canvas->MoveTo(P.x,P.y); else Canvas->LineTo(P.x,P.y); 14
Issue 점과선을섞어서선택할수있어야함 클릭한위치에서가까운개체를선택 선택한점 / 선목록을일관되게관리 Struct를써서 selection 을재정의 struct ptselection int Type; // 0 point, 1 line int Num; ; int ptselection SelNum; Sel[100]; 15 점선택관련소스수정 SelPoint 로되어있는부분을일단수정한다 fastcall TChildForm::TChildForm(TComponent* Owner,AnsiString C) : TForm(Owner) SelNum=0; void fastcall TChildForm::FormPaint(TObject *Sender) for(i=0;i<selnum;i++) if (Sel[i].Type==0) P=Screen(Point[Sel[i].Num].x,Point[Sel[i].Num].y); Canvas->Ellipse(P.x-5,P.y-5,P.x+5,P.y+5); 16
점선택관련소스수정 void fastcall TChildForm::FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) if (!DragStart) if (Shift.Contains(ssCtrl)) int p=findpoint(x,y); if (!Shift.Contains(ssShift)) SelNum=0; if (p!=-1) Sel[SelNum].Type=0; Sel[SelNum].Num=p; SelNum++;. 17 점선택관련소스수정 void fastcall TChildForm::MovePoint1Click(TObject *Sender) if (SelNum==0) return; PointXYDialog->ShowModal(); if (PointXYDialog->Result==1) float x=pointxydialog->x->text.todouble(); float y=pointxydialog->y->text.todouble(); for(i=0;i<selnum;i++) if (Sel[i].Type==0) Point[PointNum].x=Point[Sel[i].Num].x+x; Point[PointNum].y=Point[Sel[i].Num].y+y; PointNum++; 18
점선택관련소스수정 void fastcall TChildForm::AddDividePoint1Click(TObject *Sender) if (SelNum!=2 Sel[0].Type!=0 Sel[1].Type!=0) Application->MessageBox("Select two points","caution",mb_iconexclamation MB_OK); return; DividePointDialog->ShowModal(); if (DividePointDialog->Result) float m=dividepointdialog->m->text.todouble(); float n=dividepointdialog->n->text.todouble(); Point[PointNum].x=(m*Point[Sel[1].Num].x+n*Point[Sel[0].Num].x)/(m+n); Point[PointNum].y=(m*Point[Sel[1].Num].y+n*Point[Sel[0].Num].y)/(m+n); PointNum++; 19 점선택관련소스수정 void fastcall TChildForm::Polyline1Click(TObject *Sender) for(i=0;i<selnum;i++) if (Sel[i].Type!=0) Application->MessageBox("Select points only","caution",mb_iconexclamation MB_OK); return; if (SelNum<2) Application->MessageBox("Select more than two points","caution",mb_iconexclamation MB_OK); return; for(i=0;i<selnum-1;i++) Line[LineNum].PointNum=2; Line[LineNum].Point[0]=Sel[i].Num; Line[LineNum].Point[1]=Sel[i+1].Num; LineNum++; 20
점선택관련소스수정 void fastcall TChildForm::BezierCurve1Click(TObject *Sender) for(i=0;i<selnum;i++) if (Sel[i].Type!=0) Application->MessageBox("Select points only","caution",mb_iconexclamation MB_OK); return; if (SelNum!=4) Application->MessageBox("Select four points","caution",mb_iconexclamation MB_OK); return; Line[LineNum].PointNum=4; for(i=0;i<4;i++) Line[LineNum].Point[i]=Sel[i].Num; LineNum++; 21 Mouse 클릭시점과선을구분 점과선중더가까이있는개체를선택하도록 Mouse click event handler 를수정 void fastcall TChildForm::FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) if (!DragStart) if (Shift.Contains(ssCtrl)) int o=findobject(x,y); if (!Shift.Contains(ssShift)) SelNum=0; if (o!=-1) Sel[SelNum].Type=ObjectType; Sel[SelNum].Num=o; SelNum++; else TChildForm.h 수정 int fastcall FindObject(int x,int y); int ObjectType; 22
Mouse 클릭시점과선을구분 FindObject 함수를구현 가까운거리에점이있다면그걸선택한다 int fastcall TChildForm::FindObject(int x,int y) int i,mino=-1; float d,mind=10; // find point : for(i=0;i<pointnum;i++) ptpoint P=Screen(Point[i].x,Point[i].y); d=sqrt((p.x-x)*(p.x-x)+(p.y-y)*(p.y-y)); if (d<mind) MinD=d; MinO=i; ObjectType=0; if (MinD<5) return MinO; // find line : 23 선을선택하는방법 클릭한지점에서가장가까운선을찾는다 직선의경우수선의발을찾는것으로가능 Bezier 의경우약간복잡하므로일단 Hull 을기준으로사용 Hull 까지의최단거리가곡선까지의거리 선분까지의최단거리를비교 ( 실제로는양끝점까지의거리와수선의발까지의거리중가장짧은것 ) 24
선을선택하는방법 선까지의거리를고려해서가장가까운객체를선택하도록수정 가까운거리에점이없는경우가장가까운선을찾는다 // find line : MinD=10; for(i=0;i<linenum;i++) d=distancetoline(i,x,y); if (d<mind) MinD=d; MinO=i; ObjectType=1; return MinO; 25 선을선택하는방법 직선과곡선의경우에따라다른방법으로결정 결국 DistanceToSegment 함수로귀결 float fastcall TChildForm::DistanceToLine(int l,int x,int y) if (Line[l].PointNum==2) // straight line return DistanceToSegment(x,y,Point[Line[l].Point[0]],Point[Line[l].Point[1]]); else // Bezier curve float d,mind=10000; for(i=0;i<line[l].pointnum-1;i++) d=distancetosegment(x,y,point[line[l].point[i]],point[line[l].point[i+1]]); if (d<mind) MinD=d; return MinD; 26
선을선택하는방법 한점에서선분까지의최단거리구하기 float fastcall TChildForm::DistanceToSegment(int x,int y,ptpoint a,ptpoint b) float MinD,d,m; a=screen(a.x,a.y); b=screen(b.x,b.y); MinD=sqrt((a.x-x)*(a.x-x)+(a.y-y)*(a.y-y)); d=sqrt((b.x-x)*(b.x-x)+(b.y-y)*(b.y-y)); if (d<mind) MinD=d; if (a.x==b.x) d=fabs(x-a.x); (a.x, a.y) else m=(a.y-b.y)/(a.x-b.x); (x, y) d=fabs(m*x-y+a.y-m*a.x)/sqrt(m*m+1); d if (d<mind) MinD=d; return MinD; (b.x, b.y) b. y a. y m =, y a. y = m( x a. x) b. x a. x mx y + a. y m a. x = 0 mx y + a. y m a. x d = m 2 + 1 27 선그리는부분의함수화 반복되는코드를없애는것이프로그래밍의핵심 for(i=0;i<linenum;i++) DrawLine(i,clBlack,1); Canvas->Pen->Color=clRed; Canvas->Brush->Style=bsClear; for(i=0;i<selnum;i++) if (Sel[i].Type==0) P=Screen(Point[Sel[i].Num].x,Point[Sel[i].Num].y); Canvas->Ellipse(P.x-5,P.y-5,P.x+5,P.y+5); if (Sel[i].Type==1) DrawLine(Sel[i].Num,clRed,0); Canvas->Brush->Style=bsSolid; 28
선그리기함수구현 void fastcall TChildForm::DrawLine(int l,tcolor C,int hull) ptpoint P; TColor OC=Canvas->Pen->Color; if (Line[l].PointNum==2) Canvas->Pen->Color=C; P=Screen(Point[Line[l].Point[0]].x,Point[Line[l].Point[0]].y); Canvas->MoveTo(P.x,P.y); P=Screen(Point[Line[l].Point[1]].x,Point[Line[l].Point[1]].y); Canvas->LineTo(P.x,P.y); else TPoint pt[4]; Canvas->Pen->Color=clLime; for(i=0;i<4;i++) P=Screen(Point[Line[l].Point[i]].x,Point[Line[l].Point[i]].y); pt[i].x=(int)p.x; pt[i].y=(int)p.y; if (ViewHull==1 && hull) if (!i) Canvas->MoveTo(P.x,P.y); else Canvas->LineTo(P.x,P.y); Canvas->Pen->Color=C; Canvas->PolyBezier(pt,3); Canvas->Pen->Color=OC; 29 선속성설정 선의여러가지성질을바꾸기 색상, 굵기, 형태등 Line 메뉴를추가 속성설정대화상자를추가» TLinePropertyDialog.cpp 로저장 SHAPE : TShape MouseDown event handler COLOR : TColorDialog STYLE : TComboBox Items : Solid Dot Dash Dash Dot Dash Dot Dot 30
선속성설정 Event Handler 설정 SHAPE 를마우스로클릭한경우색상을바꿀수있음 void fastcall TLinePropertyDialog::SHAPEMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) if (COLOR->Execute()) SHAPE->Brush->Color=COLOR->Color; Ok, Cancel 버튼의 event handler 도작성 void fastcall TLinePropertyDialog::Button1Click(TObject *Sender) Result=1; Close(); void fastcall TLinePropertyDialog::Button2Click(TObject *Sender) Result=0; Close(); 31 선속성설정 ptline 구조체수정 struct ptline TColor Color; int Thickness,Style; int PointNum; int Point[4]; ; Line 의기본속성설정 void fastcall TChildForm::Polyline1Click(TObject *Sender) for(i=0;i<selnum-1;i++) Line[LineNum].Color=clBlack; Line[LineNum].Thickness=1; Line[LineNum].Style=0; Line[LineNum].PointNum=2;.. void fastcall TChildForm::BezierCurve1Click(TObject *Sender) Line[LineNum].Color=clBlack; Line[LineNum].Thickness=1; Line[LineNum].Style=0; Line[LineNum].PointNum=4; 32
선속성설정 Edit Property 메뉴핸들러작성 void fastcall TChildForm::EditProperty1Click(TObject *Sender) if (!SelNum) return; LinePropertyDialog->ShowModal(); if (LinePropertyDialog->Result) for(i=0;i<selnum;i++) if (Sel[i].Type==1) Line[Sel[i].Num].Color=LinePropertyDialog->SHAPE->Brush->Color; Line[Sel[i].Num].Thickness=LinePropertyDialog->THICKNESS->Text.ToInt(); Line[Sel[i].Num].Style=LinePropertyDialog->STYLE->ItemIndex; 33 선속성설정 선그리기함수수정 void fastcall TChildForm::DrawLine(int l,tcolor C,int hull) ptpoint P; TColor OC=Canvas->Pen->Color; // 원래색상 / 굵기 / 스타일을백업 int OT=Canvas->Pen->Width; int OS=Canvas->Pen->Style; if (Line[l].Type==0) Canvas->Pen->Color=C; Canvas->Pen->Width=Line[l].Thickness; Canvas->Pen->Style=Line[l].Style;. else TPoint pt[4]; Canvas->Pen->Color=clLime; // Hull 그리기위한설정 Canvas->Pen->Width=1; Canvas->Pen->Style=psSolid; Canvas->Pen->Color=C; Canvas->Pen->Width=Line[l].Thickness; Canvas->Pen->Style=Line[l].Style; Canvas->PolyBezier(pt,3); Canvas->Pen->Color=OC; // 원래대로돌리기 Canvas->Pen->Width=OT; Canvas->Pen->Style=OS; 34
More Points 새로운점의생성 점과선을이용한새로운점의선택 여러개의점과하나의선을선택 선을중심으로선택한점과대칭인점들을생성 메뉴를추가 35 More Points 새로운점의생성 메뉴핸들러의작성 맨처음에선을선택해야함 MirrorPoint 함수로귀결 void fastcall TChildForm::AddMirrorPoints1Click(TObject *Sender) if (SelNum<2) return; if (Sel[0].Type!=1 Line[Sel[0].Num].PointNum!=2) Application->MessageBox("Select a line first","caution",mb_iconexclamation); ptpoint a=point[line[sel[0].num].point[0]]; ptpoint b=point[line[sel[0].num].point[1]]; for(i=1;i<selnum;i++) Point[PointNum]=MirrorPoint(a,b,Point[Sel[i].Num]); PointNum++; 36
More Points 새로운점의생성 MirrorPoint 함수의작성 ptpoint fastcall TChildForm::MirrorPoint(ptPoint a,ptpoint b,ptpoint c) float m,n,p,q; ptpoint d,r; if (a.x==b.x) d.x=2*a.x-c.x; d.y=c.y; else if (a.y==b.y) d.x=c.x; d.y=2*b.y-c.y; (c.x, c.y) else return d; m=(a.y-b.y)/(a.x-b.x); n=a.y-m*a.x; p=-1/m; q=c.y-p*c.x; r.x=(n-q)/(p-m); r.y=m*r.x+n; d.x=2*r.x-c.x; d.y=2*r.y-c.y; y=px+q (a.x, a.y) (r.x, r.y) (d.x, d.y) (b.x, b.y) y=mx+n p r. x + q = m r. x + n n q r. x =, r. y = mx + n p m b. y a. y m =, n = a. y m a. x b. x a. x 1 p =, q = c. y p c. x m d. x = 2 r. x c. x d. y = 2 r. y c. y 37