1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | typedef struct Point { //좌표 하나를 나타내는 구조체 int x; int y; } Point; //현재 커서를 x,y 좌표로 옮기는 함수. //만일 x나 y중 하나의 값이라도 현재 콘솔의 화면 버퍼 크기를 넘어서면 작동을 안함. //"mode con: 을 소스에서 검색해보면 버퍼 최대크기를 알 수 있음. 최대크기 -1 까지가 한계다. void gotoXY(int x, int y) { COORD coordinates = {x, y}; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coordinates); } //두 Point변수의 주소를 입력하면 (두 좌표가 형성하는) 선분 위의 모든 좌표들에 입력한 문자변수ch를 출력한다. //이 때 두 좌표 그 자체에도 ch가 출력된다.(선분을 이루는 점까지 출력) void drawLine(const Point *p1,const Point *p2, char ch) { int x1 = (p1->x), y1 = (p1->y); //p1 int x2 = (p2->x), y2 = (p2->y); //p2 //일차함수 y = a*x + b float a;//기울기 float b;//y절편 int length, i;//length는 선분의 길이. 선분을 이루는 점까지 포함한다. if(x1 == x2 && y1 == y2){ gotoXY(x1, y1); _putch(ch); return; } if(x2 - x1){ a = (float)(y2 - y1) / (float)(x2 - x1); b = (float)y1 - a*(float)x1; }else{//기울기가 무한일 때의 예외처리.( /0 처리) length = y2 - y1;//x값 차이는 0이기에 y기준으로 출력된다. if(length > 0){ for(i = 0; i <= abs(length); i++){//두 점까지 포함시키기 위해 0~length까지다. gotoXY( x1, y1+i );//x값은 y=ax+b에서 x를 묶어내어 x=(y-b)/a이다. _putch(ch); } }else{ for(i = 0; i <= abs(length); i++){ gotoXY( x1, y2+i ); _putch(ch); } } return; } //좌표간의 차이가 큰 놈으로 출력한다. 그래야 점선이 출력되지 않는다. if( abs(x2 - x1) > abs(y2 - y1) ){ //x기준 출력 length = x2 - x1; //'선분+두 점'의 길이보다 1이 작다. if(length > 0){ for(i = 0; i <= abs(length); i++){//두 점까지 포함시키기 위해 0~length까지다. gotoXY(x1+i, round( a*(x1+i) + b ) );//y값은 일차식 y=ax+b를 내린 값이다. _putch(ch); } }else{ for(i = 0; i <= abs(length); i++){ gotoXY(x2+i, round( a*(x2+i) + b ) ); _putch(ch); } } }else{ //y기준 출력 length = y2 - y1; if(length > 0){ for(i = 0; i <= abs(length); i++){//두 점까지 포함시키기 위해 0~length까지다. gotoXY( round( (y1+i - b)/a ), y1+i );//x값은 y=ax+b에서 x를 묶어내어 x=(y-b)/a이다. _putch(ch); } }else{ for(i = 0; i <= abs(length); i++){ gotoXY( round( (y2+i - b)/a ), y2+i ); _putch(ch); } } // a==0일때의 예외처리가 필요 없는 이유가, a==0일 때는 y가 같을 때인데 이때는 x를 기준으로 출력하기 때문이다. 이쪽 블럭으로는 오지도 않는다는 말. } } //반올림 함수. 입력한 값을 소수 첫째자리에서 반올림하여 정수값으로 만든다. int round(double num) { double decimal = num - (int)num; if(decimal >= 0.5) return (int)num+1; else return (int)num; } | cs |
성공적.
문제가 되는 부분이나 거슬리는 부분은 지적 부탁드립니다!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | int main(){ Point pointArr_1[10]= { {0,80}, {10,70}, {20,60}, {40,50}, {60,40}, {70,30}, {60,20}, {50,10}, {30,0}, {10,10}, }; Point p2 = {30,30}; int i; //drawLine함수 실험, 성공적!! for(i = 0; i < 10; i++){ drawLine(pointArr_1 + i, &p2, '#'); } _getch();//계속하려면 아무키나.. 지워두림 return 0; } | cs |
성게 소환
걍 WIN API를 해라... 콘솔로 하는건 레알 뻘짓
조음
헤헤 이번 방학때는 콘솔로만 해볼 생각...
콘솔로 만들거면 roguelike 게임 만들던가
@고기 그것도 재밌겠네요. 근데 일단 찍어둔 거부터 만들어볼 생각...
텍스트로 벡터그래픽할꺼임?
반올림 함수 걍 (int)(val + 0.5) 하면 되는데 뭐 저리 어렵게 짰대?
ㄴ엑 그렇네 바꿔야겠다 ㄳㄳ
@나라뜨 윷놀이 판 같은걸 텍스트로 그려볼 생각임
하 역시 알고리즘은 2개 이상 생각해봐야 하는듯... 고마워요 ㅅㅅ님!
ㄴ 음수일때는?
-0.5 에 대한 반올림은 -1이어야함.
그리고 니가 작성한 선그리기 알고리즘은 DDA 야.
음. 초장부터 너무 그래픽에 신경쓰면 하다가 지치니까, 선 출력 부분은 걍 맵 파일에 텍스트로 직접 박아놓고 칸 좌표만 저장한 뒤 나중에 게임 로직 완성 후 구조를 직접 코드로 출력하는 걸 바꾸는 걸 권장.
!!
mid-point line / bresenham's 알고리즘을 구글링해봐.
codesafer // 음수일 때도 +0.5 하면 되죠. 올림이 절대값 기준으로 올림이 아님...
ㅅㅅㅅ 는 round 함수를 한번 돌려보고 오면 좋을것 같아.
codesafer // 초보한테 너무 어려운거 알려주시는듯 ㅋㅋㅋ DDA만 해도 큰 상관 없지 않나요? ㅋㅋㅋ
0을 기준으로 문자를 찍었는데 0.5 일때 우측 칸한에 찍었다 치자, -0.5일때 0 위치에 찍히면 대칭이 되겠니?
고기//던가->든가
codesafer // 그건 반올림 문제가 아니라 선 출력 알고리즘을 고쳐야 하는 문제죠. 어쨌든 반올림 함수 자체는 맞아요. 올림 (ceiling)은 절대값 기준이 아니라 + 방향 기준.
여기선 ceil 도 floor 도 아니라 round 가 맞아.
-5.5 -6.0 <- 이게 round 의 예시야.
The value of x rounded to the nearest integral 이게 argument x 에 대한 round 의 정의임.
네 성님이 말씀하시려는 건 잘 알죠. 그니까 절대값 기준의 rounding을 해야된다는 말 아닌가요? 근데 이건 우리가 흔히 알고 쓰는 표준 반올림이랑은 조금 다른 개념이라서요. 전 round 함수가 표준 반올림 구현은 완벽하다는 것이었고요. 선 출력 시엔 abs_round 같은 거 하나 새로 짜서 하면 되겠죠.
하여튼 답변 감사합니다. 오늘도 배워가네요
반올림의 정의는 근삿값을 구하는건데, -0.9 의 근삿값이 -1이냐 0이냐.
Round half away from zero도 rounding 방법의 하나긴 하지만 Round half up이 더 대중적이라고 하죠. 불론 전자도 라운딩이라고 해도 틀린 건 아니겠네요. M$ 애들이 Banker's Rounding을 round() 함수로 제공하는 걸 봐도 뭐 뭐를 쓰든 다 round라는 이름을 붙여도 큰 문지는 없긴 할 듯.
http://www.mathlove.kr/shop/board/view.php?id=mathqa&tm=1&menus=share5&no=1618
엑셀 > ROUND(-0.25,1) -0.25를 유효 자릿수 한 자리로 반올림합니다(-0.3).
그게...
https://en.wikipedia.org/wiki/Rounding
를 보시변 둘 다 맞아요.
그리고 -0.9 는 -1 나오는 거 맞을텐데요?
아! (int)로 캐스팅하는 거랑 floor는 다르네.
floor(x + 0.5) 대로라면 -0.9도 -1 나와요. 캐스팅은 소수점 날리는 거지 floor랑은 다르니깐. 틀리긴 하겠네요.
int(-0.9) -> 0
(int)(...)랑 floor(...)랑 같다고 생각한 제 착오네요.
이걸로 길게 이야기하기 싫은데 =_=
난 그래픽스 프로그래머라고...
니 방법을 쓰면 -1.4 도 0으로 보내버리잖아.
반올림 코드를 return (int)floor(num + 0.5);로 고치면 될 듯.
니 방법은 걍 1사분면에서 놀때만 써... 다들 그렇게 해...
전 이렇게 했음 if(num >= 0) return (int)(num + 0.5); else return (int)(num - 0.5); 그냥 #include없이 쓸려구요
근데 콘솔은 1사분면 밖에 없으니 뭐...
mid-point line 을 보고 이식하는게 좋아. 텍스트 해상도가 아닌 그래픽스 해상도에서도 껍데기를 재활용하고 싶다면.
그리고 for 문 반복 조건에 abs(length) 하는 애가 어딨냐.
루프에서 매번 구할 필요가 있어?
자세히는 안 봤는데, mid-point line은 어떤점이 DDA에 비해서 좋은가요?
아 그렇네요 ㅋㅋ 고쳐야겠다
for 문 두단으로 구성한것두, x1 을 min(x1, x2), x2 를 max(x1, x2) 로 정의하면 하나로 줄겠네. 물론, if 문에서 x1, x2 를 비교하고 length 양수값으로 셋, x1, x2 swap 을 할지 말지를 선택하게 하는식으로 풀어야 정상동작 하는거지.
mid-point 는 실수연산이 없어. 아예. 다 그냥 정수연산.
즉, if (x1 < x2) { length = x2 - x1; } else { length = x1 - x2; x1 ^= x2 ^= x1 ^= x2; } 으로 하면 그 뒤 올 for 문 두개가 하나로 준단 이야기.
비트연산자 ㄷㄷ
XOR 저건 xor swap인데 코세 성님이 심심하신 듯. ㅋㅋ 저건 뽀대만 날 뿐 비효율적임. 컴파일러 최적화를 생각하면 임시 변수 선언해서 정석적으로 스왑하는 게 더 효율이 높음.
ㅇㅇ 그 말이 맞음 그냥 int t 하기 귀찮았어.
그리고 츄럴아. a*(x1+i) + b 이게 for 문에서 돌고 있다. 뭔가 반성이 안되니?
for문 들어가기 전에 결정되어 있는게 a 와 x1 과 b 야.
즉 a * x1 + a * i + b 에서,
pre = a * x1 + b; 라고 미리 정해두면, for 문 안에선 pre + a * i 만 하면 된다는거야.
와 그렇네요.. 저건 진짜 바꿔야 겠어요 흐으
연산 네 개가 두 개로 줄었지?
어차피 gotoxy 랑 putch 가 열라 느려서 별 티도 안나겠지만, 버퍼에 점찍는다든지, 버퍼에 문자 찍을땐 완전 달라짐.