100% found this document useful (2 votes)
5 views

Geometric Data Structures For Computer Graphics Elmar Langetepe instant download

The document is a book titled 'Geometric Data Structures for Computer Graphics' by Elmar Langetepe and Gabriel Zachmann, which focuses on various geometric data structures used in computer graphics. It aims to provide practitioners with knowledge of efficient algorithms and data structures, enabling them to solve geometric problems effectively. The book includes detailed definitions, properties, and applications of these structures in the context of computational geometry and computer graphics.

Uploaded by

volindovanu9
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (2 votes)
5 views

Geometric Data Structures For Computer Graphics Elmar Langetepe instant download

The document is a book titled 'Geometric Data Structures for Computer Graphics' by Elmar Langetepe and Gabriel Zachmann, which focuses on various geometric data structures used in computer graphics. It aims to provide practitioners with knowledge of efficient algorithms and data structures, enabling them to solve geometric problems effectively. The book includes detailed definitions, properties, and applications of these structures in the context of computational geometry and computer graphics.

Uploaded by

volindovanu9
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 77

Geometric Data Structures For Computer Graphics

Elmar Langetepe download

https://ebookbell.com/product/geometric-data-structures-for-
computer-graphics-elmar-langetepe-46160642

Explore and download more ebooks at ebookbell.com


Here are some recommended products that we believe you will be
interested in. You can click the link to download.

New Geometric Data Structures For Collision Detection And Haptics 1st
Edition Ren Weller Auth

https://ebookbell.com/product/new-geometric-data-structures-for-
collision-detection-and-haptics-1st-edition-ren-weller-auth-4293878

Geometric Data Analysis From Correspondence Analysis To Structured


Data Analysis 1st Edition Brigitte Le Roux

https://ebookbell.com/product/geometric-data-analysis-from-
correspondence-analysis-to-structured-data-analysis-1st-edition-
brigitte-le-roux-2243462

Geometric Data Analysis From Correspondence Analysis To Structured


Data Analysis Roux B

https://ebookbell.com/product/geometric-data-analysis-from-
correspondence-analysis-to-structured-data-analysis-roux-b-997068

Geometric Structure Of Highdimensional Data And Dimensionality


Reduction English Version Chinese Edition Wang Jian Zhong

https://ebookbell.com/product/geometric-structure-of-highdimensional-
data-and-dimensionality-reduction-english-version-chinese-edition-
wang-jian-zhong-51992598
Geometric Structure Of Highdimensional Data And Dimensionality
Reduction 1st Edition Prof Jianzhong Wang Auth

https://ebookbell.com/product/geometric-structure-of-highdimensional-
data-and-dimensionality-reduction-1st-edition-prof-jianzhong-wang-
auth-4240862

Information Geometry And Population Genetics The Mathematical


Structure Of The Wrightfisher Model Julian Hofrichter

https://ebookbell.com/product/information-geometry-and-population-
genetics-the-mathematical-structure-of-the-wrightfisher-model-julian-
hofrichter-5756400

Combinatorial Inference In Geometric Data Analysis Brigitte Le Roux


Solne Bienaise Jeanluc Durand

https://ebookbell.com/product/combinatorial-inference-in-geometric-
data-analysis-brigitte-le-roux-solne-bienaise-jeanluc-durand-10670576

Mathematical Principles Of Topological And Geometric Data Analysis 1st


Edition Joharinad

https://ebookbell.com/product/mathematical-principles-of-topological-
and-geometric-data-analysis-1st-edition-joharinad-56340382

Handbook Of Variational Methods For Nonlinear Geometric Data 1st


Edition Grohs

https://ebookbell.com/product/handbook-of-variational-methods-for-
nonlinear-geometric-data-1st-edition-grohs-10871212
Geometric Data
Structures for
Computer Graphics
Geometric Data
Structures for
Computer Graphics

Elmar Langetepe
Gabriel Zachmann

A K Peters, Ltd.
Wellesley, Massachusetts
Editorial, Sales, and Customer Service Office

A K Peters, Ltd.
888 Worcester Street, Suite 230
Wellesley, MA 02482
www.akpeters.com

Copyright © 2006 by A K Peters, Ltd.

All rights reserved. No part of the material protected by this copyright notice may
be reproduced or utilized in any form, electronic or mechanical, including photo-
copying, recording, or by any information storage and retrieval system, without
written permission from the copyright owner.

Library of Congress Cataloging-in-Publication Data

Langetepe, Elmar, 1967-


Geometric data structures for computer graphics / Elmar Langetepe, Gabriel Zachmann
p. cm.
Includes bibliographical references and index.
ISBN 13: 978-1-56881-235-9 (alk. paper)
ISBN 10: 1-56881-235-3 (alk. paper)
1. Computer graphics. 2. Data structures (Computer science) I. Zachmann, Gabriel,
1967- II. Title.

T385.L364 2005
006.6–dc22
2005051464

The frontispiece and quote are from Hartmut Böhme’s Albrecht Dürer, Melencolia
I—Im Labyrinth der Deutung, page 50, published by Fischer Taschenbuch Verlag,
Frankfurt.

Printed in India
09 08 07 06 05 10 9 8 7 6 5 4 3 2 1
To Anke Grahn and Birgit Meurer.
Contents

Preface xi

1 Quadtrees and Octrees 1


1.1 Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Complexity and Construction . . . . . . . . . . . . . . . . . . . . 2
1.3 Height Field Visualization . . . . . . . . . . . . . . . . . . . . . 4
1.4 Isosurface Generation . . . . . . . . . . . . . . . . . . . . . . . . 8
1.5 Ray Shooting . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.6 3D Octree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.7 5D Octree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

2 Orthogonal Windowing and Stabbing Queries 17


2.1 Interval Trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.2 Segment Trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.3 Multi-Level Segment Trees . . . . . . . . . . . . . . . . . . . . . 28
2.4 Kd-Trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.5 Range Trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.6 The (Axis-Parallel Box/Axis-Parallel Box) Windowing Problem . 41
2.7 Texture Synthesis . . . . . . . . . . . . . . . . . . . . . . . . . . 45
2.8 Shape Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

3 BSP Trees 51
3.1 Rendering without a Z-Buffer . . . . . . . . . . . . . . . . . . . . 53
3.2 Representing Objects with BSPs . . . . . . . . . . . . . . . . . . 54
3.3 Boolean Operations . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.4 Construction Heuristics . . . . . . . . . . . . . . . . . . . . . . . 59

vii
viii Contents

4 Bounding Volume Hierarchies 65


4.1 Construction of BVHs . . . . . . . . . . . . . . . . . . . . . . . 69
4.2 Updating for Morphing Objects . . . . . . . . . . . . . . . . . . . 76
4.3 Collision Detection . . . . . . . . . . . . . . . . . . . . . . . . . 78

5 Distance Fields 85
5.1 Computation and Representation of DFs . . . . . . . . . . . . . . 87
5.2 Applications of DFs . . . . . . . . . . . . . . . . . . . . . . . . . 91

6 Voronoi Diagrams 95
6.1 Definitions and Properties . . . . . . . . . . . . . . . . . . . . . . 96
6.2 Computation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
6.3 Generalization of the Voronoi Diagram . . . . . . . . . . . . . . . 110
6.4 Applications of the Voronoi Diagram . . . . . . . . . . . . . . . . 122
6.5 Voronoi Diagrams in Computer Graphics . . . . . . . . . . . . . 133

7 Geometric Proximity Graphs 147


7.1 A Small Collection of Proximity Graphs . . . . . . . . . . . . . . 148
7.2 Classification . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
7.3 Surfaces Defined by Point Clouds . . . . . . . . . . . . . . . . . 164
7.4 Intersection Detection between Point Clouds . . . . . . . . . . . . 170

8 Kinetic Data Structures 181


8.1 General Terminology . . . . . . . . . . . . . . . . . . . . . . . . 182
8.2 Static Segment Tree . . . . . . . . . . . . . . . . . . . . . . . . . 184
8.3 Kinetic Segment Tree . . . . . . . . . . . . . . . . . . . . . . . . 185
8.4 Kinetic BSP in the Plane . . . . . . . . . . . . . . . . . . . . . . 187

9 Degeneracy and Robustness 195


9.1 Examples of Instability in Geometric Algorithms . . . . . . . . . 197
9.2 Formal Definition of Robustness and Stability . . . . . . . . . . . 204
9.3 Geometric Computing and Arithmetic . . . . . . . . . . . . . . . 206
9.4 Robust Expressions and Predicates . . . . . . . . . . . . . . . . . 244
9.5 Degeneracy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
9.6 Imprecise Arithmetic Approach . . . . . . . . . . . . . . . . . . 273
9.7 Practical Recommendations and Existing Packages . . . . . . . . 280
Contents ix

10 Dynamization of Geometric Data Structures 287


10.1 Example of Dynamization . . . . . . . . . . . . . . . . . . . . . 289
10.2 Model of the Dynamization . . . . . . . . . . . . . . . . . . . . . 296
10.3 Amortized Insert and Delete . . . . . . . . . . . . . . . . . . . . 299
10.4 Dynamization for Worst-Case Performance . . . . . . . . . . . . 308
10.5 Application to Search Query Data Structures . . . . . . . . . . . . 312

Bibliography 315

Index 333
Melencolia I, Albrecht Dürer

Geometrie ist der Königsweg ästhetischer Erkenntnis:


sie offenbart “die kunst in der natur.”
— Hartmut Böhme
Preface

In recent years, methods from computational geometry have been widely adopted
by the computer graphics community, yielding elegant and efficient algorithms.
This book aims at endowing practitioners in the computer graphics field with a
working knowledge of a wide range of geometric data structures from computa-
tional geometry. It will enable readers to recognize geometric problems and select
the most suitable data structure when developing computer graphics algorithms.
The book will focus on algorithms and data structures that have proven to be
versatile, efficient, fundamental, and easy to implement. Thus practitioners and
researchers will benefit immediately from this book in their everyday work.
Our goal is to familiarize practitioners and researchers in computer graphics
with some very versatile and ubiquitous geometric data structures, enable them to
readily recognize geometric problems during their work, modify the algorithms
to their needs, and hopefully make them curious about further powerful treasures
to be discovered in the area of computational geometry.
In order to achieve these goals in an engaging yet sound manner, the general
concept throughout the book is to present each geometric data structure in the
following way: first, the data strucure will be defined and described in detail; then,
some of its fundamental properties will be highlighted; after that, one or more
computational geometry algorithms based on the data structure will be presented;
and finally, a number of recent, representative, and practically relevant algorithms
from computer graphics will be described in detail, showing the utilization of the
data structure in a creative and enlightening way.
We do not try to provide an exhaustive survey of the topics touched upon
here—this would be far beyond the scope of this book. Neither do we aspire to
present the latest and greatest algorithms for a given problem, for two reasons.
First, the focus is on geometric data structures, and we do not want to obstruct the
view with complicated algorithms. Second, we feel that, for practical purposes, a
good trade-off between simplicity and efficiency is important.

xi
xii Preface

distance field quadtree


(Ch. 5) (Ch. 1) kd-tree (Ch. 2) BSP tree (Ch. 3)

Vornoi diagram bounding volume


(Ch. 6) hierarchy (Ch. 4)

proximity graphs
(Ch. 7)

An overview of some of the data structures presented in this book.

The intended audience is practitioners working in 3D computer graphics (VR,


CAD/CAM, entertainment, animation, etc.) and students of both computer graph-
ics and computational geometry. Readers should be familiar with the basic prin-
ciples of computer graphics and the type of problems in the area.
We have arranged the chapters in roughly increasing degree of difficulty. The
hierarchical data structures are ordered by increasing flexibility, while the non-
hierarchical chapters build on each other. Finally, the last three chapters present
generic techniques for making geometric data structures kinetic, robust, and
dynamic.
Figure provides an overview of some of the data structures that will be dis-
cussed in the following chapters. Chapter 1 presents quadtrees and octrees, which
are, arguably, the most popular data structure in computer graphics. Lifting one
of the restrictions, thus making the data structure more flexible, we arrive at kd-
trees, which are presented in Chapter 2. We can make this even more flexible,
yielding BSP trees, discussed in Chapter 3. From kd-trees, we can also derive
bounding volume hierarchies, which are described in Chapter 4. Starting with a
quadtree (or even a grid), we can store more information about the object(s), thus
arriving at distance fields (Chapter 5). In a sense, distance fields are a discretized
version of Voronoi diagrams, which are presented in Chapter 6. In Chapter 7, we
discuss the general class of geometric proximity graphs, one kind of which, the
Delaunay graph, we have already seen in Chapter 6. The general concept of spe-
Preface xiii

cialized spatial data structures for moving objects is introduced in Chapter 8 and
discussed by an example. In Chapter 9, we consider the problems of degeneracy
and robustness in geometric computing. finally, in Chapter 10, we introduce a
simple generic scheme for dynamization.

Acknowledgments
We would like to thank Prof. Dr. Reinhard Klein and Prof. Dr. Rolf Klein for their
encouragement and advice during the work. Thanks goes also to Ansgar Grüne,
Tom Kamphans, Adalbert Prokop, Manuel Wedemeier, and Michael Bazanski for
reading some parts of the manuscript. In addition, many thanks go to Jan Klein for
the great collaboration. We are grateful to Alice and Klaus Peters for initiating this
book. And we would like to thank Kevin Jackson-Mead for managing the project
and for his great patience. Part of Zachmann’s work was funded by DFG’s grant
ZA292/1.
1
Quadtrees and
Octrees
In this chapter, we introduce the quadtree and octree structures. Their definition
and complexity, the recursive construction scheme, and a standard application are
presented. Quadtrees and octrees have applications in mesh generation, as shown
in Sections 1.3, 1.4, and 1.5.

1.1. Definition
A quadtree is a tree rooted so that every internal node has four children. Every
node in the tree corresponds to a square. If a node v has children, their corre-
sponding squares are the four quadrants, as shown in Figure 1.1.
Quadtrees can store many kinds of data. We will describe the variant that
stores a set of points and suggest a recursive definition. A simple recursive split-
ting of squares is continued until there is only one point in a square. Let P be a
set of points.

NE NW SW SE

Figure 1.1. An example of a quadtree.

1
2 1. Quadtrees and Octrees

The definition of a quadtree for a set of points in a square Q = [x1Q : x2Q ] ×


[y1Q : y2Q ] is as follows:

• If |P | ≤ 1, then the quadtree is a single leaf where Q and P are stored.

• Otherwise, let QNE , QNW , QSW , and QSE denote the four quadrants. Let
xm id := (x1Q + x2Q )/2 and y m id := (y1Q + y2Q )/2, and define

PNE := {p ∈ P : p x > xm id ∧ p y > y m id },


PNW := {p ∈ P : p x ≤ xm id ∧ p y > y m id },
PSW := {p ∈ P : p x ≤ xm id ∧ p y ≤ y m id },
PSE := {p ∈ P : p x > xm id ∧ p y ≤ y m id }.
The quadtree consists of a root node v, and Q is stored at v. In the fol-
lowing, let Q(v) denote the square stored at v. Furthermore, v has four
children: the X-child is the root of the quadtree of the set PX , where X is
an element of the set {NE, NW , SW , SE}.

1.2. Complexity and Construction


The recursive definition implies a recursive construction algorithm. Only the start-
ing square must be chosen adequately. If the split operation cannot be performed
well, the quadtree is unbalanced. Despite this effect, the depth of the tree is related
to the distance between the points.

Theorem 1.1. The depth of a quadtree for a set P of points in the plane is at most
log( s/c) + 32 , where c is the smallest distance between any two points in P , and s
is the side length of the initial square.

The cost of the recursive construction and the complexity of the quadtree de-
pends on the depth of the tree.

Theorem 1.2. A quadtree of depth d that stores a set of n points has O((d + 1)n)
nodes and can be constructed in O((d + 1)n) time.

Due to the degree 4 of the internal nodes, the total number of leaves is one
plus three times the number of internal nodes. Hence, it suffices to bound the
number of internal nodes.
Any internal node v has one or more points inside Q(v). The squares of the
node of a single depth cover the initial square. Therefore, at every depth, we have
at most n internal nodes, which gives the node bound.
1.2. Complexity and Construction 3

The most time-consuming task in one step of the recursive approach is the
distribution of the points. The amount of time spent is linear only in the number
of points and the O((d + 1)n) time bound holds.
The 3D equivalents of quadtrees are octrees. The quadtree construction can be
easily extended to octrees in 3D. The internal nodes of octrees have eight children,
and the children correspond to boxes instead of squares.

Neighbor finding. A common task when utilizing quadtrees is neighbor find-


ing (also called navigation ), i.e., given a node v and a direction north, east, south
or west, find a node v  so that Q(v) is adjacent to Q(v  ). Normally, v is a leaf
and v  should be a leaf as well, but this is not necessarily unique. Obviously, one
square may have many such neighbors, as shown in Figure 1.2.
For convenience, we extend the neighbor search. The given node can also be
internal; that is, v and v  should be adjacent corresponding to the given direction
and should also have the same depth. If there is no such node, we want to find the
deepest node whose square is adjacent.
The algorithm works as follows. Suppose we want to find the north neighbor
of v. If v happens to be the SE or SW child of its parent, then its north neighbor
is easy to find—it is the NE or NW child of its parent, respectively. If v itself is
the NE or NW child of its parent, then we proceed as follows. Recursively find
the north neighbor of µ of the parent of v. If µ is an internal node, then the north
neighbor of v is a child of µ; if µ is a leaf, the north neighbor we seek is µ itself.
This simple procedure runs in time O(d + 1).
Theorem 1.3. Let T be a quadtree of depth d. The neighbor of a given node v in
T a given direction, as defined above, can be found in O(d + 1) time.

Furthermore, there is also a simple procedure that constructs a balanced quad-


tree out of a given quadtree T . This can be done in time O(d + 1)m and O(m )
space if T has m nodes. For details, see also [de Berg et al. 00].
Similar results hold for octrees as well.

Figure 1.2. The square q has many west neighbors.


4 1. Quadtrees and Octrees

1.3. Height Field Visualization


A special area in 3D visualization is the rendering of large terrains, or, more
generally, of height fields. A height field is usually given as a uniformly gridded
square array h : [0, N − 1]2 → R, N ∈ I, of height values, where N is typically
in the order of 16,384 up to several millions. In practice, such a raw height field is
often stored in some image file format, such as GIF. A regular grid is, for instance,
one of the standard forms in which the US Geological Survey publishes its data,
known as the Digital Elevation Model (DEM) [Elassal and Caruso 84].
Obviously, a regular grid is not a very efficient data structure, both with re-
gards to memory and rendering. So, alternatively, height fields can be stored
as triangular irregular networks (TINs) (see Figure 1.3). They can adapt much
better to the detail and features (or lack thereof) in the height field, so they can
approximate any surface at any desired level of accuracy with fewer polygons
than any other representation [Lindstrom et al. 96]. However, due to their much
more complex structure, TINs do not lend themselves to interactive visualization
as well as more regular representations.
The problem in terrain visualization is that, if the user looks at it from a low
viewpoint directed at the horizon, there are a few parts of the terrain that are very
close, while the majority of the visible terrain is at a greater distance. Close parts
of the terrain should be rendered with high detail, while distant parts should be
rendered with very little detail to maintain a high frame rate.
To solve this problem, a data structure is needed that allows us to quickly
determine the desired level of detail in each part of the terrain. Quadtrees are
such a data structure, particularly because they seem to be a good compromise
between the simplicity of non-hierarchical grids and the good adaptivity of TINs.
The general idea is to construct a quadtree over the grid, and then traverse this
quadtree top-down in order to render it. At each node, we decide whether the
detail offered by rendering it is enough, or if we have to go down further.

Figure 1.3. A terrain (left), a TIN of its height field (middle), and a superposition (right)
[Wahl et al. 04]. (See Color Plate I.)
1.3. Height Field Visualization 5

T-vertices!

4 8

Figure 1.4. In order to use a quadtree for Figure 1.5. A quadtree defines a recur-
defining a height field mesh, it should be sive subdivision scheme yielding a 4-8
balanced. (Courtesy Prof. R. Klein and mesh. The dots denote the newly added
R. Wahl, Bonn University.) vertices. Some vertices have degree 4,
and some have 8 (hence the name).

One problem with quadtrees (and quadrangle-based data structures in general)


is that nodes are not quite independent of each other. Assume we have constructed
a quadtree over some terrain, as depicted in Figure 1.4. If we render that as-
is, there will be a gap (a crack ) between the top-left square and the fine-detail
squares inside the top-right square. The vertices causing this problem are called
T-vertices. Triangulating them would help in theory, but in practice, this leads
to long and thin triangles that have problems on their own. The solution is to
triangulate each node.
Thus, a quadtree offers a recursive subdivision scheme to define a triangulated
regular grid (see Figure 1.5): start with a square subdivided into two right-angle
triangles; with each recursion step, subdivide the longest side of all triangles (the
hypotenuse), yielding two new right-angle triangles each (hence, this scheme is
sometimes referred to as “longest edge bisection”) [Lindstrom and Pascucci 01].
This yields a mesh where all vertices have degree 4 or 8 (except the border ver-
tices), which is why such a mesh is often called a 4-8 mesh.
This subdivision scheme induces a directed acyclic graph (DAG) on the set
of vertices: vertex j is a child of i if it is created by a split of a right angle at
vertex i. This will be denoted by an edge (i, j ). Note that almost all vertices are
created twice (see Figure 1.5), so all nodes in the graph have four children and
two parents (except the border vertices).
During rendering, we will choose cells of the subdivision at different levels.
Let M 0 be the fully subdivided mesh (which corresponds to the original grid) and
M be the current, incompletely subdivided mesh. M corresponds to a subset of
6 1. Quadtrees and Octrees

the DAG of M 0 . The condition of being crack-free can be reformulated in terms


of the DAGs associated with M 0 and M:

M is crack-free ⇔
M does not have any T-vertices ⇔
∀(i, j ) ∈ M : (i , j ) ∈ M, where parent(j ) = {i, i }. (1.1)

In other words: you cannot subdivide one triangle alone; you also must subdivide
the one on the other side. During rendering, this means that if you render a vertex,
you also have to render all its ancestors (remember that a vertex has two parents).
Rendering such a mesh generates (conceptually) a single, long list of vertices
that are then fed into the graphics pipeline as a single triangle strip. The pseu-
docode for the algorithm looks like this (simplified):
submesh(i, j )
if error(i) < τ then
return
end if
if Bi outside viewing frustum then
return
end if
submesh( j , c l )
V += p i
submesh( j , c r )
where error(i) is some error measure for vertex i, and Bi is the sphere around
vertex i that completely encloses all descendant triangles.
Note that this algorithm can produce the same vertex multiple times consec-
utively; this is easy to check, of course. In order to produce one strip, the algo-
rithm has to copy older vertices to the current front of the list at places where it
makes a “turn”; again, this is easy to detect, and the interested reader is referred
to [Lindstrom and Pascucci 01].
One can speed up the culling a bit by noticing that if Bi is completely inside
the frustum, we do not need to test the child vertices anymore.
We still need to think about the way we store our terrain subdivision mesh.
Eventually, we will want to store it as a single linear array for two reasons:

• The tree is complete, so it really would not make sense to store it using
pointers.

• We want to map the file that holds the tree into memory as-is (for instance,
with the Unix mmap function), so pointers would not work at all.
1.3. Height Field Visualization 7

red
red
blue

level 0 1 2 3 4

blue

Figure 1.6. The 4-8 subdivision can Figure 1.7. The red quadtree can be
be generated by two interleaved stored in the unused “ghost” nodes of
quadtrees. The solid lines connect the blue quadtree. (See Color Plate III.)
siblings that share a common parent.
(See Color Plate II.)

We should keep in mind, however, that with current architectures, every memory
access that cannot be satisfied by the cache is extremely expensive (this is even
more so with disk accesses, of course).
The simplest way to organize the terrain vertices is a matrix layout. The disad-
vantage is that there is no cache locality at all across the major index. To improve
this, people often introduce some kind of blocking, where each block is stored in a
matrix and all blocks are arranged in matrix order, too. Unfortunately, Lindstrom
and Pascucci [Lindstrom and Pascucci 01] report that this is, at least for terrain
visualization, worse than the simple matrix layout by a factor 10!
Enter quadtrees. They offer the advantage that vertices on the same level are
stored fairly close in memory. The 4-8 subdivision scheme can be viewed as two
quadtrees that are interleaved (see Figure 1.6): we start with the first level of the
“red” quadtree that contains just the one vertex in the middle of the grid, which
is the one that is generated by the 4-8 subdivision with the first step. Next comes
the first level of the “blue” quadtree that contains four vertices, which are the
vertices generated by the second step of the 4-8 subdivision scheme. This process
repeats logically. Note that the blue quadtree is exactly like the red one, except it
is rotated by 45°. When you overlay the red and the blue quadtree, you get exactly
the 4-8 mesh.
Notice that the blue quadtree contains nodes that are outside the terrain grid;
we will call these nodes “ghost nodes.” The nice thing about them is that we can
store the red quadtree in place of these ghost nodes (see Figure 1.7). This reduces
the number of unused elements in the final linear array down to 33%.
During rendering, we need to calculate the indices of the child vertices, given
the three vertices of a triangle. It turns out that by cleverly choosing the indices
of the top-level vertices, this can be done as efficiently as with a matrix layout.
8 1. Quadtrees and Octrees

The interested reader can find more about this topic in [Lindstrom et al. 96,
Lindstrom and Pascucci 01, Balmelli et al. 01, Balmelli et al. 99], and many
others.

1.4. Isosurface Generation


One technique (among many others) of visualizing a 3D volume is to extract
isosurfaces and render those as a regular polygonal surface. It can be used to
extract the surfaces of bones or organs in medical scans, such as MRIs and CTs.
Assume for the moment that we are given a scalar field f : R3 → R. Then the
task of finding an isosurface would “just” be to find all solutions (i.e., all roots)
x ) = t.
of the equation f (
Since we live in a discrete world (at least in computer graphics), the scalar
field is usually given in the form of a curvilinear grid : the vertices of the cells
are called nodes, and we have one scalar and a 3D point stored at each node (see
Figure 1.8). Such a curvilinear grid is usually stored as a 3D array, which can be
conceived as a regular 3D grid (here, the cells are often called voxels ).
The task of finding an isosurface for a given value t in a curvilinear grid
amounts to finding all cells of which at least one node (i.e., corner) has a value
less than t and one node has a value greater than t. Such cells are then tri-
angulated according to a look-up table (see Figure 1.9). So, a simple algo-
rithm works as follows [Lorensen and Cline 87]: compute the sign for all nodes
(⊕  > t ,  < t), and then considering each cell in turn, use the eight signs as
an index into the look-up table, and triangulate it (if at all).

physical
space ⊕

⊕ ⊕ ⊕
node cell ⊕
?

⊕ ⊕
computational
space

Figure 1.8. A scalar field is often given in Figure 1.9. Cells straddling the isosurface
the form of a curvilinear grid. By doing are triangulated according to a look-up
all calculations in computational space, table. In some cases, several triangu-
we can usually save a lot of computa- lations are possible, which must be re-
tional effort. solved by heuristics.
1.4. Isosurface Generation 9

Notice that in this algorithm, we have used only the 3D array; we have not
used the information about exactly where in space the nodes are (except when
actually producing the triangles). We have, in fact, made a transition from com-
putational space (i.e., the curvilinear grid) to computational space (i.e., the 3D
array). So in the following, we can, without loss of generality, restrict ourselves
to consider only regular grids, that is, 3D arrays.
The question is: how can we improve the exhaustive algorithm? One problem
is that we must not miss any little part of the isosurface. So, we need a data
structure that allows us to discard large parts of the volume where the isosurface
is guaranteed to not be. This calls for octrees.
The idea is to construct a complete octree over the cells of the grid [Wilhelms
and Gelder 90] (for the sake of simplicity, we will assume that the grid’s size is
a power of two). The leaves point to the lower-left node of their associated cell
(see Figure 1.10). Each leaf ν stores the minimum νmin and the maximum νmax
of the eight nodes of the cell. Similarly, each inner node of the octree stores the
minimum/maximum of its eight children.
Observe that an isosurface intersects the volume associated with a node ν
(inner or leaf node) if and only if νmin ≤ t ≤ νmax . This already suggests how the
algorithm works: start with the root and visit recursively all the children where
the condition holds. At the leaves, construct the triangles as usual.
This can be accelerated further by noticing that if the isosurface crosses an
edge of a cell, that edge will be visited exactly four times during the complete
procedure. Therefore, when we visit an edge for the first time, we compute the
vertex of the isosurface on that edge, and store the edge together with the vertex
in a hash table. So, whenever we need a vertex on an edge, we first try to look
up that edge in the hash table. Our observation also allows us to keep the size
of the hash table fairly low. When an edge has been visited for the fourth time,
we know that it cannot be visited anymore; therefore, we remove it from the hash
table.

y x=0 m0 m0

y=0 1

Figure 1.10. Octrees offer a simple way Figure 1.11. Volume data layout should
to compute isosurfaces efficiently.. match the order of traversal of the oc-
tree.
10 1. Quadtrees and Octrees

1.5. Ray Shooting


Ray shooting is an elementary task that frequently arises in ray tracing, volume
visualization, and games for collision detection or terrain following. The task is,
basically, to find the earliest hit of a given ray when following that ray through a
scene composed of polygons or other objects.
A simple way to avoid checking the ray against all objects is to partition the
universe into a regular grid (see Figure 1.12). With each cell, we store a list of
objects that occupy that cell (at least partially). Then we just walk along the ray
from cell to cell, and check the ray against all those objects that are stored with
that cell.
In this scheme (and others), we need a technique called mailboxes that pre-
vents us from checking the ray twice against the same object [Glassner 89]. Every
ray gets a unique ID (we just increment a global variable holding that ID when-
ever we start with a new ray); during traversal, we store the ray’s ID with the
object whenever we have performed an intersection test with it. But before doing
an intersection test with an object, we look into its mailbox to see whether the
current ray’s ID is already there; if so, we know that we have already performed
the intersection test in an earlier cell.
In the following, we will present two methods that use octrees to further re-
duce the number of objects considered.

1.6. 3D Octree
A canonical way to improve any grid-based method is to construct an octree (see
Figure 1.13). Here, the octree leaves store lists of objects (or, rather, pointers to

Figure 1.12. Ray shooting can be imple- Figure 1.13. The same scenario utilizing
mented efficiently with a grid. an octree.
1.6. 3D Octree 11

objects). Since we are dealing now with polygons and other graphical objects, the
leaf rule for the octree construction process must be changed slightly: maximum
depth reached, or only one polygon/object occupies the cell. We can try to better
approximate the geometry of the scene by changing the rule to stop only when
there are no objects in the cell (or the maximum depth is reached).
How do we traverse an octree along a given ray? As in the case of a grid,
we have to make “horizontal” steps, which actually advance along the ray. With
octrees, though, we also need to make “vertical” steps, which traverse the octree
up or down.
All algorithms for ray shooting with octrees can be classified into two classes:

• Bottom-up: this method starts at that leaf in the octree that contains the
origin of the ray; from there, it tries to find that neighbor cell that is stabbed
next by the ray, etc.

• Top-down: this method starts at the root of the octree and tries to recurse
down into exactly those nodes and leaves that are stabbed by the ray.

Here, we will describe a top-down method [Revelles et al. 00]. The idea is
to work only with the ray parameter in order to decide which children of a node
must be visited.
Let the ray be given by

x = p + t d

and a voxel v by

[xl , xh ] × [y l , y h ] × [zl , zh ].

In the following, we will describe the algorithm assuming that all d i > 0; later,
we will show that the algorithm works also for all other cases.
First, observe that if we already have the line parameters of the intersection of
the ray with the borders of a cell, it is trivial to compute the line intervals halfway
in between (see Figure 1.14):

1 l
t mα = (t + t hα ) , α ∈ {x, y, z}. (1.2)
2 α
So, for eight children of a cell, we need to compute only three new line param-
eters. Clearly, the line intersects a cell if and only if max{t li } < min{t hj }. The
algorithm can be outlined as follows:
12 1. Quadtrees and Octrees

t hy

t hx
tm tm l
y > tx
y
tm
x

t lx
t ly tm l
y < tx

Figure 1.14. Line parameters are trivial to Figure 1.15. The sub-cell that must be tra-
compute for children of a node. versed first can be found by simple com-
parisons. Here, only the case t lx > t ly is
depicted.

traverse( v, t l , t h )
compute t m
determine order in which sub-cells are hit by the ray
for all sub-cells v i that are hit do
traverse( v i , t l |t m , t m |t h )
end for
where t l |t m means that we construct the lower boundary for the respective cell by
passing the appropriate components from t l and t m .
To determine the order in which sub-cells should be traversed, we first need to
determine which sub-cell is being hit first by the ray. In 2D, this is accomplished
by two comparisons (see Figure 1.15). Then the comparison of t mx with t my tells
us which cell is next.
In 3D, this takes a little bit more work, but is essentially the same. First, we
determine on which side the ray has been entering the current cell by Table 1.1.
Next, we determine the first sub-cell to be visited by Table 1.2 (see Figure 1.16
for the numbering scheme). The first column is the entering side determined in
the first step. The third column yields the index of the first sub-cell to be visited:
start with an index of zero; if one or both of the conditions of the second column

y 3 7
max{t li } Side
0 6
t lx YZ 5
t ly XZ 2 4
t lz XY z
x
Table 1.1. Determines the entering Figure 1.16. Sub-cells are numbered
side. according to this scheme.
1.7. 5D Octree 13

current exit side


sub-cell YZ XZ XY
Side condition index bits
0 4 2 1
t mz < t lx 0 1 5 3 ex
XY
t my < t lx 1 2 6 ex 3
3 7 ex ex
t mx < t ly 0
XZ 4 ex 6 5
t mz < t ly 2
5 ex 7 ex
t my < t lx 1 6 ex ex 7
YZ
t mz < t lx 2 7 ex ex ex

Table 1.2. Determines the first sub-cell. Table 1.3. Determines the traversal order
of the sub-cells.

hold, then the corresponding bit in the index as indicated by the third column
should be set.
Finally, we can traverse all sub-cells according to Table 1.3, where “ex” means
the exit side of the ray for the current sub-cell.
If the ray direction contains a negative component(s), then we just have to
mirror all tables along the respective axis (axes) conceptually. This can be imple-
mented efficiently by an XOR operation.

1.7. 5D Octree
In the previous, simple algorithm, we still walk along a ray every time we shoot
it into the scene. However, rays are essentially static objects, just like the geom-
etry of the scene! This is the basic observation behind the following algorithm
[Arvo and Kirk 87, Arvo and Kirk 89]. Again, it makes use of octrees to adap-
tively decompose the problem.
The underlying technique is a discretization of rays, which are 5D objects.
Consider a cube enclosing the unit sphere of all directions. We can identify any
ray’s direction with a point on that cube; hence, it is called a direction cube (see
Figure 1.17). The nice thing about it is that we can now perform any hierarchical
partitioning scheme that works in the plane, such as an octree: we just apply the
scheme individually on each side.
Using the direction cube, we can establish a one-to-one mapping between
direction vectors and points on all six sides of the cube, i.e.:

S2 ↔ [−1, +1]2 × {+x, −x, +y, −y, +z, −z}.


14 1. Quadtrees and Octrees

d

u u v + =
v

Figure 1.17. With the direction cube, we Figure 1.18. A uv interval on the direction
can discretize directions and organize cube plus a xyz interval in 3-space yield
them with any hierarchical partitioning a beam.
scheme.

We will denote the coordinates on the cube’s side by u and v. Here, {+x, −x, . . .}
are just some kind of IDs that are used to identify the side of the cube, on which
a point (u, v) lives (we could have used {1, . . . , 6} just as well).
Within a given universe B = [0, 1]3 (we assume it is a box), we can represent
all possibly occurring rays by points in
R = B × [−1, +1]2
(1.3)
× {+x, −x, +y, −y, +z, −z},
which can be implemented conveniently by six copies of 5D boxes.
Returning to our goal, we now build six 5D octrees as follows. Associate
(conceptually) all objects with the root. Partition a node in the octree if there are
too many objects associated with it and the node’s cell is too large. If a node is
partitioned, we must also partition its set of objects and assign each subset to one
of the children.
Observe that each node in the 5D octree defines a beam in 3-space: the xyz-
interval of the first three coordinates of the cell define a box in 3-space, and the
remaining two uv-intervals define a cone in 3-space. Together (more precisely,
taking their Minkowski sum), they define a beam in 3-space that starts at the cell’s
box and extends in the general direction of the cone (see Figure 1.18).
Since we have now defined what a 5D cell of the octree represents, it is almost
trivial to define how objects are assigned to sub-cells: we just compare the bound-
ing volume of each object against the sub-cells’ 3D beam. Note that an object can
be assigned to several sub-cells (just like in regular 3D octrees). The test whether
or not an object intersects a beam could be simplified further by enclosing a beam
with a cone and then checking the objects’ bounding sphere against that cone.
This just increases the number of false positives a bit.
Having computed the six 5D octrees for a given scene, ray tracing through
that octree is almost trivial: map the ray onto a 5D point via the direction cube;
1.7. 5D Octree 15

1 2 3 4
Figure 1.19. By sorting objects within Figure 1.20. By truncating the beam
each 5D leaf, we can often stop check- (or rather, the list of objects), we can
ing ray intersection quite early. save a lot of memory usage of a 5D oc-
tree, while reducing performance only
insignificantly.

start with the root of the octree that is associated with the side of the direction
cube onto which the ray was mapped; find the leaf in that octree that contains the
5D point (i.e., the ray); and check the ray against all objects associated with that
leaf.
By locating a leaf in one of the six 5D octrees, we have discarded all ob-
jects that do not lie in the general direction of the ray. But we can optimize the
algorithm even further.
First of all, we sort all objects associated with a leaf along the dominant axis
of the beam by their minimum (see Figure 1.19). If the minimum coordinate of
an object along the dominant axis is greater than the current intersection point,
then we can stop—all other possible intersection points are farther away.
Second, we can utilize ray coherence as follows. We maintain a cache for
each level in the ray tree that stores the leaves of the 5D octrees that were visited
last time. When following a new ray, we first look into the octree leaf in the cache
to see whether it is contained therein before we start searching for it from the root.
Another trick (which works with other ray acceleration schemes as well) is to
exploit the fact that we do not need to know the first occluder between a point on
a surface and a light source. Any occluder suffices to assert that the point is in
shadow. So, we also keep a cache with each light source, which stores the object
(or a small set) that was an occluder last time.
Finally, we would like to mention a memory optimization technique for 5D
octrees, because they can occupy a lot of memory. It is based on the observation
that within a beam defined by a leaf of the octree, the objects at the back (almost)
never intersect with a ray emanating from that cell (see Figure 1.20). So, we store
16 1. Quadtrees and Octrees

objects with a cell only if they are within a certain distance. Should a ray not hit
any object, we start a new intersection query with another ray that has the same
direction and a starting point just behind that maximum distance. Obviously, we
have to make a trade-off between space and speed here, but when chosen properly,
the cutoff distance should not reduce performance too much, while still saving a
significant amount of memory.
2
Orthogonal Windowing
and Stabbing Queries

In this chapter, we introduce some tree-based geometric data structures for an-
swering windowing and stabbing queries. Such queries are useful in many com-
puter graphics algorithms.
A stabbing query reports all objects that are stabbed by a single object. For a
set of segments S, a typical stabbing query reports all segments that are stabbed
by a single query line l. On the other hand, a windowing query reports all objects
that lie inside a window. For a set of points S, a typical windowing query reports
all points of S inside query box B.
We start with some simple queries and data structures, and then progress to
more sophisticated queries. Furthermore, we present time and space bounds for
construction and queries.
All data structures are considered to be static; that is, we assume that the set
of objects will not change over time and we do not have to consider Insert and
Delete operations. Such operations might be very costly or complicated. For
example, the hierarchical structure of the balanced kd-tree in Section 2.4 requires
a lot of reconstruction effort if new elements are inserted or old elements are
deleted.
For a dynamization, we use the simple and efficient generic dynamization
techniques presented in Chapter 10. We consider simple WeakDelete operations
for all data structures in order to apply the dynamization approach adequately. A
WeakDelete operation marks an object as deleted; the object is not removed from
the memory. After a set of efficient WeakDelete operations, it is necessary to
reconstruct the complete structure; see Chapter 10 for details. The corresponding
running times can be found in Section 10.5.

17
18 2. Orthogonal Windowing and Stabbing Queries

The presented pseudocode algorithms make use of straightforward and


self-explanatory operations. For example, in Algorithm 2.2, the operation
L. ListInsert(s) indicates that the element s is inserted into the list L.
For each data structure, first, we consider the query operation. Then we give
a sketch of the construction and query operations. Additionally, the correspond-
ing algorithms are represented in pseudocode. We also give short proofs for the
complexity of construction and query operations. A query is denoted by the fol-
lowing:

• the dimension of the space,

• a tuple (object/query object) of the corresponding objects,

• the type of the query operation.

For example, in Section 2.1, the corresponding query is denoted as a one-


dimensional (interval/point) stabbing query.

2.1. Interval Trees


The interval tree is used for answering the following one-dimensional (inter-
val/point) stabbing query efficiently:

• Input: a set S of closed intervals on the line;

• Query: a single value xq ∈ R;

• Output: all intervals I ∈ S with xq ∈ I.

For example, in Figure 2.1, there are seven intervals s1 , s2 , . . . , s7 and a query
value xq . For convenience, the intervals are illustrated by horizontal line seg-
ments si in 2D and the query value is illustrated as a horizontal line xq . The
(interval/point) stabbing query should report the segments s2 , s5 , and s6 . We can
assume that a segment si is represented by the x-coordinate of its left and right
endpoints li and ri , respectively.
We construct a data structure that answers the above query efficiently. The
information of the intervals are stored in a binary tree. Let S denote a set of n
intervals [li , ri ] for i = 1, . . . , n. The binary interval tree is constructed recur-
sively, as shown in the construction sketch. A node of the tree is dedicated to a
median value of the endpoint of all segments. For all segments that contain this
value, there are two lists for checking wether a query value is also covered by
2.1. Interval Trees 19

xq xmed

s1
s2
s3
s4
s5

s6
s7

xmed
ML = {l5 , l6 , l3 }
MR = {r5 , r3 , r6 }

Interval tree Interval tree

Lmed = {s2 , s7 } Rmed = {s1 , s4 }

Figure 2.1. An example of the node information in an interval tree. For convenience,
the intervals are represented by 2D line segments. The left and right endpoints li and
ri of the segments si hit by the median xmed are represented in sorted lists ML and
MR , respectively.

those intervals; see Figure 2.1 for an example of the node information. The query
operation will be explained in detail later in this chapter.
The following is the interval tree construction algorithm sketch (see also Al-
gorithm 2.1):

• Input: given a set of intervals S represented by [li , ri ] for i = 1, . . . , n;

• If S is empty, then the interval tree is an empty leaf. Otherwise, allocate a


node v with two children;

• For the node v, compute the median xmed of {l1 , . . . , ln , r1 , . . . , rn }. This


means that half of the interval endpoints lie to the left and half of the interval
endpoints lie to the right of xmed . Note that the median normally will not
be equal to a value li or ri ;

• Let Lmed denote the set of intervals to the left of xmed , let Smed denote the
set of intervals that contain xmed , and let Rmed denote the set of intervals to
the right of xmed ;
20 2. Orthogonal Windowing and Stabbing Queries

IntervalTree(S) (S is a set of intervals {[l1 , r1 ], . . . , [ln , rn ]} together with sorted


lists for {li }, {ri }, and {li } ∪ {ri })
if S = ∅ then
return Nil
else
v := new node
v. xmed := Median(S)
v. Smed := HitSegments(v. xmed , S)
Rmed := RightSegments(v. xmed , S)
Lmed := LeftSegments(v. xmed , S)
v. ML := SortLeftEndPoints(v. Smed )
v. MR := SortRightEndPoints(v. Smed )
v. LeftChild := IntervalTree(Lmed )
v. RightChild := IntervalTree(Rmed )
return v
end if
Algorithm 2.1. Computing an interval tree for a set of intervals, recursively.

• At the root v, store xmed and build a sorted list ML for all left endpoints of
Smed and a sorted list MR for all right endpoints of Smed ;
• Build the interval trees for Lmed and Rmed recursively for the children of v.
Algorithm 2.1 computes the interval tree efficiently, as shown in Lemma 2.1.
Lemma 2.1. The interval tree for a set S of n intervals has size O(n) and can
be constructed in O(n log n) time.
Proof: In a preliminary step, we sort the interval endpoints by li order, by ri order,
and by total (li and ri ) order. Obviously, the depth of the constructed tree is in
O(log n). Let n v denote the number of intervals at node v. Computing the median
takes O(n v ) time using the total order of the interval endpoints. Let m v = | Smed |
for the set of intervals Smed at v. Computing ML and MR from the li and ri order
takes at most O(m v ) time. Since all occurring sets Smed are distinct, this gives
O(n) altogether. Maintaining the li , ri , and total order for the recursive steps
takes O(n v ) time. Altogether, with the recursive steps, we have the recursive cost
function T (n v ) ≤ 2T ( n2v ) + O(n v ). Together with the depth O(log n) of the tree,
we conclude the given result. 
Next, we consider the recursive stabbing query operation for a value xq ∈ R
and the root v of the interval tree. The median xmed of node v is used for binary
branching. The node information is used for answering the stabbing query for the
intervals that contain the median.
2.1. Interval Trees 21

IntervalStabbing(v, xq ) (v is the root of an interval tree, xq ∈ R)


D := new list
if v. xmed < xq then
L := v. ML
f := L. First
while f = Nil and f < xq do
D. ListInsert(Seg(f ))
f := L. Next
end while
D 1 := IntervalStabbing(v. LeftChild, xq )
else if v. xmed ≥ xq then
R := MR (v)
l := R. Last
while l = Nil and l > xq do
D. ListInsert(Seg(l))
l := R. Prev
end while
D 1 := IntervalStabbing(v. RightChild, xq )
end if
D := D. ListAdd(D 1 )
return D
Algorithm 2.2. Answering a stabbing query for an interval tree v and a value xq ,
recursively.

The following is the stabbing query operation sketch:


• Input: given the root v of an interval tree and the query point xq ∈ R;

• If xq < xmed then

– Scan the sorted list ML of the left endpoints in increasing order and
report all stabbed segments. Stop if xq is smaller than the current left
endpoint;

– Recursively, continue with the interval tree of Lmed ;

• If xq > xmed then

– Scan the sorted list MR of the right endpoints in decreasing order and
report all stabbed segments. Stop if xq is bigger than the current right
endpoint;

– Recursively, continue with the interval tree of Rmed .


22 2. Orthogonal Windowing and Stabbing Queries

For example, assume that there is a stabbing query at a node v as pointed out
in Figure 2.1. Starting from the left with ML , the segments s5 and s6 are reported
since xq lies to the right of l5 and l6 . Then the stabbing query recursively goes on
with Lmed and will find s2 .

Lemma 2.2. An (interval/point) stabbing query with an interval tree for a set S
of n intervals and a value xq ∈ R report all k intervals I of S with xq ∈ I in
O(k + log n) time.

Proof: Scanning through MR or ML at node v takes time proportional to the num-


ber of reported stabbed intervals because we stop as soon as we find an interval
that does not contain the point xq . The sets Smed of all v are distinct, which gives
O(k) for all scannings. The size of the considered subtree is divided by at least
two at every level, which gives O(log n) for the path length. Altogether, the given
bound holds. 

The interval tree was considered in [Edelsbrunner 80] and [McCreight 80].
Note that the interval tree has no direct generalization to higher dimensions, but
can support combined queries; see Section 2.6.
A WeakDelete operation for a segment s (see Chapter 10) can be done in
O(log n) time. We find the corresponding node with s ∈ Smed and mark the
endpoints as deleted in the sorted lists ML and MR .

Lemma 2.3. A WeakDelete operation for a segment s in an interval tree of n


intervals is performed in O(log n) time.

2.2. Segment Trees


A segment tree is constructed for answering the following 2D (line segment/line)
stabbing query efficiently:

• Input: a set S of segments in the plane;

• Query: a vertical1 line l;

• Output: all segments s ∈ S crossed by l.

Let S = {s1 , s2 , . . . , sn } be the set of segments and let E be the sorted set
of x-coordinates of the endpoints. We assume general position; that is, E =
1 Arbitrary query lines l can be handled by application of a transformation matrix; see Section 9.5.3.
2.2. Segment Trees 23

{e 1 , e 2 , . . . , e 2n } with e i < e j for i < j (Section 9.5). For the construction of the
tree, we can split E into 2n + 1 atomic intervals

[−∞, e 1 ], [e 1 , e 2 ], . . . , [e 2n−1 , e 2n ], [e 2n , ∞],

which represent the leaves of the segment tree.


The segment tree is a balanced binary tree. Each internal node v represents an
elementary interval I, which is split into intervals Il and Ir for the two children
of v. The intervals are split with respect to the endpoints of the segments; see
Figure 2.2. In the first place, the segment tree is a one-dimensional search tree for
the x-coordinates of the endpoints of the segments. The one-dimensional search
tree contains search keys in every node; the data points are represented in the
leaves of the tree. Every node v additionally represents an interval Iv so that all
points in the (sub)tree represented by root node v are in Iv . In Section 2.5, we
generalize the one-dimensional search tree to arbitrary dimension d. An example
of a one-dimensional search tree is given in Figure 2.7.
The following is the one-dimensional search tree sketch (see also Algori-
thm 2.3):

• Input: given a sorted list S of n elements x1 , x2 , . . . , xn ;

• Output: the root node of a one-dimensional search tree for S;

• If |S| = 0, then set v := Nil and return v;

• Otherwise, if |S| ≥ 1 then

v1 v2
v3

v4

Figure 2.2. A segment tree for a set of segments. The segment s is minimally covered
by the nodes v 1 , v 2 , v 3 , and v 4 .
24 2. Orthogonal Windowing and Stabbing Queries

SearchTree(S) (S is a sorted list {x1 , . . . , xn })


if S = ∅ then
v := Nil
else
v := new node
n := |S|
m :=  n2 
(L, R) := S. Split(m )
v. Key := S. Get(m )
v.I := [S. First, S. Last])
/* Alternatively: v.I := [x1 , . . . , xn ] */
v. LeftChild := SearchTree(L)
v. RightChild := SearchTree(R)
end if
return v
Algorithm 2.3. Construction of a simple balanced one-dimensional search tree.

– Allocate a node v with two children for the root of the tree;

– Choose the element xm of S with m :=  n2  and insert the branch


key xm at v. Insert the interval I = [x1 , xn ] at v. If necessary, I also
contains the data points of the interval;

– Compute a one-dimensional search tree v l for x1 , . . . , xm and com-


pute a one-dimensional search tree v r for xm +1 , . . . , xn ;

– Insert v r as the right child of v and v l as the left child of v;

– Finally, return the node v.

Obviously, the corresponding one-dimensional tree has height O(log n). We


can find an element xi in logarithmic time by starting from the root and branching
towards xi using the keys in the nodes. The sketch and the pseudocode of this
simple query procedure are omitted.
So far, we have constructed a tree that represents the information of the end-
points of the segments plus the dummy nodes ∞ and −∞. Obviously, this is not
sufficient for answering a stabbing query. Additionally, we have to incorporate the
information of the segments into the tree. Therefore, let us consider a single seg-
ment s represented by the x-coordinates (e j , e k ) of its endpoints. The segment
s can be represented by a consecutive set of elementary intervals. We choose
a minimal set of elementary intervals (or the corresponding nodes) in the one-
dimensional search tree so that s is fully covered. Due to Algorithm 2.3, every
2.2. Segment Trees 25

node represents an elementary interval v.I and, if necessary, its data points. Mini-
mal means that the nodes are chosen as close as possible to the root of the tree; see
Figure 2.2. We store s in each of the associated nodes. More precisely, let v.pred
be the predecessor of v in the interval tree. A segment s = (e j , e k ) represented
by the x-coordinates of the endpoints is stored in a list v.L at v iff v.I ⊆ [e j , e k ]
and (v.pred).I ⊆ [e j , e k ]. Thus, each node represents an elementary interval v.I
and a list of segments v.L; for an example, see Figure 2.3.
The one-dimensional search tree with intervals v.I is built up in O(n log n)
time, as was shown previously. However, we need to show how the segment
partitions are inserted into the tree. This is done by the following recursive insert
procedure, which has to run for every segment s = (e j , e k ).
The following is the sketch of the segment insertion in the one-dimensional
search tree:

• Input: given a line segment s = (e j , e k ) and the root v of the segment tree;

• If the interval v.I is already a subset of the interval [e j , e k ], then insert s


into the segment set v.L and stop;

• Otherwise, let v. LeftChild and v. RightChild be the children of v. This


means that the interval v.I equals (v. LeftChild).I ∪ (v. RightChild).I;

– If the interval [e j , e k ] and the set (v. LeftChild).I overlap, then run
the insert procedure recursively with s and the segment tree
v. LeftChild;

– If the interval [e j , e k ] and the set (v. RightChild).I overlap, then run
the insert procedure recursively with s and the segment tree
v. RightChild.

By inserting every segment into the one-dimensional search tree of the line
segment’s endpoints, we will finally obtain the segment tree. Altogether, Algori-
thm 2.4 constructs the segment tree by using Algorithm 2.5 as a subroutine. Note
that Sx . Extend extends Sx by the dummy elements ∞ and −∞.

Lemma 2.4. The segment tree for n segments can be built in O(n log n) and uses
O(n log n) space.

Proof: The binary tree has depth O(logn) and O(n) nodes. At each level of the
segment tree T , a segment s = (e j , e k ) is stored at most twice. To prove this fact,
let us assume that three nodes u l , u m , and u r of the same level in T contain the
26 2. Orthogonal Windowing and Stabbing Queries

SegmentTree(S) (S is a set of line segments given by endpoints)


Sx := S. SortX
Sx := Sx . Extend
v := SearchTree(Sx )
while s = ∅ do
s := S. First
SegmentInsertion(v, s)
S. DeleteFirst
end while
Algorithm 2.4. Building a segment tree.

segment s. The intervals of their parents do not overlap. This is because they can
overlap only if a pair of u l , u m , and u r has a common predecessor. In this case,
the segment has to be inserted for the predecessor. If the intervals of the parents
of u l , u m , and u r do not overlap, the intermediate node of u l , u m , and u r must
have a parent whose interval fully contains [e j , e k ]; therefore, s is not inserted at
u m which gives a contradiction. Summing up over all segments and levels, the
segment tree has space O(n log n).
With a similar argument, one can prove that, during the insertion operation,
only four nodes on every level could be visited. Thus a single segment is inserted
in O(log n) time, which gives the overall time bound. 

For a stabbing query with a vertical line l, we will proceed as follows. We start
with the x-coordinate lx of the vertical line l and the root node v of a segment tree.

SegmentInsertion(v, s) (v is one-dimensional search tree of the x-coordinates of


the endpoints of segment set S, s ∈ S.)
e j := s. LeftXCoord
e k := s. RightXCoord
if v.I ⊂ [e j , e k ] then
(v.L). ListAdd(s)
else
if (v. LeftChild).I ∩ [e j , e k ] = ∅ then
SegmentInsertion(v. LeftChild, s)
end if
if (v. RightChild).I ∩ [e j , e k ] = ∅ then
SegmentInsertion(v. RightChild, s)
end if
end if
Algorithm 2.5. Insertion of a segment s in the segment tree.
2.2. Segment Trees 27

StabbingQuery(v, q ) (v is the root node of a segment tree and l a vertical line


segment)
L := Nil
if v = Nil and lx ∈ v.I then
L := v.L
Ll := StabbingQuery(v. LeftChild, l)
Lr := StabbingQuery(v. RightChild, l)
L. ListAdd(Lr )
L. ListAdd(Ll )
end if
return L
Algorithm 2.6. The stabbing query for a segment tree.

The following is the sketch of the stabbing query with a segment tree:
• Input: given the x-coordinate lx of the vertical line l and the root node v of
a segment tree;

• If lx is inside the interval v.I, then report all segments in v.L;

• If v is not a leaf, then for the children v. LeftChild and v. RightChild of v,


we know that the interval v.I equals (v. LeftChild).I ∪ (v. RightChild).I
and we proceed as follows:

– If lx lies inside (v. LeftChild).I, then start a query with segment tree
v. LeftChild and lx ;

– Else we have lx ∈ (v.RightChild).I, and we start a query with seg-


ment tree v.RightChild and lx .

Figure 2.3 shows an example for a segment query. The shaded intervals indi-
cate the query path from the root to the leaf.
Lemma 2.5. A vertical line stabbing query for n segments in the plane can be
answered by a segment tree in time O(k + log n), where k stands for the number
of reported segments.

Proof: Obviously, the tree is traversed with O(log n) steps. The running time
O(k + log n) stems from the cardinality k of the corresponding sets v.L. 

The segment tree was first considered in [Bentley 77] and extended to higher
dimensions by several authors; see, for example, [Edelsbrunner and Maurer 81]
and [Vaishnavi and Wood 82].
28 2. Orthogonal Windowing and Stabbing Queries
l

s2 s3 s2

s1 s1 s3 s5
s2 s4
s1 s4 s2

s3
s5
s2
s1
s4

Figure 2.3. The query procedure for a segment tree and a query line l . The shaded
intervals indicate the query path from the root to the leaf. All segments stored at the
corresponding nodes are reported.

A WeakDelete operation can be performed in O(log n) time (see Chapter 10).


We have to mark a segment s in every list v.L with s ∈ v.L. By construction,
a segment s is stored at most twice in every level of the segment tree and occurs
at most O(log n) times. We proceed as if we would like to insert the segment s,
which can be done in O(log n) time; see also the proof of Lemma 2.4.

Lemma 2.6. A WeakDelete operation for a segment tree of n segments in the


plane can be performed in time O(log n).

2.3. Multi-Level Segment Trees


In the preceding sections, we considered segment trees for 2D objects. Now, we
generalize the approach. Namely, we consider the following multi-dimensional
stabbing problem and make use of a set of inductively defined segment trees.
Multi-level segment trees support (axis-parallel box/point) stabbing queries as
follows:

• Input: a set S of n axis-parallel boxes in Rd ;

• Query: a query point q in Rd ;

• Output: all boxes B ∈ S with q ∈ B.


2.3. Multi-Level Segment Trees 29

Figure 2.4. A stabbing query for a set of bounding boxes can report all polygonal
objects near q .

This stabbing query has many applications in computer graphics. For exam-
ple, objects in the sphere may be approximated by their axis-parallel bounding
box. For every point q, the (bounding box/point) stabbing query will report all
objects that are near q. Figure 2.4 shows a simple example in 2D.
A single d-dimensional box Bi can be defined by a set of d axis-parallel
segments {Si1 , Si2 , . . . , Sid } starting at a unique corner Ci = (Ci1 , Ci2 , . . . , Cid );
see Figure 2.5 for a 2D example. A multi-level segment tree T d with respect to
(x1 , x2 , . . . , xd ), where xi denotes the ith coordinate, is inductively defined.

T1

s11 s41 s31

s11 s31 s11 s31


s41
s41
T{s1 T{s1
11 } 41 ,s 31 }

s1
s12 s4
s42
q
s3 s32
s2 s42
s32

Figure 2.5. Some parts of the multi-level segment tree of four boxes in 2D. The seg-
ment tree T 1 and the relevant trees for the nodes u 1 and u 2 with Lu 1 = {s11 } and
Lu 2 = {s31 , s41 } for the query point q are shown.
30 2. Orthogonal Windowing and Stabbing Queries

MLSegmentTree(B, d) (B is a set of boxes in dimension d; each box is repre-


sented by a set of line segments)
S := B. FirstSegmentsExtract
T := SegmentTree(S)
T . Dim := d
if d > 1 then
N := T . GetAllNodes
while N = ∅ do
u := N. First N. DeleteFirst
L := u.L
B := new list
while L = ∅ do
s := L. First L. DeleteFirst
B := B. ListAdd( s. Box(d − 1) )
end while
u. Pointer := MLSegmentTree(B, d − 1)
B. Deallocate
end while
end if
return T
Algorithm 2.7. The inductive construction algorithm of a multi-level segment tree.

The following is the multi-level segment tree construction sketch:

• Build a one-dimensional segment tree T 1 for all segments Si1 with respect
to the x1 coordinate;

• For every node u of T 1 :

– Build a (d − 1)-dimensional multi-level segment tree Tud−1 for the set


of boxes
{(Sj 2 , . . . , Sj d ) : Sj 1 ∈ u.L},
where u.L denotes the set of segments stored at u in T 1 ;

– Associate a pointer in T 1 from u to Tud−1 .

A query is answered recursively in a straightforward manner. If a set of seg-


ments in dimension j has to be reported, we recursively check the corresponding
tree with dimension j − 1. The corresponding box is reported only if the query
succeeds in every dimension. This means that finally we get an answer for a
tree Tu1 .
2.3. Multi-Level Segment Trees 31

MLSegmentTreeQuery(T , q, d) (q ∈ Rd , T is the root node of a d-dimensional


multi-level segment tree)
if d = 1 then
L := StabbingQuery(T , q )
A := L. GetBoxes
else
A := new list
L := SearchTreeQuery(T , q. First)
while L = do
t := (L. First). Pointer
B := MLSegmentTreeQuery(t, q. Rest, d − 1)
A. ListAdd(B)
L. DeleteFirst
end while
return A
end if
Algorithm 2.8. The inductive query operation for a multi-level segment tree.

More precisely, a query for a query point q = (q 1 , q 2 , . . . , q d ) and a multi-


level segment tree T d for a set of segments S1 , . . . , Sn is given in the following
sketch.
The following is the multi-level segment tree query sketch:
• If d = 1, answer the question with the corresponding segment tree T 1 ; see
Algorithm 2.6 and return the boxes associated to the segments reported;

• Otherwise, traverse T 1 and find the nodes u 1 , . . . , u l so that q 1 lies in the


intervals (u i ).I of the nodes u i of T 1 for i = 1 to l;

• For 1 ≤ i ≤ l, answer recursively the query (q 2 , . . . , q d ) for the tree Tu(d−1)


i
of the set of segments (u i ).L stored at u i , respectively.
The following time and space bounds hold.
Theorem 2.7. A multi-level segment tree for a set of n axis-parallel boxes in
dimension d can be built up in O(n logd n) time and needs O(n logd n) space. A
(axis-parallel box/point) stabbing query can be answered in O(k + logd n) time.

Proof: First, we sort the segment endpoints with respect to every dimension. The
first segment tree T 1 is built up in O(n log n) time, occupies space O(n log n),
and has O(n) nodes. Next, we construct (d − 1)-dimensional segment trees for
the set v.L of every node v in T 1 . By induction, we can assume that the (d − 1)-
dimensional segment tree for a node v ∈ T 1 with |v.L| segments is computed in
32 2. Orthogonal Windowing and Stabbing Queries

O(|v.L| log(d−1) |v.L|). Additionally, as already proven, every segment si occurs


in O(log n) lists v.L, i.e., in O(log n) (d − 1)-dimensional segment trees. This

means that v∈T 1 |v.L| is in O(n log n), and the overall running time is given by

|v.L| log(d−1) |v.L| ≤ D n logd n .
v∈T 1

The required space can be measured as follows. The whole structure T d consists
of one-dimensional segment trees T 1 only. In how many trees will a part of the
box Bi appear? In the first tree T 1 , the segment si1 of the box Bi appears in
O(log n) nodes. Therefore, it appears in O(log n) trees Tv(d−1) . In each of these
trees, the segment si2 of the box Bi appears again in O(log n) nodes. Altogether,
segments of Bi appear in


d
logi ∈ O(logd n)
i=1

nodes. Summing up over all n segments gives O(n logd n) entries. By the same
argument, the number of trees is bounded by O(logd n) also, so that the number
of empty nodes is in O(n logd n).
Analogously, a query traverses along O(logd n) nodes. For the first tree T 1 ,
the query traverses O(log n) nodes and therefore O(log n) new queries have to be
considered. Recursively, O(log n) nodes in O(log n) new trees may be traversed.
In the final segment trees, k boxes may also be reported. Altogether


d
logi ∈ O(logd n)
i=1

nodes are visited, and the running time for a query is O(k + logd n). 

A WeakDelete operation (see Chapter 10) for a segment s in the multi-level


segment tree can be performed in O(logd n) time due to the number of nodes
where s appears and due to the time for visiting all these nodes. Technically, we
can reinsert the segment s recursively into the multi-level segment tree.

Lemma 2.8. A WeakDelete operation for a d-dimensional multi-level segment


tree for a set of n axis-parallel boxes can be performed in time O(logd n).

Next, we turn to the subject of orthogonal windowing queries.


2.4. Kd-Trees 33

2.4. Kd-Trees
In general, a windowing query considers a set of objects and a multi-dimensional
window, such as a box. All objects that intersect with the window should be
reported. Thus, one can easily decide which objects are located within a certain
area, one of the classic problems arising in computer graphics.
The kd-tree is a natural generalization of the one-dimensional search tree con-
sidered in the previous section. It can be viewed also as a generalization of
quadtrees. With the help of a kd-tree, one can efficiently answer (point/axis-
parallel box) windowing queries as follows:
• Input: a set of points S in Rd ;
• Query: an axis-parallel d-dimensional box B;
• Output: all points p ∈ S with p ∈ B.
Let D be a set of n points in Rk . For convenience, we assume that for a while
k = 2, and that all x- and y-coordinates are different. If this is not the case, we will
have a so-called degenerate situation, and we can apply the techniques presented
in Section 9.5. Let us further assume that we have decided to split D along the
x-axis, and that we have determined the split value s of the x-coordinates. Then
we split D by the split line x = s into subsets
D <s = {(x, y) ∈ D|x < s},
D >s = {(x, y) ∈ D|x > s}.
We repeat the process recursively with the constructed subsets. For every split
operation, we have to determine the split axis and the split value. The simplest
strategy is to choose the axes in a cyclic manner (i.e., x-, y-, z-axis, etc.)—this is
called a cyclic kd-tree. More precisely, a kd-tree for dimension d is constructed
as follows.
The following is the inductive construction of a kd-tree sketch:
• Input: given a set of points D in dimension d and the split coordinate xi ;
• If D is empty, then return an empty node v;
• Otherwise, allocate a node v for the root of the kd-tree with two children
v. LeftChild and v. RightChild. Choose a split value si with respect to the
chosen coordinate xi . Split the set D into subsets
D <si = {(x1 , . . . , xi , . . . , xn ) ∈ D|xi < s},
D >si = {(x1 , . . . , xi , . . . , xn ) ∈ D|xi > s};
34 2. Orthogonal Windowing and Stabbing Queries

KdTreeConstr(D, i) (D is a set of points in Rd , i ∈ {1, . . . , d})


if D = ∅ then
v := Nil
else
v := new node
if |D| = 1 then
v.Elem ent := D.Elem ent
v. LeftChild := Nil v. RightChild := Nil
else
s := D. SplitValue(i)
v. Split := s v. Dim := i
D <s := D. Left(i, s)
D >s := D. Right(i, s)
j := (i mod d) + 1
v. LeftChild := KdTreeConstr(D <s , j )
v. RightChild := KdTreeConstr(D >s , j )
end if
end if
return v
Algorithm 2.9. Recursive construction of a kd-tree.

• Recursively, build the kd-trees v. LeftChild and v. RightChild for the set
D <s and D >s with respect to the next coordinate xj , j = (i mod d) + 1,
respectively. Finally, return the node v.
The tree will be built up by KdTreeConstr(D, 1). Thus, we simply obtain
a binary tree. The balance of the tree depends on the choice of the split value in
procedure SplitValue. In case of d = 2, we obtain a 2D kd-tree2 of the point set D;
see Figure 2.6. Each internal node of the tree corresponds to a split line. For every
node v of the kd-tree, we define the rectangle R(v), the region of v, which is the
intersection of half-planes corresponding to the path from the root to v. For the
root r, R(r) is the plane itself; the children of r, say λ and ρ, correspond to two
half-planes R(λ) and R(ρ), and so on. The set of rectangles {R(l)|l is a leaf}
gives a non-overlapping partition of the plane into rectangles. Every R(l) has
exactly one point of D inside. We do not have to store the rectangles explicitly;
they are given by the path from the root to the corresponding node; see Figure 2.6.
For simplicity, we again consider the 2D case. The kd-tree in 2D efficiently
supports range queries of axis-parallel rectangles. If Q is an axis-parallel rectan-
gle, the set of sites v ∈ D with v ∈ Q can be computed as follows. We have to
2 According to [de Berg et al. 00], the term k-d tree was meant as a template to be specialized like

2-d tree, 3-d tree, etc. Today, it is customary to specialize it like 2D kd-tree, etc.
2.4. Kd-Trees 35

Y
i x=5
g x<5 5<x

d
k
y=4 y=5
b y<4 4<y y<5 5<y

f
c x=2 x=3 x=8 x=7
x<3 3<x
x<2
2<x x<8 8<x x<7 7<x
a b d
j
j g
e h y=3 y=2 y=6
y<3 3<y y<2 2<y y<6 6<y
a
e c h f k i
X

Figure 2.6. A 2D kd-tree and a rectangular range query. Each node corresponds
to a split line. Additionally, each node represents a unique rectangular range R(v)
according to the path from the root to the node.

compute all nodes v with:


R(v) ∩ Q = ∅ .

If the condition holds for node v, it will hold also for the predecessor u of v in
the kd-tree since R(v) ⊂ R(u). Thus, we can start searching from the root to the
leaves. Finally, if we reach a leaf of the tree with the given property, we still have
to check whether the data point of the leaf is inside Q.
For general dimension d, every node v implicitly represents an orthogonal
box in dimension d. Analogously, the box is given by the path from the root to v.
For the query operation, we store the current orthogonal box explicitly during the
query process. We obtain the following query procedure.
The following is the d-dimensional axis-parallel query sketch:

• Input: the root r of a kd-tree in dimension d and a d-dimensional or-


thogonal range R. The d-dimensional orthogonal box Q(v) defines the
range associated with node v. In the beginning, Q(r) represents the full
d-dimensional space for the root r;

• Let v be the current node;

• If v is a leaf, then check whether the element v. Element stored in v lies in


R, and if so, report the element. Stop the procedure;

• Otherwise, the given Q(v) is split into the regions Q(v. LeftChild) and
Q(v. RightChild) for the left and the right child of v by using the split line
v. Split;

• If Q(v. LeftChild) or Q(v. RightChild), respectively, is fully contained in


R, then report all points in v. LeftChild or v. RightChild, respectively;
Random documents with unrelated
content Scribd suggests to you:
The Project Gutenberg eBook of Our Story
Book: Jingles, Stories and Rhymes for Little
Folks
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: Our Story Book: Jingles, Stories and Rhymes for Little
Folks

Author: Various

Release date: August 20, 2016 [eBook #52860]


Most recently updated: October 23, 2024

Language: English

Credits: Produced by Richard Tonsing, Juliet Sutherland and the


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

*** START OF THE PROJECT GUTENBERG EBOOK OUR STORY


BOOK: JINGLES, STORIES AND RHYMES FOR LITTLE FOLKS ***
LITTLE DROPS OF WATER, LITTLE GRAINS
OF SAND.
Our Story Book

JINGLES, STORIES and RHYMES


for
LITTLE FOLKS

Profusely Illustrated
NEW YORK
CUPPLES & LEON COMPANY

Copyright, 1916, by
Cupples & Leon Company
CONTENTS
Wideawake Willy.
The Little Old Woman.
Quite an Adventure.
Funny Toys
There Were Seven Froggies of Lee
Tinklebell Tales.
Holiday Time.
The Twins.
The Five Little Frogs.
The Doings of Dickie and Daisy
Follow My Leader.
Bunny and Chick, Or Sulky Sammy.
Doggie and Puss.
Tittle-tattle Tales, Or Fireside Stories.
The Kitkin Family
Wooden Toys.
Tick Tock
Neddy
Silly Billy and the Kittens.
Tony
The Cats and the Cheese, Or, Better Agree Than Go to Law.
The Doll's House to Let.
The Fox That Lost His Tail, Or, Do Not Follow Every Fashion You See.
Mrs. Gamp and Mrs. Puss-cat
The Conceited Golliwog.
Dolly's Name.
The Call to Arms.
To London Town.
A Home in the Wainscot.
We Three
Under the Big Hat, Or, Jackie and Me.
The Naughty Kitten.
The Goose Girl.
Goose-girl.
Waiting For Tea.
The Boy and the Bogie.
Baby's Bunnie.
My First Visit.
Ten Raw Recruits.
The Dog and the Wolf; Or, Be Content With Your Lot.
Castles in the Air.
A Day at the Farm.
Doggie's Woes.
Off to the Front.
Tommy's First Day at School.
Mister Jack-in-the-box.
Hector Protector.
The Muzzle That Fitted Too Much.
In the Farmyard.
Fir Tree Farm.
Pity the Sorrows of a Poor Little Dog.
Too Sure.
Firefly.
What a Mistake.
Charlie's Playthings.
How Pussy Caught Her First Bird.
A Rat Hunt.
Nip and the Pigeons.
Wideawake Willy.

H
is real name was Willy. But because he was always on the look-
out for any fun or mischief, Father and Mother called him
“Wideawake” as well.
One sunny day he and his little friends went out to play cricket.
“I'm going to make a hundred runs!” cried Willy, as he took up the
bat. But when he had made ten, Freddy bowled him out, and the
other boys were glad it was their turn to go in.
In the winter, father made a big slide, and Willy and the boys went
down it as fast as they could go. When he fell half way down, Willy
thought it great fun, and called to his snowman as he glided past,
“Look at me old fellow! Don't you wish you could slide?”
On his birthday he had a party. It was a merry time. They played
“Blind Man's Buff,” and “Puss in the Corner,” and Willy always
managed to catch the little girls by their long curls.
It was spring when Wideawake Willy went exploring. He shot his
Teddy Bear, and tied it up in a scarlet handkerchief, for he knew
explorers ate bears. Then he stuck a long feather in his hat, and
strode gaily down the road. Presently he came to a big house. The
door was open, and a lady asked him to go in. She showed him all
sorts of wonderful things. What he liked best was Chin Chan, the
Chinese boy, whose long pig-tail touched the floor. The lady told
Willy that in China he lived in a boat, and helped his mother look
after the chickens.
After seeing other Chinese people, he went home and told his
mother what strange things he had seen.
The Little Old Woman.
T
here was an old Woman,
And what do you think?
She lived upon nothing but
Victuals and drink;
And though victuals and drink
Were the chief of her diet,
This little Old Woman could never be quiet.

This little Old Woman (the story so goes)


Had nothing to wear but
Abundance of clothes.
And, oh, let me weep
At the dismal news,
She would have been barefooted, but for her shoes.

This Little Old Woman,


Twas always the case,
Never looked in the glass
But she saw her own face;
And what was still worse,
Yet, we vouch for its truth,
By growing so old, she had lost all her youth.
This Little Old Woman,
The tale too declares,
Had nothing to sit on
But sofas and chairs.
No place to repose in
At night but her bed;
No pillows, but those made of down, for her head.

This Little Old Woman,


We here may remark,
Had no house to live in,
But one in the park,
And none to wait on her,
Poor soul, but her maids,
With some livery servants of different grades.

This Little Old Woman,


I'm sorry to tell,
Had always bad health,
When she was not quite well.
And hard was her lot,
For they tell me that she
Was ever in want
When she wanted her tea.

This Little Old Woman,


On dying, we find,
Left nothing—except
A large fortune, behind.
So pity her fate,
Gentle reader, and say,
Such women are not to be found every day.
Welcome to our website – the perfect destination for book lovers and
knowledge seekers. We believe that every book holds a new world,
offering opportunities for learning, discovery, and personal growth.
That’s why we are dedicated to bringing you a diverse collection of
books, ranging from classic literature and specialized publications to
self-development guides and children's books.

More than just a book-buying platform, we strive to be a bridge


connecting you with timeless cultural and intellectual values. With an
elegant, user-friendly interface and a smart search system, you can
quickly find the books that best suit your interests. Additionally,
our special promotions and home delivery services help you save time
and fully enjoy the joy of reading.

Join us on a journey of knowledge exploration, passion nurturing, and


personal growth every day!

ebookbell.com

You might also like