This repository has been archived on 2021-07-14. You can view files and clone it, but cannot push or open issues or pull requests.
scriptcraft/src/main/js/modules/drone/arc.js

268 lines
8.1 KiB
JavaScript

/*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 );
});
};