/* * MICROCAD Drawing utility * * This is a "boiled down" set of routines to draw a MICROCAD drawing file. * The main purpose of this program is to provide a starting point, should * you wish to incorporate code to display a MICROCAD drawing file in one * of your own programs. Most of this file should be useable without much * modification. * * Copyright 1992-1995 Dave Dunfield * All rights reserved. * * Permission granted for personal (non-commercial) use only. * * Compile with: cc mcdraw -fop */ #include /* Misc constants */ #define VERTICAL 480 /* Screen height */ #define HORIZONTAL 640 /* Screen width */ #define ARC_RES 64 /* Resolution of each ARC quadrant */ #define PBYTES 11 /* # of prefix bytes */ /* Drawing buffer codes */ #define LINE 0x01 /* Object is a LINE */ #define BOX 0x02 /* Object is a BOX */ #define CIRCLE 0x03 /* Object is a CIRCLE */ #define TEXT 0x04 /* Object is a TEXT string */ #define ARC 0x05 /* Object is an ARC */ #define GROUP 0x06 /* Object is a GROUP of objects */ #define ACOPY 0x07 /* Copy an object (absolute) */ #define RCOPY 0x08 /* Copy an object (relative) */ /* Global storage */ char drawing[32000], font[4608], vmode; unsigned dpos; /* * Sine table for drawing ARC's */ unsigned sine[] = { 0, 1608, 3216, 4821, 6424, 8022, 9616, 11204, 12785, 14359, 15924, 17479, 19024, 20557, 22078, 23586, 25079, 26557, 28020, 29465, 30893, 32302, 33692, 35062, 36410, 37736, 39040, 40320, 41575, 42806, 44011, 45190, 46341, 47464, 48559, 49624, 50660, 51665, 52639, 53581, 54491, 55368, 56212, 57022, 57797, 58538, 59243, 59913, 60547, 61144, 61705, 62228, 62714, 63162, 63571, 63943, 64276, 64571, 64826, 65043, 65220, 65358, 65457, 65516 }; /* * Main program - read files and draw image */ main(argc, argv) int argc; char *argv[]; { unsigned i; FILE *fp; if(argc < 2) abort("\nUse: MCDRAW [font file]\n\nCopyright 1992-1995 Dave Dunfield\nAll rights reserved.\n"); /* Read in the font file */ concat(drawing, (argc > 2) ? argv[2] : "MICROCAD", ".FNT"); fp = fopen(drawing, "rbvq"); fget(font, sizeof(font), fp); fclose(fp); /* Read in drawing file */ concat(drawing, argv[1], ".DWG"); fp = fopen(drawing, "rbvq"); fget(drawing, PBYTES, fp); i = fget(drawing, sizeof(drawing), fp); fclose(fp); /* Zero unused portion of drawing file */ while(i < sizeof(drawing)) drawing[i++] = 0; /* Setup PC video screen */ if(!init_video()) abort("VGA required!"); video_mode(0x11); /* Draw all the objects */ dpos = 0; while(!draw_object(0, 0)); /* Wait for carriage-return and exit */ fgets(drawing, 100, stdin); video_mode(vmode); } /* * Draws an object from the drawing file */ draw_object(xoffset, yoffset) int xoffset, yoffset; { int x, y, i, j, k, l; char buffer[80], *ptr; i = drawing[dpos++]; x = dvalue() + xoffset; y = dvalue() + yoffset; switch(i) { case LINE : line(x, y, x+dvalue(), y+dvalue()); break; case BOX : box(x, y, x+dvalue(), y+dvalue()); break; case CIRCLE : circle(x, y, dvalue()); break; case TEXT : i = dvalue(); ptr = buffer; do *ptr++ = j = drawing[dpos++]; while(j); text(buffer, x, y, i); break; case ARC: arc(x, y, dvalue(), dvalue(), dvalue()); break; case GROUP : i = dvalue(); j = dpos; while((dpos - j) < i) draw_object(x, y); break; case RCOPY : i = dpos - 5; i += dvalue(); goto gocopy; case ACOPY : i = dvalue(); gocopy: j = dpos; dpos = i+1; k = dvalue(); l = dvalue(); dpos = i; draw_object(x - k, y - l); dpos = j; break; default: abort("Corrupt drawing file!"); case 0 : return -1; } return 0; } /* * Draw a line from point (x1, y1) to (x2, y2) */ line(x1, y1, x2, y2) int x1, y1, x2, y2; { int i, w, h; /* If 'X' is greater, increment through 'X' coordinate */ if((w = abs(x1 - x2)) >= (h = abs(y1 - y2))) { if(x1 > x2) { i = x1; x1 = x2; x2 = i; i = y1; y1 = y2; y2 = i; } if(y1 < y2) { for(i=0; i < w; ++i) set_pixel(x1+i, y1+scale(i, h, w)); } else { for(i=0; i < w; ++i) set_pixel(x1+i, y1-scale(i, h, w)); } } /* If 'Y' is greater, increment through 'Y' coordinate */ else { if(y1 > y2) { i = x1; x1 = x2; x2 = i; i = y1; y1 = y2; y2 = i; } if(x1 < x2) { for(i=0; i < h; ++i) set_pixel(x1+scale(i, w, h), y1+i); } else { for(i=0; i < h; ++i) set_pixel(x1-scale(i, w, h), y1+i); } } set_pixel(x2, y2); } /* * Draw a box with opposite corners (x1, y1) to (x2, y2) */ box(x1, y1, x2, y2) int x1, y1, x2, y2; { line(x1, y1, x2, y1); /* Top */ line(x1, y1, x1, y2); /* Left side */ line(x2, y1, x2, y2); /* Right side */ line(x1, y2, x2, y2); /* Bottom */ } /* * Draw a circle about point (x, y) of radius (r) */ circle(x, y, r) int x, y, r; { int i, j, k, rs, lj; rs = (lj = r)*r; for(i=0; i <= r; ++i) { j = k = sqrt(rs - (i*i)); do { set_pixel(x+i, y+j); set_pixel(x+i, y-j); set_pixel(x-i, y+j); set_pixel(x-i, y-j); } while(++j < lj); lj = k; } } /* * Draw an arc centered at (x, y), radius r at vectors v1, v2 */ arc(x, y, r, v1, v2) int x, y, r; unsigned char v1, v2; { int rs, i, j, ax, x1, y1, x2, y2; x2 = -1; rs = r*r; do { j = (ARC_RES-1) - (i = v1 & (ARC_RES-1)); switch(v1 & (ARC_RES*3)) { case ARC_RES*0 : /* Quadrant one */ x1 = x + (ax = scale(r, sine[i], -1)); y1 = y - sqrt(rs - (ax*ax)); break; case ARC_RES*1 : /* Quadrant two */ x1 = x + (ax = scale(r, sine[j], -1)); y1 = y + sqrt(rs - (ax*ax)); break; case ARC_RES*2 : /* Quadrant three */ x1 = x - (ax = scale(r, sine[i], -1)); y1 = y + sqrt(rs - (ax*ax)); break; case ARC_RES*3 : /* Quadrant four */ x1 = x - (ax = scale(r, sine[j], -1)); y1 = y - sqrt(rs - (ax*ax)); } if(x2 != -1) line(x2, y2, x1, y1); x2 = x1; y2 = y1; } while(v1++ != v2); } /* * Draw a text string on the screen at specified scale */ text(string, x, y, s) char *string; int x, y, s; { unsigned i, j, b; unsigned char *ptr; y -= scale(24, s, 100); while(*string) { ptr = &font[(*string++ - ' ') * 48]; for(i=0; i < 24; ++i) { b = (*ptr++ << 8) | *ptr++; for(j=0; j < 16; ++j) { if(b & 0x8000) set_pixel(x+scale(j,s,100), y+scale(i,s,100)); b <<= 1; } } x += scale(18, s, 100); } } /* * Retrieve a 16 bit value from the drawing list */ dvalue() { return (drawing[dpos++] << 8) | drawing[dpos++]; } /* * Scale a value by a fraction, using a 32 bit intermediate result, * and round up/down to nearest integer ressult. */ scale(value, mul, div) asm { MOV AX,8[BP] ; Get value MUL WORD PTR 6[BP] ; Multiply to 32 bit product MOV BX,4[BP] ; Get divisor DIV BX ; Divide back to 16 bit result SHR BX,1 ; /2 for test JZ scale1 ; Special case (/1) INC DX ; .5 rounds up SUB BX,DX ; Set 'C' if remainder > half ADC AX,0 ; Increment result to scale scale1: } /* * Initialize the video display */ init_video() asm { ; First, determine current mode for later MOV AH,0Fh ; Get video mode INT 10h ; Call BIOS MOV DGRP:_vmode,AL ; Save video mode ; Check for VGA present MOV AX,1A00h ; Get display code INT 10h ; Call BIOS CMP AL,1Ah ; VGA supported? MOV AL,-1 ; Assume yes JZ initv1 ; It does exist XOR AX,AX ; Report failure initv1: } /* * Set active video mode & clear screen */ video_mode(mode) asm { MOV AL,4[BP] ; Get mode XOR AH,AH ; Func 0 - set mode INT 10h ; Issue mode } /* * Set a graphic pixel. */ set_pixel(x, y) asm { MOV DX,4[BP] ; Get Y coordinate CMP DX,VERTICAL ; In range? JAE noset ; No, skip it MOV CX,6[BP] ; Get X coordinate CMP CX,HORIZONTAL ; In range? JAE noset ; No, skip it MOV AH,0Ch ; Write pixel function MOV AL,0fh ; Pixel SET attribute XOR BH,BH ; Zero page INT 10h ; Call int 16 noset: }