• Search in book...
• Toggle Font Controls
© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2021
M. Cywiak, D. CywiakMulti-Platform Graphics Programming with Kivyhttps://doi.org/10.1007/978-1-4842-7113-1_9

# 9. Parametric 3D Plotting

(1)
Leon, Guanajuato, Mexico
(2)
Queretaro, Mexico

In this chapter, we present basic elements for parametric 3D plotting. A typical example on this subject consists of a three-dimensional circular helix, shown in Figure 9-1, which was obtained from a screenshot of this program running on an Android cell phone.

The buttons in Figure 9-1 have the same functionality as described in Chapter 7.

## 9.1 Parametric Equations

Three-dimensional parametric equations consist of representing the three-dimensional spatial coordinates (x, y, z) as functions of a single parameter. For programming purposes, this parameter can be an integer that varies between 0 and a maximum value.

To begin with, as in previous chapters, we define the parameters, which are required to perform the projection:
D=4000;
VX=180; VY=200; VZ=0;
Next, we create three one-dimensional arrays of size N+1, ranging from entries 0 to N. The x and z arrays will store the horizontal and depth coordinates, and the y array will store the values corresponding to the function. The code looks as follows:
N=200;
x=np.zeros(N+1); z=np.zeros(N+1); y=np.zeros(N+1);
The parametric equations are shown in Listing 9-1.
Pi=np.pi;
L=50;
for n in range (0,N+1):
x[n]=L*np.sin(12*Pi/N*n);
z[n]=L*np.cos(12*Pi/N*n);
y[n]=n/N*100;
Listing 9-1

Filling the Parametric Variables

In this code, the x[n] and z[n] arrays store the values that correspond to the parametric equations of a circle with radius equal to L. This can be verified by calculating (x[n]**2 + (z[n])**2), which equals L2. The number 12 in the sine and cosine arguments represents the speed of rotation of the helix. If this number is lowered, the helix will exhibit fewer turns.

In the for loop, the y[n]=n/N*100 directive fills the vertical array with values ranging from 0 to 100, which corresponds to the height of the helix.

To visualize how the helix is drawn, note that as n increases within the for loop, the path of a circle is generated on the x-z plane. At the same time, the vertical height, y, increases, thereby generating the circular helix.

## 9.2 Plotting

The plot is performed by the GraphFunction(B) function . First, the function creates an array to store the complete set of points that makes up the helix, using the PointList = np.zeros ((N + 1, 2)) directive. Then, we draw the lines that join the set of points, by utilizing this directive:
PointList.tolist(), width=1.3)) ;
The corresponding code is shown in Listing 9-2.
#Array to store list of points
PointList=np.zeros( (N+1,2) );
def GraphFunction(B):
global x,y, z, N, D, VX, VY, VZ, Shift;
B.ids.Screen1.canvas.clear(); #Clear the screen
#Color to draw
for n in range (0,N+1):    #Drawing
Factor=(D-VZ)/(D-(z[n]+ZG0)-VZ);
xA=XC+Factor*(x[n]-VX)+VX+ (P/(1-P))*VX;
yA=YC+Factor*(y[n]-VY)+VY+ (P/(1-P))*VY;
PointList[n]=xA,yA;
PointList.tolist(), width=1.3)) ;
Listing 9-2

Code for Plotting the Function

The code listings for main.py and File.kv are shown in Listings 9-3 and 9-4, respectively.
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics import Line, Color
from kivy.clock import Clock
import os
import numpy as np
from kivy.lang import Builder
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'File.kv')
)
#Avoid Form1 of being resizable
from kivy.config import Config
Config.set("graphics","resizable", False)
Config.set('graphics', 'width',  '480');
Config.set('graphics', 'height', '680');
#These values are adjusted by the function
#Initialize() after Clock.schedule_once
#has been executed.
#Canvas center= (XC,YC) and width and height =(W,H);
D=4000;
VX=180; VY=200; VZ=0;
#Factor0 =(D-VZ) / (D/2-VZ);
N=200;
x=np.zeros(N+1); z=np.zeros(N+1); y=np.zeros(N+1);
P=0.5;
ZG0=P*D;
Pi=np.pi;
L=50;
for n in range (0,N+1):
x[n]=L*np.sin(12*Pi/N*n);
z[n]=L*np.cos(12*Pi/N*n);
y[n]=n/N*100;
#Array to store list of points
PointList=np.zeros( (N+1,2) );
def GraphFunction(B):
global x,y, z, N, D, VX, VY, VZ, Shift;
B.ids.Screen1.canvas.clear(); #Clear the screen
#Color to draw
for n in range (0,N+1):    #Drawing
Factor=(D-VZ)/(D-(z[n]+ZG0)-VZ);
xA=XC+Factor*(x[n]-VX)+VX+ (P/(1-P))*VX;
yA=YC+Factor*(y[n]-VY)+VY+ (P/(1-P))*VY;
PointList[n]=xA,yA;
PointList.tolist(), width=1.3)) ;
def RotateFunction(B, Sense):
global x, y, z, D, N;
if Sense==-1:
Teta=np.pi/180*(-4.0);
else:
Teta=np.pi/180*(4.0);
Cos_Teta=np.cos(Teta)
Sin_Teta=np.sin(Teta);
X0=0;  Y0=0;  Z0=ZG0 # Center of rotation
for n in range(0,N+1):
if (B.ids.Button3.state=="down" or
B.ids.Button4.state=="down"):
yP=(y[n]-Y0)*Cos_Teta
+ (x[n]-X0)*Sin_Teta;
xP=-(y[n]-Y0)*Sin_Teta
+(x[n]-X0)*Cos_Teta;
y[n]=yP;
x[n]=xP;
if (B.ids.Button5.state=="down" or
B.ids.Button6.state=="down"):
yP=(y[n]-Y0)*Cos_Teta
+ z[n]*Sin_Teta;
zP=-(y[n]-Y0)*Sin_Teta
+z[n]*Cos_Teta;
y[n]=yP;
z[n]=zP;
if (B.ids.Button7.state=="down" or
B.ids.Button8.state=="down"):
xP=(x[n]-X0)*Cos_Teta
+ z[n]*Sin_Teta;
zP=-(x[n]-X0)*Sin_Teta
+z[n]*Cos_Teta;
x[n]=xP;
z[n]=zP;
class Form1(FloatLayout):
def __init__(Handle, **kwargs):
super(Form1, Handle).__init__(**kwargs);
Event1=Clock.schedule_once(
Handle.Initialize);
def Initialize(B, *args):
global W,H, XC,YC;
W,H=B.ids.Screen1.size ;
XI,YI=B.ids.Screen1.pos
XC=XI+int (W/2);
YC=YI+int(H/2)-80;
def Button1_Click(B):
GraphFunction(B);
def Button2_Click(B):
B.ids.Screen1.canvas.clear();
def Button3_Click(B):
RotateFunction(B,1),
GraphFunction(B);
def Button4_Click(B):
RotateFunction(B,-1),
GraphFunction(B);
def Button5_Click(B):
RotateFunction(B,-1),
GraphFunction(B);
def Button6_Click(B):
RotateFunction(B,1),
GraphFunction(B);
def Button7_Click(B):
RotateFunction(B,-1),
GraphFunction(B);
def Button8_Click(B):
RotateFunction(B,1),
GraphFunction(B);
# This is the Start Up code .
class StartUp (App):
def build (BU):
BU.title="Form1"
return Form1();
if __name__ =="__main__":
StartUp().run();
Listing 9-3

Code for the main.py File

#:set W 440
#:set H 440
<Form1>:
id : Form1
StencilView:
id: Screen1
size_hint: None,None
pos_hint: {"x":0.04, "y":0.34}
size: W,H
canvas.before:
Color:
rgba: 0.9, 0.9, 0, 1
RoundedRectangle:
pos:  self.pos
size: self.size
Button:
id: Button1
on_press: Form1.Button1_Click()
text: "Button1"
size_hint: None,None
pos_hint: {"x": 0.2, "y":0.03}
size: 100,30
Button:
id: Button2
on_press: Form1.Button2_Click()
text: "Button2"
size_hint: None,None
pos_hint: {"x": 0.63, "y":0.03}
size: 100,30
Button:
id: Button3
on_press: Form1.Button3_Click()
text: "Button3"
size_hint: None,None
pos_hint: {"x": 0.05, "y":0.12}
size: 100,30
always_release: True
Button:
id: Button4
on_press: Form1.Button4_Click()
text: "Button4"
size_hint: None,None
pos_hint: {"x": 0.73, "y":0.12}
size: 100,30
Button:
id: Button5
on_press: Form1.Button5_Click()
text: "Button5"
size_hint: None,None
pos_hint: {"x": 0.05, "y":0.20}
size: 100,30
Button:
id: Button6
on_press: Form1.Button6_Click()
text: "Button6"
size_hint: None,None
pos_hint: {"x": 0.73, "y":0.20}
size: 100,30
Button:
id: Button7
on_press: Form1.Button7_Click()
text: "Button7"
size_hint: None,None
pos_hint: {"x": 0.05, "y":0.28}
size: 100,30
Button:
id: Button8
on_press: Form1.Button8_Click()
text: "Button8"
size_hint: None,None
pos_hint: {"x": 0.73, "y":0.28}
size: 100,30
Listing 9-4

Cod e for the file.kv File

## 9.3 Summary

In this chapter, we described elements for parametric 3D plotting. As a typical example, we presented code for a three-dimensional circular helix.

• No Comment
..................Content has been hidden....................