Grouping Shapes

A 3D object is often made of several parts — a car body and roof, a tree trunk and leaves, a planet and its moon. Right now, if you want to rotate or move a compound object, you have to transform every part separately.

A Group solves this. You add shapes into a group, and any transform you apply to the group — position, rotation, scale — applies to all the shapes inside it, acting as one unit.

Open In Jupyter K-12

The Problem — Moving Parts Separately

The scene below builds a simple spacecraft: a body (box) and a cockpit dome (sphere). To tilt the whole craft, you need to update the rotation of every piece. With two parts that’s manageable — but imagine ten.

import scene3d

scene = scene3d.Scene()
scene.set_sky(scene3d.Sky.DEEP_SPACE)
scene.set_ground(length=12, width=12)

body = scene3d.Shapes.Box(width=3, height=0.8, depth=1.2)
body.set_color('#4488ff')
body.set_position(0, 1.2, 0)
scene.add(body)

cockpit = scene3d.Shapes.Sphere(diameter=1.0, segments=14)
cockpit.set_color('#88ccff')
cockpit.set_position(0.8, 1.8, 0)
scene.add(cockpit)

# To tilt the craft you must rotate every piece individually
body.set_rotation(z=15)
cockpit.set_rotation(z=15)

The Solution — scene3d.Group()

Wrap the parts in a Group. Now one call to set_rotation tilts the whole craft.

import scene3d

scene = scene3d.Scene()
scene.set_sky(scene3d.Sky.DEEP_SPACE)
scene.set_ground(length=12, width=12)

craft = scene3d.Group()

body = scene3d.Shapes.Box(width=3, height=0.8, depth=1.2)
body.set_color('#4488ff')
body.set_position(0, 1.2, 0)
craft.add(body)

cockpit = scene3d.Shapes.Sphere(diameter=1.0, segments=14)
cockpit.set_color('#88ccff')
cockpit.set_position(0.8, 1.8, 0)
craft.add(cockpit)

scene.add(craft)

craft.set_rotation(z=15)

How Group Works

craft = scene3d.Group()   # create an empty group
craft.add(body)           # add a shape into the group
craft.add(cockpit)        # add another shape
scene.add(craft)          # add the group to the scene

craft.set_rotation(z=15)    # rotates ALL shapes inside together
craft.set_position(2, 0, 0) # moves ALL shapes inside together

Key rules: - Shapes are added to the group with craft.add(), not directly to the scene. - The group is added to the scene with scene.add(craft). - Individual shapes keep their positions relative to the group’s center. - A transform on the group moves/rotates everything inside — a transform on an individual shape moves only that shape within the group.

The group’s center is the origin (0, 0, 0) by default. That’s the pivot point for all rotations.

{ “question_type”: “multiple_choice”, “question”: “After adding two shapes to a Group, you call group.set_rotation(y=45). What rotates?”, “options”: [ { “key”: “a”, “text”: “Only the first shape added” }, { “key”: “b”, “text”: “Only the last shape added” }, { “key”: “c”, “text”: “Both shapes together, as one unit” }, { “key”: “d”, “text”: “Nothing — you must call set_rotation on each shape” } ], “answer”: “c”, “submitted_answer”: “” }

{ “question_type”: “true_false”, “question”: “When using a Group, you add shapes to the group AND also separately add them to the scene.”, “answer”: “False”, “submitted_answer”: “” }

Groups and Animation — A Remote Pivot Point

Here’s the deeper reason groups are so powerful: they give you a remote pivot point for rotation.

If you rotated the moon directly — moon.set_rotation(y=angle) — it would spin around its own center. The moon would just twirl in place at position (4, 0, 0). No orbit.

A group solves this. The group’s center sits at (0, 0, 0) — right where the planet is. The moon is placed 4 units away inside the group. Rotating the group pivots everything around that center, sweeping the moon in a wide circle around the planet.

Think of it like a clock hand: the hand is pinned at one end (the group center) and the tip (the moon) swings in an arc.

Click Stop (■) when you’ve watched the orbit.

import scene3d

scene = scene3d.Scene()
scene.set_sky(scene3d.Sky.DEEP_SPACE)

planet = scene3d.Shapes.Sphere(diameter=2, segments=24)
planet.set_material(scene3d.Material.Planets.Earth)
scene.add(planet)

moon_group = scene3d.Group()
moon = scene3d.Shapes.Sphere(diameter=0.6, segments=14)
moon.set_material(scene3d.Material.Planets.Moon)
moon.set_position(4, 0, 0)
moon_group.add(moon)
scene.add(moon_group)

angle = 0.0

@scene.on_frame
def animate(dt):
  global angle
  angle += 40 * dt
  moon_group.set_rotation(y=angle)

scene.run()

{ “question_type”: “multiple_choice”, “question”: “Why does rotating moon_group make the moon travel in a circle around the planet?”, “options”: [ { “key”: “a”, “text”: “The moon has a circular mesh” }, { “key”: “b”, “text”: “The moon is offset from the group’s center, so rotating the group swings it in an arc” }, { “key”: “c”, “text”: “set_rotation always produces circular motion” }, { “key”: “d”, “text”: “The planet attracts the moon with gravity” } ], “answer”: “b”, “submitted_answer”: “” }

{ “question_type”: “multiple_choice”, “question”: “If you called moon.set_rotation(y=angle) every frame instead of moon_group.set_rotation(y=angle), what would happen?”, “options”: [ { “key”: “a”, “text”: “The moon would orbit the planet exactly as before” }, { “key”: “b”, “text”: “The moon would spin in place at (4, 0, 0) without moving” }, { “key”: “c”, “text”: “The moon would fly off in a straight line” }, { “key”: “d”, “text”: “Both the planet and moon would rotate” } ], “answer”: “b”, “submitted_answer”: “” }

Try It Yourself

The scene below builds a compound shape — a central box with four corner spheres — all inside a single group. Use the slider to set the spin speed. All five parts rotate together with one set_rotation call per frame.

import scene3d

SPEED = 45 #@param {type:"slider", min:10, max:180, step:10}

scene = scene3d.Scene()
scene.set_sky('#1a1a2e')
scene.set_ground(length=12, width=12)

compound = scene3d.Group()

center = scene3d.Shapes.Box(width=1.5, height=1.5, depth=1.5)
center.set_color('#e94560')
compound.add(center)

for dx, dz in [(-1.5, -1.5), (1.5, -1.5), (-1.5, 1.5), (1.5, 1.5)]:
  ball = scene3d.Shapes.Sphere(diameter=0.5, segments=10)
  ball.set_color('#4488ff')
  ball.set_position(dx, 0, dz)
  compound.add(ball)

compound.set_position(0, 1.5, 0)
scene.add(compound)

angle = 0.0

@scene.on_frame
def animate(dt):
  global angle
  angle += SPEED * dt
  compound.set_rotation(y=angle)

scene.run()

Think of a compound object you’d like to build in 3D — a robot, a car, a rocket, a solar system.

List the parts it would have. Which parts would go in the same group, and why? Would different parts need to be in different groups so they can move independently? Describe how the groups would be structured.