How to Draw a Sphere - gnembon/fabric-carpet GitHub Wiki
Carpet has a built-in tool to draw basic shapes. Sphere is one of them, so you don't have to use scarpet to draw it, but its a good exercise to do something like that. /draw sphere
is even much more efficient, only looking up the blocks it knows it needs to check to draw a sphere, so it will be always much faster comparing to the other methods below, but you wouldn't be able to feel it since setting up that many blocks in the air would cause the game to struggle for a good second or two anyways, regardless of how inefficient your math is. In the following examples we will be drawing circles centred around (100,100,100) with a radius of 50 blocks. To clear the drawing area, we can just use the following command:
/script run scan(100,100,100,50,50,50,set(_,'air'))
Easiest way to draw a circle is with draw command:
/draw sphere 100 100 100 50 glass
or to draw it over existing terrain without affecting existing blocks besides air:
/draw sphere 100 100 100 50 glass replace air
To safely remove it without affecting existing blocks, we just need to draw back a circle using air blocks:
/draw sphere 100 100 100 50 air replace glass
but that's not a method that allows us to use mathematics. For that we need scarpet
Note: to draw in a large area, you van to adjust /carpet fillLimit
since /script fill
and /script scan
are limited to 32k volume as per /fill
command regulations.
It works like vanilla fill, just allows us to draw blocks that test positive at a given block position. The first set of coordinates, origin - indicates the (0,0,0) reference for the expression, so what would x
, y
, and z
coordinates be referring to. Setting origin to (0,0,0) makes so that these correspond directly to the world coordinates, but by shifting the zero reference to the center of the sphere we can simplify our expression.
Since the circle has 50 blocks radius, the drawing area spans from (50,50,50) to (150,150,150), centered around (100,100,100)
Lets draw a solid ball first:
/script fill 100 100 100 50 50 50 150 150 150 "x*x + y*y + z*z <= 50*50" glass replace air
That works because that succeeds for a lot of blocks. Doing perfect ==
for a sphere would only match in a few spots, for that we would need to round it a bit:
/script fill 100 100 100 50 50 50 150 150 150 "round(sqrt(x*x + y*y + z*z)) == 50" glass replace air
Note we need to quote the expression since there are other parameters for the command that come after
This is a more generalised command that evaluates an expression in the area. Instead of relying on the command to draw blocks, we can draw them ourselves using set
command:
/script scan 100 100 100 50 50 50 150 150 150 if( round(sqrt(x*x + y*y + z*z)) == 50, set(_, 'glass'))
or (to skip existing blocks)
/script scan 100 100 100 50 50 50 150 150 150 if( air(_) && round(sqrt(x*x + y*y + z*z)) == 50, set(_, 'glass'))
In the last case we don't even use coordinates system from the scan and fill commands, meaning that we need to do all the math on our own:
/script run
c = l(100,100,100);
r = 50;
scan(c,r,r,r,
if ( air(_),
v=pos(_)-c;
if( round(sqrt(v:0*v:0+v:1*v:1+v:2*v:2))==r,
set(_, 'glass')
)
)
);`
Little confusing? its because we need to set the stage for drawing ourselves. First we defined the center c
and radius r
and used scan
function to iterate over blocks in the area. We want to skip blocks that are not air blocks (which include also 'cave_air'
and 'void_air'
), then we defined a vector v
pointing from the center of the circle to the current evaluated point, and then assessed that the length of this vector needs to be equal to the circle radius. Little overkill, but we made it. This can be actually optimised a little bit using the reduce function instead of listing all the coordinates:
/script run
c = l(100,100,100);
r = 50;
scan(c,r,r,r,
if ( air(_) && round(sqrt(reduce(pos(_)-c, _a+_*_, 0)))==r,
set(_, 'glass')
)
);