2D Sphere Approximation

Project Not Completed

An interactive dashboard created with Shiny for Python to approximate a 2D sphere with radius \(r\) to be used in wood turning.

The sphere is approximated by \(n\)-polygon where \(n = 2^\text{level}\), and level is the discretization level.

The distance from any node to center \[ r_n = \frac{r}{\sin\left(\frac{\pi}{2} - \frac{\pi}{n}\right)} \]

The points to approximate the sphere can be calculated by \[ p_n = r_n\cdot\left(\cos\left(\theta\cdot\frac{2\pi}{n} + \phi_n\right), \sin\left(\theta\cdot\frac{2\pi}{n} + \phi_n\right)\right), \phi_n = \frac{\pi}{n}, \theta \in [1, \ldots, n] \]

The side length of the \(n\)-polygon: \[ s_n = 2r_n\cos\left(\frac{\pi}{2} - \frac{\pi}{n}\right) \]

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| viewerHeight: 420

from shiny import App, render, ui, reactive
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

app_ui = ui.page_fluid(
    ui.layout_sidebar(
        ui.sidebar(
            ui.input_slider(
                id = "radius", 
                label = "radius", 
                min = 1, max = 10, value = 1, 
                step=0.5
            ),
            ui.input_slider(
                id = "level", 
                label = "Level", 
                min = 2, max = 7, value = 2, 
                step = 1),
        ),
        ui.layout_columns(
            ui.value_box("n", ui.output_text("n")),
            ui.value_box("radius", ui.output_text("radius")),
            fill = False
        ), 
        ui.layout_columns(
            ui.output_data_frame("points_datafrane")
        ), 
        ui.layout_columns(
            ui.output_plot("plot")
        )
    ),
)


def server(input, output, session):

    @reactive.calc
    def points():
        r = input.radius()
        n = 2 ** input.level()
        r_n = r / np.sin(np.pi * (0.5 - 1/n))
        phi_n = np.pi/n
        points_data = pd.DataFrame([
            (
                r_n * np.cos(i * 2 * np.pi / n + phi_n), 
                r_n * np.sin(i * 2 * np.pi / n + phi_n)
            ) for i in range(1, n+1)
        ]).rename({0: 'x', 1: 'y'}, axis=1)
        return points_data

    @render.data_frame
    def points_datafrane():
        return points()

    @render.text
    def n():
        return str(2 ** input.level())

    @render.text
    def radius():
        return str(input.radius())

    @output
    @render.plot
    def plot():
        t = np.arange(0.0, 4.0, 0.01)
        s = input.radius() * np.sin(
            (2 * np.pi / input.n()) * (t - input.radius() / 2)
        )
        fig, ax = plt.subplots()
        ax.set_ylim([-2, 2])
        ax.plot(t, s)
        ax.grid()


app = App(app_ui, server)
Back to top