/*global require*/ 'use strict'; /************************************************************************ ### Drone.arc() method The arc() method can be used to create 1 or more 90 degree arcs in the horizontal or vertical planes. This method is called by cylinder() and cylinder0() and the sphere() and sphere0() methods. #### Parameters arc() takes a single parameter - an object with the following named properties... * radius - The radius of the arc. * blockType - The type of block to use - this is the block Id only (no meta). See [Data Values][dv]. * meta - The metadata value. See [Data Values][dv]. * orientation (default: 'horizontal' ) - the orientation of the arc - can be 'vertical' or 'horizontal'. * stack (default: 1 ) - the height or length of the arc (depending on the orientation - if orientation is horizontal then this parameter refers to the height, if vertical then it refers to the length ). * strokeWidth (default: 1 ) - the width of the stroke (how many blocks) - if drawing nested arcs it's usually a good idea to set strokeWidth to at least 2 so that there are no gaps between each arc. The arc method uses a [bresenham algorithm][bres] to plot points along the circumference. * fill - If true (or present) then the arc will be filled in. * quadrants (default: `{topleft:true,topright:true,bottomleft:true,bottomright:true}` - An object with 4 properties indicating which of the 4 quadrants of a circle to draw. If the quadrants property is absent then all 4 quadrants are drawn. #### Examples To draw a 1/4 circle (top right quadrant only) with a radius of 10 and stroke width of 2 blocks ... arc({blockType: blocks.iron, meta: 0, radius: 10, strokeWidth: 2, quadrants: { topright: true }, orientation: 'vertical', stack: 1, fill: false } ); ![arc example 1](img/arcex1.png) [bres]: http://en.wikipedia.org/wiki/Midpoint_circle_algorithm [dv]: http://www.minecraftwiki.net/wiki/Data_values ***/ /* do the bresenham thing */ function bresenham( x0,y0,radius, setPixel, quadrants ) { // // credit: Following code is copied almost verbatim from // http://en.wikipedia.org/wiki/Midpoint_circle_algorithm // Bresenham's circle algorithm // var f = 1 - radius; var ddF_x = 1; var ddF_y = -2 * radius; var x = 0; var y = radius; var defaultQuadrants = {topleft: true, topright: true, bottomleft: true, bottomright: true}; quadrants = quadrants?quadrants:defaultQuadrants; /* II | I ------------ III | IV */ if ( quadrants.topleft || quadrants.topright ) setPixel(x0, y0 + radius ); // quadrant I/II topmost if ( quadrants.bottomleft || quadrants.bottomright ) setPixel(x0, y0 - radius ); // quadrant III/IV bottommost if ( quadrants.topright || quadrants.bottomright ) setPixel(x0 + radius, y0 ); // quadrant I/IV rightmost if ( quadrants.topleft || quadrants.bottomleft ) setPixel(x0 - radius, y0 ); // quadrant II/III leftmost while ( x < y ) { if(f >= 0 ) { y--; ddF_y += 2; f += ddF_y; } x++; ddF_x += 2; f += ddF_x; if ( quadrants.topright ) { setPixel(x0 + x, y0 + y ); // quadrant I setPixel(x0 + y, y0 + x ); // quadrant I } if ( quadrants.topleft ) { setPixel(x0 - x, y0 + y ); // quadrant II setPixel(x0 - y, y0 + x ); // quadrant II } if ( quadrants.bottomleft ) { setPixel(x0 - x, y0 - y ); // quadrant III setPixel(x0 - y, y0 - x ); // quadrant III } if ( quadrants.bottomright ) { setPixel(x0 + x, y0 - y ); // quadrant IV setPixel(x0 + y, y0 - x ); // quadrant IV } } }; function getStrokeDir( x,y ) { var absY = Math.abs(y ); var absX = Math.abs(x ); var strokeDir = 0; if ( y > 0 && absY >= absX ) strokeDir = 0 ; //down else if ( y < 0 && absY >= absX ) strokeDir = 1 ; // up else if ( x > 0 && absX >= absY ) strokeDir = 2 ; // left else if ( x < 0 && absX >= absY ) strokeDir = 3 ; // right return strokeDir; }; /* The daddy of all arc-related API calls - if you're drawing anything that bends it ends up here. */ function arcImpl( params ) { var drone = params.drone; var orientation = params.orientation?params.orientation:'horizontal'; var quadrants = params.quadrants?params.quadrants:{ topright:1, topleft:2, bottomleft:3, bottomright:4 }; var stack = params.stack?params.stack:1; var radius = params.radius; var strokeWidth = params.strokeWidth?params.strokeWidth:1; drone.chkpt('arc2' ); var x0, y0, gotoxy,setPixel; if ( orientation == 'horizontal' ) { gotoxy = function( x,y ) { return drone.right(x ).fwd(y );}; drone.right(radius ).fwd(radius ).chkpt('center' ); switch ( drone.dir ) { case 0: // east case 2: // west x0 = drone.z; y0 = drone.x; break; case 1: // south case 3: // north x0 = drone.x; y0 = drone.z; } setPixel = function( x, y ) { x = ( x-x0 ); y = ( y-y0 ); if ( params.fill ) { // wph 20130114 more efficient esp. for large cylinders/spheres if ( y < 0 ) { drone .fwd( y ).right( x ) .cuboidX( params.blockType, params.meta, 1, stack, Math.abs( y * 2 ) + 1 ) .back( y ).left( x ); } }else{ if ( strokeWidth == 1 ) { gotoxy(x,y ) .cuboidX( params.blockType, params.meta, 1, // width stack, // height strokeWidth // depth ) .move('center' ); } else { var strokeDir = getStrokeDir( x, y ); var width = 1, depth = 1; switch ( strokeDir ) { case 0: // down y = y-( strokeWidth - 1 ); depth = strokeWidth; break; case 1: // up depth = strokeWidth; break; case 2: // left width = strokeWidth; x = x-(strokeWidth-1 ); break; case 3: // right width = strokeWidth; break; } gotoxy( x, y ) .cuboidX( params.blockType, params.meta, width, stack, depth ) .move( 'center' ); } } }; }else{ // vertical gotoxy = function( x,y ) { return drone.right(x ).up(y );}; drone.right(radius ).up(radius ).chkpt('center' ); switch ( drone.dir ) { case 0: // east case 2: // west x0 = drone.z; y0 = drone.y; break; case 1: // south case 3: // north x0 = drone.x; y0 = drone.y; } setPixel = function( x, y ) { x = ( x - x0 ); y = ( y - y0 ); if ( params.fill ) { // wph 20130114 more efficient esp. for large cylinders/spheres if ( y < 0 ) { drone .up( y ).right( x ) .cuboidX( params.blockType, params.meta, 1, Math.abs( y * 2 ) + 1, stack ) .down( y ).left( x ); } }else{ if ( strokeWidth == 1 ) { gotoxy( x, y ) .cuboidX( params.blockType, params.meta, strokeWidth, 1, stack ) .move( 'center' ); }else{ var strokeDir = getStrokeDir( x,y ); var width = 1, height = 1; switch ( strokeDir ) { case 0: // down y = y - ( strokeWidth - 1 ); height = strokeWidth; break; case 1: // up height = strokeWidth; break; case 2: // left width = strokeWidth; x = x - ( strokeWidth - 1 ); break; case 3: // right width = strokeWidth; break; } gotoxy(x,y ) .cuboidX(params.blockType, params.meta, width, height, stack ) .move('center' ); } } }; } /* setPixel assumes a 2D plane - need to put a block along appropriate plane */ bresenham(x0,y0,radius,setPixel,quadrants ); params.drone.move('arc2' ); }; module.exports = function(Drone){ Drone.extend(function arc( params ) { params.drone = this; arcImpl( params ); }); };