Skip to content

How to Draw a Sphere

Ghoulboy edited this page Jun 3, 2020 · 2 revisions

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'))

Using /draw circle <center> <radius> <block> (replace <replacement>)? command

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

Using /script fill <origin> <from> <to> <expression> <block> (replace <replacement>)?

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

Using /script scan <origin> <from> <to> expr

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'))

Using /script run expr

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')
  )
);