Tools

There are some useful tools that might help you explore the limited 3D world provided by Thebes.jl.

General

Thebes.carpetFunction
carpet(n; kind=:circular)

Draw a circular carpet centered at the origin, using current Luxor parameters.

If kind is not :circular, the carpet will be a square.

Points that can't be rendered are not included in the final shape.

source
Thebes.drawcubeFunction
drawcube(n=10, action=:stroke)

Draw a cube. drawcube(1) draws a wireframe unit cube.

source
@drawsvg begin
background("grey20")
helloworld()
sethue("grey40")
carpet(400)
drawcube(150)
axes3D()
end 800 400

There are some basic geometry utility functions - some of them are analogous to their Luxor 2D counterparts.

Distances

Luxor.betweenFunction
between(p1::Point3D, p2::Point3D, x=0.5)
between((p1::Point3D, p2::Point3D), x=0.5)

Find a point on a line between two 3D points. If x is 0.5, the returned point should be halfway between them.

source
Luxor.distanceFunction
distance(p1::Point3D, p2::Point3D)

Return the distance between two points.

source
Luxor.midpointFunction
midpoint(pt1::Point3D, pt2::Point3D)

Find the midpoint between two points. See also between().

source

Rotations

The task of rotating points in 3D space is given to Rotations.jl, a powerful and sophisticated package that offers many advanced functions for rotating things in 3D space. For Thebes, you'll probably only need the basics, but there's things like quaternions if you want to get fancy.

This code draws a cyan square lying in the XY plane with a corner at the 3D origin. The square is then rotated about the Z axis by 180° and drawn in purple. Then the square is rotated again, about the X axis, by 90° and drawn in orange.

@drawsvg begin

eyepoint(Point3D(150, 250, 350))
perspective(520)

function drawsquare(ptlist)
    pin(ptlist, gfunction = (p3, p2) ->
        poly(p2, :fill, close=true))
end

square = [
    Point3D(0, 0, 0),
    Point3D(100, 0, 0),
    Point3D(100, 100, 0),
    Point3D(0, 100, 0)
    ]

sethue("cyan")
drawsquare(square)

sethue("purple")
rotateby!(square, RotZ(π))
drawsquare(square)

sethue("orange")
rotateby!(square, RotX(-π/2))
drawsquare(square)

axes3D(160)

end 800 400

The most useful rotation functions are RotX(), RotY(), RotZ(), RotXY(), and RotXYZ(), which rotate around the axes. All the other permutations are available. A RotXYZ() rotation takes three angles. The right-most rotation is applied first, so RotXYZ() applies the Z rotation, followed by the Y, then followed by the X.

You can compose two or more rotations by multiplying them together, eg RotX(π/2) * RotZ(π/4).

The rotation functions without ! return new points or arrays of points.

There are also functions that accept a second 3D point, the about point. The rotation is applied around that point, rather than an axis. In the next example, the square is rotated in Z about the corner point at Point3D(100, 100, 0) and drawn in green.

@drawsvg begin
    background("grey20")
    setlinejoin("bevel")

    eyepoint(Point3D(350, 350, 350))
    perspective(400)

    function drawsquare(ptlist)
        pin(ptlist, gfunction=(p3, p2) ->
            poly(p2, :fill, close=true))
    end

    square = [
        Point3D(0, 0, 0),
        Point3D(100, 0, 0),
        Point3D(100, 100, 0),
        Point3D(0, 100, 0)
    ]

    sethue("grey40")
    drawsquare(square)

    sethue("green")
    rotateby!(square, Point3D(100, 100, 0), RotZ(π))
    drawsquare(square)

    axes3D(160)
end 800 400
  • rotateby(point::Point3D, r::Rotation)

  • rotateby(point::Point3D, about::Point3D, r::Rotation)

  • rotateby(ptlist::Array{Point3D, 1}, r::Rotation)

  • rotateby(ptlist::Array{Point3D, 1}, about::Point3D, r::Rotation)

  • rotateby(ptlist::Array{Point3D, 1}, angleX, angleY, angleZ)

  • rotateby(pt::Point3D, angleX, angleY, angleZ)

  • rotateby(point::Point3D, about::Point3D, angleX, angleY, angleZ)

  • rotateby(m::Object, angleX, angleY, angleZ)

  • rotateby(m::Object, pt::Point3D, angleX, angleY, angleZ)

Those with ! mutate the array of points in place. (You can't modify a single point.)

  • rotateby!(ptlist::Vector{Point3D}, r::RotXYZ{Float64})

  • rotateby!(ptlist::Array{Point3D, 1}, angleX, angleY, angleZ)

  • rotateby!(ptlist::Array{Point3D, 1}, existingpt::Point3D, angleX, angleY, angleZ)

  • rotateby!(ptlist::Array{Point3D, 1}, existingpt::Point3D, r::Rotation)

  • rotateby!(m::Object, angleX, angleY, angleZ)

  • rotateby!(m::Object, pt::Point3D, angleX, angleY, angleZ)

Thebes.rotateXFunction
rotateX(pt3D::Point3D, rad)

Return a new point resulting from rotating the point around the x axis by an angle in radians.

Rotations are anticlockwise when looking along axis from 0 to +axis.

source
Thebes.rotateYFunction
rotateY(pt3D::Point3D, rad)

Return a new point resulting from rotating the point around the y axis by an angle in radians.

Rotations are anticlockwise when looking along axis from 0 to +axis.

source
Thebes.rotateZFunction
rotateZ(pt3D::Point3D, rad)

Return a new point resulting from rotating the point around the z axis by an angle in radians.

source
Thebes.rotateby!Function
rotateby!(ptlist::Array{Point3D, 1}, angleX, angleY, angleZ)

Modify a list of points by rotating each one around the x, y, and z axes by angleX, angleY, angleZ.

source
rotateby!(ptlist::Array{Point3D, 1}, existingpt::Point3D, angleX, angleY, angleZ)
rotateby!(ptlist::Array{Point3D, 1}, existingpt::Point3D, r::Rotation)
rotateby!(ptlist::Array{Point3D, 1}, r::Rotation=RotXYZ{Float64})

Rotate each point in the list by rotation (or angleX, angleY, angleZ) around another point (or origin).

source
rotateby!(o::Object, r::Rotation)
rotateby!(o::Object, angleX, angleY, angleZ)

Rotate an object through rotation r, or around the x, y, and/or z axis by angleX, angleY, angleZ.

source
rotateby!(o::Object, pt::Point3D, angleX, angleY, angleZ)
rotateby!(o::Object, pt::Point3D, r::Rotation=RotXYZ(0, 0, 0))

Rotate an object around a point by rotation r, or angleX, angleY, angleZ.

source
Thebes.rotatebyFunction
rotateby(pt::Point3D, angleX, angleY, angleZ)
rotateby(ptlist::Array{Point3D, 1}, angleX, angleY, angleZ)
rotateby(point::Point3D, r::Rotation)
rotateby(ptlist::Array{Point3D, 1}, r::Rotation)

Return a new point/list of points resulting from rotating around the x, y, and z axes by angleX, angleY, angleZ.

The Z rotation is first, then the Y, then the X.

A 3×3 rotation matrix parameterized by the "Tait-Bryant" XYZ Euler angle convention, consisting of first a rotation about the Z axis by theta3, followed by a rotation about the Y axis by theta2, and finally a rotation about the X axis by theta1.

source
rotateby(point::Point3D, about::Point3D, angleX, angleY, angleZ)
rotateby(point::Point3D, about::Point3D, r::Rotation)
rotateby(ptlist::Array{Point3D, 1}, about::Point3D, r::Rotation)
source
rotateby(o::Object, angleX, angleY, angleZ)

Rotate a copy of the object by angleX, angleY, angleZ.

source
rotateby(o::Object, pt::Point3D, angleX, angleY, angleZ)
rotateby(o::Object, pt::Point3D, r::Rotation=RotXYZ(0, 0, 0))

Rotate a copy of the object around a point by rotation r, or angleX, angleY, angleZ.

source

Position and scale

You can change the position and scale of things. moveby() makes a copy, moveby!() moves the original.

In the next example, the square is first moved by -100/-100/0, then copies are moved upwards by the loop index i.

@drawsvg begin
    background("grey20")
    setlinejoin("bevel")

    eyepoint(Point3D(400, 400, 500))
    perspective(600)

    function drawsquare(ptlist)
        pin(ptlist, gfunction=(p3, p2) ->
            poly(p2, :fill, close=true))
    end

    square = [
        Point3D(0, 0, 0),
        Point3D(100, 0, 0),
        Point3D(100, 100, 0),
        Point3D(0, 100, 0)
    ]

    sethue("grey40")
    drawsquare(square)

    moveby!(square, Point3D(-100, -100, 0))

    setopacity(0.6)
    for i in 10:10:200
        randomhue()
        drawsquare(moveby.(square, Point3D(0, 0, i)))
    end

    axes3D(200)
end 800 400

scaleby!() changes the scale of a list of points.

@drawsvg begin
    background("grey20")
    setlinejoin("bevel")

    helloworld()

    function drawsquare(ptlist)
        pin(ptlist, gfunction=(p3, p2) ->
            poly(p2, :fill, close=true))
    end

    axes3D(160)

    square = [
        Point3D(0, 0, 0),
        Point3D(100, 0, 0),
        Point3D(100, 100, 0),
        Point3D(0, 100, 0)
    ]

    sethue("red")
    drawsquare(square)

    sethue("blue")
    scaleby!(square, 0.5, 2, 1)
    moveby!(square, Point3D(0, 0, 30))
    drawsquare(square)
end 800 350
Thebes.moveby!Function
moveby!(ptlist::Point3D, x, y, z)
moveby!(ptlist::Point3D, pt::Point3D)

Move all points in the list by a vector.

source
moveby!(o::Object, x, y, z)
moveby!(o::Object, pt::Point3D)

Set the position of object to Point3D(x, y, z).

source
Thebes.movebyFunction
moveby(pt::Point3D, d::Point3D)

Return a new point that's the result of moving a point pt by a vector d.

source
moveby(o::Object, x, y, z)
moveby(o::Object, pt::Point3D)

Set the position of a copy of the object to Point3D(x, y, z).

source
Thebes.scaleby!Function
scaleby!(ptlist::Array{Point3D, 1}, x, y, z)

Scales a list of points by multiplying by x in X, y in Y, z in Z.

source
scaleby!(o::Object, x, y, z)

Scale object by x in x, y in y, and z in z.

source
scaleby!(o::Object, d)

Scale object by d in x, d in y, and d in z.

source

Coordinates

A useful function is sphericaltocartesian(). This takes three values using spherical coordinates - radius, azimuthal angle, polar angle - and converts them to Cartesian coordinates - x, y, and z.

The sphericaltocartesian() function accepts three arguments: (ρ, θ, φ) - a radius, azimuthal angle, and a polar angle, in that order, and converts it to a 3D point in Cartesian coordinates.

Note

One way to find out whether someone is a mathematician or a physicist is to ask them the order and meaning of the arguments (ρ, θ, φ) in a sphericaltocartesian() function. If ρ is the radius, is θ the azimuthal angle or the polar angle, and is φ the polar angle or the azimuthal angle? We're using the mathematicians’ order, apparently.

This animation shows the green semicircle changing its azimuthal angle from 0° through 360° while the the orange dot changes its polar angle from 0° to 180°.

spherical coordinates animation

Thebes.sphericaltocartesianFunction
sphericaltocartesian(ρ, θ, ϕ)

Return Point3D(x, y, z) corresponding to (ρ, θ, ϕ):

  • ρ is the distance from the origin (ie radius)

  • θ is the azimuthal angle (the longitude) 0 is +x, π is -x, 2π is +x

  • ϕ is the polar angle (the colatitude) 0 is North Pole, π is South Pole

There are two major conventions for spherical coordinate notation.

In physics books:

(ρ, θ, φ) gives the radial distance, polar angle (colatitude), and azimuthal angle (longitude)

In mathematics books:

(ρ, θ , φ ) gives the radial distance, azimuthal angle (longitude), and polar angle (colatitude)

So we're using the mathematics one here.

source
sphericaltocartesian((ρ, θ, ϕ))

Return Point3D(x, y, z) corresponding to (ρ, θ, ϕ).

source
Thebes.cartesiantosphericalFunction
cartesiantospherical(x, y, z)

Return (ρ, θ, ϕ) (radius, longitude, colatitude) of the Point3D(x, y, z).

source
cartesiantospherical(pt::Point3D)

Return (ρ, θ, ϕ) (radius, longitude, colatitude) of pt.

source
Thebes.pointsperpendicularFunction
pointsperpendicular(p1::Point3D, p2::Point3D, radius, angles = [0, π])

Find points perpendicular to a line joining p1 and p2. Points are radius units away from the line.

source