100% found this document useful (1 vote)
18 views

Maya Python for Games and Film A Complete Reference for Maya Python and the Maya Python API 1st Edition Adam Mechtley - Read the ebook now or download it for a full experience

The document promotes various ebooks related to Python programming, particularly in the context of Maya, including titles like 'Maya Python for Games and Film' and 'Practical Maya Programming with Python'. It outlines the different programming interfaces available in Maya, such as MEL, Python, and C++, and discusses how to execute Python commands within Maya using tools like the Command Line and Script Editor. The document also emphasizes the importance of Python in enhancing Maya's functionality and provides guidance on creating custom scripts and buttons in the Maya GUI.

Uploaded by

raukasbuqii
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
18 views

Maya Python for Games and Film A Complete Reference for Maya Python and the Maya Python API 1st Edition Adam Mechtley - Read the ebook now or download it for a full experience

The document promotes various ebooks related to Python programming, particularly in the context of Maya, including titles like 'Maya Python for Games and Film' and 'Practical Maya Programming with Python'. It outlines the different programming interfaces available in Maya, such as MEL, Python, and C++, and discusses how to execute Python commands within Maya using tools like the Command Line and Script Editor. The document also emphasizes the importance of Python in enhancing Maya's functionality and provides guidance on creating custom scripts and buttons in the Maya GUI.

Uploaded by

raukasbuqii
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 86

Instant Ebook Access, One Click Away – Begin at ebookgate.

com

Maya Python for Games and Film A Complete


Reference for Maya Python and the Maya Python API
1st Edition Adam Mechtley

https://ebookgate.com/product/maya-python-for-games-and-
film-a-complete-reference-for-maya-python-and-the-maya-
python-api-1st-edition-adam-mechtley/

OR CLICK BUTTON

DOWLOAD EBOOK

Get Instant Ebook Downloads – Browse at https://ebookgate.com


Click here to visit ebookgate.com and download ebook now
Instant digital products (PDF, ePub, MOBI) available
Download now and explore formats that suit you...

Practical Maya Programming with Python 1st Edition


Galanakis

https://ebookgate.com/product/practical-maya-programming-with-
python-1st-edition-galanakis/

ebookgate.com

Python The Complete Manual The essential handbook for


Python users Master Python today First Edition Unknown

https://ebookgate.com/product/python-the-complete-manual-the-
essential-handbook-for-python-users-master-python-today-first-edition-
unknown/
ebookgate.com

Black hat Python Python programming for hackers and


pentesters 1st Edition Seitz

https://ebookgate.com/product/black-hat-python-python-programming-for-
hackers-and-pentesters-1st-edition-seitz/

ebookgate.com

Python for Beginners 2018 Edition Learn to Code with


Python Mark Lassoff

https://ebookgate.com/product/python-for-beginners-2018-edition-learn-
to-code-with-python-mark-lassoff/

ebookgate.com
Python Essential Reference 3rd Edition Beazley

https://ebookgate.com/product/python-essential-reference-3rd-edition-
beazley/

ebookgate.com

MEL Scripting for Maya Animators 1st Edition Mark R.


Wilkins

https://ebookgate.com/product/mel-scripting-for-maya-animators-1st-
edition-mark-r-wilkins/

ebookgate.com

Python pocket reference 5ed. Edition Mark Lutz

https://ebookgate.com/product/python-pocket-reference-5ed-edition-
mark-lutz/

ebookgate.com

Python for Finance 1st Edition Yuxing Yan

https://ebookgate.com/product/python-for-finance-1st-edition-yuxing-
yan/

ebookgate.com

Python Tricks A Buffet of Awesome Python Features Dan


Bader

https://ebookgate.com/product/python-tricks-a-buffet-of-awesome-
python-features-dan-bader/

ebookgate.com
Chapter

Maya Command Engine


1
and User Interface

CHAPTER OUTLINE
Interacting with Maya 4
Maya Embedded Language 5
Python 5
C++ Application Programming Interface 6
Python API 6
Executing Python in Maya 6
Command Line 6
Script Editor 8
Maya Shelf 10
Maya Commands and the Dependency Graph 11
Introduction to Python Commands 15
Flag Arguments and Python Core Object Types 19
Numbers 20
Strings 20
Lists 20
Tuples 21
Booleans 21
Flag = Object Type 21
Command Modes and Command Arguments 22
Create Mode 22
Edit Mode 23
Query Mode 23
Python Command Reference 24
Synopsis 25
Return Value 25
Related 25
Flags 25
Python Examples 26
Python Version 26
Python Online Documentation 26
Concluding Remarks 27
Maya Python for Games and Film. DOI: 10.1016/B978-0-12-378578-7.00001-6
© 2012 Elsevier Inc. All rights reserved. 3
4 CHAPTER 1 Maya Command Engine and User Interface

BY THE END OF THIS CHAPTER, YOU WILL BE ABLE TO:


■ Compare and contrast the four Maya programming interfaces.
■ Use the Command Line and Script Editor to execute Python commands.
■ Create a button in the Maya GUI to execute custom scripts.
■ Describe how Python interacts with Maya commands.
■ Define nodes and connections.
■ Describe Maya’s command architecture.
■ Learn how to convert MEL commands into Python.
■ Locate help for Python commands.
■ Compare and contrast command arguments and flag arguments.
■ Define the set of core Python data types that work with Maya commands.
■ Compare and contrast the three modes for using commands.
■ Identify the version of Python that Maya is using.
■ Locate important Python resources online.

To fully understand what can be done with Python in Maya, we must first
discuss how Maya has been designed. There are several ways that users
can interact with or modify Maya. The standard method is to create content
using Maya’s graphical user interface (GUI). This interaction works like any
other software application: Users press buttons or select menu items that
create or modify their documents or workspaces. Despite how similar Maya
is to other software, however, its underlying design paradigm is unique in
many ways. Maya is an open product, built from the ground up to be capable
of supporting new features designed by users. Any Maya user can modify or
add new features, which can include a drastic redesign of the main interface
or one line of code that prints the name of the selected object.
In this chapter, we will explore these topics as you begin programming in
Python. First, we briefly describe Maya’s different programming options and
how they fit into Maya’s user interface. Next, we jump into Python by exploring
different means of executing Python code in Maya. Finally, we explore some
basic Maya commands, the primary means of modifying the Maya scene.

INTERACTING WITH MAYA


Although the focus of this book is on using Python to interact with Maya,
we should briefly examine all of Maya’s programming interfaces to better
understand why Python is so unique. Autodesk has created four different
Interacting with Maya 5

Maya user interface

MEL Python C++

Maya Command Engine Maya API

Maya application core

■ FIGURE 1.1 The architecture of Maya’s programming interfaces.

programming interfaces to interact with Maya, using three different pro-


gramming languages. Anything done in Maya will use some combination
of these interfaces to create the result seen in the workspace. Figure 1.1
illustrates how these interfaces interact with Maya.

Maya Embedded Language


Maya Embedded Language (MEL) was developed for use with Maya and is
used extensively throughout the program. MEL scripts fundamentally define
and create the Maya GUI. Maya’s GUI executes MEL instructions and Maya
commands. Users can also write their own MEL scripts to perform most
common tasks. MEL is relatively easy to create, edit, and execute, but it is
also only used in Maya and has a variety of technical limitations. Namely,
MEL has no support for object-oriented programming. MEL can only com-
municate with Maya through a defined set of interfaces in the Command
Engine (or by calling Python). We will talk more about the Command
Engine later in this chapter.

Python
Python is a scripting language that was formally introduced to Maya in
version 8.5. Python can execute the same Maya commands as MEL using
Maya’s Command Engine. However, Python is also more robust than MEL
because it is an object-oriented language. Moreover, Python has existed
since 1980 and has an extensive library of built-in features as well as a
large community outside of Maya users.
6 CHAPTER 1 Maya Command Engine and User Interface

C++ Application Programming Interface


The Maya C++ application programming interface (API) is the most
flexible way to add features to Maya. Users can add new Maya objects
and features that can execute substantially faster than MEL alternatives.
However, tools developed using the C++ API must be compiled for new
versions of Maya and also for each different target platform. Because of
its compilation requirements, the C++ API cannot be used interactively
with the Maya user interface, so it can be tedious to test even small bits
of code. C++ also has a much steeper learning curve than MEL or Python.

Python API
When Autodesk introduced Python into Maya, they also created wrappers
for many of the classes in the Maya C++ API. As such, developers can use
much of the API functionality from Python. The total scope of classes acces-
sible to the Python API has grown and improved with each new version of
Maya. This powerful feature allows users to manipulate Maya API objects
in ordinary scripts, as well as to create plug-ins that add new features to Maya.
In this book, we focus on the different uses of Python in Maya, including
commands, user interfaces, and the Python API. Before we begin our
investigation, we will first look at the key tools that Maya Python program-
mers have at their disposal.

EXECUTING PYTHON IN MAYA


Maya has many tools built into its GUI that allow users to execute Python
code. Before you begin programming Python code in Maya, you should
familiarize yourself with these tools so that you know not only what tool
is best for your current task, but also where to look for feedback from your
scripts.

Command Line
The first tool of interest is the Command Line. It is located along the
bottom of the Maya GUI. You can see the Command Line highlighted in
Figure 1.2.
The Command Line should appear in the Maya GUI by default. If you
cannot see the Command Line, you can enable it from the Maya main
menu by selecting Display → UI Elements → Command Line.
The far left side of the Command Line has a toggle button, which says
“MEL” by default. If you press this button it will display “Python.”
Executing Python in Maya 7

■ FIGURE 1.2 Programming interfaces in the Maya GUI.

The language displayed on this toggle button tells Maya which scripting
language to use when executing commands entered in the text field imme-
diately to the right of the button. The right half of the Command Line, a
gray bar, displays the results of the commands that were entered in the text
field. Let’s create a polygon sphere using the Command Line.
1. Switch the Command Line button to “Python.” The button is located on
the left side of the Command Line.
2. Click on the text field in the Command Line and enter the following
line of text.
import maya.cmds;

3. Press Enter.
4. Next enter the following line of code in the text field.
maya.cmds.polySphere();

5. Press Enter. The above command will create a polygon sphere object
in the viewport and will print the following results on the right side
of the Command Line.
# Result: [u'pSphere1', u'polySphere1']
8 CHAPTER 1 Maya Command Engine and User Interface

You can use the Command Line any time you need to quickly execute a
command. The Command Line will only let you enter one line of code at
a time though, which will not do you much good if you want to write a
complicated script. To perform more complex operations, you need the
Script Editor.

Script Editor
One of the most important tools for the Maya Python programmer is the Script
Editor. The Script Editor is an interface for creating short scripts to interact
with Maya. The Script Editor (shown on the right side in Figure 1.2) consists
of two panels. The top panel is called the History Panel and the bottom panel
is called the Input Panel. Let’s open the Script Editor and execute a command
to make a sphere.
1. Open a new scene by pressing Ctrl + N.
2. Open the Script Editor using either the button located near the bottom
right corner of Maya’s GUI, on the right side of the Command Line
(highlighted in Figure 1.2), or by navigating to Window → General
Editors → Script Editor in Maya’s main menu. By default the Script
Editor displays two tabs above the Input Panel. One tab says “MEL”
and the other tab says “Python.”
3. Select the Python tab in the Script Editor.
4. Click somewhere inside the Input Panel and type the following lines of
code.
import maya.cmds;
maya.cmds.polySphere();

5. When you are finished press the Enter key on your numeric keypad. If
you do not have a numeric keypad, press Ctrl + Return.
The Enter key on the numeric keypad and the Ctrl + Return shortcut are
used only for executing code when working in the Script Editor. The reg-
ular Return key simply moves the input cursor to the next line in the Input
Panel. This convention allows you to enter scripts that contain more than
one line without executing them prematurely.
Just as in the Command Line example, the code you just executed created a
generic polygon sphere. You can see the code you executed in the History
Panel, but you do not see the same result line that you saw when using the
Command Line. In the Script Editor, you will only see a result line printed
when you execute a single line of code at a time.
6. Enter the same lines from step 4 into the Input Panel, but do not execute
them.
Executing Python in Maya 9

7. Highlight the second line with your cursor by triple-clicking it and then
press Ctrl + Return. The results from the last command entered should
now be shown in the History Panel.
# Result: [u'pSphere2', u'polySphere2']

Apart from printing results, there are two important things worth noting
about the previous step. First, highlighting a portion of code and then press-
ing Ctrl + Return will execute only the highlighted code. Second, high-
lighting code in this way before executing it prevents the contents of the
Input Panel from emptying out.
Another useful feature of the Script Editor is that it has support for marking
menus. Marking menus are powerful, context-sensitive, gesture-based
menus that appear throughout the Maya application. If you are unfamiliar
with marking menus in general, we recommend consulting any basic Maya
user’s guide.
To access the Script Editor’s marking menu, click and hold the right mouse
button (RMB) anywhere in the Script Editor window. If you have nothing
selected inside the Script Editor, the marking menu will allow you to
quickly create new tabs (for either MEL or Python) as well as navigate
between the tabs. As you can see, clicking the RMB, quickly flicking to
the left or right, and releasing the RMB allows you to rapidly switch
between your active tabs, no matter where your cursor is in the Script Edi-
tor window. However, the marking menu can also supply you with context-
sensitive operations, as in the following brief example.

1. Type the following code into the Input Panel of the Script Editor, but
do not execute it.
maya.cmds.polySphere()

2. Use the left mouse button (LMB) to highlight the word polySphere in
the Input Panel.
3. Click and hold the RMB to open the Script Editor’s marking menu. You
should see a new set of options in the bottom part of the marking menu.
4. Move your mouse over the Command Documentation option in the
bottom of the marking menu and release the RMB. Maya should
now open a web browser displaying the help documentation for the
polySphere command.

As you can see, the Script Editor is a very useful tool not only for creating
and executing Python scripts in Maya, but also for quickly pulling up infor-
mation about commands in your script. We will look at the command
documentation later in this chapter.
10 CHAPTER 1 Maya Command Engine and User Interface

At this point, it is worth mentioning that it can be very tedious to continually


type common operations into the Script Editor. While the Script Editor
does allow you to save and load scripts, you may want to make your script
part of the Maya GUI. As we indicated earlier, clicking GUI controls
in Maya simply calls commands or executes scripts that call commands.
Another tool in the Maya GUI, the Shelf, allows you to quickly make a
button out of any script.

Maya Shelf
Now that you understand how to use the Command Line and the Script
Editor, it is worth examining one final tool in the Maya GUI that will be
valuable to you. Let’s say you write a few lines of code in the Script Editor
and you want to use that series of commands later. Maya has a location for
storing custom buttons at the top of the main interface, called the Shelf,
which you can see in Figure 1.3. If you do not see the Shelf in your GUI
layout, you can enable it from Maya’s main menu using the Display →
UI Elements → Shelf option.
You can highlight lines of code in the Script Editor or Command Line and
drag them onto the Shelf for later use with the middle mouse button

■ FIGURE 1.3 The Shelf.


Maya Commands and the Dependency Graph 11

(MMB). In the following example, you will create a short script and save it
to the Shelf.
1. Type in the following code into the Script Editor, but do not execute
it (when executed, this script will create a polygon sphere and then
change the sphere’s vertex colors to red).
import maya.cmds;
maya.cmds.polySphere(radius=5);
maya.cmds.polyColorPerVertex(
colorRGB=[1,0,0],
colorDisplayOption=True
);

2. Click the Custom tab in the Shelf. You can add buttons to any shelf, but
the Custom shelf is a convenient place for users to store their own
group of buttons.
3. Click and drag the LMB over the script you typed into the Script Editor
to highlight all of its lines.
4. With your cursor positioned over the highlighted text, click and hold
the MMB to drag the contents of your script onto the Shelf.
5. If you are using Maya 2010 or an earlier version, a dialog box will
appear. If you see this dialog box, select “Python” to tell Maya that
the script you are pasting is written using Python rather than MEL.
6. You will now see a new button appear in your Custom tab. Left-click on
your new button and you should see a red sphere appear in your viewport
as in Figure 1.3. If you are in wireframe mode, make sure you enter shaded
mode by clicking anywhere in your viewport and pressing the number 5 key.
You can edit your Shelf, including tabs and icons, by accessing the
Window → Settings/Preferences → Shelf Editor option from the main
Maya window. For more information on editing your Shelf, consult the
Maya documentation or a basic Maya user’s guide. Now that you have an
understanding of the different tools available in the Maya GUI, we can start
exploring Maya commands in greater detail.

MAYA COMMANDS AND THE DEPENDENCY GRAPH


To create a polygonal sphere with Python, the polySphere command must
be executed in some way or other. The polySphere command is part of the
Maya Command Engine. As we noted previously, the Maya Command
Engine includes a set of commands accessible to both MEL and Python.
As we briefly discussed previously, Maya is fundamentally composed of a core
and a set of interfaces for communicating with that core (see Figure 1.1).
The core contains all the data in a scene and regulates all operations on these
12 CHAPTER 1 Maya Command Engine and User Interface

data—creation, destruction, editing, and so on. All of the data in the core are
represented by a set of objects called nodes and a series of connections that
establish relationships among these nodes. Taken together, this set of relation-
ships among nodes is called the Dependency Graph (DG).
For example, the polygon sphere object you created earlier returned the
names of two nodes when you created it: a node that describes the geome-
try of the sphere and a transform node that determines the configuration of
the sphere shape in space. You can see information on nodes in an object’s
network using the Attribute Editor (Window → Attribute Editor in the
main menu) or as a visual representation in the Hypergraph (Window →
Hypergraph: Connections in the main menu). Because this point is so
important, it is worth looking at a brief example.
1. If you no longer have a polygon sphere in your scene, create one.
2. With your sphere object selected, open the Hypergraph displaying
connections by using the Window → Hypergraph: Connections option
from the main menu.
3. By default, the Hypergraph should display the connections for your
currently selected sphere as in Figure 1.4. If you do not see anything,

■ FIGURE 1.4 The Hypergraph.


Maya Commands and the Dependency Graph 13

then select the option Graph → Input and Output Connections from
the Hypergraph window’s menu.
As you can see, a default polygon sphere consists of four basic nodes
connected by a sequence of arrows that show the flow of information. The
first node in the network is a polySphere node, which contains the para-
meters and functionality for outputting spherical geometry (e.g., the radius,
the number of subdivisions, and so on). In fact, if you highlight the arrow
showing the connection to the next node, a shape node, you can see what
data are being sent. In this case, the polySphere node’s output attribute is
piped into the inMesh attribute of the shape node.
If you were to delete the construction history of this polygonal sphere
(Edit → Delete by Type → History from the main menu), the polySphere
node would disappear and the sphere’s geometry would then be statically
stored in the shape node (pSphereShape1 in Figure 1.4). In short, if the
polySphere node were destroyed, its mesh information would be copied into
the pSphereShape node, and you would no longer be able to edit the radius
or number of subdivisions parametrically; you would have to use modeling
tools to do everything by hand.
While you can also see that information is piped from the shape node into a
shadingGroup node (to actually render the shape), there is a node that
appears to be floating on its own (pSphere1 in Figure 1.4). This separate
node is a special kind of object, a transform node, which describes the posi-
tion, scale, and orientation of the polygonal sphere’s geometry in space. The
reason why this node is not connected is because it belongs to a special part
of the DG, called the Directed Acyclic Graph (DAG). For right now, it suf-
fices to say that the DAG essentially describes the hierarchical relationship of
objects that have transform nodes, including what nodes are their parents
and what transformations they inherit from their parents.
The Maya DG is discussed in greater detail in Chapter 11 in the context of
the Maya API, yet this principle is critical for understanding how Maya
works. We strongly recommend consulting a Maya user guide if you feel
like you need further information in the meantime.
Although Maya is, as we pointed out, an open product, the data in the core
are closed to users at all times. Autodesk engineers may make changes
to the core from one version to another, but users may only communicate
with the application core through a defined set of interfaces that Autodesk
provides.
One such interface that can communicate with the core is the Command
Engine. In the past, Maya commands have often been conflated with
14 CHAPTER 1 Maya Command Engine and User Interface

Maya user interface

MEL Python C++

Maya Command Engine Maya API

Maya application core

■ FIGURE 1.5 Python’s interaction with the Maya Command Engine.

MEL. Indeed, commands in Maya may be issued using MEL in either


scripts or GUI elements like buttons. However, with the inclusion of
Python scripting in Maya, there are now two different ways to issue Maya
commands, which more clearly illustrates the distinction.
Figure 1.5 highlights how Python interacts with the Maya Command
Engine. While Python can use built-in commands to retrieve data from
the core, it can also call custom, user-made commands that use API inter-
faces to manipulate and retrieve data in the core. These data can then be
returned to a scripting interface via the Command Engine. This abstraction
allows users to invoke basic commands (which have complex underlying
interfaces to the core) via a scripting language.
MEL has access to over 1,000 commands that ship with Maya and has been
used to create almost all of Maya’s GUI. While Python has access to nearly
all the same commands (and could certainly also be used to create Maya’s
GUI) there is a subset of commands unavailable to Python. The commands
unavailable to Python include those specifically related to MEL or that deal
with the operating system. Because Python has a large library of utilities
that have grown over the years as the language has matured outside of
Maya, this disparity is not a limitation.
Maya has documentation for all Python commands so it is easy to look up
which commands are available. In addition to absent commands mentioned
previously, there are some MEL scripts that appear in MEL command doc-
umentation as though they were commands. Because these are scripts
rather than commands, they do not appear in the Python command
Introduction to Python Commands 15

documentation and are not directly available to Python. Again, this absence
is also not a limitation, as it is possible to execute MEL scripts with Python
when needed. Likewise, MEL can call Python commands and scripts when
required.1
Another important feature of the Maya Command Engine is how easy it is to
create commands that work for MEL and Python. Maya was designed so that
any new command added will be automatically available to both MEL and
Python. New commands can be created with the Maya C++ API or the Python
API. Now that you have a firmer understanding of how Maya commands fit
into the program’s architecture, we can go back to using some commands.

INTRODUCTION TO PYTHON COMMANDS


Let’s return to Maya and open up the Script Editor. As discussed earlier in
this chapter, the top panel of the Script Editor is called the History Panel.
This panel can be very useful for those just learning how to script or even
for advanced users who want to figure out what commands are being
executed. By default, the History Panel will echo (print) most Maya
commands being executed. You can also make the History Panel show
all commands being executed, including commands called by the GUI
when you press a button or open a menu. To see all commands being exe-
cuted, select the History → Echo All Commands option from the Script
Editor’s menu. While this option can be helpful when learning, it is gener-
ally inadvisable to leave it enabled during normal work, as it can degrade
Maya’s performance. Right now, we will go through the process of creating
a cube and look at the results in the History Panel (Figure 1.6).
1. In the menu for the Script Editor window, select Edit → Clear History
to clear the History Panel’s contents.
2. In the main Maya window, navigate to the menu option Create →
Polygon Primitives → Cube.
3. Check the History Panel in the Script Editor and confirm that you see
something like the following results.
polyCube -w 1 -h 1 -d 1 -sx 1 -sy 1 -sz 1 -ax 0 1 0 -cuv 4 -ch 1;
// Result: pCube1 polyCube1 //

The first line shown is the polyCube MEL command, which is very similar
to the polySphere command we used earlier in this chapter. As you can see,

1
MEL can call Python code using the python command. Python can call MEL code
using the eval function in the maya.mel module. Note that using the python command
in MEL executes statements in the namespace of the __main__ module. For more infor-
mation on namespaces and modules, see Chapter 4.
16 CHAPTER 1 Maya Command Engine and User Interface

■ FIGURE 1.6 The results of creating a polygon cube.

a MEL command was called when you selected the Cube option in the
Polygon Primitives menu. That MEL command was displayed in the
Script Editor’s History Panel.
Because Maya’s entire interface is written with MEL, the History Panel
always echoes MEL commands when using the default Maya interface.
Custom user interfaces could call the Python version of a command, in
which case the History Panel would display the Python command.
This problem is not terribly troublesome for Python users though. It does
not take much effort to convert a MEL command into Python syntax, so
this feature can still help you learn which commands to use. The following
example shows what the polyCube command looks like with Python.
import maya.cmds;
maya.cmds.polyCube(
w=1, h=1, d=1, sx=1, sy=1, sz=1,
ax=(0, 1, 0), cuv=4, ch=1
);

If you execute these lines of Python code they will produce the same result as
the MEL version. However, we need to break down the Python version of the
command so we can understand what is happening. Consider the first line:
import maya.cmds;
Introduction to Python Commands 17

This line of code imports a Python module that allows you to use any Maya
command available to Python. There is only one module that holds all
Maya commands and you only need to import it once per Maya session.
Once it is in memory you don’t need to import it again (we only have
you reimport it for each example in case you’re picking the book back
up after a break from Maya). We will discuss modules in greater depth in
Chapter 4. The next line of code is the Python command.
maya.cmds.polyCube(
w=1, h=1, d=1, sx=1, sy=1, sz=1,
ax=(0, 1, 0), cuv=4, ch=1
);

As you can see, the name of the command, polyCube, is prefixed by the
name of the module, maya.cmds. The period between them represents that
this command belongs to the Maya commands module. We then supply the
command several flag arguments inside of parentheses. A key-value pair
separated by the equals sign, such as w=1, represents the name and value
for the flag argument, and each of these pairs is separated by a comma.

Each flag may be added using a shorthand abbreviation or long version of


the flag name. Although many Maya programmers tend to use the shorthand
flag names in their code, it can make the code more difficult to read later. In
the previous example, the command is using the shorthand flags so it is hard
to understand what they mean. Here is the same version of the command
with long flag names.
maya.cmds.polyCube(
width=1,
height=1,
depth=1,
subdivisionsX=1,
subdivisionsY=1,
subdivisionsZ=1,
axis=(0, 1, 0),
createUVs=4,
constructionHistory=1
);

The long names are easier to read and so it can be good practice to use them
when scripting. Code that is easier to read can be much easier to work with—
especially if you or a coworker has to make any changes several months later!
You may now be wondering how to find the long flag names in the future.

1. Type the following lines into the Script Editor and press Ctrl + Return
to execute them.
import maya.cmds;
print(maya.cmds.help('polyCube'));
18 CHAPTER 1 Maya Command Engine and User Interface

2. Look for the results in the History Panel, which should look like the
following lines.
Synopsis: polyCube [flags] [String...]
Flags:
–e –edit
–q –query
–ax –axis Length Length Length
–cch –caching on|off
–ch –constructionHistory on|off
–cuv –createUVs Int
–d –depth Length
–h –height Length
–n –name String
–nds –nodeState Int
–o –object on|off
–sd –subdivisionsDepth Int
–sh –subdivisionsHeight Int
–sw –subdivisionsWidth Int
–sx –subdivisionsX Int
–sy –subdivisionsY Int
–sz –subdivisionsZ Int
–tx –texture Int
–w –width Length

Command Type: Command

As you can see, the result first displays the command for which help was
requested—polyCube in this case. The following items in brackets, [flags]
and [String...], show MEL syntax for executing the command. In
MEL, the command is followed by any number of flag arguments and then
any number of command arguments. We’ll differentiate these two items
momentarily.
Next, the output shows the list of flags for the command, displaying the
short name on the left, followed by the long name in the middle column.
Each flag is prefixed by a minus symbol, which is required to indicate a
flag in MEL syntax, but which you can ignore in Python. To the very right
of each flag name is the data type for each argument, which tells us what
kind of value each flag requires.
We can see how flags work with the polyCube command. Consider the
following example.
import maya.cmds;
maya.cmds.polyCube();
Executing this command causes Maya to create a polygon cube with default
properties. The parentheses at the end of the command basically indicate that
Flag Arguments and Python Core Object Types 19

we want Maya to do something—execute a command in this case. Without


them, the command will not execute. We will discuss this topic further in
Chapter 3 when we introduce functions. For now, it suffices to say that
any command arguments we wish to specify must be typed inside of the
parentheses, as in the following alternative example.
import maya.cmds;
maya.cmds.polyCube(name='myCube', depth=12.5, height=5);

If you execute the previous lines, Maya will create a polygon cube named
“myCube” with a depth of 12.5 units and a height of 5 units. The first flag
we set, name, is a string, as indicated in the help results. A string is a sequence
of letters and numbers inside of quotation marks, and is used to represent a
word or words. Immediately afterward is a comma before the next flag, depth.
We specify that the depth should be the decimal number 12.5. Such values are
listed as type Length in the help results. Last, we provided the height flag and
supplied a value of 5. In this case, we used the long names of the flags, but we
could also have used the short ones to do the same thing.
import maya.cmds;
maya.cmds.polyCube(n='myCube', d=12.5, h=5);

Looking at the help results, you can see that the axis flag takes three decimal
numbers. To specify this kind of argument in Python, we use what is called
a tuple. A tuple is basically a sequence of objects inside of parentheses, sepa-
rated by commas. The following lines show an example of the same command
using a tuple to specify a different axis.
import maya.cmds;
maya.cmds.polyCube(
name='myCube',
depth=12.5,
height=5,
axis=(1,1,0)
);

FLAG ARGUMENTS AND PYTHON CORE


OBJECT TYPES
As you have seen, most Maya Python commands have flags, which allow
you to change the default settings of the command being executed. Each flag
argument must be passed a value. A flag’s value can be one of several
different built-in Python types. Table 1.1 lists Python’s core object types
that are used by Maya commands.
Note that Table 1.1 is not a complete list of Python core object types—
there are many others that you may use for other purposes in your scripts.
20 CHAPTER 1 Maya Command Engine and User Interface

Table 1.1 Python Core Object Types Used by Maya Commands


Type Examples

Numbers 1
−5
3.14159
9.67
Strings "Maya"
'ate'
"my dog's"
"""homework"""
Lists [1, "horse", 'town']
Tuples (1, "two", 'three')
Booleans True
False
1
0

However, the core object types in this list are the only ones that Maya
commands have been designed to use, so we may ignore the others for now.
Other Python data types are discussed in Chapter 2. Let’s focus for now on
the five types in this list.

Numbers
Maya commands expecting Python numbers will accept any real number.
Examples could include integer as well as decimal numbers, which corre-
spond to int/long and float/double types, respectively, in other languages.

Strings
The string type is any sequence of letters or numbers enclosed in single quota-
tion marks, double quotation marks, or a matching pair of triple quotation
marks of either type. For instance, “boat”, “house”, and “car” are equivalent
to ‘boat’, ‘house’, and ‘car’ as well as to “““boat”””, “““house”””, and
“““car”””. However, the string “3” is different from the number object 3.
Strings are typically used to name objects or parameters that are accessible
from the Maya user interface.

Lists
A list is a sequence of any number of Python objects contained within the
bracket characters [ and ]. A comma separates each object in the list. Any
Python object may be in a list, including another list!
Flag Arguments and Python Core Object Types 21

Tuples
The Python tuple is very similar to the list type except that it is not muta-
ble, which means it cannot be changed. We discuss mutability in greater
detail in Chapter 2. Tuples are contained inside of ordinary parentheses,
( and ).

Booleans
A Boolean value in Python can be the word True or False (which must
have the first letter capitalized), or the numbers 1 and 0 (which correspond
to the values True and False, respectively). These values are typically used
to represent states or toggle certain command modes or flags.

Flag = Object Type


To find out what type of object a command flag requires, you can use the
help command. As you saw earlier in this chapter it will give you a list of
the command flags and what type of value they require. The argument type
is not an option—you must pass a value of the required type or you will get
an error. Using the polyCube command as an example, let’s look at its
width flag and pass it correct and incorrect argument types.

1. Create a new scene by pressing Ctrl + N.


2. Execute the Maya help command for the polyCube command in the
Script Editor:
import maya.cmds;
print(maya.cmds.help('polyCube'));

3. Look for the width flag in the results displayed in the History Panel
and find its argument type on the right side:
–w –width Length

As you can see, the width flag requires a Length type argument, as shown
to the right of the flag name. This is technically not a Python type but we
can deduce that Length means a number, so we should pass this flag some
sort of number. If the number needed to be a whole number, the flag would
specify Int to the right of the flag instead of Length. We can therefore also
deduce that the flag may be passed a decimal number in this case. Let’s
first pass a correct argument.
4. Type the following command into the Script Editor and press Ctrl +
Return to execute it.
maya.cmds.polyCube(width=10);
22 CHAPTER 1 Maya Command Engine and User Interface

You should see the following result in the Script Editor’s History Panel.
# Result: [u'pCube1', u'polyCube1'] #

The result lets us know that the command succeeded and also shows that the
command returned a Python list containing the names of two new nodes that
have been created to make our cube object: “pCube1” (a transform node)
and “polyCube1” (a shape node). Now, let’s see what happens when we inten-
tionally supply the width flag with the wrong data type.
5. Type the following command into the Script Editor and press Ctrl +
Return to execute it.
maya.cmds.polyCube(width='ten');

This time the command returns an error.


# Error: TypeError: file <maya console> line 1: Invalid
arguments for flag 'width'. Expected distance, got str #

The error tells you that the argument for the width flag was incorrect and it
expected a distance value. Even though the help command showed the
width flag needed a Length type, Maya is now calling it a distance type.
This can be confusing at first but most of the time it is very clear what
the flag argument requires simply by looking at the flag in context. The
help command does not describe what each flag does, but you can get
more detailed descriptions using the Python Command Reference, which
we will examine shortly.

COMMAND MODES AND COMMAND ARGUMENTS


Maya commands often have more than one mode in which they can work.
Some commands may be available to use in create mode, edit mode, and/or
query mode, while certain flags may only be available in certain modes.

Create Mode
Most commands at least have a create mode. This mode allows users to
create new objects in the scene and specify any optional parameters. By
default, the polyCube command operates in create mode.
1. Create a new Maya scene.
2. Execute the following lines in the Script Editor to create a new cube.
import maya.cmds;
maya.cmds.polyCube();

Note that you do not have to do anything special to execute commands in


create mode. Leave the cube in your scene for the next steps.
Command Modes and Command Arguments 23

Edit Mode
Another mode that many commands support is edit mode. This mode
allows users to edit something the command has created.
3. Execute the following line in the Script Editor to change the cube’s
width.
maya.cmds.polyCube('pCube1', edit=True, width=10);

As you can see, you specified that the command should operate in edit
mode by setting the edit flag with a value of True. In edit mode, you were
able to change the width of the object named “pCube1” to a value of 10. It
is worth mentioning that some flags in MEL do not require an argument,
such as the edit flag (see help output previously). Such flags, when
invoked from Python, simply require that you set some value (True) to
indicate their presence.
Another important point worth noting is the syntax for operating in edit and
query modes. The first argument we pass to the command is called a
command argument, and specifies the name of the node on which to operate.
As we saw in the help output previously, MEL syntax expects command
arguments to follow flag arguments, while Python requires the opposite
order. The reason for Python’s syntax requirement will be discussed in
greater detail in Chapter 3. Leave the cube in the scene for the next step.

Query Mode
The last mode that many commands support is query mode. This mode
permits users to request information about something the command has
already created.
4. Execute the following line in the Script Editor to print the cube’s width.
maya.cmds.polyCube('pCube1', query=True, width=True);

The result in the History Panel should display something like the
following line.
# Result: 10.0 #

As with edit mode, query mode requires that you specify a command argu-
ment first and then set the query flag with a value of True. Another point
worth noting is that, although the width flag normally requires a decimal
number (when being invoked from create or edit mode), you simply pass
it a value of True in query mode. The basic idea is that in this case, the
Command Engine is only interested in whether or not the flag has been
set, and so it will not validate the value you are passing it.
24 CHAPTER 1 Maya Command Engine and User Interface

As you can see, these three modes allowed you to create an object, change
it, and finally pull up information about its current state. We also noted at
the outset of this section that some flags are only compatible with certain
command modes. While the help command will not give you this informa-
tion, the Command Reference documentation will.

PYTHON COMMAND REFERENCE


Another place you can get more help on a command is from the Maya help
documents. These documents detail every Python command available. The
Python Command Reference is shown in Figure 1.7. Let’s browse the Python
Command Reference to find more information on the polyCube command.
1. In the main Maya window select the menu item Help → Python Com-
mand Reference.
2. At the top of the page is a text field. Click in the search field and enter
the word polyCube.
3. The page will update to show you only the polyCube command. Select the
polyCube command from the list under the label “Substring: polyCube”.
Clicking this link will show you detailed information for the polyCube

■ FIGURE 1.7 Python Command Reference.


Python Command Reference 25

command. As you can see, the Command Reference documents break up


information for all commands into different sections, which we will
now look at in more detail.

Synopsis
The synopsis provides a short description of what the command does. In
this case the synopsis states:
polyCube is undoable, queryable, and editable.
The cube command creates a new polygonal cube.

Return Value
The return value section describes what the command will return when it is
executed. In this case the documentation states:
string[] Object name and node name.
This description shows us that it returns a list of string type objects, which
will be the name of the object (transform node) and the (polyCube) node
that were created.

Related
This section can help you with finding commands that are similar to the
command at which you are looking. For the polyCube command, this
section lists other commands for creating primitive polygon objects:
polyCone, polyCylinder, polyPlane, polySphere, polyTorus

Flags
For the polyCube command, the axis flag is listed first. It shows a short
description of what the flag does and then it lists the argument type to pass
to it. The documentation shows the following text:
[linear, linear, linear]
The command requires a Python list (or tuple) type and that list should hold
three real numbers. If int is not specified for an argument type, then the argu-
ment may be a decimal number, though integer numbers are still valid. In
this case, if we were to define the argument for the flag it could be something
like [1.00, 0, 0].
As we noted earlier, the documentation also displays icons to represent the
command mode(s) with which each flag is compatible.
26 CHAPTER 1 Maya Command Engine and User Interface

■ C (Create): The flag can appear in the create mode.


■ E (Edit): The flag can appear in the edit mode.
■ Q (Query): The flag can appear in the query mode.
■ M (Multiuse): The flag can be used multiple times.
In MEL, multiuse flags are invoked by appending them multiple times after a
command. In Python, however, you can only specify a flag once. As such,
Python requires that you pass a tuple or list argument for a multiuse flag,
where each element in the tuple or list represents a separate use of the flag.

Python Examples
This section can be very useful for those just learning how to script with
Python or those learning how to work with Maya using Python. Here
you can find working examples of the command in use. Some example
sections can have several different samples to help you understand how
the command works. The example for the polyCube command in the docu-
ments shows you how to create a polygon cube and also how to query an
existing cube’s width.

PYTHON VERSION
One final point that is worth discussing is how to locate which version of
Python your copy of Maya is using. Python has been integrated into Maya
since version 8.5, and each new version of Maya typically integrates the
newest stable version of Python as well. Since newer versions of Python
will have new features, you may want to investigate them. First, find out
what version of Python your version of Maya is using.
1. Open up the Script Editor and execute the following lines.
import sys;
print(sys.version);

You should see a result print in the Script Editor’s History Panel that looks
something like the following lines.
2.6.4 (r264:75706, Nov 3 2009, 11:26:40)
[GCC 4.0.1 (Apple Inc. build 5493)]

In this example, our copy of Maya is running Python version 2.6.4.

PYTHON ONLINE DOCUMENTATION


Once you know what version of Python you are running, you can look up
the Python documentation online. The Python web site is located at
Concluding Remarks 27

http://www.python.org/. If you navigate to the Documentation section,


you should see documentation for multiple versions of Python.
As you learn to program using Python you might also be interested in down-
loading Python for your operating system if it does not already include it.
You can find the latest versions of Python at the Python web site. If you plan
to write Python scripts that interact with Maya, it is advisable that you install
the same version that Maya is using. For the most part, many versions of
Python that you will see in Maya are almost identical. However, a newer
version of Python, 3.x, may break a few things that work in older versions.
If you choose to install Python for your operating system, you will be able
to use a Python interpreter, such as the Python IDLE, which acts just like
the Maya Script Editor but for your operating system. This can be useful
for creating tools outside of Maya that communicate with Maya using
Python. Moreover, you could write tools using Python that have nothing
to do with Maya, yet may be helpful for your project’s organization or
interaction with other software like MotionBuilder.

CONCLUDING REMARKS
In this chapter you have learned how Maya commands and Python work
within Maya’s architecture. We have introduced a few methods of entering
Maya commands with Python to modify Maya scenes. We have also
explained how to look up help and read the Python Command Reference doc-
umentation and how to find information about your version of Python. In the
chapters that immediately follow, we will further explain some of the underly-
ing mechanics and syntax of Python and then start creating more complicated
scripts to use in Maya.
Chapter
2
Python Data Basics

CHAPTER OUTLINE
Variables and Data 30
Variables in MEL 33
Keywords 33
Python’s Data Model 34
Mutability 35
Reference Counting 36
del() 37
The None Type 37
Using Variables with Maya Commands 37
Capturing Results 39
getAttr and setAttr 40
Compound Attributes 40
connectAttr and disconnectAttr 41
Working with Numbers 43
Number Types 43
Basic Operators 44
Working with Booleans 45
Boolean and Bitwise Operators 45
Working with Sequence Types 46
Operators 46
Concatenation 47
Indexing and Slicing 47
String Types 50
Escape Sequences and Multiline Strings 50
Raw Strings 51
Unicode Strings 51
Formatting Strings 52
More on Lists 53
del() 54
Nested Lists 54
Other Container Types 56
Sets 57
Operators 57
Dictionaries 58
Operators 59
Dictionaries in Practice 60
Concluding Remarks 62
Maya Python for Games and Film. DOI: 10.1016/B978-0-12-378578-7.00002-8
© 2012 Elsevier Inc. All rights reserved. 29
30 CHAPTER 2 Python Data Basics

BY THE END OF THIS CHAPTER, YOU WILL BE ABLE TO:


■ Define and manipulate data in variables.
■ Compare and contrast Python’s and MEL’s typing systems.
■ Recast variables as different data types.
■ Prevent naming conflicts with built-in keywords.
■ Explain how Python manages memory.
■ Compare and contrast mutable and immutable types.
■ Use variables in conjunction with commands to manipulate attributes.
■ Describe the different numeric types in Python.
■ Use mathematical operators with numeric types.
■ Use logical and bitwise operators with Boolean variables.
■ Use common operators with sequence types.
■ Manipulate nested lists.
■ Create Unicode and raw strings.
■ Format strings that contain variable values.
■ Compare and contrast sets and dictionaries with sequence types.

In Chapter 1, we covered executing commands in Maya using Python. To do


anything interesting, however, we need more tools to create programs. In this
chapter we will explore some of the basics for working with variables in
Python. Because much of this information is available in the online Python
documentation, we will not belabor it a great deal. However, because we
make use of certain techniques throughout the text, it is critical that you have
an overview here.
We begin by discussing variables in the context of the data types you learned
in Chapter 1, and show how you can use variables along with some basic
Maya commands. Thereafter, we discuss some more interesting properties
of the basic sequence types we discussed in Chapter 1. Finally, we discuss
two additional, useful container types.

VARIABLES AND DATA


The basic unit of data storage in any programming language is a variable.
A variable is a name that points to some specific data type. The mechanism
for creating a variable in Python is the assignment operator (=). Because
Python does not require (or allow) declaration without an assignment,
Variables and Data 31

creating a variable is as simple as separating a name and a value with the


assignment operator.
1. Execute the following line in a Python tab in the Script Editor, or from
the Command Line. This line simply creates a variable named contents,
and assigns a string value to it.
contents = 'toys';

2. Once you create this variable, you can substitute in this name anywhere
you would like this value to appear. Execute the following line of code.
You should see the string “toys in the box” print on the next line.
print(contents+' in the box');

3. You can change the value assigned to this variable at any time, which
will substitute in the new value anywhere the name appears. Execute
the following lines of code, which should print “shoes in the box” in
the History Panel.
contents = 'shoes';
print(contents+' in the box');

4. You can also assign completely different types of values to the variable.
Execute the following line of code.
contents = 6;
Python is what is called a strong, dynamically typed language. The line of
code in step 4 demonstrates dynamic typing. You are able to change the type
of any Python variable on-the-fly, simply by changing its assignment value.
However, because it is strongly typed, you cannot simply add this new value
to a string.
5. Try to execute the following statement.
print('there are '+contents+' things in the box');
The console should supply an error message like the following one.
# Error: TypeError: file <maya console> line 1: cannot
concatenate 'str' and 'int' objects #

Because Python is strongly typed, you cannot so easily intermix different


types of variables. However, you can now perform addition with another
number.
6. Execute the following line of code, which should print the number 16.
print(contents+10);

In Python, to intermix types, you must explicitly recast variables as the


expected type.
32 CHAPTER 2 Python Data Basics

7. Execute the following line in Python to cast the number stored in


contents to its string representation. You should see “there are 6
things in the box” in the History Panel.

print('there are '+str(contents)+' things in the box');

In this case, we called the str() function to recast contents as a string. We


discuss functions in greater detail in Chapter 3. Table 2.1 lists some other
built-in functions for recasting variables.
Python provides a built-in function, type(), which allows you to determine
the type of a variable at any point.
8. Execute the following line to confirm the type of the contents variable.
type(contents);

You should see the following line in the History Panel, which indicates
that the variable is currently pointing to an integer.

# Result: <type 'int'> #

As you can see, casting the variable to a string as you did in step 7 did not
change its inherent type, but only converted the value we retrieved from the
variable. As such, you would still be able to add and subtract to and from
this variable as a number. You could also reassign a string representation to
it by assigning the recast value.
9. Execute the following lines to convert the variable to a string.
contents = str(contents);
print(type(contents));

You should see the following output in the History Panel.

<type 'str'>

Table 2.1 Python Functions for Recasting Variable Types


Function Casts To Notes

float() Decimal number If argument is string, raises ValueError if not properly formatted
int() Integer number If argument is string, raises ValueError if not properly formatted
If argument is decimal number, result is truncated toward 0
Allows optional argument to specify non-base 10
str() String
unicode() Unicode string Allows optional argument to specify encoding
Variables and Data 33

Variables in MEL
Variables in Python are incredibly flexible. Compare the previous example
with a comparable MEL snippet.
1. Enter the following lines in a MEL tab in the Script Editor. You should
again see the output “toys in the box” in the History Panel.
$contents = "toys";
print($contents+" in the box");

While MEL allows—but does not require—that you explicitly provide the
variable’s type, the variable $contents is statically typed at this point. It is
a string, and so cannot now become a number. Those unfamiliar with MEL
should also note that it requires variable names to be prefixed with a dollar
sign ($).
2. Execute the following lines in MEL.
$contents = 6;
print("there are "+$contents+" things in the box");

Because you get the output you expect (“there are 6 things in the box”), you
may think that MEL has implicitly handled a conversion in the print call. How-
ever, the number 6 stored in $contents is not in fact a number, but is a string.
You can confirm this fact by trying to perform addition with another number.
3. Execute the following line in MEL.
print($contents+10);

Whereas the Python example printed the number 16 in this case, MEL has
printed 610! In MEL, because the type of the variable cannot change, MEL
implicitly assumed that the number 10 following the addition operator (+)
was to be converted to a string, and the two were to be concatenated. While sea-
soned MEL developers should be well aware of this phenomenon, readers who
are new to Maya will benefit from understanding this difference between MEL
and Python, as you may occasionally need or want to call statements in MEL.
On the other hand, all readers who are as yet unfamiliar with Python would
do well to remember that variable types could change on-the-fly. This
feature offers you a great deal of flexibility, but can also cause problems
if you’re frequently reusing vague, unimaginative variable names.

Keywords
Apart from issues that may arise from vague variable names, Python users
have some further restrictions on names available for use. Like any other
language, Python has a set of built-in keywords that have special meanings.
34 CHAPTER 2 Python Data Basics

You saw one of these keywords—import—in Chapter 1. To see a list of


reserved keywords, you can execute the following lines in a Python tab
in the Script Editor.

import keyword;
for kw in keyword.kwlist: print(kw);

The list of reserved keywords printed out are used for various purposes,
including defining new types of objects and controlling program flow.
We will be covering a number of these keywords throughout the text, but
it is always a good idea to refer to the Python documentation if you would
like more information. The important point right now is that these words all
have special meanings, and you cannot give any of these names to your
variables.

Python’s Data Model


It is now worth highlighting a few points about Python’s data model, as it
has some bearing on other topics in this chapter (as well as many topics we
discuss later in this book). Although we must be brief, we recommend
consulting Section 3.1 of Python Language Reference online if you are
interested in more information.
To maximize efficiency, statically typed languages allocate a specific
amount of memory for different types of variables. Because this amount
of memory is specified in advance, you cannot simply assign a new
type to a name at some later point. On the other hand, as you have
seen in this chapter, Python lets you change the type of a variable at
any point. However, the underlying mechanisms are actually somewhat
subtler.
In Python, variables are just names that point to data. All data are objects,
and each object has an identity, a type, and a value.
■ An object’s identity describes its address in memory.
■ An object’s type (which is itself an object) describes the data type it
references.
■ An object’s value describes the actual contents of its data.
While we will discuss objects in greater detail in Chapter 5, programmers
coming from other languages may find this principle novel.
When you create a new variable and assign it a value, your variable is
simply a name pointing to an object with these three properties: an identity,
Variables and Data 35

a type, and a value.1 Consider a situation where you cast an integer to a


string, such as the following lines.
var = 5;
var = str(var);

In this case, you are not actually altering the data’s underlying type, but are
pointing to some different piece of data altogether. You can confirm this
fact by using the built-in id() function, which provides the address to the
data. For instance, printing the identity at different points in the following
short sample will show you different addresses when the variable is an inte-
ger and when it is a string.
var = 5;
print('int id',id(var));
var = str(var);
print('str id',id(var));

Mutability
In Python, objects can be either mutable or immutable. Briefly construed,
mutable objects can have their values changed (mutated), while immutable
objects cannot.2 We briefly mentioned this concept in Chapter 1 when com-
paring lists and tuples.
In fact, tuples, strings, and numbers are all immutable. As a consequence,
when you assign a new integer value to a variable, instead of changing
the underlying value of the object to which the variable is pointing, the
variable instead points to another piece of data. You can see the effects
of this concept in the following short code example, which will print the
identity for the variable after different integer assignments.
var = 5;
print('5 id',id(var));
var = 6;
print('6 id',id(var));

1
C++ programmers should note that although Python variables are references to data, they
cannot simply be used like pointers. To shoehorn Python into the language of C++, it
always passes parameters by value, but the value of a variable in Python is a reference.
The consequence is that reassigning a variable inside of a function has no effect on its
value outside the function. Chapter 9 discusses these consequences of Python’s data
model in Maya’s API.
2
Immutable containers that reference mutable objects can have their values changed if the
value of one of the mutable objects in the container changes. These containers are still
considered immutable, however, because identities to which they refer do not change.
See the “Nested Lists” section in this chapter for more information.
36 CHAPTER 2 Python Data Basics

Variables Data objects


type: int
value: 10

■ FIGURE 2.1 Two variables may point to the same object.

We examine the effects of mutability on sequence types later in this


chapter.

Reference Counting
As part of its data model, Python uses a system known as reference count-
ing to manage its memory. The basic idea is that rather than requiring
developers to manually allocate and deallocate memory, data are garbage
collected when there are no more names referencing them.
An interesting side effect of this paradigm is that two immutable variables
with the same assignment (e.g., data with the same type and value) may in
fact be pointing to the same data in memory (Figure 2.1). You can see this
principle in action by assigning the same numeric value to two different
variables and printing their identities.
v1 = 10;
v2 = 10;
print('v1 id', id(v1));
print('v2 id', id(v2));

In this case, both v1 and v2 are pointing to the same piece of data. If these
two variables are assigned different data (e.g., some other number, a string,
etc.), then the reference count for their previous data (the integer 10) drops
to zero. When the reference count for objects drops to zero, Python nor-
mally automatically garbage collects the data to free up memory as needed.
Although this concept has only a few consequences for our current discus-
sion, it becomes more important in later chapters as we discuss modules,
classes, and using the API.
It is important to note that while variables pointing to data with an immu-
table type may show this behavior, two separate assignments to mutable
objects with the same type and value are always guaranteed to be different.
For example, even though the following lists contain the same items, they
will be guaranteed to have unique identities.
Using Variables with Maya Commands 37

list1 = [1, 2, 3];


list2 = [1, 2, 3];
print('list1 id', id(list1));
print('list2 id', id(list2));

The assignment of one variable to another variable pointing to a mutable


object, however, results in both pointing to the same data.
list1 = [1, 2, 3];
list2 = list1;
print('list1 id', id(list1));
print('list2 id', id(list2));

This concept has important consequences that we will cover later.

del()
Python has a built-in function, del(), which allows you to delete variables.
Note that this process is not the same as deleting the data referenced by the
variable: Python’s garbage collector will still manage those data. Using this
function with a variable name simply clears the name (and thus eliminates a
reference to its data). The following example illustrates that, even though
v1 and v2 will reference the same data, deleting v1 has no effect on v2.
Trying to access v1 after this point would result in a NameError.
v1 = 5;
v2 = 5;
del(v1);
print(v2);

The None Type


Because we use it throughout the text in some cases, it is worth noting that,
because of how Python’s variables work, Python also implements a None type.
var = None;

One use of this type is to initialize a name without wastefully allocating


memory if it is unnecessary. For instance, many of our API examples in
this text use the None type to declare names for class objects whose values
are initialized elsewhere. We will talk more about class objects starting in
Chapter 5.

USING VARIABLES WITH MAYA COMMANDS


As we noted in Chapter 1, a Maya scene is fundamentally composed of
nodes and connections. Each node has a number of different attributes, such
as the radius of a polySphere, the height of a polyCube, or the maximum
38 CHAPTER 2 Python Data Basics

number of influences in a skinCluster. Although we will talk about


attributes in much greater detail when we introduce the API in later chapters,
it suffices for now to say that they describe the actual data housed in a node.
You have already seen some multimodal, built-in commands designed to
work with different node types. For example, the polySphere command
not only creates nodes for a polygon sphere, but also allows you to query
and edit polySphere nodes. You can use these commands along with
variables very easily. Let’s work through a quick example.
1. Create a new Maya scene and execute the following lines in the Script
Editor.
import maya.cmds;
sphereNodes = maya.cmds.polySphere();

Recall that the results of the polySphere command return a list of object
names (a transform node and a shape node). You can store this result in
a variable when you create a cube. The sphereNodes list now contains
the names of the two objects that were created. If you were to print this list,
you would see something like the following line.
[u'pSphere1', u'polySphere1']

Note that this list does not contain the Maya nodes themselves, but simply
contains their names. For example, using the del() function on this list
would not actually delete Maya nodes, but would simply delete a list with
two strings in it.
2. We will discuss this syntax later in this chapter, but you can use square
bracket characters to access items in this list. For example, you can store
the name of the polySphere node (“polySphere1”) in another variable.
sphereShape = sphereNodes[1];

3. Now that you have stored the name of the shape in a variable, you can
use the variable in conjunction with the polySphere command to query
and edit values. For example, the following lines will store the sphere’s
radius in a variable, and then multiply the sphere’s radius by 2. Remem-
ber that in each flag argument, the first name is the flag, and the second
name is the value for the flag.
rad = maya.cmds.polySphere(
sphereShape, q=True, radius=True
);
maya.cmds.polySphere(sphereShape, e=True, radius=rad*2);

4. You could now reread the radius attribute from the sphere and store it
in a variable to create a cube the same size as your sphere.
Using Variables with Maya Commands 39

rad = maya.cmds.polySphere(
sphereShape, q=True, radius=True
);
maya.cmds.polyCube(
width=rad*2,
height=rad*2,
depth=rad*2
);

Hopefully you see just how easy it is to use variables in conjunction with
Maya commands.

Capturing Results
In practice, it is critical that you always capture the results of your commands
in variables rather than making assumptions about the current state of your
nodes’ attributes. For example, Maya will sometimes perform validation
on your data, such as automatically renaming nodes to avoid conflicts.
Some Maya users may insist that this behavior makes certain tools impos-
sible without advanced, object-oriented programming techniques. In reality,
you simply need to take care that you use variables to store your com-
mands’ results, and do not simply insert literal values—especially object
names—into your code. The following example illustrates this point.
1. Create a new Maya scene and execute the following lines in the Script
Editor. This code will create a sphere named “head.”
import maya.cmds;
maya.cmds.polySphere(name='head');

2. Now execute the following line to try to make a cube with the same name.
maya.cmds.polyCube(name='head');

If you actually look at your cube in the scene, you can see that Maya has
automatically renamed it to “head1” so it will not conflict with the name
you gave the sphere. However, if you were writing a standalone tool that
creates objects with specific names and then tries to use those specific
names, your artists may run into problems if they already have objects in
the scene with conflicting names.
3. Try to execute the following line, and you will get an error.
maya.cmds.polyCube('head', q=True, height=True);

Always remember that Maya commands work with nodes and attributes on
the basis of their names, which are simply strings. When using ordinary
Maya commands, you should always capture the results of your commands
40 CHAPTER 2 Python Data Basics

since there is no default mechanism for maintaining a reference to a specific


node. In Chapter 5 we will see how the pymel module provides an alterna-
tive solution to this problem.

getAttr and setAttr


While there are many commands that pair with common node types, not all
nodes have such commands (and sometimes not all attributes have flags).
It may also become tedious to memorize and use commands with modes,
flags, and so on. Fortunately, Maya provides two universal commands for
getting and setting attribute values that will work with any nodes: getAttr
and setAttr. We’ll demonstrate a quick example.
1. Open a new Maya scene and execute the following lines of code. These
lines will create a new locator and store the name of its transform node
in a variable called loc.
import maya.cmds;
loc = maya.cmds.spaceLocator()[0];

2. Execute the following lines to store the locator’s x-scale in a variable


and print the result, which will be 1 by default.
sx = maya.cmds.getAttr(loc+'.scaleX');
print(sx);

The getAttr command allows you to get the value of any attribute on any
node by simply passing in a string with the node’s name, a period, and the
attribute name. We’ll talk more about working with strings shortly, but the
complete string that we passed to the getAttr command was actually “loca-
tor1.scaleX” in this case.
3. Execute the following lines to double the sx value and assign the new
value to the node’s attribute.
sx *= 2;
maya.cmds.setAttr(loc+'.scaleX', sx);

The setAttr command works just like the getAttr command, and allows
you to set any attribute value on any node by passing a string with the
node’s name, a period, and the attribute name.

Compound Attributes
Many attributes will simply be a string, a Boolean, or a number value.
However, some attributes are called compound attributes, and may contain
several values. It is important to note that these attribute types work differ-
ently with the getAttr and setAttr commands compared to other built-in
commands.
Using Variables with Maya Commands 41

The reason for this difference is that the getAttr and setAttr commands do
not know what data type the particular attribute expects or contains until they
look it up by name. Other commands only work with specific attributes on
specific nodes, and so can work in a more straightforward way, as you will
see in the remainder of this example.
4. In the scene you created in the previous steps, execute the following
line to print the locator’s translation using the xform command.
print(maya.cmds.xform(loc, q=True, translation=True));

As you would expect the result is simply a list: [0.0, 0.0, 0.0].
5. Likewise, when using the xform command, you can set a new translation
value using a list, as in the following line.
maya.cmds.xform(loc, translation=[0,1,0]);

The xform command can work in this way because it is designed to work
exclusively with transform nodes, and the command internally knows
about the data type for the appropriate attribute. On the other hand,
getAttr and setAttr are not so straightforward.

6. Execute the following line to print the locator’s translation using the
getAttr command.

print(maya.cmds.getAttr(loc+'.translate'));

As you can see from the output, the command returns a list that contains
a tuple: [(0.0, 1.0, 0.0)].
7. Using setAttr for a compound attribute also uses a different paradigm.
Execute the following line to set a new translation value for the locator.
maya.cmds.setAttr(loc+'.translate', 1, 2, 3);

As you can see, setting a compound attribute like translation using setAttr
requires that you specify each value in order (x, y, z in this case).

connectAttr and disconnectAttr


The mechanism for transforming data in the Maya scene is to connect
attributes: an output of one node connects to some input on another node.
For instance, you saw in Chapter 1 how the output attribute of a polySphere
node is connected to the inMesh attribute of a shape node when you execute
the polySphere command. While Chapter 1 showed how you can delete
the construction history of an object to consolidate its node network, the
connectAttr and disconnectAttr commands allow you to reroute con-
nections in altogether new ways.
The basic requirement for attributes to be connected is that they be of the same
type. For example, you cannot connect a string attribute to a decimal number
42 CHAPTER 2 Python Data Basics

attribute. However, Maya does perform some built-in conversions for you
automatically, such as converting angle values into decimal numbers. The finer
distinctions between these types of values will be clearer when we discuss the
API later in this book. In the following short example, you will create a basic
connection to control one object’s translation with another object’s rotation.
1. Open a new Maya scene and execute the following lines to create a
sphere and a cube and store the names of their transform nodes.
import maya.cmds;
sphere = maya.cmds.polySphere()[0];
cube = maya.cmds.polyCube()[0];

2. Execute the following lines to connect the cube’s y-rotation to the


sphere’s y-translation.
maya.cmds.connectAttr(cube+'.ry', sphere+'.ty');
maya.cmds.select(cube);

Use the Rotate tool (E) to rotate the cube around its y-axis and observe the
behavior. The result is very dramatic! Because you are mapping a rotation
in degrees to a linear translation, small rotations of the cube result in large
translations in the sphere.
It is also worth mentioning that the connection is only one way. You cannot
translate the sphere to rotate the cube. In Maya, without some very compli-
cated hacks, connections can only flow in one direction. Note, too, that an
output attribute can be connected to as many inputs as you like, but an
input attribute can only have a single incoming connection.
3. Execute the following line to disconnect the two attributes you just
connected.
maya.cmds.disconnectAttr(cube+'.ry', sphere+'.ty');

The disconnectAttr command works very similarly to the connectAttr


command. It simply expects two strings that name the nodes and their attributes.
4. Execute the following lines to create a multiplyDivide node between
the two attributes to scale the effect.
mult = maya.cmds.createNode('multiplyDivide');
maya.cmds.connectAttr(cube+'.ry', mult+'.input1X');
maya.cmds.setAttr(mult+'.input2X', 1.0/90.0);
maya.cmds.connectAttr(mult+'.outputX', sphere+'.ty');
maya.cmds.select(cube);

Now if you rotate the cube, the sphere translates 1 unit for every 90 degrees
of rotation.
Working with Numbers 43

WORKING WITH NUMBERS


In Chapter 1, we introduced some basic object types with which Maya
commands have been designed to work. Although we lumped numbers
together generally, they are actually decomposable into more specific types.
Because you may want to take advantage of these specific types’ features,
and because you may encounter them when testing the types of your
variables, it is worth briefly discussing them.

Number Types
Although you can often intermix different types of numbers in Python,
there are four types of numbers: integers, long integers, floating-point num-
bers, and complex numbers. We briefly cover them here, but you can read
more about these different types in Section 5.4 of Python Standard Library.
As you saw in the beginning of this chapter, an integer is simply a whole
(nonfractional) number. Integers can be positive or negative. The type of
an integer in Python is given as int, as the following code illustrates.
var = -5;
print(type(var));

A long integer differs from an integer only in that it occupies more space in
memory. In many cases, ordinary integers suffice and are more efficient, but
long integers may be useful for computation that deals with large numbers.
In a language like C or C++, a long integer occupies twice as many bits in
memory as an ordinary integer. In Python, a long integer can occupy as
much memory as you require. To create a long integer, you can simply
assign a really long value to your variable and it will become a long integer
automatically, or you can suffix the value with the character l or L. The type
in Python is given as long.
var = -5L;
print(type(var));

Floating-point numbers are any numbers, positive or negative, with digits


after a decimal point. You can explicitly indicate that a whole number value
is to be a floating-point number by adding a decimal point after it. The type
of a floating-point number is float.
var = -5.0;
print(type(var));

Finally, Python allows you to use complex numbers, which consist of both a
real and an imaginary component. It is highly unlikely that you will need to
work with complex numbers regularly. You can create a complex number by
44 CHAPTER 2 Python Data Basics

suffixing the imaginary part with the character j or J. The type is given as
complex.
var = -5+2j;
print(type(var));

You can also access the individual real and imaginary parts of complex
numbers using the real and imag attributes.
print(var.real, var.imag);

Basic Operators
Once you start creating variables, you will want to do things with them.
Operators are special symbols you can include in your code to perform spe-
cial functions. (Note that we also include some related built-in functions and
object methods in our tables.) Section 5 of Python Standard Library includes
information on various operators, and Section 3.4 of Python Language
Reference contains information on overloading operators, which allows
you to assign special functionality to these symbols. Here, we briefly review
some of the basic operators for working with numeric values.
Thus far, you’ve seen some of the basic math operators, such as + for addition
and – for subtraction or negation. Section 5.4 in Python Standard Library con-
tains a table of Python’s built-in operators for working with numeric types.
In Table 2.2 we have recreated the parts of this table containing the most
common operators.
Note that many of these operators also allow in-place operations when used
in conjunction with the assignment operator (=). For example, the following

Table 2.2 Important Numeric Operators


Operation Result Notes

x+y Sum of x and y


x−y Difference of x and y
x*y Product of x and y
x/y Quotient of x and y If x and y are integers, result is
rounded down
x // y Floored quotient of x and y Use with floating-point numbers
to return a decimal result identical
to x/y if x and y were integers
x%y Remainder of x / y
divmod(x, y) Tuple that is (x // y, x % y)
pow(x, y) x to the y power
x ** y x to the y power
Working with Booleans 45

lines create a variable, v1, with a value of 2, and then subtracts 4 from the
same variable, resulting in −2.
v1 = 2;
v1 -= 4;
print(v1);

Note that Python does not support incremental operators such as ++ and −−.
For those readers who are new to programming, it is important to point out
how division works with integer numbers. Remember that integers are
whole numbers. Consequently, the result is always rounded down. The fol-
lowing floating-point division results in 0.5.
1.0/2.0;

The following integer division results in 0.


1/2;

On the other hand, the following integer division results in −1.


-1/2;

You can also mix an integer and a floating-point number, in which case
both are treated as floats. The following division results in 0.5.
1.0/2;

It is also worth mentioning, for those unacquainted with the finer points of
computer arithmetic, that floating-point operations often result in minor
precision errors as a consequence of how they are represented internally.
It is not uncommon to see an infinitesimally small number where you might
actually expect zero. These numbers are given in scientific notation, such as
the following example.
-1.20552649145e-10

WORKING WITH BOOLEANS


Another basic type that is used with Maya commands is the Boolean type
(called bool in Python). Recall that in practice, Boolean values True and False
are interchangeable with 1 and 0, respectively. This principle becomes more
important when using conditional statements, as you will see in Chapter 3.

Boolean and Bitwise Operators


Some operators are especially important when you are working with
Boolean values. You will use these operators widely in Chapter 3 as we
discuss using conditional statements to control program flow. In Table 2.3
46 CHAPTER 2 Python Data Basics

Table 2.3 Important Boolean and Bitwise Operators


Operation Result Notes

x or y True if either x or y is True Only evaluates y if x is False


x and y True only if both x and y are True Only evaluates y if x is True
not x False if x is True; True if x is False
x|y Bitwise or of x and y
x&y Bitwise and of x and y
x^y Bitwise exclusive-or of x and y

we have consolidated the most common operators from tables in Sections


5.2 and 5.4.1 of Python Standard Library.
When working with Boolean variables, the bitwise operators for and (&)
and or (|) function just like their Boolean operator equivalents. The exclu-
sive-or operator (^), however, will only return True if either x or y is True,
but will return False if they are both True or both False.
There are more bitwise operators that we have not shown here only because
we do not use them throughout this text. Programmers who are comfortable
with binary number systems should be aware that bitwise operators also
work with int and long types.3

WORKING WITH SEQUENCE TYPES


In Chapter 1, three of the variable types introduced can be called sequence
types. Sequence types include strings, lists, and tuples. These types basically
contain a group of data in a linear sequence. While each of these types is
obviously unique, they also share some properties, which we briefly discuss
in this section.

Operators
As with numbers and Booleans, sequence types have a set of operators that
allow you to work with them conveniently. Section 5.6 of Python Standard
Library has a table of operations usable with sequence types. We have
selected the most common of these operations to display in Table 2.4.
Because they are perhaps not as immediately obvious as math operators,
some merit a bit of discussion.

3
It is also worth noting that the bitwise inversion operator (~) is not equivalent to the Boolean
not operator when working with Boolean variables. Booleans are not simply 1-bit values, but
are built on top of integers. Consequently, ~False is −1 and ~True is −2.
Working with Sequence Types 47

Table 2.4 Important Sequence Operators


Operation Result Notes

x in s True if x is in s Searches for item in lists/tuples


Searches for character sequence in strings
x not in s True if x is not in s (see in operator)
s+t Concatenation of s and t
s[i] ith item in s First index is 0
Negative value for i is relative to len(s)
s[i:j] Slice of s from i to j (see index operator)
i is starting index, j is end of slice
If i is omitted, i is 0
If j is omitted or greater than len(s), j is len(s)
s[i:j:k] Slice of s from i to j with step k (see index operator)
len(s) Length of s
min(s) Smallest item in s Corresponds to ASCII code for strings
max(s) Largest item in s Corresponds to ASCII code for strings
s.index(x) Index of first x in s
s.count(x) Total occurrences of x in s

Concatenation
The first operator worthy of a little discussion is the concatenation operator
(+). As you saw in the examples at the outset of this chapter, MEL concate-
nated two strings that we expected to be numbers. Python allows sequence
concatenation in the same way. Concatenation creates a new sequence com-
posed of all of the elements of the first sequence, followed by all of the ele-
ments in the second sequence. You can concatenate any sequence types, but
only with other sequences of the same type. You cannot concatenate a list with
a tuple, for instance.
The following line produces the string “Words make sentences.”
'Words' + ' ' + 'make' + ' ' + 'sentences.';

Likewise, the following line produces the list [1, 2, 3, 4, 5, 6].


[1,2,3] + [4,5,6];

Indexing and Slicing


An important operator for sequence types is the index operator, represented
by the square bracket characters ([ and ]). At its most basic, it corresponds
to the index operator found in most languages. Note also that sequence
types all use zero-based indices (Figure 2.2). For example, the following
line results in just the “c” character, as it occupies index 2 in the string.
48 CHAPTER 2 Python Data Basics

string value: a b c d e
indices: 0 1 2 3 4

■ FIGURE 2.2 Sequences use zero-based indices.

tuple elements: 1 2 3 4 5
negative indices: -5 -4 -3 -2 -1

■ FIGURE 2.3 Negative indices are relative to sequence length.

string value: H o l y c a t s , P y t h o n i s a w e s o m e
indices: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

■ FIGURE 2.4 Slicing a string from index 5 to 9.

'abcde'[2];

Because lists are mutable, you can also use this operator to set individual
values for a list. The following line would result in the list [0, 2, 3].
[1,2,3][0] = 0;

However, because they are immutable, you cannot use the index operator to
change individual items in a tuple or characters in a string. The following
two lines would fail.
'abcde'[2] = 'C';
(1,2,3)[0] = 0;

You can use a series of index operators to access elements from sequences
embedded in sequences. For instance, the following example will extract
just the “x” character.
('another', 'example')[1][1];

Another important feature of Python’s index operator is that it allows you to


supply a negative index. Supplying a negative index gives the result relative
to the length of the sequence (which is one index beyond the final element;
Figure 2.3). For example, the following line would print the number 4.
print((1,2,3,4,5)[-2]);

Python’s index operator also offers powerful, concise syntax for generating
slices from sequences. You can think of a slice as a chunk extracted from a
sequence. The most basic syntax for a slice includes two numbers inside
the square brackets, separated by a colon. The first number represents the
start index, and the second number represents the end index of the slice
(Figure 2.4). Consider the following example.
Other documents randomly have
different content
The Project Gutenberg eBook of Revenge of
the Vera
This ebook is for the use of anyone anywhere in the United States
and most other parts of the world at no cost and with almost no
restrictions whatsoever. You may copy it, give it away or re-use it
under the terms of the Project Gutenberg License included with this
ebook or online at www.gutenberg.org. If you are not located in the
United States, you will have to check the laws of the country where
you are located before using this eBook.

Title: Revenge of the Vera

Author: Henry Hasse

Illustrator: Joseph Doolin

Release date: June 8, 2020 [eBook #62351]


Most recently updated: October 18, 2024

Language: English

Credits: Produced by Greg Weeks, Mary Meehan and the Online


Distributed Proofreading Team at http://www.pgdp.net

*** START OF THE PROJECT GUTENBERG EBOOK REVENGE OF THE


VERA ***
Revenge of the Vera
By HENRY HASSE

The unarmed freighter Vera was plowing through


space to meet the deadliest pirate of the
Void—rocketing into battle against a fighting
ship even the Space Patrol could not vanquish.

[Transcriber's Note: This etext was produced from


Planet Stories Fall 1943.
Extensive research did not uncover any evidence that
the U.S. copyright on this publication was renewed.]
The man seemed too big for the single, cushioned seat of the tiny
space cruiser. But he did not remain in the seat long, and when he
moved it was with a swift surety that belied his bulk. He stepped
over to the visipanel, peered into it and saw only a few pinpoints of
stars. His eyes, as icy as those stars, narrowed until they, too, were
but pinpoints. He grasped the directional finder and swung it in
eccentric parabolas across all the heavens before him. The star
pinpoints swung to and fro, in and out of the visipanel ... and then
he saw it.
A vague, darker shape against the blackness, blotting out a few of
the stars. Lucky! he thought, as he leaped back to the controls to
change direction. Lucky to find it before the Earth Patrol got there.
The news had already gone out. But he did not exult; his lips
tightened into a thin hard line, and his throat tightened too, with the
foreboding that crowded out all hope.
As he drew swiftly nearer, he could see the huge luxury liner
helplessly drifting. He could see the black ragged hole in the hull. He
could see the name on the prow, Martian Princess. He could see
other things which he didn't want to see, which he didn't want to
approach but knew he must. Numerous tiny white-faced things,
staring and bloated and reflecting the leprous sunlight....
The tiny cruiser clanged against the larger bulk, and her magniplates
held. The man was already in space-suit. With a trembling hand he
brushed back his blonde hair, then pulled down the Crystyte-fronted
helmet. He stepped out into space.
He did not immediately board the liner. Instead he moved among the
scores of drifting corpses, using a propulsion pistol. He pulled each
corpse to him, stared searchingly into its face, then thrust it away
with a shudder. Before he had half finished he was sick; but he felt
hope surging in him again, for he had not yet found what he was
looking for. Perhaps, after all ... somehow ... she had not taken this
liner....
He boarded it, moved along the corridors and into the staterooms.
But all was a shambles. The pirates had struck as usual: sudden,
ruthlessly ramming; had smashed completely through this liner like
an eggshell! He saw corpses half encased in spacesuits, but it had
been a futile effort. Many of the passengers had holes blasted
through them, tiny pencil-thin holes of concentrated atom-blasts at
close range, mute evidence of the pirates' deadly work. The once
gorgeous salons were stripped of the silks and fineries. Staterooms
thoroughly looted. Even the corpses stripped of all personal jewelry
and other finery.
The grim-faced young man, the only moving and living thing aboard,
noticed all this but secondarily. His heart was pounding with a newly
rising hope. For in none of the staterooms had he found her.
He moved through the ragged gap and out the other side of the
ship. More drifting corpses, hugging the hull because of the slight
gravity. Methodically he moved among them, pulling them around,
thrusting them away.
And then—one he did not thrust away. His face beneath the helmet
stared, and became suddenly anguished. He hugged the body tightly
to him. Using the pistol, he propelled his way back through the hull.
He carried the girl back along the main deck, and there laid her
gently down away from the others. He stared down, his face twisting
helplessly, his fists clenching and unclenching.
She had been young, lovely. Her face was somehow still beautiful, as
he remembered it. She had died quickly, he saw, and was glad of
that. He would leave her here on the deck, for he knew the Patrol
men would tow this liner back to Earth, where she would wish to be.
He looked long, so that the vision of her would remain in his mind
always; then he turned and strode firmly back to his cruiser. His face
as he looked out to the stars was wet beneath the glass—but there
was no one there to see. There was no one there to hear—but his
lips moved in a grim and terrible oath.
George Marnay, of Tri-Planet News Service, tugged at the big guy's
arm.
"Come on, now, what do you say? Let's get out of here. You've had
enough of that stuff, and you're talking too much. You're heading for
more trouble than you've ever seen in one night!"
The big man peered at the smaller one through a tangle of blonde
hair which fell over blue and bleary eyes. Then he slammed his glass
down on the bar and jerked his arm away, staggering a little. Marnay
caught him and steadied him.
"Quit pushin' me, dammit," the big man said thickly. "Lemme 'lone.
Go 'way, go 'way!"
"All right, mister, it's your funeral," Marnay shrugged. But as the
other man moved away, threading unsteadily among the tables,
Marnay turned and continued to watch him worriedly. And he
listened even more worriedly.
The fellow's voice was thick, but it was still loud over the din in the
room. He had become increasingly voluble as the potent tsith took
effect. Obviously this was his first trip to Mars, and he didn't know
the Red Halo was named sardonically: it was the rendezvous for the
worst cut-throats of three planets!

Marnay frowned. He hated men who became voluble under liquor,


but there was something about this big, blonde guy he liked in spite
of it! Something beyond the fact that he was an Earthman and an
American. Now Marnay wished for the fellow's own sake that he'd
shut up. But it was probably too late. Every outlaw in the place must
have known by this time that the blonde Earthman was from the
freighter, Vera—and that the Vera was leaving tomorrow on a sneak
trip, with ten million dollars in supplies and mining equipment for the
Callisto colonies!
One of the richest shipments ever to go out from Mars, and now,
due to a few drinks and one bragging tongue it was a secret no
longer. Marnay knew this information would soon be spreading
through the criminal honeycombs of the Martian capital-city. He also
knew if any of Prather's men were here—almost a certainty—the
Vera would never get beyond the asteroids, much less to Callisto.
Through the haze of smoke he watched the motley little groups that
filled the room. Tall, frail Venusians, pallid and dreamy-eyed and
apparently docile, but who wouldn't hesitate to slit a throat on the
slightest provocation. Leathery, heavy-lidded Martians, eternally
sullen and quarrelsome. Earthmen, with that swaggering superiority
and egotism which they'll probably retain to the end of time, making
them the most hated men in the system. Marnay wondered how
many in this room were Prather's men; probably a few of each race,
but who could ever pick 'em out? Marnay had tried. That's what he
was here for.
Suddenly he became tense. This was what he had feared. He saw
the big Earthman stagger heavily against a table.
A mean looking Martian jumped up and shoved him violently away;
the Martian's hand flew to his heat-gun, obviously awaiting an
excuse to use it. But the Earthman only stared at him stupidly for a
moment, swayed, and then bowed low, almost losing his balance.
He mumbled a thick apology and moved away. The Martian
glowered, called him something not very nice. Other Martians at the
table laughed.
Marnay sighed in relief. The bartender, grinning, touched him on the
elbow. "If he's a friend of yours," he said, "better get him out of
here."
"He's no friend of mine," Marnay snapped. "But I think you're right,
anyway." He moved across the room. Already he could see many of
the spacemen listening to the words "Vera" and "cargo," a little too
attentively.
Marnay grasped the fellow's arm firmly, said "Come on." He steered
him back to the bar, easily. Then past it toward the door. But the
fellow smelled the fresh air and rebelled.
"You damn fool," Marnay said, struggling with the Earthman's two
hundred pounds, "I'm gonna keep you out of trouble in spite of
yourself!"
"Aw-w, we're all friends here," the other said very loud, peering
around happily. A couple of Venusians at the bar snickered at the
naive words.
"Mister, if you only knew!" Marnay said. "Come on, now, I know a
better place than this." He added: "I'll buy you a drink there."
"M-m-m ... nope. You gotta buy me a drink."
"That's what I said," Marnay sighed.
"Oh-h. Then whyn't you say so?" Leaning on Marnay affectionately,
he allowed himself to be steered outside.
Marnay sighed with relief and quickened his pace, pulling the other
along after him. His only thought now was to get him away from the
dives of this spacerfront street.
"Mister, you sure spilled the beans," Marnay muttered, more to
himself than to his friend. "I know I wouldn't want to be riding the
Vera this trip. You've endangered the life of every man aboard! Why
didn't you just send Prather an engraved invitation to come help
himself to that cargo?"
"I would have, but didn't know where to reach him; besides that
wouldn't have been very subtle, would it now?" The fellow's speech
was no longer thick. He suddenly quit leaning on Marnay,
straightened up and pulled him around a corner into a dim side
street. He stood there grinning in Marnay's face.
Marnay's face was something to grin at. His mouth was hanging
open as though on hinges.
"You can close it now," the other said, as he reached out and closed
it for him.
"Say! You—you were just putting on an act back there!" Marnay
finally managed to exclaim, inanely.
"A good one, I hope."
"No, I'm wrong." Marnay shook his head slowly. "Couldn't have been
an act, I stood right there and watched you drink at least eight
tsiths. My own record's four—and then they carried me out."
"You just thought you saw me drink 'em. Good trick, if you know
how."
Marnay nodded. Then he looked at the man narrowly, grasped his
arm and said, "Come on, let's get away from here. And listen!
Whatever it is you've got up your sleeve, I want in on it! I'm George
Marnay. Tri-Planet News Service."
"Bob Kennett," the other said simply, sticking out his hand. "And you
are in on it. That wasn't an accident when I bumped into you there
at the bar. I thought I'd like to know you, because I heard you were
making a few undercover inquiries about Prather! Mind telling me
just what your interest is in that pirate?"
Marnay replied, his voice suddenly gloomy. "Guess I should have
said I'm formerly of Tri-Planet News. You see, it's an old, familiar
story. I was on an assignment back on Earth, and I happened to
uncover a huge spacer-contract graft ... you know, millions being
side-tracked into private pockets...."
"Well?"
"Well," Marnay wailed, "how was I to know that one of the big-shots
implicated was my boss's brother-in-law? So to shut me up I was
given this assignment. Sent out here to get a line on Prather, or else.
It's a cute side-track, often used; what we newsmen call the
graveyard assignment."
Kennett was interested. "Uh-huh," he nodded. "And just how much
of a line have you got on Prather so far?"
"I'll give you one guess! Precisely nothing. Oh, of course I know all
the stories. In the past few years the Patrol has destroyed his base
of operations on Io, and again on Mercury, and twice on Ceres. But
that pirate's as elusive as the last pea on the plate! Always he's one
jump ahead of them, because of his spy system." Marnay shrugged
hopelessly. "I suppose some of his men were in the Halo tonight, but
how would I know 'em? I hear they drift in and out like ghosts. And
that, by the way, is why I was trying to shut you up with that story
you were broadcasting."
"And that," Kennett said very grimly, "is exactly what I wanted to do,
broadcast it. Subtly, of course."
"Yes, I gather that, now. And I think I see your idea. You
deliberately want Prather to go after the Vera! But—what then?"
Kennett stopped and looked straight at Marnay. When he replied his
voice was suddenly ice: "What then? Then I'll accomplish what the
entire Patrol has been trying for five years. I'll get that pirate."
Marnay, looking at him, saw a sudden bitter look in his eyes, and
grim lines around his mouth. He knew that the other men had said
what Kennett just said—but Prather was still free in the spaceways.
"Mighty big order," Marnay ventured.
"I realize that! But it's taken me three years to evolve this plan, and
I think it'll work." He looked steadily at Marnay. "Are you in with
me?"
"Try and keep me out!"
"Good. But I want to make it plain it's more than a newsstory we're
after. This will be all or nothing, Prather's life or ours, and if my plan
misses there can't be a second chance. And remember Prather's
clever, he can smell a trap a light-year away. Just now, everything
depends on how good my acting was tonight."
"Then I'd say you've nothing to worry about," Marnay replied, "for
you sure fooled me."
Kennett nodded. "We'll see. We'll know for sure tomorrow. Whether
we go pirate-hunting or not...."

They obtained cheap lodging on one of the dark, rear streets


bordering the spaceport. Marnay slept, but not Kennett. He paced
the narrow room, nervously, smoking vile Venusian cigarettes and
awaiting the dawn.
The Martian dawn was breaking when there came a knock at the
door. Kennett muttered, "At last!" and sprang to the door to admit
someone. A Martian.
Marnay, suddenly awake, saw that he was the same mean looking
Martian whom Kennett had nearly had trouble with the night before!
"V'Norghi, of the Martian Secret Police," Kennett said, presenting
him.
The fellow only nodded sullenly, addressed himself to Kennett: "It
would seem it is working, your plan. Shortly after you left the Halo
last night ... I made certain inquiries ... learned that the news of the
Vera's secret trip had spread into the ... uh ... the proper
channels...." The Martian seemed a little reluctant.
"All right, all right, V'Norghi," Kennett said impatiently. "What else?
You know what I want to know!"
"Well ... yes, a small, fast cruiser did leave here. About an hour after
midnight ... quite hurriedly it would seem. Now understand, Kennett,
I couldn't say—"
"No, you couldn't say it was some of Prather's men. Like hell you
can't! What destination? Where is Prather's new base? Mercury?
Venus?"
V'Norghi started to shake his head negatively, but didn't. He looked
distressed. Kennett paced up and down the room. He turned
suddenly on the Martian and laughed mirthlessly.
"Oh, you don't need to answer! I know as well as you do where he
is! Out on one of the Jupiter satellites somewhere—probably Io, his
old base."
The Martian looked even more distressed, and Kennett nodded,
satisfied. "Sure, I knew it all the time. That's why I let it be known
the Vera was going out to Callisto." He clapped the Martian on the
shoulder. "It's all right, V'Norghi, you've done me a mighty big favor
as it is. Thanks, thanks a lot."
They shook hands solemnly. Kennett said, smiling a little: "Don't
worry, V'Norghi; when you see me again there won't be any more
Prather."
"Goodbye, Kennett. I wish you good luck." But the Martian's voice
was sad, as though he thought Kennett wouldn't have it.
Kennett turned to Marnay when the Martian had gone. "The Martian
Secret Police!" he said contemptuously. "They're very little above the
outlaw scum of this city, themselves. I'm sure Prather buys them off,
and I think V'Norghi almost hopes I won't succeed! But you see, I
happened to save his life once; and whatever else you say of the
Martians, you can't say they aren't conscientious toward their
obligations."
"I see," Marnay nodded. "But what about that cruiser he says left
here? You really think that was some of Prather's men, hurrying to
tell him the Vera's on the way out there with a rich cargo?"
"I'm sure of it. That's the way Prather's always worked."
"But they're going where? Out around Jupiter you said. You can't be
sure that's where Prather is!"
"Oho, but that's exactly what I can be sure of! You see, I've not only
kept abreast of Prather's activities currently, but I've studied every
available past record on him. His methods, his escapes, his shiftings.
Not even the Patrol has kept tabs on him as I have. Admittedly, he's
as clever as he is ruthless. But I know his system now."
"All right," Marnay conceded. "Your calculations tell you he's out
around Jupiter now. Won't that be about like looking for the
proverbial needle in the haystack?"
"I guess so. That's why we're not going to look for him at all. He's
coming right out into space after the Vera—I hope."
So he was coming out after the Vera! Marnay suddenly remembered
the time when Prather had smashed through a cordon of Patrol
ships, demolishing four and outrunning the rest. Marnay grimaced,
but he merely said: "All right, when do we leave?"
"About noon, if I can wait that long! That cruiser that left here last
night is probably very fast, and the Vera is just a slow old freighter;
so if we give them about twelve hours' start, and Prather acts at
once, he ought to meet us somewhere just the other side of the
asteroids."
"Oh," Marnay said, pretty feebly. So the Vera was just a slow old
freighter. And with it they were going to capture the most ruthless
pirate of the century! That was certainly a bright picture. Marnay
began to wonder, wryly, how the hell he had ever gotten into this,
and why!

And his first sight of the Vera was nothing to inspire confidence.
Shortly before noon they proceeded to the spaceport, past the
Commercial locks, the Patrol locks, and on to the opposite side of
the vast plaza.
There, in the farthest and most obscure lock, Marnay saw the Vera—
long, heavy, clumsy looking. He recognized it when they were yet a
hundred yards away, because the name Vera was emblazoned across
the prow with a bold flourish that seemed somehow out of keeping
with the crude ship.
"It's just occurred to me," Marnay said. "Vera is an unusual name for
a freighter! That's a girl's name. Sounds sentimental or something."
"Does it?" Kennett said. Marnay looked at him queerly, but Kennett
said nothing more.
They came nearer, and Marnay began to see the ship clearer, and it
suddenly seemed to him there was something wrong with the whole
thing. It was more than merely clumsy. It was grotesque.
Marnay stopped. "Say! I never saw a spacer quite like that before. It
looks kind of funny, yet I can't say exactly—"
"Come on, come on," Kennett said, taking his arm and hurrying him.
"Never mind that. Supplies are all aboard, all we have to do is
leave."
It was quiet around the spacer. No activity. They entered the bow
lock. Marnay looked down a long, empty, silent corridor.
"What the hell!" he exclaimed. "Ain't there any crew?"
Kennett said: "Yes. You and me are the crew."
"Uh huh. But we are taking this misshapen piece of junk clear out to
Jupiter? I got that part of it right?"
"That's right. I decided it would be a long grind all by myself. There
are minor annoyances, such as having to sleep sometimes."
"Where's that ten million dollar cargo," Marnay grinned, staring
around.
Kennett said very seriously: "Oh, we're carrying quite a cargo all
right. And it's all for Prather if he wants it. But it's hardly the cargo
he thinks he's going to get."
"The generosity of your information overwhelms me." Marnay stared
around some more. Everything was stark and bare, save for the
necessary controls. "Wish we had a couple of long range atom-blasts
like the Patrol ships carry. Any aboard?"
"No weapons at all aboard," Kennett stated.
"How comforting! You sure make a fellow feel right at home."
"You don't sound very confident in me and the Vera," Kennett said.
"Don't you believe we can get Prather?"
"Oh sure! I believe in fairies, and Santa Claus, and the Easter rabbit,
and you and the Vera. Hell, don't get me wrong, Kennett. You can't
shake me now, you've picked yourself a crew!"
Kennett permitted himself a smile as he moved swiftly to the
controls. "Good! I knew you'd think that way."
Marnay said: "Don't flatter me, I never think. There's always been
idiocy in my family and this proves it."

They'd been about twelve hours out when Kennett handed over the
controls. He moved down the corridor into the middle part of the
ship. Marnay heard him pounding and moving around back there for
hours, but couldn't imagine what he was doing.
When he returned, Kennett pulled a lever and a heavy double door
slid across, isolating the control room and part of the corridor from
the rest of the ship. He volunteered no explanation, however.
On Marnay's off-duty he moved back toward those doors,
experimentally. Kennett stopped him with: "Sorry, but this will have
to be our quarters from now on."
Marnay nodded to himself. He was a newsman, a good one, and he
knew people. He could see that Kennett was restless and impatient
for action despite the fact that he was deliberately holding the Vera's
speed down.
But Marnay said nothing, and on the second day out, Kennett
seemed a little more talkative. He said: "I guess you've been
wondering why I want so much to get Prather."
Marnay shrugged, but looked at his companion shrewdly.
"Wondering? No, I'm not wondering. Only last month Tri-Planet
Metals boosted the reward up to a half million."
"That so? I didn't know that. But then, I haven't thought much about
the reward angle." Kennett sounded as though he meant it. He went
on: "You know how Prather works, I suppose."
"Ramming?" Marnay said. "Yes, I know. His ship is supposed to be
built of some tough new metal he found on Mercury. I've heard that
even his tubes are made of it, and are slightly expansive under
pressure, giving him greater speed than any tubes yet known."
"That's probably true," Kennett said. "But the important thing is, he
can ram completely through any ordinary spacer. And usually does."
Marnay nodded. "I've heard such stories."
"You've heard such stories," Kennett repeated with a startling
bitterness. "But I saw one—just one. Three years ago when I was a
rookie on the Earth Patrol. We received a flash that Prather had
rammed and looted a passenger liner enroute from Mars to Earth.
The Salvage men were sent out to rescue any possible survivors.
What headquarters really meant was that they were to do the
mopping up—they knew there wouldn't be any survivors. I wasn't on
Salvage duty then, but I grabbed a swift Patrol boat and got out
there first, anyway...."
Kennett paused, and for a moment Marnay saw horror in the other's
eyes. Then Kennett continued:
"You know, Marnay, when a Patrol man applies for leave after a job
like that, and stays drunk for a week, nothing is thought of it. I
didn't even apply for leave. I simply left duty, and I stayed drunk for
a month, not a week. After which, headquarters told me I was
relieved from duty permanently. I didn't care. Not any more."
Marnay waited. He knew Kennett hadn't finished. For a single
instant, the space of a memory, Kennett caught his breath in his
throat; then:
"You see," he said, turning away, "the girl I loved was on that liner;
and I found her. She was returning to Earth and we were to be
married. Her name was Vera, too."

The Vera lumbered along at about half speed. The fourth day they
passed beyond the asteroid belt.
"Double duty now," Kennett pronounced grimly. "It may be only a
matter of hours until Prather sights us—but I want to be sure of
sighting him first!"
"Okay!" Marnay said.
From then on, one of the men stayed always by the visipanel,
manipulating the dial which magnified space for a thousand mile
radius. But all remained a vast swimming blackness. An occasional
meteor flashed across, but no sign of any spaceship.
Once Marnay, at the controls, gave a few experimental blasts with
the rocket speeds. The Vera jerked a little. At once Kennett was
leaping to his side, spinning him around in the seat.
"What the hell!" he yelled, his face a little pale. "Do you want to—"
He didn't finish, but turned away, as the rockets purred smoothly
again. Marnay smiled to himself. Had Kennett been about to say,
"blow us up?" Was that the secret of the Vera?
Maybe. Marnay grew serious as he pondered on it—the rest of the
ship back there which Kennett had shut off. Suppose the ship was
full of Tynyte space-bombs? Marnay remembered the Patrol's
encounters with Prather. They'd tried atom-blasts at first, but before
they could take effect the tough pirate ship slid from beneath them
like an eel in oil. Then they had tried Tynyte bombs. But the pirate
ship was reputedly so fast that not one of the bombs could reach its
mark with any effectiveness.
How could Kennett, then, in the plodding Vera, hope to succeed with
Tynyte bombs?
A sudden fantastic thought flooded Marnay's brain—something about
super speed—but he immediately dispensed with that idea. He was
no spaceman, but he knew enough about Spacer construction to
know that Kennett had no hidden speed here in the Vera. No, it was
something else he must have up his sleeve....
Kennett went back into the middle of the ship a few more times, as
though on trips of inspection, but didn't stay long.
At last Marnay said, in his impatience: "We'll be meeting up with
Prather any minute now! Hadn't you better give me my orders?"
"No orders," Kennett replied with amazing calmness.
"But—damn it, man! At least I want to know what to expect!"
"I'm sorry, Marnay. Bear with me just a little longer now. If I told you
any more you might become panicky at the last second and ruin
everything. That absolutely mustn't happen. I will tell you just this
much: there's never been a Spacer like this before, it's something
utterly revolutionary in Spacer construction. I worked on it three
years, building it almost single-handed, just for the sole moment
when I'd meet up with Prather. It worked all right on a tiny model—
but if the real thing doesn't work we won't be alive to know it. If
only Prather would hurry!"

Kennett turned the visipanel dial nervously, watching the swimming,


empty blackness. "Maybe he hasn't swallowed the bait!" he
exclaimed. "Maybe those weren't even his men that left Mars, and
he doesn't know we're out here at all! Say, if that dirty V'Norghi has
double-crossed me...." Kennett stopped, laughed shortly. "Well,
nothing we can do now. I feel it only fair to tell you, Marnay: we
haven't enough fuel to take us on to Callisto, or back to Mars either.
I was depending on Prather for our return fuel."
Marnay looked up with a wry grimace. "D'you know, Kennett, that's
one thing I like about you. You're always telling me such comforting
things at the most unexpected moments!"
But Prather showed up. It was hours later, and startlingly sudden.
Kennett called from the visipanel:
"There he is, I almost missed him! I told you he's clever, he's got his
ship painted solid black! Now listen, Marnay. I'm going to keep him
in the panel, you stay at the controls and obey my instructions."
"Okay, but how close is he?" Marnay asked nervously.
"Dial shows a thousand miles—off the starboard bow, and he's
approaching fast. Maintain your present speed."
Marnay did, but wished he was at the panel instead of Kennett.
"Click on the radio," Kennett called a minute later. "But don't answer
if he sends a message through. He doesn't seem to be suspicious of
anything yet, but I know he's sighted us."
Kennett continued to watch. He called: "Cut speed to one quarter.
One quarter, damn it!" as Marnay fumbled with the tube control.
"There, that's it, good. That'll show him we've sighted him, but he
mustn't suspect we're too anxious to meet up with him."
"I'm not anxious to," Marnay replied. And then he jumped as a cold,
strange voice came through the open radio.
"The Vera? Hello! This is Prather. You will please go into a drift while
I board you. You have a cargo I should like to inspect." The voice
was mocking, but at the same time anticipatory.
So the bait had worked! Marnay reached automatically for the shut-
off control, but Kennett's voice stabbed at him: "Leave 'em alone!
You will maintain one-quarter speed. And leave our sender off, don't
answer; let him think we've got no radio at all, so he'll ram us."
So he'll ram us! That was a nice pleasant thought, Marnay thought,
wiping sweat from his brow. But he obeyed Kennett's orders.
Again came Prather's voice: "Attention Vera! You will go into a drift
immediately or take the consequences. Last warning."
Marnay had an overwhelming desire to shut off the tubes, but he
didn't. He maintained their one-quarter speed. Then through the
speaker the two men heard:
"They have no radio. We'll ram. It's just as well." Marnay could
almost imagine Prather's shrug.

From the panel Kennett said "Okay! Fine! He's still coming at us. You
can lock the controls now, and come over here."
Marnay did that willingly. He peered into the panel. Then he gasped.
The huge, black pirate ship was looming up terrifyingly large, filling
half of space, speeding straight at them. It couldn't have been a
hundred miles away, Marnay thought, and in another minute it
would smash through the Vera like an eggshell!
Marnay waited for Kennett to make some move. He made none.
Then, in a sudden flood of horror, Marnay realized the other's
purpose. Revenge, of course, he had known that! But he was going
to sacrifice both their lives for it!
Kennett shook him away angrily. "Keep cool! You'll see something in
a minute—get ready!"
Still they watched and waited. The detector dial registered the
swiftly diminishing distance—fifty miles, twenty miles, ten....
Then Kennett was on his feet, moving with swift surety to the wall,
opening a small iron locker. There Marnay glimpsed a complete set
of odd looking controls. Swiftly Kennett plugged in a bank of
connections, electric cables. He grasped a heavy lever. He stood
there, looking over at the detector dial. It showed three miles.
"All right," Kennett yelled, "hold tight!... Now!"
His hand came down on the lever.

For a moment Marnay thought the pirate ship had rammed them, or
that they had exploded, or both. He and Kennett were suddenly
hurtling outward ... at terrific speed ... their tiny compartment away
from the rest of the ship!
He looked in the panel and his heart leaped. They had indeed
exploded—very systematically! He saw fully a score of miniature
Veras speeding away from each other in a perfect, ever widening
circle! Each was a tiny spacer with its own motivating blast. He
recalled the puzzling construction of the Vera as he'd first seen it,
and now suddenly he understood; it had been segmented!
Where the huge original Vera had been was now only a huge steel
framework, from which the score of miniature Veras were speeding
away in their widening circle. The pirate ship was blasting violently
with its forward rockets, but it was too late. It crashed into the
framework, crumpling and tangling it and carrying it forward on the
momentum.
"Now watch!" Kennett was yelling unnecessarily in Marnay's ear.
"Space-bombs such as you never saw before—each of those Veras,
ten tons of Tynyte!"
"But—they're going away...."
"They're equipped with magniplates! And only barely enough rocket
power to hurl them away from each other. Just watch!"
The mile-wide circle of miniature Veras was slowing, as each of their
feeble rocket-blasts ceased. And then they came heading swiftly
back to their original source, the magniplates pulling them back.
As though endowed with some uncanny intelligence, they came; as
though aware of the revenge entrusted to them, and the significance
of their name.
The first one struck near the pirate ship's prow, letting loose its
death. The ship lifted like a proud black stallion rearing in the air.
The tough metal hull held—but only for a second. Another Vera
struck. The blast hurled the ship directly into a host of others which
exploded in a holocaust that ripped the black hull open like a sardine
can. The rest of the Veras came speeding into the mass to let loose
their death, complete and final.
As Marnay turned from that scene in the panel he felt sick and a
little weak; Kennett was pale, but the grim little lines were gone
from around his mouth and a bitter look was no longer in his eyes.
"Well, it's all over," he said with startling calmness. "I've done what I
swore three years ago I'd do. I think I named my ship well."
He stared long and wistfully into space. "Yes, they're gone now—all
the Veras are gone. Except one. This one's left to take us back, so
we'd better start sifting through all that mess out there for enough
fuel."
Slowly they drifted in, to begin the grim task.
*** END OF THE PROJECT GUTENBERG EBOOK REVENGE OF THE
VERA ***

Updated editions will replace the previous one—the old editions will
be renamed.

Creating the works from print editions not protected by U.S.


copyright law means that no one owns a United States copyright in
these works, so the Foundation (and you!) can copy and distribute it
in the United States without permission and without paying
copyright royalties. Special rules, set forth in the General Terms of
Use part of this license, apply to copying and distributing Project
Gutenberg™ electronic works to protect the PROJECT GUTENBERG™
concept and trademark. Project Gutenberg is a registered trademark,
and may not be used if you charge for an eBook, except by following
the terms of the trademark license, including paying royalties for use
of the Project Gutenberg trademark. If you do not charge anything
for copies of this eBook, complying with the trademark license is
very easy. You may use this eBook for nearly any purpose such as
creation of derivative works, reports, performances and research.
Project Gutenberg eBooks may be modified and printed and given
away—you may do practically ANYTHING in the United States with
eBooks not protected by U.S. copyright law. Redistribution is subject
to the trademark license, especially commercial redistribution.

START: FULL LICENSE


THE FULL PROJECT GUTENBERG LICENSE
PLEASE READ THIS BEFORE YOU DISTRIBUTE OR USE THIS WORK

To protect the Project Gutenberg™ mission of promoting the free


distribution of electronic works, by using or distributing this work (or
any other work associated in any way with the phrase “Project
Gutenberg”), you agree to comply with all the terms of the Full
Project Gutenberg™ License available with this file or online at
www.gutenberg.org/license.

Section 1. General Terms of Use and


Redistributing Project Gutenberg™
electronic works
1.A. By reading or using any part of this Project Gutenberg™
electronic work, you indicate that you have read, understand, agree
to and accept all the terms of this license and intellectual property
(trademark/copyright) agreement. If you do not agree to abide by all
the terms of this agreement, you must cease using and return or
destroy all copies of Project Gutenberg™ electronic works in your
possession. If you paid a fee for obtaining a copy of or access to a
Project Gutenberg™ electronic work and you do not agree to be
bound by the terms of this agreement, you may obtain a refund
from the person or entity to whom you paid the fee as set forth in
paragraph 1.E.8.

1.B. “Project Gutenberg” is a registered trademark. It may only be


used on or associated in any way with an electronic work by people
who agree to be bound by the terms of this agreement. There are a
few things that you can do with most Project Gutenberg™ electronic
works even without complying with the full terms of this agreement.
See paragraph 1.C below. There are a lot of things you can do with
Project Gutenberg™ electronic works if you follow the terms of this
agreement and help preserve free future access to Project
Gutenberg™ electronic works. See paragraph 1.E below.
1.C. The Project Gutenberg Literary Archive Foundation (“the
Foundation” or PGLAF), owns a compilation copyright in the
collection of Project Gutenberg™ electronic works. Nearly all the
individual works in the collection are in the public domain in the
United States. If an individual work is unprotected by copyright law
in the United States and you are located in the United States, we do
not claim a right to prevent you from copying, distributing,
performing, displaying or creating derivative works based on the
work as long as all references to Project Gutenberg are removed. Of
course, we hope that you will support the Project Gutenberg™
mission of promoting free access to electronic works by freely
sharing Project Gutenberg™ works in compliance with the terms of
this agreement for keeping the Project Gutenberg™ name associated
with the work. You can easily comply with the terms of this
agreement by keeping this work in the same format with its attached
full Project Gutenberg™ License when you share it without charge
with others.

1.D. The copyright laws of the place where you are located also
govern what you can do with this work. Copyright laws in most
countries are in a constant state of change. If you are outside the
United States, check the laws of your country in addition to the
terms of this agreement before downloading, copying, displaying,
performing, distributing or creating derivative works based on this
work or any other Project Gutenberg™ work. The Foundation makes
no representations concerning the copyright status of any work in
any country other than the United States.

1.E. Unless you have removed all references to Project Gutenberg:

1.E.1. The following sentence, with active links to, or other


immediate access to, the full Project Gutenberg™ License must
appear prominently whenever any copy of a Project Gutenberg™
work (any work on which the phrase “Project Gutenberg” appears,
or with which the phrase “Project Gutenberg” is associated) is
accessed, displayed, performed, viewed, copied or distributed:
This eBook is for the use of anyone anywhere in the United
States and most other parts of the world at no cost and with
almost no restrictions whatsoever. You may copy it, give it away
or re-use it under the terms of the Project Gutenberg License
included with this eBook or online at www.gutenberg.org. If you
are not located in the United States, you will have to check the
laws of the country where you are located before using this
eBook.

1.E.2. If an individual Project Gutenberg™ electronic work is derived


from texts not protected by U.S. copyright law (does not contain a
notice indicating that it is posted with permission of the copyright
holder), the work can be copied and distributed to anyone in the
United States without paying any fees or charges. If you are
redistributing or providing access to a work with the phrase “Project
Gutenberg” associated with or appearing on the work, you must
comply either with the requirements of paragraphs 1.E.1 through
1.E.7 or obtain permission for the use of the work and the Project
Gutenberg™ trademark as set forth in paragraphs 1.E.8 or 1.E.9.

1.E.3. If an individual Project Gutenberg™ electronic work is posted


with the permission of the copyright holder, your use and distribution
must comply with both paragraphs 1.E.1 through 1.E.7 and any
additional terms imposed by the copyright holder. Additional terms
will be linked to the Project Gutenberg™ License for all works posted
with the permission of the copyright holder found at the beginning
of this work.

1.E.4. Do not unlink or detach or remove the full Project


Gutenberg™ License terms from this work, or any files containing a
part of this work or any other work associated with Project
Gutenberg™.

1.E.5. Do not copy, display, perform, distribute or redistribute this


electronic work, or any part of this electronic work, without
prominently displaying the sentence set forth in paragraph 1.E.1
with active links or immediate access to the full terms of the Project
Gutenberg™ License.

1.E.6. You may convert to and distribute this work in any binary,
compressed, marked up, nonproprietary or proprietary form,
including any word processing or hypertext form. However, if you
provide access to or distribute copies of a Project Gutenberg™ work
in a format other than “Plain Vanilla ASCII” or other format used in
the official version posted on the official Project Gutenberg™ website
(www.gutenberg.org), you must, at no additional cost, fee or
expense to the user, provide a copy, a means of exporting a copy, or
a means of obtaining a copy upon request, of the work in its original
“Plain Vanilla ASCII” or other form. Any alternate format must
include the full Project Gutenberg™ License as specified in
paragraph 1.E.1.

1.E.7. Do not charge a fee for access to, viewing, displaying,


performing, copying or distributing any Project Gutenberg™ works
unless you comply with paragraph 1.E.8 or 1.E.9.

1.E.8. You may charge a reasonable fee for copies of or providing


access to or distributing Project Gutenberg™ electronic works
provided that:

• You pay a royalty fee of 20% of the gross profits you derive
from the use of Project Gutenberg™ works calculated using the
method you already use to calculate your applicable taxes. The
fee is owed to the owner of the Project Gutenberg™ trademark,
but he has agreed to donate royalties under this paragraph to
the Project Gutenberg Literary Archive Foundation. Royalty
payments must be paid within 60 days following each date on
which you prepare (or are legally required to prepare) your
periodic tax returns. Royalty payments should be clearly marked
as such and sent to the Project Gutenberg Literary Archive
Foundation at the address specified in Section 4, “Information
about donations to the Project Gutenberg Literary Archive
Foundation.”

• You provide a full refund of any money paid by a user who


notifies you in writing (or by e-mail) within 30 days of receipt
that s/he does not agree to the terms of the full Project
Gutenberg™ License. You must require such a user to return or
destroy all copies of the works possessed in a physical medium
and discontinue all use of and all access to other copies of
Project Gutenberg™ works.

• You provide, in accordance with paragraph 1.F.3, a full refund of


any money paid for a work or a replacement copy, if a defect in
the electronic work is discovered and reported to you within 90
days of receipt of the work.

• You comply with all other terms of this agreement for free
distribution of Project Gutenberg™ works.

1.E.9. If you wish to charge a fee or distribute a Project Gutenberg™


electronic work or group of works on different terms than are set
forth in this agreement, you must obtain permission in writing from
the Project Gutenberg Literary Archive Foundation, the manager of
the Project Gutenberg™ trademark. Contact the Foundation as set
forth in Section 3 below.

1.F.

1.F.1. Project Gutenberg volunteers and employees expend


considerable effort to identify, do copyright research on, transcribe
and proofread works not protected by U.S. copyright law in creating
the Project Gutenberg™ collection. Despite these efforts, Project
Gutenberg™ electronic works, and the medium on which they may
be stored, may contain “Defects,” such as, but not limited to,
incomplete, inaccurate or corrupt data, transcription errors, a
copyright or other intellectual property infringement, a defective or
Welcome to Our Bookstore - The Ultimate Destination for Book Lovers
Are you passionate about books and eager to explore new worlds of
knowledge? At our website, we offer a vast collection of books that
cater to every interest and age group. From classic literature to
specialized publications, self-help books, and children’s stories, we
have it all! Each book is a gateway to new adventures, helping you
expand your knowledge and nourish your soul
Experience Convenient and Enjoyable Book Shopping Our website is more
than just an online bookstore—it’s a bridge connecting readers to the
timeless values of culture and wisdom. With a sleek and user-friendly
interface and a smart search system, you can find your favorite books
quickly and easily. Enjoy special promotions, fast home delivery, and
a seamless shopping experience that saves you time and enhances your
love for reading.
Let us accompany you on the journey of exploring knowledge and
personal growth!

ebookgate.com

You might also like