2014-12-28 16:07:08 +01:00
|
|
|
/*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' );
|
|
|
|
};
|
|
|
|
|
2015-01-03 21:57:30 +01:00
|
|
|
module.exports = function(Drone){
|
|
|
|
Drone.extend(function arc( params ) {
|
|
|
|
params.drone = this;
|
|
|
|
arcImpl( params );
|
|
|
|
});
|
|
|
|
};
|