Examples
========

.. _chamfered-mounting-plate:

.. doctest::

   >>> # 2D — chamfered mounting plate
   >>> from scadpy import circle, cuboid, rectangle, sphere, square, text
   >>> from scadpy import x, y, z, GRAY, ORANGE
   >>> import numpy as np
   >>> import math

   >>> PLATE_WIDTH  = 80
   >>> PLATE_HEIGHT = 50
   >>> PLATE_THICKNESS = 10
   >>> HOLE_RADIUS  = 4
   >>> HOLE_MARGIN  = 10
   >>> CHAMFER_SIZE = 8

   >>> base  = rectangle([PLATE_WIDTH, PLATE_HEIGHT])
   >>> plate = base.chamfer(CHAMFER_SIZE)

   >>> for position, normal in zip(base.vertex_coordinates, base.vertex_normals):
   ...     hole_center = position - HOLE_MARGIN * np.sqrt(2) * normal
   ...     plate -= circle(HOLE_RADIUS).translate(hole_center)

   >>> plate.to_screen() # doctest: +SKIP

.. render-example::
   :name: examples_plate
   :example: plate

.. _extruded-plate-with-label:

.. doctest::

   >>> # 3D — extruded plate with label
   >>> TEXT_THICKNESS = 2

   >>> extruded_plate = plate.linear_extrude(PLATE_THICKNESS)
   >>> label = text("ScadPy", size=15).linear_extrude(TEXT_THICKNESS)
   >>> labelized_extruded_plate = extruded_plate | label.translate(z(PLATE_THICKNESS))
   >>> labelized_extruded_plate.to_screen() # doctest: +SKIP

.. render-example::
   :name: examples_labelized_extruded_plate
   :example: labelized_extruded_plate

.. _honeycomb-plate:

.. doctest::

   >>> # 3D — mounting plate with honeycomb pattern
   >>> HEX_RADIUS = 5
   >>> HEX_WALL_THICKNESS = 1
   >>> NB_X, NB_Y = 5, 4

   >>> spacing_x = HEX_RADIUS * np.sqrt(3)
   >>> spacing_y = HEX_RADIUS * 1.5

   >>> hole = circle(HEX_RADIUS, segment_count=6).rotate(30)
   >>> hole = hole.shrink(HEX_WALL_THICKNESS / 2)

   >>> holes = hole.linear_pattern(
   ...     counts=[NB_X, math.ceil(NB_Y / 2)],
   ...     steps=[np.array([spacing_x, 0]), np.array([0, 2 * spacing_y])],
   ... )
   >>> holes += holes.translate([spacing_x / 2, spacing_y])
   >>> holes = holes.translate(-holes.centroid)

   >>> honeycomb_extruded_plate = extruded_plate - holes.linear_extrude(PLATE_THICKNESS)
   >>> honeycomb_extruded_plate.to_screen() # doctest: +SKIP

.. render-example::
   :name: examples_honeycomb_extruded_plate
   :example: honeycomb_extruded_plate

.. _waved-sphere:

.. doctest::

   >>> # 3D — apply a math function to a solid coordinates
   >>> waved_sphere = sphere(10)
   >>> vertex_coordinates = waved_sphere.vertex_coordinates
   >>> vertex_coordinates[:, 2] += 2 * np.sin(vertex_coordinates[:, 0])
   >>> waved_sphere = waved_sphere.recoordinate(vertex_coordinates)
   >>> waved_sphere.to_screen() # doctest: +SKIP

.. render-example::
   :name: examples_waved_sphere
   :example: waved_sphere

.. _ball-bearing:

.. doctest::

   >>> # 3D — parametric ball bearing
   >>> BALL_RADIUS    = 3
   >>> RACE_RADIUS    = 15
   >>> NB_BALLS       = 11
   >>> CLEARANCE      = 0.1
   >>> RING_HEIGHT    = 7
   >>> RACE_THICKNESS = 10

   >>> groove  = circle(BALL_RADIUS + CLEARANCE) | rectangle([BALL_RADIUS, RING_HEIGHT])
   >>> race    = rectangle([RACE_THICKNESS, RING_HEIGHT]) - groove
   >>> bearing = race.radial_extrude(axis=y(), pivot=x(RACE_RADIUS)).color(GRAY)

   >>> ball = sphere(BALL_RADIUS).color(ORANGE)
   >>> bearing += ball.radial_pattern(count=NB_BALLS, axis=y(), pivot=x(RACE_RADIUS))

   >>> bearing.to_screen() # doctest: +SKIP

.. render-example::
   :name: examples_bearing
   :example: bearing
   :keep-color:

.. _dice:

.. doctest::

   >>> # 3D — dice
   >>> SIZE = 20

   >>> dice = cuboid(SIZE)
   >>> pip  = sphere(SIZE / 12).translate(z(SIZE / 2))

   >>> one   = pip
   >>> two = (
   ...     pip.translate([SIZE / 4, SIZE / 4, 0]) +
   ...     pip.translate([-SIZE / 4, -SIZE / 4, 0])
   ... )
   >>> three = one + two
   >>> four  = two + two.rotate(90, z())
   >>> five  = one + four
   >>> six   = four + pip.translate(x(SIZE / 4)) + pip.translate(x(-SIZE / 4))

   >>> dice -= (
   ...     one
   ...     + two.rotate(90, x())
   ...     + three.rotate(90, y())
   ...     + four.rotate(-90, y())
   ...     + five.rotate(-90, x())
   ...     + six.rotate(-180, x())
   ... )

   >>> dice.to_screen() # doctest: +SKIP

.. render-example::
   :name: examples_dice
   :example: dice

.. _storage-box:

.. doctest::

   >>> # 3D — storage box
   >>> SIZE_OUTER = 20
   >>> SIZE_INNER = 18
   >>> FILLET = 1
   >>> BASE_HEIGHT = 10
   >>> CUT_HEIGHT = 8
   >>> CAP_HEIGHT_OUTER = 1.5
   >>> CAP_HEIGHT_INNER = 3
   >>> CAP_OFFSET_X = 25
   >>> CUT_OFFSET_Z = 2

   >>> outer_base = square(SIZE_OUTER).fillet(FILLET).linear_extrude(BASE_HEIGHT)
   >>> inner_cut = square(SIZE_INNER).linear_extrude(CUT_HEIGHT).translate(z(CUT_OFFSET_Z))
   >>> base = outer_base - inner_cut

   >>> cap_outer = square(SIZE_OUTER).fillet(FILLET).linear_extrude(CAP_HEIGHT_OUTER)
   >>> cap_inner = square(SIZE_INNER).linear_extrude(CAP_HEIGHT_INNER)
   >>> cap = (cap_outer | cap_inner).translate(x(CAP_OFFSET_X))

   >>> storage_box = base + cap
   >>> storage_box.to_screen() # doctest: +SKIP

.. render-example::
   :name: examples_storage_box
   :example: storage_box

.. _helicoid:

.. doctest::

   >>> # 3D — helicoid (circle swept along a helix)

   >>> RADIUS = 5.0
   >>> PITCH = 2.0
   >>> TURNS = 4
   >>> SEGMENTS_PER_TURN = 64

   >>> t = np.linspace(0, TURNS * 2 * np.pi, TURNS * SEGMENTS_PER_TURN + 1)
   >>> helix = np.column_stack([
   ...     RADIUS * np.cos(t),
   ...     RADIUS * np.sin(t),
   ...     t / (2 * np.pi) * PITCH,
   ... ])

   >>> helicoid = circle(0.5).path_extrude(helix)
   >>> helicoid.to_screen() # doctest: +SKIP

.. render-example::
   :name: examples_helicoid
   :example: helicoid
