• 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_5

# 5. Programming Three-Dimensional Polygons

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

In this chapter, we describe how to construct and rotate polygons by using the equations obtained in Chapter 4.

As an example, Figure 5-1 shows a screenshot of the program running on an Android cell phone.

Figure 5-1 shows the program running on an Android cell phone. Two polygons are placed on the screen. Button1 draws the polygons and Button2 clears the screen. The three buttons at the left—Button3, Button5, and Button7—rotate one of the two polygons in the counterclockwise direction, in the x − y, x − z, or y − z planes, respectively. Accordingly, Button4, Button6, and Button8 rotate the polygon in the clockwise direction. One of the polygons is selected each time that Button9 is pressed. The selected polygon number is indicated by a label.

## 5.1 Polygon Structure

Similar to the two-dimensional case, these three-dimensional polygons require a structure with the properties of the polygon. To describe the contents and use of the structures, we focus on the structure that corresponds to the cube polygon shown in Listing 5-1.

The first entry in the structure corresponds to the number of vertices +1, as in the two-dimensional case. Next, we list the vertices that correspond to the front face of the cube, referring to their corresponding offsets. Then we list the vertices of the back face. The last entries of the structure correspond to the color of the polygon and its center of rotation.

In this example, the center of rotation coincides with the (x,y,z) offsets of the polygon. The polygon structure is given in Listing 5-1. First, let’s define the dimensions of the cube as LX=60, LY=60, and LZ=60. Offsets from the origin for the (x,y,z) directions are defined as OffX=-70, OffY=-40, and OffZ=200, respectively.
#Offsets from the origin. OffZ is measured
#starting from the screen
OffX=-70; OffY=-40; OffZ=200;
#Dimensions of the cube
LX=60; LY=60; LZ=60;
Cube=[
5,                #Number of vertexes +1
#Front face
[ LX+OffX,  LY+OffY,  LZ+OffZ],    #Vertex 1
[ LX+OffX, -LY+OffY,  LZ+OffZ],    #Vertex 2
[-LX+OffX, -LY+OffY,  LZ+OffZ],    #Vertex 3
[-LX+OffX,  LY+OffY,  LZ+OffZ],    #Vertex 4
[ LX+OffX,  LY+OffY,  LZ+OffZ],    #Vertex 1
#Back face
[ LX+OffX,  LY+OffY,  -LZ+OffZ],    #Vertex 1
[ LX+OffX, -LY+OffY,  -LZ+OffZ],    #Vertex 2
[-LX+OffX, -LY+OffY,   -LZ+OffZ],   #Vertex 3
[-LX+OffX,  LY+OffY,   -LZ+OffZ],   #Vertex 4
[ LX+OffX,  LY+OffY,   -LZ+OffZ],   #Vertex 1
#Red, green, blue color .
#Entries must be between 0 and 1. Place= 2*Num+1
[0,0,1],
#Center of rotation. Entry=2*Num+2
[OffX,OffY,OffZ]
];
Listing 5-1

Three-Dimensional Polygon Structure

## 5.2 Basic Functions

Now that the structure of the polygon has been constructed, we need a function to draw the polygon on the screen. This function is DrawEdges(P,B), as shown in Listing 5-2.

DrawEdges(P,B) receives two parameters. The first parameter points to the structure. The second parameter is a handle to the widgets defined in the kv file.

The edges of the polygon are drawn by calculating the position of each vertex on the screen using Equations (4.​11) and (4.​12) from Chapter 4. For convenience, they are repeated here:
(5.1)
(5.2)
The pseudocode for DrawEdges(P,B) is shown in Listing 5-2.
def DrawEdges(P, B):
Num=P[0]; # Reading number of face edges +1
r,g,b=P[2*Num+1]; # Reading colors
for n in range(1,Num): # Drawing front edges
Factor=(D-VZ)/(D-P[n][2]-VZ);
x1=XC+Factor*(P[n][0]-VX)+VX;
y1=YC+Factor*(P[n][1]-VY)+VY;
Factor=(D-VZ)/(D-P[n+1][2]-VZ);
x2=XC+Factor*(P[n+1][0]-VX)+VX;
y2=YC+Factor*(P[n+1][1]-VY)+VY;
Draw_line_from (x1,y1) to (x2,y2)
using (r,g,b) color.
for n in range(Num+1,2*Num): #Drawing back edges
Factor=(D-VZ)/(D-P[n][2]-VZ);
x1=XC+Factor*(P[n][0]-VX)+VX;
y1=YC+Factor*(P[n][1]-VY)+VY;
Factor=(D-VZ)/(D-P[n+1][2]-VZ);
x2=XC+Factor*(P[n+1][0]-VX)+VX;
y2=YC+Factor*(P[n+1][1]-VY)+VY;
Draw_line_from (x1,y1) to (x2,y2)
using (r,g,b) color.
#Drawing edges between back and front faces
for n in range(1,Num):
Factor=(D-VZ)/(D-P[n][2]-VZ);
x1=XC+Factor*(P[n][0]-VX)+VX;
y1=YC+Factor*(P[n][1]-VY)+VY;
Factor=(D-VZ)/(D-P[Num+n][2]-VZ);
x2=XC+Factor*(P[Num+n][0]-VX)+VX;
y2=YC+Factor*(P[Num+n][1]-VY)+VY;
Draw_line_from (x1,y1) to (x2,y2)
using (r,g,b) color.
Listing 5-2

The Function to Draw Polygon Edges

As you can see in Listing 5-2, the edges are drawn by calculating the coordinates of each vertex in the screen using Equations (5.1) and (5.2). In the first for loop, the edges of the front face are drawn. In the second for loop, the edges of the back face are drawn. Finally, in the last for loop, the edges going from the front to the back face vertices are drawn.

For illustrative purposes, we will only fill one face of the polygons. The FillPolygon(B,P) function , shown in Listing 5-3, receives two parameters. The P parameter gives access to the polygon structure, while the B parameter gives access to the screen widgets. Before we declare the function, we allocate a region of memory of 30 points of intersection with the scan line, as described in Chapter 3 for the two-dimensional case. We do this by using:
Intersect=np.zeros(30);
The corresponding pseudocode for FillPolygon(B,P) is shown in Listing 5-3.
def FillPolygon(P, B):
Num=P[0]; #Reading number of face edges +1
r,g,b=P[2*Num+1]; #Reading colors
#Calculating polygon YMin, Ymax
#for limiting number of line scans
Factor=(D-VZ)/(D-P[Num+1][2]-VZ);
YMin=YC+Factor*(P[Num+1][1]-VY)+VY;
YMax=YMin;
for n in range(Num+1,2*Num):
Factor=(D-VZ)/(D-P[n+1][2]-VZ);
if YMin>YC+Factor*(P[n+1][1]-VY)+VY:
YMin=YC+Factor*(P[n+1][1]-VY)+VY;
if YMax<YC+Factor*(P[n+1][1]-VY)+VY:
YMax=YC+Factor*(P[n+1][1]-VY)+VY;
#We have now (YMin, YMax). We now proceed
#filling lines between Ymin and Ymax
for y in np.arange (YMin, YMax, 2):
Counter=0;
#We search line cuts, segment by segment
for n in range(Num+1, 2*Num):
#We first order the two vertices of each
#segment such that Y1<Y2
Factor1=(D-VZ)/(D-P[n][2]-VZ);
YA=YC+Factor1*(P[n][1]-VY)+VY;
Factor2=(D-VZ)/(D-P[n+1][2]-VZ);
YB=YC+Factor2*(P[n+1][1]-VY)+VY;
if ( YA<YB ):
Y1=YA;
X1=XC+Factor1*(P[n][0]-VX)+VX;
Y2=YB;
X2=XC+Factor2*(P[n+1][0]-VX)+VX;
else:
Y1=YB;
X1=XC+Factor2*(P[n+1][0]-VX)+VX;
Y2=YA;
X2=XC+Factor1*(P[n][0]-VX)+VX;
if (Y1<=y and y<Y2):
if (Y2-Y1)!=0:
M=(X2-X1)/(Y2-Y1);
else:
#if Y1=Y2, the slope becomes
#infinite. Therefore,
#we assign to M a large value
M=1.0e8;
#We store the x value
Intersect[Counter]=(y-Y1)*M+X1;
#And we increment Counter as a
#new value has being stored
Counter=Counter+1;
#Our polygon is a closed figure. We ask If
#an even number of cuts have occurred
if(Counter>1 and Counter %2==0):
#Intersect1 stores ordered pairs of
#x values
Intersect1=np.sort(Intersect[0:Counter]);
#We proceed tracing horizontal lines
#between pairs of intersections
for n in range(0,Counter,2):
XIntersect1=int(Intersect1[n]);
XIntersect2=int(Intersect1[n+1]);
Y=int(y);
Draw line from (XIntersect1,Y) to (Xintersect2,Y)
using (r,g,b) color.
Listing 5-3

Pseudocode for the FillPolygon(B,P) Function

Note that FillPolygon(P,B) is analogous to the two-dimensional case described in Chapter 3. Here, one face of the polygon is filled with horizontal lines beginning from its minimum y-coordinate up to its maximum. The only difference here is that we need to calculate the x and y coordinates according to Equations (5.1) and (5.2) because we have depth dependence.

The rotation of the polygon consists of updating the vertices’ values stored in the structure. For this, we use Equations (4.​26) and (4.​27) obtained in Chapter 4, replacing θ with −θ. Then, these equations read as follows:
(5.3)
(5.4)

In Equations (5.3) and (5.4), the plane of rotation is x-y. Analogous equations are used for rotations in the x-z and y-z planes.

To simplify the code for the three planes of rotation, each component in the x, y, and z directions will be referred to as X1, X2, and X3, respectively. Then, for a given plane of rotation, we use a pair of global variables called p and q. They correspond to coordinates Xp and Xq. The appropriate code is given in Listing 5-4.
def Rotate(P, Sense):
global p,q;
if Sense==-1:
Teta=np.pi/180*(-5);
else:
Teta=np.pi/180*(5);
Cos_Teta=np.cos(Teta)
Sin_Teta=np.sin(Teta);
Num=P[0];
#Reading center of rotation
RCp=P[2*Num+2][p]; RCq=P[2*Num+2][q];
for n in range(1,Num+1): #Rotating front face
Xp=(P[n][p]-RCp)*Cos_Teta
+  (P[n][q]-RCq)*Sin_Teta +RCp;
Xq=-(P[n][p]-RCp)*Sin_Teta
+  (P[n][q]-RCq)*Cos_Teta +RCq;
P[n][p]=Xp;
P[n][q]=Xq;
#Rotating Back face
for n in range(Num+1,2*Num+1):
Xp=(P[n][p]-RCp)*Cos_Teta
+  (P[n][q]-RCq)*Sin_Teta +RCp;
Xq=-(P[n][p]-RCp)*Sin_Teta
+  (P[n][q]-RCq)*Cos_Teta +RCq;
P[n][p]=Xp;
P[n][q]=Xq;
Listing 5-4

Function to Rotate Polygon Points

As indicated in Listing 5-4, RCp and RCq refer to the center of rotation.

The complete code for main.py and File.kv are shown in Listings 5-5 and 5-6, 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
Builder.load_file(
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", True)
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.
#These values are canvas center (XC,YC)
#and width and height (W,H);
XC=0; YC=0; W=0; H=0;
Flag=False;
NUMBER=0;
#==============  Polygon structures  ===============
#The first entry corresponds to the
#number of vertexes +1.
#Following pair of entries correspond to the list
#of vertexes ordered in clockwise direction.
#The first vertex is repeated in the
#list as the last vertex.
#Following pair of entries correspond to the center
#of rotation in screen coordinates
#The triplet before the end of the structure
#corresponds to polygon colors, red, green, blue.
D=800;
VX=-20; VY=0; VZ=0;
LX=60; LY=60; LZ=60; # Cube dimensions
#Offsets from the origin. OffZ is measured
#starting from the screen
OffX=-70; OffY=-40; OffZ=200;
Cube=[
5,                #Number of vertexes +1
#Front face
[ LX+OffX,  LY+OffY,  LZ+OffZ],    #Vertex 1
[ LX+OffX, -LY+OffY,  LZ+OffZ],    #Vertex 2
[-LX+OffX, -LY+OffY,  LZ+OffZ],    #Vertex 3
[-LX+OffX,  LY+OffY,  LZ+OffZ],    #Vertex 4
[ LX+OffX,  LY+OffY,  LZ+OffZ],    #Vertex 1
#Back face
[ LX+OffX,  LY+OffY,  -LZ+OffZ],    #Vertex 1
[ LX+OffX, -LY+OffY,  -LZ+OffZ],    #Vertex 2
[-LX+OffX, -LY+OffY,   -LZ+OffZ],   #Vertex 3
[-LX+OffX,  LY+OffY,   -LZ+OffZ],   #Vertex 4
[ LX+OffX,  LY+OffY,   -LZ+OffZ],   #Vertex 1
#Red, green, blue color.
#Entries must be between 0 and 1. Place= 2*Num+1
[0,0,1],
#Center of rotation. Entry=2*Num+2
[OffX,OffY,OffZ]
];
L=90; LZ=20;  # Pentagon dimensions
#Offsets from the origin. OffZ is measured
#starting from the screen
OffX=90; OffY=80; OffZ=100;
Pentagon=[
6,   #Number of vertexes +1
#Front face
#Vertex 1
[ 1*L+OffX,       0*L+OffY,     LZ+OffZ],
#Vertex 2
[ 0.309*L+OffX,  .95*L+OffY,    LZ+OffZ],
#Vertex 3
[ -0.809*L+OffX,  0.58*L+OffY,  LZ+OffZ],
#Vertex 4
[ -0.809*L+OffX,  -0.58*L+OffY, LZ+OffZ],
#Vertex 5
[ 0.309*L+OffX,  -0.95*L+OffY,  LZ+OffZ],
#Vertex 1 repeated
[ 1*L+OffX,       0*L+OffY,     LZ+OffZ],
#Back face
#Vertex 1
[ 1*L+OffX,       0*L+OffY,     -LZ+OffZ],
#Vertex 2
[ 0.309*L+OffX,  .95*L+OffY,    -LZ+OffZ],
#Vertex 3
[ -0.809*L+OffX,  0.58*L+OffY,  -LZ+OffZ],
#Vertex 4
[ -0.809*L+OffX,  -0.58*L+OffY, -LZ+OffZ],
#Vertex 5
[ 0.309*L+OffX,  -0.95*L+OffY,  -LZ+OffZ],
#Vertex 1 repeated
[ 1*L+OffX,       0*L+OffY,     -LZ+OffZ],
[1,0,0],  #red, green, blue color.
#Center of rotation. Entry=2*Num+2
[OffX,OffY,OffZ]
];
#---------------------------------------------------
def DrawEdges(P, B):
Num=P[0]; #Reading number of face edges +1
r,g,b=P[2*Num+1]; #Reading colors
for n in range(1,Num): #Drawing front edges
Factor=(D-VZ)/(D-P[n][2]-VZ);
x1=XC+Factor*(P[n][0]-VX)+VX;
y1=YC+Factor*(P[n][1]-VY)+VY;
Factor=(D-VZ)/(D-P[n+1][2]-VZ);
x2=XC+Factor*(P[n+1][0]-VX)+VX;
y2=YC+Factor*(P[n+1][1]-VY)+VY;
B.ids.Screen1.canvas.add( Color(r,g,b) );
B.ids.Screen1.canvas.add( Line(points=
(int(x1),int(y1),int(x2),int(y2)),
width=2) );
for n in range(Num+1,2*Num): #Drawing back edges
Factor=(D-VZ)/(D-P[n][2]-VZ);
x1=XC+Factor*(P[n][0]-VX)+VX;
y1=YC+Factor*(P[n][1]-VY)+VY;
Factor=(D-VZ)/(D-P[n+1][2]-VZ);
x2=XC+Factor*(P[n+1][0]-VX)+VX;
y2=YC+Factor*(P[n+1][1]-VY)+VY;
B.ids.Screen1.canvas.add( Color(r,g,b) );
B.ids.Screen1.canvas.add( Line(points=
(int(x1),int(y1),int(x2),int(y2)),
width=2) );
#Drawing edges between back and front faces
for n in range(1,Num):
Factor=(D-VZ)/(D-P[n][2]-VZ);
x1=XC+Factor*(P[n][0]-VX)+VX;
y1=YC+Factor*(P[n][1]-VY)+VY;
Factor=(D-VZ)/(D-P[Num+n][2]-VZ);
x2=XC+Factor*(P[Num+n][0]-VX)+VX;
y2=YC+Factor*(P[Num+n][1]-VY)+VY;
B.ids.Screen1.canvas.add( Color(r,g,b) );
B.ids.Screen1.canvas.add( Line(points=
(int(x1),int(y1),int(x2),int(y2)),
width=2) );
#---------------------------------------------------
def Rotate(P, Sense):
global p,q;
if Sense==-1:
Teta=np.pi/180*(-5);
else:
Teta=np.pi/180*(5);
Cos_Teta=np.cos(Teta)
Sin_Teta=np.sin(Teta);
Num=P[0];
#Reading center of rotation
RCp=P[2*Num+2][p]; RCq=P[2*Num+2][q];
for n in range(1,Num+1): #Rotating front face
Xp=(P[n][p]-RCp)*Cos_Teta
+  (P[n][q]-RCq)*Sin_Teta +RCp;
Xq=-(P[n][p]-RCp)*Sin_Teta
+  (P[n][q]-RCq)*Cos_Teta +RCq;
P[n][p]=Xp;
P[n][q]=Xq;
#Rotating Back face
for n in range(Num+1,2*Num+1):
Xp=(P[n][p]-RCp)*Cos_Teta
+  (P[n][q]-RCq)*Sin_Teta +RCp;
Xq=-(P[n][p]-RCp)*Sin_Teta
+  (P[n][q]-RCq)*Cos_Teta +RCq;
P[n][p]=Xp;
P[n][q]=Xq;
#---------------------------------------------------
#Function for filling one polygon face, line by line
#Array to store intersection points
Intersect=np.zeros(30);
def FillPolygon(P, B):
Num=P[0]; #Reading number of face edges +1
r,g,b=P[2*Num+1]; #Reading colors
#Calculating polygon YMin, Ymax
#for limiting number of line scans
Factor=(D-VZ)/(D-P[Num+1][2]-VZ);
YMin=YC+Factor*(P[Num+1][1]-VY)+VY;
YMax=YMin;
for n in range(Num+1,2*Num):
Factor=(D-VZ)/(D-P[n+1][2]-VZ);
if YMin>YC+Factor*(P[n+1][1]-VY)+VY:
YMin=YC+Factor*(P[n+1][1]-VY)+VY;
if YMax<YC+Factor*(P[n+1][1]-VY)+VY:
YMax=YC+Factor*(P[n+1][1]-VY)+VY;
#We have now (YMin, YMax). We now proceed
#filling lines between Ymin and Ymax
for y in np.arange (YMin, YMax, 2):
Counter=0;
#We search line cuts, segment by segment
for n in range(Num+1, 2*Num):
#We first order the two vertices of each
#segment such that Y1<Y2
Factor1=(D-VZ)/(D-P[n][2]-VZ);
YA=YC+Factor1*(P[n][1]-VY)+VY;
Factor2=(D-VZ)/(D-P[n+1][2]-VZ);
YB=YC+Factor2*(P[n+1][1]-VY)+VY;
if ( YA<YB ):
Y1=YA;
X1=XC+Factor1*(P[n][0]-VX)+VX;
Y2=YB;
X2=XC+Factor2*(P[n+1][0]-VX)+VX;
else:
Y1=YB;
X1=XC+Factor2*(P[n+1][0]-VX)+VX;
Y2=YA;
X2=XC+Factor1*(P[n][0]-VX)+VX;
if (Y1<=y and y<Y2):
if (Y2-Y1)!=0:
M=(X2-X1)/(Y2-Y1);
else:
#if Y1=Y2, the slope becomes
#infinite. Therefore,
#we assign to M a large value
M=1.0e8;
#We store the x value
Intersect[Counter]=(y-Y1)*M+X1;
#And we increment Counter as a
#new value has being stored
Counter=Counter+1;
#Our polygon is a closed figure. We ask If
#an even number of cuts have occurred
if(Counter>1 and Counter %2==0):
#Intersect1 stores ordered pairs of
#x values
Intersect1=np.sort(Intersect[0:Counter]);
#We proceed tracing horizontal lines
#between pairs of intersections
for n in range(0,Counter,2):
XIntersect1=int(Intersect1[n]);
XIntersect2=int(Intersect1[n+1]);
Y=int(y)
#Picking the color and drawing the
#horizontal line between x pairs
B.ids.Screen1.canvas.add(
Color(r,g,b) );
B.ids.Screen1.canvas.add( Line(
points=(XIntersect1,Y,
XIntersect2,Y), width=1.0) );
#---------------------------------------------------
class Form1(FloatLayout):
def __init__(Handle, **kwargs):
super(Form1, Handle).__init__(**kwargs);
Event1=Clock.schedule_once(
Handle.Initialize);
Event2=Clock.schedule_interval(
Handle.Temporal,0.1);
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);
def Temporal(B, *args):
global Flag, NUMBER, p, q;
if (Flag==True):
if (B.ids.Button3.state=="down"):
Sense=-1; p=0; q=1;
if(B.ids.Button5.state=="down"):
Sense=-1; p=0;q=2;
if(B.ids.Button7.state=="down"):
Sense=-1; p=1;q=2;
if(B.ids.Button4.state=="down"):
Sense=1; p=0;q=1;
if(B.ids.Button6.state=="down"):
Sense=1;p=0;q=2;
if(B.ids.Button8.state=="down"):
Sense=1;p=1;q=2;
B.ids.Screen1.canvas.clear();
if(NUMBER==0):
Rotate(Cube, Sense);
if (NUMBER==1):
Rotate(Pentagon, Sense);
DrawEdges(Cube,B);
FillPolygon(Cube,B);
DrawEdges(Pentagon,B);
FillPolygon(Pentagon,B);
def Button1_Click(B):
B.ids.Screen1.canvas.clear();
DrawEdges(Cube, B);
FillPolygon(Cube,B);
DrawEdges(Pentagon, B);
FillPolygon(Pentagon,B)
def Button2_Click(B):
B.ids.Screen1.canvas.clear();
def Button3_Click(B):
global Flag;
Flag=True;
def Button3_Release(B):
global Flag ;
Flag=False;
def Button4_Click(B):
global Flag;
Flag=True;
def Button4_Release(B):
global Flag;
Flag=False;
def Button5_Click(B):
global Flag;
Flag=True;
def Button5_Release(B):
global Flag;
Flag=False;
def Button6_Click(B):
global Flag;
Flag=True;
def Button6_Release(B):
global Flag;
Flag=False;
def Button7_Click(B):
global Flag;
Flag=True;
def Button7_Release(B):
global Flag;
Flag=False;
def Button8_Click(B):
global Flag;
Flag=True;
def Button8_Release(B):
global Flag;
Flag=False;
def Button9_Click(B):
global NUMBER;
NUMBER=(NUMBER+1)%2;
B.ids.Label1.text=str(NUMBER);
# This is the Start Up code.
class StartUp (App):
def build (BU):
BU.title="Form1"
return Form1();
if __name__ =="__main__":
StartUp().run();
Listing 5-5

The main.py Code Listing

#: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()
on_release: Form1.Button3_Release()
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()
on_release:  Form1.Button4_Release()
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()
on_release:  Form1.Button5_Release()
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()
on_release:  Form1.Button6_Release()
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()
on_release:  Form1.Button7_Release()
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()
on_release:  Form1.Button8_Release()
text: "Button8"
size_hint: None,None
pos_hint: {"x": 0.73, "y":0.28}
size: 100,30
Button:
id: Button9
on_press: Form1.Button9_Click()
text: "Button9"
size_hint: None,None
pos_hint: {"x": 0.38, "y":0.12}
size: 100,30
Label:
id: Label1
text: "0"
font_size: 30
color: 1,1,0,1
size_hint: None,None
pos_hint: {"x": 0.38, "y":0.20}
size: 100,30
Listing 5-6

The File.kv Code Listing

## 5.3 Summary

In this chapter, we introduced the required structures and functions to construct and rotate three-dimensional polygons. We described how to map them on the computer's screen using the equations obtained in Chapter 4.

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

You can't read the all page of ebook, please click here login for view all page.