[libsnl] 01/02: Imported Upstream version 0.2.1.svn.18

Wolfgang Fütterer wlfuetter-guest at moszumanska.debian.org
Thu Mar 26 11:03:26 UTC 2015


This is an automated email from the git hooks/post-receive script.

wlfuetter-guest pushed a commit to branch master
in repository libsnl.

commit b10ac052abccbb652422c45aa0458e0c996b706a
Author: Wolfgang Fuetterer <debian at wlf-online.de>
Date:   Thu Mar 12 14:04:26 2015 +0100

    Imported Upstream version 0.2.1.svn.18
---
 Doxyfile                       |  274 +++
 license.txt                    |  340 ++++
 src/dynamicArray.h             |  263 +++
 src/makefile                   |   50 +
 src/ptrList.h                  |  912 ++++++++++
 src/snlCircularOffsetCurve.cpp |  480 +++++
 src/snlCircularOffsetCurve.h   |   79 +
 src/snlCtrlPoint.cpp           |   77 +
 src/snlCtrlPoint.h             |   44 +
 src/snlCtrlPointNet.cpp        |  656 +++++++
 src/snlCtrlPointNet.h          |  135 ++
 src/snlCtrlPointNetCurve.cpp   |  238 +++
 src/snlCtrlPointNetCurve.h     |   57 +
 src/snlCtrlPointNetSurface.cpp |  835 +++++++++
 src/snlCtrlPointNetSurface.h   |   88 +
 src/snlCurve.cpp               | 1484 +++++++++++++++
 src/snlCurve.h                 |  140 ++
 src/snlCurveBase.h             |   54 +
 src/snlKnotVector.cpp          |  866 +++++++++
 src/snlKnotVector.h            |  139 ++
 src/snlMatrix_4x4.cpp          |  210 +++
 src/snlMatrix_4x4.h            |   74 +
 src/snlMeshable.cpp            |   33 +
 src/snlMeshable.h              |   66 +
 src/snlNurbsCommon.cpp         |  829 +++++++++
 src/snlNurbsCommon.h           |   83 +
 src/snlPoint.cpp               |  424 +++++
 src/snlPoint.h                 |   85 +
 src/snlSquareLinear.cpp        |  184 ++
 src/snlSquareLinear.h          |   62 +
 src/snlSurface.cpp             | 3925 ++++++++++++++++++++++++++++++++++++++++
 src/snlSurface.h               |  370 ++++
 src/snlSurfaceBase.h           |   59 +
 src/snlSurfaceOfRevolution.cpp |  182 ++
 src/snlSurfaceOfRevolution.h   |   68 +
 src/snlSurface_pointLoop.cpp   |  794 ++++++++
 src/snlSurface_pointLoop.h     |   22 +
 src/snlSurface_projection.cpp  | 2243 +++++++++++++++++++++++
 src/snlSurface_projection.h    |   23 +
 src/snlTest.cpp                | 1689 +++++++++++++++++
 src/snlTransform.cpp           |  343 ++++
 src/snlTransform.h             |   56 +
 src/snlTriangleMesh.cpp        |  143 ++
 src/snlTriangleMesh.h          |   87 +
 src/snlUtil.cpp                |  155 ++
 src/snlUtil.h                  |   73 +
 src/snlVector.cpp              |  493 +++++
 src/snlVector.h                |  111 ++
 src/snlVersion.h               |   21 +
 src/snlVertex.cpp              |   88 +
 src/snlVertex.h                |   59 +
 src/snlVertexNet.cpp           |  393 ++++
 src/snlVertexNet.h             |   76 +
 53 files changed, 20734 insertions(+)

diff --git a/Doxyfile b/Doxyfile
new file mode 100644
index 0000000..d1e471b
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,274 @@
+# Doxyfile 1.4.1-KDevelop
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME           = libSNL.kdevelop
+PROJECT_NUMBER         = "Version 0.2"
+OUTPUT_DIRECTORY       = ./doc
+CREATE_SUBDIRS         = NO
+OUTPUT_LANGUAGE        = English
+USE_WINDOWS_ENCODING   = NO
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = YES
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+ALWAYS_DETAILED_SEC    = NO
+INLINE_INHERITED_MEMB  = NO
+FULL_PATH_NAMES        = YES
+STRIP_FROM_PATH        = /home/stodas/
+STRIP_FROM_INC_PATH    = 
+SHORT_NAMES            = NO
+JAVADOC_AUTOBRIEF      = NO
+MULTILINE_CPP_IS_BRIEF = NO
+DETAILS_AT_TOP         = NO
+INHERIT_DOCS           = YES
+DISTRIBUTE_GROUP_DOC   = NO
+TAB_SIZE               = 8
+ALIASES                = 
+OPTIMIZE_OUTPUT_FOR_C  = NO
+OPTIMIZE_OUTPUT_JAVA   = NO
+SUBGROUPING            = YES
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL            = YES
+EXTRACT_PRIVATE        = NO
+EXTRACT_STATIC         = NO
+EXTRACT_LOCAL_CLASSES  = YES
+EXTRACT_LOCAL_METHODS  = NO
+HIDE_UNDOC_MEMBERS     = NO
+HIDE_UNDOC_CLASSES     = NO
+HIDE_FRIEND_COMPOUNDS  = NO
+HIDE_IN_BODY_DOCS      = NO
+INTERNAL_DOCS          = NO
+CASE_SENSE_NAMES       = YES
+HIDE_SCOPE_NAMES       = NO
+SHOW_INCLUDE_FILES     = YES
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = YES
+SORT_BRIEF_DOCS        = NO
+SORT_BY_SCOPE_NAME     = NO
+GENERATE_TODOLIST      = YES
+GENERATE_TESTLIST      = YES
+GENERATE_BUGLIST       = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS       = 
+MAX_INITIALIZER_LINES  = 30
+SHOW_USED_FILES        = YES
+SHOW_DIRECTORIES       = YES
+FILE_VERSION_FILTER    = 
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET                  = NO
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = NO
+WARN_FORMAT            = "$file:$line: $text"
+WARN_LOGFILE           = 
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT                  = ./src
+FILE_PATTERNS          = *.c \
+                         *.cc \
+                         *.cxx \
+                         *.cpp \
+                         *.c++ \
+                         *.java \
+                         *.ii \
+                         *.ixx \
+                         *.ipp \
+                         *.i++ \
+                         *.inl \
+                         *.h \
+                         *.hh \
+                         *.hxx \
+                         *.hpp \
+                         *.h++ \
+                         *.idl \
+                         *.odl \
+                         *.cs \
+                         *.php \
+                         *.php3 \
+                         *.inc \
+                         *.m \
+                         *.mm \
+                         *.C \
+                         *.CC \
+                         *.C++ \
+                         *.II \
+                         *.I++ \
+                         *.H \
+                         *.HH \
+                         *.H++ \
+                         *.CS \
+                         *.PHP \
+                         *.PHP3 \
+                         *.M \
+                         *.MM \
+                         *.C \
+                         *.H \
+                         *.tlh \
+                         *.diff \
+                         *.patch \
+                         *.moc \
+                         *.xpm \
+                         *.dox
+RECURSIVE              = YES
+EXCLUDE                = 
+EXCLUDE_SYMLINKS       = NO
+EXCLUDE_PATTERNS       = 
+EXAMPLE_PATH           = 
+EXAMPLE_PATTERNS       = *
+EXAMPLE_RECURSIVE      = NO
+IMAGE_PATH             = 
+INPUT_FILTER           = 
+FILTER_PATTERNS        = 
+FILTER_SOURCE_FILES    = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER         = NO
+INLINE_SOURCES         = NO
+STRIP_CODE_COMMENTS    = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION    = YES
+VERBATIM_HEADERS       = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = YES
+COLS_IN_ALPHA_INDEX    = 5
+IGNORE_PREFIX          = 
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML          = YES
+HTML_OUTPUT            = html
+HTML_FILE_EXTENSION    = .html
+HTML_HEADER            = 
+HTML_FOOTER            = 
+HTML_STYLESHEET        = 
+HTML_ALIGN_MEMBERS     = YES
+GENERATE_HTMLHELP      = NO
+CHM_FILE               = 
+HHC_LOCATION           = 
+GENERATE_CHI           = NO
+BINARY_TOC             = NO
+TOC_EXPAND             = NO
+DISABLE_INDEX          = NO
+ENUM_VALUES_PER_LINE   = 4
+GENERATE_TREEVIEW      = NO
+TREEVIEW_WIDTH         = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = NO
+LATEX_OUTPUT           = latex
+LATEX_CMD_NAME         = latex
+MAKEINDEX_CMD_NAME     = makeindex
+COMPACT_LATEX          = NO
+PAPER_TYPE             = a4wide
+EXTRA_PACKAGES         = 
+LATEX_HEADER           = 
+PDF_HYPERLINKS         = NO
+USE_PDFLATEX           = NO
+LATEX_BATCHMODE        = NO
+LATEX_HIDE_INDICES     = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF           = NO
+RTF_OUTPUT             = rtf
+COMPACT_RTF            = NO
+RTF_HYPERLINKS         = NO
+RTF_STYLESHEET_FILE    = 
+RTF_EXTENSIONS_FILE    = 
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN           = NO
+MAN_OUTPUT             = man
+MAN_EXTENSION          = .3
+MAN_LINKS              = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML           = NO
+XML_OUTPUT             = xml
+XML_SCHEMA             = 
+XML_DTD                = 
+XML_PROGRAMLISTING     = YES
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF   = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD       = NO
+PERLMOD_LATEX          = NO
+PERLMOD_PRETTY         = YES
+PERLMOD_MAKEVAR_PREFIX = 
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = YES
+MACRO_EXPANSION        = NO
+EXPAND_ONLY_PREDEF     = NO
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           = 
+INCLUDE_FILE_PATTERNS  = 
+PREDEFINED             = 
+EXPAND_AS_DEFINED      = 
+SKIP_FUNCTION_MACROS   = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+TAGFILES               = 
+GENERATE_TAGFILE       = libSNL.tag
+ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = YES
+PERL_PATH              = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS         = YES
+HIDE_UNDOC_RELATIONS   = YES
+HAVE_DOT               = NO
+CLASS_GRAPH            = YES
+COLLABORATION_GRAPH    = YES
+GROUP_GRAPHS           = YES
+UML_LOOK               = NO
+TEMPLATE_RELATIONS     = NO
+INCLUDE_GRAPH          = YES
+INCLUDED_BY_GRAPH      = YES
+CALL_GRAPH             = NO
+GRAPHICAL_HIERARCHY    = YES
+DIRECTORY_GRAPH        = YES
+DOT_IMAGE_FORMAT       = png
+DOT_PATH               = 
+DOTFILE_DIRS           = 
+MAX_DOT_GRAPH_WIDTH    = 1024
+MAX_DOT_GRAPH_HEIGHT   = 1024
+MAX_DOT_GRAPH_DEPTH    = 1000
+DOT_TRANSPARENT        = NO
+DOT_MULTI_TARGETS      = NO
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+SEARCHENGINE           = NO
diff --git a/license.txt b/license.txt
new file mode 100644
index 0000000..60549be
--- /dev/null
+++ b/license.txt
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+

+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+

+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+

+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+

+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+

+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/src/dynamicArray.h b/src/dynamicArray.h
new file mode 100644
index 0000000..5b3b1c7
--- /dev/null
+++ b/src/dynamicArray.h
@@ -0,0 +1,263 @@
+// Copyright 2005 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Library General Public License for more details.
+
+// *** Dynamically Allocated Paged Array ***
+
+#ifndef DYNAMICARRAY_H
+#define DYNAMICARRAY_H
+
+#define DYNAMICARRAY_DEFAULT_PAGE_SIZE 512
+
+template < class T > class dynamicArray
+{
+    public:
+
+        virtual ~dynamicArray();
+
+        dynamicArray();
+        dynamicArray ( const dynamicArray<T>& arrayToCopy );
+        dynamicArray ( int pageSize );
+
+        dynamicArray<T>& operator= ( const dynamicArray<T>& arrayToCopy );
+
+        void copyFrom ( const dynamicArray<T>& arrayToCopy );
+
+        int pageSize();
+
+        T* copyOfElements();
+
+        int sizeAllocated();
+
+        int maxIndex();
+
+        T& operator[] ( int index );
+
+    protected:
+
+        void grow ( int bySize );
+
+    private:
+
+        int     page_size;
+        
+        T**     array_pages;
+
+        int     num_pages;
+
+        int     array_size;
+
+        int     max_index;  // Maximum index number that has been used.
+
+        T       null_element;
+};
+
+template < class T > dynamicArray<T>::~dynamicArray()
+{
+    if ( array_pages )
+    {
+        for ( int pageIndex = 0; pageIndex < num_pages; pageIndex ++ )
+            delete[] array_pages [ pageIndex ];
+    }
+}
+
+template < class T > dynamicArray<T>::dynamicArray()
+{
+    page_size = DYNAMICARRAY_DEFAULT_PAGE_SIZE;
+    num_pages = 0;
+    array_pages = 0;
+    array_size = 0;
+    max_index = 0;
+}
+
+template < class T > dynamicArray<T>::dynamicArray ( const dynamicArray<T>& arrayToCopy )
+{
+    // Copy constructor.
+    // -----------------
+
+    copyFrom ( arrayToCopy );
+}
+
+template < class T > dynamicArray<T>::dynamicArray ( int pageSize )
+{
+    // Constructor.
+    // ------------
+    // initialSize:     Initial number of elements in array.
+    // pageSize:        Granularity of array size increase. Size is number of elements NOT bytes.
+
+    page_size = pageSize;
+    num_pages = 0;
+    array_pages = 0;
+    array_size = 0;
+    max_index = 0;
+}
+
+template < class T > dynamicArray<T>& dynamicArray<T>::operator= ( const dynamicArray<T>& arrayToCopy )
+{
+    // Assignment operator.
+    // --------------------
+
+    if ( array_pages )
+    {
+        for ( int pageIndex = 0; pageIndex < num_pages; pageIndex ++ )
+            delete[] array_pages [ pageIndex ];
+    }
+
+    copyFrom ( arrayToCopy );
+}
+
+template < class T > void dynamicArray<T>::copyFrom ( const dynamicArray<T>& arrayToCopy )
+{
+    // Copy contents of another dynamic array into this.
+    // -------------------------------------------------
+    // Notes:   Type <T> must have an assignment operator that doesn't
+    //          fuck things up.
+
+    page_size = arrayToCopy.page_size;
+    num_pages = arrayToCopy.num_pages;
+    array_size = arrayToCopy.array_size;
+    max_index = arrayToCopy.max_index;
+
+    if ( ! arrayToCopy.array_pages )
+    {
+        num_pages = 0;
+        array_size = 0;
+        max_index = 0;
+        array_pages = 0;
+    }
+    else
+    {
+        array_pages = new T* [ num_pages ];
+    
+        for ( int page = 0; page < num_pages; page ++ )
+        {
+            T* pageToCopy = arrayToCopy.array_pages [ page ];
+    
+            T* newPage = new T [ page_size ];
+    
+            array_pages [ page ] = newPage;
+            
+            for ( int index = 0; index < page_size; index ++ )
+                newPage [ index ] = pageToCopy [ index ];
+        }
+    }
+}
+
+template < class T > int dynamicArray<T>::pageSize()
+{
+    return page_size;
+}
+
+template < class T > T* dynamicArray<T>::copyOfElements()
+{
+    // Return pointer to copy of array elements.
+    // -----------------------------------------
+    // Notes:       Caller owns returned array.
+
+    if ( ! num_pages ) return 0;
+
+    int arraySize = array_size;
+
+    T* retArray = new T [ arraySize ];
+
+    int retArrayIndex = 0;
+
+    for ( int page = 0; page < num_pages; page ++ )
+    {
+        T* currentPage = array_pages [ page ];
+        
+        for ( int index = 0; index < page_size; index ++ )
+            retArray [ retArrayIndex ++ ] = currentPage [ index ];
+    }
+
+    return retArray;
+}
+
+template < class T > int dynamicArray<T>::sizeAllocated()
+{
+    // Return size of all allocated elements.
+    // --------------------------------------
+
+    array_size = num_pages * page_size;
+
+    return array_size;
+}
+
+template < class T > int dynamicArray<T>::maxIndex()
+{
+    // Return maximum index number that has been referenced.
+    // -----------------------------------------------------
+
+    return max_index;
+}
+
+template < class T > void dynamicArray<T>::grow ( int bySize )
+{
+    // Grow array.
+    // -----------
+    // bySize:      Grow by this many elements.
+    //
+    // Notes:       Will only increase size to page boundaries.
+
+    int growNumPages = bySize / page_size;
+
+    if ( bySize % page_size > 0 )
+        growNumPages ++;
+
+    // Grow pages array.
+
+    T** newPageArray = new T* [ num_pages + growNumPages ];
+
+    if ( array_pages )
+    {
+        for ( int pageIndex = 0; pageIndex < num_pages; pageIndex ++ )
+            newPageArray [ pageIndex ] = array_pages [ pageIndex ];
+
+        delete[] array_pages;
+    }
+    
+    // Populate pages array with new pages.
+
+    for ( int pageIndex = 0; pageIndex < growNumPages; pageIndex ++ )
+    {
+        T* newPage = new T [ page_size ];
+        newPageArray [ num_pages + pageIndex ] = newPage;
+    }
+
+    array_pages = newPageArray;
+    
+    num_pages += growNumPages;
+
+    array_size = num_pages * page_size;
+}
+
+template < class T > T& dynamicArray<T>::operator[] ( int index )
+{
+    // Return reference to array element at index.
+    // -------------------------------------------
+    // index:       Array index.
+
+    if ( index < 0 ) return null_element;  // No negative indices allowed.
+
+    if ( ! array_pages || index >= array_size )
+        grow ( index - array_size + 1 );
+
+    if ( index > max_index ) max_index = index;
+
+    int pageIndex = index / page_size;
+
+    int elementIndex = index % page_size;
+
+    return array_pages [ pageIndex ] [ elementIndex ];
+}
+
+#endif
+
diff --git a/src/makefile b/src/makefile
new file mode 100644
index 0000000..12161e6
--- /dev/null
+++ b/src/makefile
@@ -0,0 +1,50 @@
+# libSNL Source Directory Root Makefile
+# -------------------------------------
+
+objFiles := snlCircularOffsetCurve.o \
+            snlCtrlPoint.o snlCtrlPointNet.o snlCtrlPointNetCurve.o snlCtrlPointNetSurface.o snlCurve.o \
+            snlKnotVector.o \
+            snlMatrix_4x4.o snlMeshable.o \
+            snlNurbsCommon.o \
+            snlPoint.o \
+            snlSquareLinear.o snlSurface.o snlSurface_pointLoop.o \
+            snlSurface_projection.o snlSurfaceOfRevolution.o \
+            snlTransform.o snlTriangleMesh.o \
+            snlUtil.o \
+            snlVector.o snlVertex.o snlVertexNet.o
+
+libName = libSNL.so.0.2
+
+export cflags = -Wall -fPIC -g 
+export cname = g++
+
+CXXFLAGS := $(cflags)
+CFLAGS := $(cflags)
+
+libSNL:     $(objFiles)
+	@       echo
+	@       echo "*** Building Shared Library ***"
+	@       echo
+	        $(cname) $(cflags) -shared -o $(libName) $(objFiles)
+
+include                 make.dep
+
+PHONY : dep
+dep:                    make.dep
+
+make.dep :              $(objFiles:.o=.h) $(extraIncl)
+	@                   echo
+	@                   echo "*** Building Dependencies ***"
+	@                   echo
+	                    $(cname) -MM $(objFiles:.o=.cpp) > make.dep
+	@                   echo
+	@                   echo "*** Dependencies Built Okay ***"
+	@                   echo
+
+PHONY : clean
+clean:
+	                    rm $(objFiles) make.dep $(libName) snlTest
+
+test:       snlTest.cpp
+	        $(cname) $(cflags) snlTest.cpp -o snlTest $(objFiles)
+
diff --git a/src/ptrList.h b/src/ptrList.h
new file mode 100644
index 0000000..80801f1
--- /dev/null
+++ b/src/ptrList.h
@@ -0,0 +1,912 @@
+/* 
+ *  Copyright (C) 2000 Scott A.E. Lanham.
+ *  -------------------------------------
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// *** Doubly linked List of Pointers Template ***
+
+#ifndef PTRLIST_H
+#define PTRLIST_H
+
+template < class T > class ptrListItem
+{
+
+    // Linked list of pointers to type T
+
+    public:
+
+        virtual ~ptrListItem();
+        
+        ptrListItem();
+        ptrListItem ( const ptrListItem<T>& item );
+        
+        ptrListItem ( T* ptr, ptrListItem<T>* chain, bool delObjPtdTo, bool insert = false );
+
+        // Operators
+
+        ptrListItem<T>& operator= ( const ptrListItem<T>& item );
+
+        // Linked list functions.
+        ptrListItem<T>* next();
+        ptrListItem<T>* prev();
+        ptrListItem<T>* first();
+        ptrListItem<T>* last();
+        ptrListItem<T>* atIndex ( int index );
+
+        void replace ( T* ptr, bool delObjPtdTo );
+
+        bool hasItem ( T* ptr, bool traverse );
+
+        virtual ptrListItem<T>* getItem ( T* ptr );
+
+        T* getPtr();
+
+        virtual unsigned count();  // Number of items in list.        
+        
+        void link ( ptrListItem<T>* linkTo, bool next );  // Link a node into chain after this one.
+        void unlink();  // Unlink this node from chain.
+        void unlinkPrev ( ptrListItem<T>* newPrev );
+        void unlinkNext ( ptrListItem<T>* newNext );
+
+        void cascadeDelete();  // Whole list is being deleted.
+
+    protected:
+
+        void setNext ( ptrListItem<T>* next );
+        void setPrevious ( ptrListItem<T>* prev );
+
+        T*                  pointer;
+        bool                delObj;  // Delete the object this item points to when item is deleted.
+
+        ptrListItem<T>*     prevLnk;  // Linked list.
+        ptrListItem<T>*     nextLnk;
+};
+
+template < class T > class ptrList
+{
+    // Container for ptrListItem<T>
+
+    public:
+
+        virtual ~ptrList();
+        
+        ptrList();
+        ptrList ( const ptrList<T>& list );
+
+        ptrList<T>& operator= ( const ptrList<T>& list );
+
+        void copyFrom ( const ptrList<T>& list );
+
+        T* first();
+        T* last();
+        T* prev();
+        T* next();
+        T* current();
+        T* atIndex ( int index );        
+
+        virtual void clear();
+        virtual void truncate();
+        virtual void append ( T* ptr, bool delObj );
+        virtual void insert ( T* ptr, int index, bool delObj );
+        virtual void insert ( T* ptr, T* before, bool delObj );
+        virtual void replace ( T* ptr, bool delObj );
+        virtual void move ( T* ptr, int toIndex );
+        virtual void move ( T* ptr, T* after );
+        virtual void remove ( T* ptr );
+        virtual void remove();
+        virtual bool hasItem ( T* ptr );
+        virtual int itemIndex ( T* ptr );
+
+        virtual unsigned count();
+
+    protected:
+
+        ptrListItem<T>* cItem;  // Current ptrListItem.
+        ptrListItem<T>* fItem;  // First item in list.
+};
+
+// *** Implementation - Appears here so any type can be used ***
+
+template < class T > ptrListItem<T>::~ptrListItem()
+{
+    // Delete object pointed to if required.
+    if ( delObj && pointer ) delete pointer;
+
+    // Unlink this item from chain.
+    unlink();
+}
+
+template < class T > ptrListItem<T>::ptrListItem()
+{
+    pointer = 0;
+    prevLnk = 0;
+    nextLnk = 0;
+}
+
+template < class T > ptrListItem<T>::ptrListItem ( const ptrListItem<T>& item )
+{
+    // Copy constructor.
+    // -----------------
+    // Notes:   Copies pointer NOT the object being pointed to.
+
+    pointer = item.pointer;
+
+    delObj = false;  // The pointer is being copied so don't mark it for deletion.
+
+    prevLnk = 0;
+    nextLnk = 0;
+}
+
+template < class T > ptrListItem<T>::ptrListItem ( T* ptr, ptrListItem<T>* chain, bool delObjPtdTo, bool insert )
+{
+    // Constructor
+    // -----------
+    // ptr:         Pointer to store.
+    // chain:       Root (start) of ptrListItem linked list.
+    // delObjPtdTo: Delete object this item points to when item is deleted.
+    // insert:      Insert into list instead of the default which is to append at the end.
+
+    pointer = ptr;
+    delObj = delObjPtdTo;
+
+    // Set next link.
+    
+    if ( insert && chain )
+    {
+        nextLnk = chain;
+    }
+    else
+    {
+        // Put at end of chain.
+        nextLnk = 0;
+    }
+    
+    // Set previous link.
+    
+    if ( chain )
+    {
+        if ( insert )
+        {
+            if ( chain -> prev() )
+                ( chain -> prev() ) -> setNext ( this );
+            
+            prevLnk = chain -> prev();
+            
+            chain -> setPrevious ( this );
+        }
+        else
+        {
+            prevLnk = chain -> last();
+
+            // Link into chain.
+            ( chain -> last() ) -> setNext ( this );
+        }
+    }
+    else
+        prevLnk = 0;
+}
+
+template < class T > ptrListItem<T>& ptrListItem<T>::operator= ( const ptrListItem<T>& item )
+{
+    // Assignment Operator.
+    // --------------------
+
+    if ( this != & item )
+    {
+        if ( delObj && pointer ) delete pointer;
+    
+        // Assume control of the pointer but don't use items linkage.
+    
+        pointer = item.pointer;
+    
+        delObj = item.delObj;
+    
+        item.delObj = false;
+    }
+
+    return *this;
+}
+
+template < class T > ptrListItem<T>* ptrListItem<T>::next()
+{
+    return nextLnk;
+}
+
+template < class T > ptrListItem<T>* ptrListItem<T>::prev()
+{
+    return prevLnk;
+}
+
+template < class T > ptrListItem<T>* ptrListItem<T>::first()
+{
+    ptrListItem*     cSelect;
+    ptrListItem*     rSelect;
+
+    if ( prevLnk )
+        cSelect = prevLnk;
+    else
+    {
+        cSelect = 0;
+        rSelect = this;
+    }
+
+    while ( cSelect )
+    {
+        rSelect = cSelect;
+        cSelect = cSelect -> prev();
+    }
+
+    return rSelect;
+}
+
+template < class T > ptrListItem<T>* ptrListItem<T>::last()
+{
+    ptrListItem*     cSelect;
+    ptrListItem*     rSelect;
+
+    if ( nextLnk )
+        cSelect = nextLnk;
+    else
+    {
+        cSelect = 0;
+        rSelect = this;
+    }
+
+    while ( cSelect )
+    {
+        rSelect = cSelect;
+        cSelect = cSelect -> next();
+    }
+
+    return rSelect;
+}
+
+template < class T > ptrListItem<T>* ptrListItem<T>::atIndex ( int index )
+{
+    // Return item at index.
+    // ---------------------
+    
+    ptrListItem<T>* itm = first();
+    int count = 0;
+
+    while ( itm && count < index )
+    {
+        count ++;
+        itm = itm -> next();
+    }
+
+    return itm;
+}
+
+template < class T > bool ptrListItem<T>::hasItem ( T* ptr, bool traverse )
+{
+    // Does the chain of items hold given pointer
+    // ------------------------------------------
+    // ptr:         Pointer to item.
+    // traverse:    Move forward through primSelects linked by the "next" variable.
+
+    if ( ptr == pointer ) return true;  // Don't process anymore already found match.
+
+    if ( ! traverse ) return false;  // No point in continuing traverse is false.
+
+    bool found = false;
+
+    ptrListItem<T>* itm = next();
+
+    while ( itm && ( ! found ) )
+    {
+        found =  itm -> hasItem ( ptr, false );
+
+        itm = itm -> next();
+    }
+
+    return found;
+}
+
+template < class T > ptrListItem<T>* ptrListItem<T>::getItem ( T* ptr )
+{
+    // Get item if pointer is part of list
+    // -----------------------------------
+    // ptr:     Pointer to search for.
+    //
+    // Notes:   Only a forward traversal is performed.
+
+    if ( ptr == pointer ) return this;  // Don't process anymore already found match.
+
+    bool found = false;
+
+    ptrListItem<T>* itemFound = 0;
+
+    ptrListItem<T>* itm = next();
+
+    while ( itm && ( ! found ) )
+    {
+        found = itm -> hasItem ( ptr, false );
+
+        if ( found ) itemFound = itm;
+
+        itm = itm -> next();
+    }
+
+    return itemFound;
+}
+
+template < class T > unsigned ptrListItem<T>::count()
+{
+    // Return number of nodes in list
+    // ------------------------------
+
+    ptrListItem<T>* itm = first();
+    unsigned count = 0;
+
+    while ( itm )
+    {
+        count ++;
+        itm = itm -> next();
+    }
+
+    return count;
+}
+
+template < class T > void ptrListItem<T>::setNext ( ptrListItem<T>* next )
+{
+    // Set the "next" node of this node.
+    // ---------------------------------
+
+    if ( next == nextLnk ) return;  // Stops recursive endless loop.
+
+    ptrListItem<T>* oldNext = nextLnk;
+
+    nextLnk = next;
+
+    // If there is already a next node reset it's previous to the new node.
+    if ( oldNext ) oldNext -> setPrevious ( next );
+}
+
+template < class T > void ptrListItem<T>::setPrevious ( ptrListItem<T>* prev )
+{
+
+    // Set the previous node to "prev"
+    // -------------------------------
+
+    if ( prev == prevLnk ) return;
+
+    ptrListItem<T>* oldPrev = prevLnk;
+
+    prevLnk = prev;
+
+    // If there is already a previous node then reset it's next to this node.
+    if ( oldPrev ) oldPrev -> setNext ( prev );
+}
+
+template < class T > void ptrListItem<T>::link ( ptrListItem<T>* linkTo, bool next )
+{
+    // Link a node into chain after this one.
+    // --------------------------------------
+    // linkTo:    Node to link to.
+    // next:      If true then link node after this one otherwise before this one.
+    
+    if ( next )
+    {
+        if ( nextLnk )
+        {
+            linkTo -> setNext ( nextLnk );
+            
+            nextLnk -> setPrevious ( linkTo );
+        }
+        
+        linkTo -> setPrevious ( this );
+        
+        nextLnk = linkTo;
+    }
+    else
+    {
+        if ( prevLnk )
+        {
+            linkTo -> setPrevious ( prevLnk );
+            
+            prevLnk -> setNext ( linkTo );
+        }
+        
+        linkTo -> setNext ( this );
+        
+        prevLnk = linkTo;
+    }    
+}
+
+template < class T > void ptrListItem<T>::unlink()
+{
+    // Unlink this object from chain
+    // -----------------------------
+
+    if ( prevLnk ) prevLnk -> unlinkNext ( nextLnk );
+    if ( nextLnk ) nextLnk -> unlinkPrev ( prevLnk );
+
+    nextLnk = 0;
+    prevLnk = 0;
+}
+
+template < class T > void ptrListItem<T>::unlinkNext ( ptrListItem<T>* newNext )
+{
+    // Unlink the next object in chain
+    // -------------------------------
+    // newNext:     The object which is now being linked as next.
+
+    nextLnk = newNext;
+}
+
+template < class T > void ptrListItem<T>::unlinkPrev ( ptrListItem<T>* newPrev )
+{
+    // Unlink the prev object in chain
+    // -------------------------------
+    // newPrev:     The object which is now being linked as prev.
+
+    prevLnk = newPrev;
+}
+
+template < class T > T* ptrListItem<T>::getPtr()
+{
+    return pointer;
+}
+
+template < class T > void ptrListItem<T>::cascadeDelete()
+{
+    // Cascade down the linked list to delete entire list.
+    // ---------------------------------------------------
+    
+    if ( nextLnk )
+    {
+        nextLnk -> cascadeDelete();
+        delete nextLnk;
+    }
+}
+
+template < class T > void ptrListItem<T>::replace ( T* ptr, bool delObjPtdTo )
+{
+    // Replace object pointed to with ptr.
+    // -----------------------------------
+    // ptr:             New object to point to.
+    // delObjPtdTo:     Owns pointer and can / should delete it.
+
+    if ( pointer && delObj )
+        delete pointer;
+
+    pointer = ptr;
+
+    delObj = delObjPtdTo;
+}
+
+// *** Pointer List Object ***
+
+template < class T > ptrList<T>::~ptrList()
+{
+    if ( ! cItem ) return;
+
+    // Delete all associated items.
+
+    ptrListItem<T>* delItem = cItem -> last();
+    ptrListItem<T>* prevItem;
+
+    while ( delItem )
+    {
+        prevItem = delItem -> prev();
+        delete delItem;
+        delItem = prevItem;
+    }
+}
+
+template < class T > ptrList<T>::ptrList()
+{
+    cItem = 0;
+    fItem = 0;
+}
+
+template < class T > ptrList<T>::ptrList ( const ptrList<T>& list )
+{
+    // Copy Constructor.
+    // -----------------
+
+    copyFrom ( list );
+}
+
+template < class T > ptrList<T>& ptrList<T>::operator= ( const ptrList<T>& list )
+{
+    clear();
+
+    copyFrom ( list );
+}
+
+template < class T > void ptrList<T>::copyFrom ( const ptrList<T>& list )
+{
+    // Copy contents of list into this.
+    // --------------------------------
+
+    cItem = 0;
+    fItem = 0;
+    
+    ptrListItem<T>* itemToCopy = list.fItem;
+
+    ptrListItem<T>* newItem;
+
+    ptrListItem<T>* newCItem;
+
+    // Copy pointers across but they can't be automatically deleted.
+
+    while ( itemToCopy )
+    {
+        newItem = new ptrListItem<T> ( itemToCopy -> getPtr(), cItem, false, false );
+
+        if ( ! fItem ) fItem = newItem;
+
+        if ( itemToCopy == list.cItem ) newCItem = newItem;
+
+        itemToCopy = itemToCopy -> next();
+    }
+
+    // Point new list to same position.
+    cItem = newCItem;
+}
+template < class T > void ptrList<T>::clear()
+{
+    // Delete all items at once
+    // ------------------------
+
+    if ( ! cItem ) return;
+
+    ptrListItem<T>* delItem = cItem -> last();
+    ptrListItem<T>* prevItem;
+
+    while ( delItem )
+    {
+        prevItem = delItem -> prev();
+        delete delItem;
+        delItem = prevItem;
+    }
+    
+    cItem = 0;
+    fItem = 0;
+}
+
+template < class T > void ptrList<T>::truncate()
+{
+    // Delete all items after current item.
+
+    if ( cItem )
+        cItem -> cascadeDelete();
+}
+
+template < class T > void ptrList<T>::append ( T* ptr, bool delObj )
+{
+    // Append pointer to list
+    // ----------------------
+    // ptr:     Pointer to append.
+    // delObj:  Delete object ptr points to when list item is deleted.
+
+    ptrListItem<T>* item = new ptrListItem<T> ( ptr, cItem, delObj );
+
+    cItem = item;
+
+    if ( ! item -> prev() ) fItem = item;
+}
+
+template < class T > void ptrList<T>::insert ( T* ptr, int index, bool delObj )
+{
+    // Insert pointer into list
+    // ------------------------
+    // ptr:     Pointer to append.
+    // index:   Index to insert new pointer into.
+    // delObj:  Delete object ptr points to when list item is deleted.
+    
+    ptrListItem<T>* item;
+    
+    if ( this -> atIndex ( index ) )    
+        item = new ptrListItem<T> ( ptr, cItem, delObj, true );
+    else
+        item = new ptrListItem<T> ( ptr, cItem, delObj, false );        
+    
+    cItem = item;
+
+    if ( ! item -> prev() ) fItem = item;
+}
+
+template < class T > void ptrList<T>::insert ( T* ptr, T* before, bool delObj )
+{
+    // Insert pointer into list
+    // ------------------------
+    // ptr:             Pointer to append.
+    // after:           Insert new pointer after given pointer.
+    // delObj:          Delete object ptr points to when list item is deleted.
+
+    if ( ! cItem )
+    {
+        this -> append ( ptr, delObj );
+        return;
+    }
+
+    ptrListItem<T>* insertAt = cItem -> getItem ( ptr );
+    
+    ptrListItem<T>* item;
+    
+    if ( insertAt )
+        item = new ptrListItem<T> ( ptr, insertAt, delObj, true );
+    else
+        item = new ptrListItem<T> ( ptr, cItem, delObj, false );        
+    
+    cItem = item;
+
+    if ( ! item -> prev() ) fItem = item;
+}
+
+template < class T > void ptrList<T>::replace ( T* ptr, bool delObj )
+{
+    // Replace current items pointer with ptr.
+    // ---------------------------------------
+    // ptr:         Pointer to now use.
+    // delObj:      List owns object.
+
+    if ( cItem )
+        cItem -> replace ( ptr, delObj );
+}
+
+template < class T > void ptrList<T>::move ( T* ptr, int toIndex )
+{
+    // Move pointer to different position in list.
+    // -------------------------------------------
+    // ptr:        Pointer to move within list.
+    // toIndex:    Index to move item to.
+    
+    if ( ! cItem ) return;
+    
+    ptrListItem<T>* item = cItem -> getItem ( ptr );
+    
+    if ( item )
+    {
+        ptrListItem<T>* itemAtPosition = cItem -> atIndex ( toIndex );
+        
+        if ( item != itemAtPosition )
+        {    
+            item -> unlink();
+        
+            itemAtPosition -> link ( item, false );
+        }
+
+        if ( ! item -> prev() ) fItem = item;
+    }
+}
+
+template < class T > void ptrList<T>::move ( T* ptr, T* toPositionOf )
+{
+    // Move pointer to different position in list.
+    // -------------------------------------------
+    // ptr:            Pointer to move within list.
+    // toPositionOf:   Move into position in list that this pointer occupies.
+    
+    if ( ! cItem ) return;
+    
+    if ( ptr == toPositionOf ) return;
+    
+    ptrListItem<T>* itemToMove = cItem -> getItem ( ptr );
+    
+    ptrListItem<T>* itemAtPosition = cItem -> getItem ( toPositionOf );
+    
+    if ( itemToMove && itemAtPosition )
+    {
+        itemToMove -> unlink();
+        
+        itemAtPosition -> link ( itemToMove, false );
+
+        cItem = itemToMove;
+
+        if ( ! itemToMove -> prev() ) fItem = itemToMove;
+    }
+}
+
+template < class T > void ptrList<T>::remove ( T* ptr )
+{
+    // Remove pointer from list
+    // ------------------------
+    // ptr:     Pointer to remove.
+
+    if ( cItem )
+    {
+        ptrListItem<T>* item = fItem -> getItem ( ptr );
+
+        if ( item )
+        {
+            if ( cItem == item )
+            {
+                // About to delete the current item so get new cItem.
+                if ( item -> prev() )
+                    cItem = item -> prev();  // Make current item previous in list.
+                else
+                    cItem = item -> next();  // If no previous in list then make current item next in list.
+            }
+
+            delete item;
+        }
+
+        if ( cItem )
+            if ( ! cItem -> prev() ) fItem = cItem;
+        else
+            fItem = 0;
+    }
+}
+
+template < class T > void ptrList<T>::remove()
+{
+    // Remove current item from list
+    // -----------------------------
+
+    if ( ! cItem ) return;
+
+    ptrListItem<T>* prevItem = cItem -> prev();
+    ptrListItem<T>* nextItem = cItem -> next();
+
+    delete cItem;
+
+    if ( nextItem )
+    {
+        cItem = nextItem;
+        if ( !prevItem ) fItem = nextItem;
+    }
+    else if ( prevItem )
+        cItem = prevItem;
+    else
+    {
+        cItem = 0;
+        fItem = 0;
+    }
+}
+
+template < class T > T* ptrList<T>::first()
+{
+    if ( cItem )
+    {
+        cItem = fItem;
+        return ( cItem -> getPtr() );
+    }    
+
+    return 0;
+}
+
+template < class T > T* ptrList<T>::last()
+{
+    if ( cItem )
+    {
+        ptrListItem<T>* item = cItem -> last();
+        cItem = item;
+        return ( item -> getPtr() );
+    }    
+
+    return 0;
+}
+
+template < class T > T* ptrList<T>::prev()
+{
+    if ( cItem )
+    {
+        ptrListItem<T>* item = cItem -> prev();
+        
+        if ( item )
+        {
+            cItem = item;
+            return ( item -> getPtr() );
+        }
+        else
+            return 0;
+    }    
+
+    return 0;
+}
+
+template < class T > T* ptrList<T>::next()
+{
+    if ( cItem )
+    {
+        ptrListItem<T>* item = cItem -> next();
+        
+        if ( item )
+        {
+            cItem = item;
+            return ( item -> getPtr() );
+        }
+        else
+            return 0;
+    }    
+
+    return 0;
+}
+
+template < class T > unsigned ptrList<T>::count()
+{
+    if ( cItem )
+        return ( cItem -> count() );    
+
+    return 0;
+}
+
+template < class T > T* ptrList<T>::current()
+{
+    // Get current item
+    // ----------------
+
+    if ( cItem )
+        return ( cItem -> getPtr() );
+
+    return 0;
+}
+
+template < class T > T* ptrList<T>::atIndex ( int index )
+{
+    // Make item at index current and return it.
+    // -----------------------------------------
+    // index:    Index of pointer to find. Index 0 is first item in list.
+    
+    if ( cItem )
+    {
+        ptrListItem<T>* item = cItem -> atIndex ( index );
+        
+        if ( item )
+        {
+            cItem = item;
+            return ( item -> getPtr() );
+        }
+        else
+            return 0;
+    }    
+
+    return 0;    
+}
+
+template < class T > bool ptrList<T>::hasItem ( T* ptr )
+{
+    // Return True if Item "ptr" is Part of List
+    // -----------------------------------------    
+
+    if ( cItem )
+    {
+        return ( cItem -> first() ) -> hasItem ( ptr, true );
+    }
+
+    return false;
+}
+
+template < class T > int ptrList<T>::itemIndex ( T* ptr )
+{
+    // Find Item's Index.
+    // ------------------
+    
+    if ( ! cItem ) return -1;
+    
+    ptrListItem<T>* item = cItem -> first();
+    
+    int index = 0;
+    
+    while ( item && ( item -> getPtr() != ptr ) )
+    {
+        index ++;
+        
+        item = item -> next();
+    }
+    
+    return index;
+}
+
+#endif
+
+
+
diff --git a/src/snlCircularOffsetCurve.cpp b/src/snlCircularOffsetCurve.cpp
new file mode 100644
index 0000000..4a50593
--- /dev/null
+++ b/src/snlCircularOffsetCurve.cpp
@@ -0,0 +1,480 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** NURBS Curve of Circular Offset from Base Curve ***
+
+#include "snlCircularOffsetCurve.h"
+#include "snlCtrlPointNet.h"
+#include "snlUtil.h"
+
+snlCircularOffsetCurve::snlCircularOffsetCurve()
+{
+    base_curve = 0;    
+    chord_offsetCurve = 0;
+    angle_offsetCurve = 0;
+    tangent_offsetCurve = 0;
+    axis_start = 0;
+    axis_end = 0;
+}
+
+snlCircularOffsetCurve::~snlCircularOffsetCurve()
+{
+    if ( base_curve ) delete base_curve;
+    if ( chord_offsetCurve ) delete chord_offsetCurve;
+    if ( angle_offsetCurve ) delete angle_offsetCurve;
+    if ( tangent_offsetCurve ) delete tangent_offsetCurve;
+    if ( axis_start ) delete axis_start;
+    if ( axis_end ) delete axis_end;
+}
+
+snlCircularOffsetCurve::snlCircularOffsetCurve ( snlCircularOffsetCurve& copyFrom )
+{
+    // Copy constructor.
+    // -----------------
+    
+    if ( copyFrom.base_curve )
+        base_curve = new snlCurve ( * ( copyFrom.base_curve ) );
+    else
+        base_curve = 0;
+    
+    if ( copyFrom.chord_offsetCurve )
+        chord_offsetCurve = new snlCurve ( * ( copyFrom.chord_offsetCurve ) );
+    else
+        chord_offsetCurve = 0;
+        
+    if ( copyFrom.angle_offsetCurve )
+        angle_offsetCurve = new snlCurve ( * ( copyFrom.angle_offsetCurve ) );
+    else
+        angle_offsetCurve = 0;
+        
+    if ( copyFrom.tangent_offsetCurve )
+        tangent_offsetCurve = new snlCurve ( * ( copyFrom.tangent_offsetCurve ) );
+    else
+        tangent_offsetCurve = 0;
+        
+    if ( copyFrom.axis_start )
+        axis_start = new snlPoint ( * ( copyFrom.axis_start ) );
+    else
+        axis_start = 0;
+        
+    if ( copyFrom.axis_end )
+        axis_end = new snlPoint ( * ( copyFrom.axis_end ) );
+    else
+        axis_end = 0;
+}
+
+snlCircularOffsetCurve::snlCircularOffsetCurve ( snlCurve* baseCurve, snlPoint* axisStart, snlPoint* axisEnd )
+{
+    // Construct new circular offset curve.
+    // ------------------------------------
+    // baseCurve:        Curve to offset from.    
+    // startBaseParam:   Param to start at on base curve.    
+    //
+    // Notes:    The offset is the circular distance about the given axis from the base curve.
+    //           Size must be greater or equal to degree + 1.
+    //           All objects passed as pointers are owned by this.
+    
+    base_curve = baseCurve;
+    
+    axisStart -> normalise();
+    axisEnd -> normalise();
+    
+    axis_start = axisStart;
+    axis_end = axisEnd;
+    
+    // Generate and zero offset curve.
+    
+    chord_offsetCurve = new snlCurve ( *baseCurve );
+    angle_offsetCurve = new snlCurve ( *baseCurve );
+    tangent_offsetCurve = new snlCurve ( *baseCurve );
+    
+    int arraySize = baseCurve -> controlPointNet().size();    
+    
+    snlCtrlPoint* ctrlPts = chord_offsetCurve -> controlPointNet().getCtrlPtsPtr();
+    
+    for ( int index = 0; index < arraySize; index ++ )
+        ctrlPts [ index ].zero();
+        
+    ctrlPts = angle_offsetCurve -> controlPointNet().getCtrlPtsPtr();
+    
+    for ( int index = 0; index < arraySize; index ++ )
+        ctrlPts [ index ].zero();
+        
+    ctrlPts = tangent_offsetCurve -> controlPointNet().getCtrlPtsPtr();
+    
+    for ( int index = 0; index < arraySize; index ++ )
+        ctrlPts [ index ].zero();
+}
+
+void snlCircularOffsetCurve::refine ( double tolerance )
+{
+    // Refine control point net until tolerance is achieved.
+    // -----------------------------------------------------
+    //
+    // Notes:    This is a little tricky because the offset_curve's control points
+    //           have to be evaluted relative to the base_curve to get the points to test
+    //           for flatness.
+    
+    bool tolOk = false;    
+    
+    int deg = base_curve -> degree();
+    
+    int numTestPts = deg + 1;
+    
+    snlPoint* testPoints = new snlPoint [ numTestPts ];
+    
+    snlPoint** testPtPtrs = new snlPoint* [ numTestPts ];
+    
+    for ( int index = 0; index < numTestPts; index ++ )
+        testPtPtrs [ index ] = testPoints + index;
+
+    while ( ! tolOk )
+    {
+        tolOk = true;
+        
+        for ( int index = 0; (unsigned) index < ( base_curve -> controlPointNet().size() ) - deg; index ++ )
+        {
+            const snlCtrlPoint* basePts = base_curve -> controlPointNet().getCtrlPts();
+            
+            const snlCtrlPoint* chordOffsetPts = chord_offsetCurve -> controlPointNet().getCtrlPts();
+            const snlCtrlPoint* angleOffsetPts = angle_offsetCurve -> controlPointNet().getCtrlPts();
+            const snlCtrlPoint* tangentOffsetPts = tangent_offsetCurve -> controlPointNet().getCtrlPts();
+        
+            // Generate points to test for flatness.
+            
+            for ( int ptIndex = 0; ptIndex < numTestPts; ptIndex ++ )
+            {
+                testPoints [ ptIndex ] = basePts [ index + ptIndex ];
+                applyOffset ( testPoints [ ptIndex ],
+                              chordOffsetPts [ index + ptIndex ],
+                              angleOffsetPts [ index + ptIndex ],
+                              tangentOffsetPts [ index + ptIndex ] );
+            }
+        
+            // Test for flatness
+        
+            double flatness = ( base_curve ->controlPointNet() ).snlCtrlPointNet::calcFlatness ( testPtPtrs, numTestPts );            
+            
+            if ( flatness > tolerance )
+            {
+                // Insert knot into surface. Half way between existing knots.
+                
+                const snlKnotVector& knotVect = base_curve -> knotVector();
+            
+                int insertIndex = index + deg;
+            
+                knot insertParam = ( ( knotVect.val ( insertIndex + 1 )
+                                       - knotVect.val ( insertIndex ) ) / 2 )
+                                       + knotVect.val ( insertIndex );
+
+                base_curve -> insertKnot ( insertParam, true );
+                chord_offsetCurve -> insertKnot ( insertParam, true );
+                angle_offsetCurve -> insertKnot ( insertParam, true );
+                tangent_offsetCurve -> insertKnot ( insertParam, true );
+
+                tolOk = false;
+
+                index ++;  // If this is not done then nothing converges if the curvature is too great.
+            }
+        }
+    }
+    
+    delete[] testPoints;
+}
+
+void snlCircularOffsetCurve::applyOffset ( snlPoint& point, snlPoint chordOffset, snlPoint angleOffset, snlPoint tangentOffset ) const
+{
+    // Rotate point about axis by circular offset.
+    // -------------------------------------------
+    // point:          Point to rotate.
+    // chordOffset:    Point containing weighted chord offset.
+    // angleOffset:    Point containing weighted angle offset.
+    // tangentOffset:  Point containing weighted tangent offset.    
+    
+    chordOffset.normalise();
+    angleOffset.normalise();
+    tangentOffset.normalise();
+    
+    double angle = 0.0;
+    
+    // Calculate angle to rotate by by adding all offsets together.
+    
+    double dist = distToLine ( *axis_start, *axis_end, point );
+    
+    // CHORD.
+    
+    if ( chordOffset.x() != 0.0 )
+        angle = chordOffset.x() / dist;    
+    
+    // ANGLE.    
+    angle += angleOffset.x();
+    
+    // TANGENT.
+    
+    if ( tangentOffset.x() != 0.0 )
+    {
+        angle += asin ( tangentOffset.x() / dist );
+    }
+    
+    // Apply angle.
+    
+    if ( angle != 0.0 )
+    {
+        snlTransform transf;    
+    
+        transf.rotate ( angle, *axis_start, *axis_end );
+    
+        transf.transform ( point );
+    }
+}
+
+int snlCircularOffsetCurve::numOffsets()
+{
+    return base_curve -> size();
+}
+
+void snlCircularOffsetCurve::generateOffsets ( int type, double startOffset, double endOffset )
+{
+    // Generate offsets as linear interpolation between given start and end points.
+    // ----------------------------------------------------------------------------
+    //
+    // type:           Type of offset to process.
+    // startOffset:    Start value of offsets.
+    // endOffset:      Ending value of offsets.    
+    
+    // Generate offset array.
+    
+    int arraySize = base_curve -> controlPointNet().size();    
+    
+    double offsetStep = ( endOffset - startOffset ) / (double) ( arraySize - 1 );
+    
+    snlCtrlPoint* ctrlPts;
+    
+    switch ( type )
+    {        
+        case CHORD:
+            ctrlPts = chord_offsetCurve -> controlPointNet().getCtrlPtsPtr();
+            break;
+            
+        case ANGLE:
+            ctrlPts = angle_offsetCurve -> controlPointNet().getCtrlPtsPtr();                
+            break;
+            
+        case TANGENT:
+            ctrlPts = tangent_offsetCurve -> controlPointNet().getCtrlPtsPtr();                
+            break;
+    }
+    
+    for ( int index = 0; index < arraySize; index ++ )
+    {
+        ctrlPts [ index ].x ( startOffset + (double) index * offsetStep );
+        ctrlPts [ index ].w ( 1.0 );
+    }
+}
+
+void snlCircularOffsetCurve::offset ( int index, int type, double val, double weight )
+{
+    switch ( type )
+    {
+        case CHORD:
+        
+            ( chord_offsetCurve -> controlPointNet().getCtrlPtsPtr() ) [ index ].x ( val );
+            ( chord_offsetCurve -> controlPointNet().getCtrlPtsPtr() ) [ index ].weight ( weight );
+            
+            break;
+    
+        case ANGLE:
+        
+            ( angle_offsetCurve -> controlPointNet().getCtrlPtsPtr() ) [ index ].x ( val );
+            ( angle_offsetCurve -> controlPointNet().getCtrlPtsPtr() ) [ index ].weight ( weight );
+            
+            break;
+        
+        case TANGENT:
+            
+            ( tangent_offsetCurve -> controlPointNet().getCtrlPtsPtr() ) [ index ].x ( val );
+            ( tangent_offsetCurve -> controlPointNet().getCtrlPtsPtr() ) [ index ].weight ( weight );
+            
+            break;            
+    }
+}
+
+double snlCircularOffsetCurve::offset ( int index, int type )
+{
+    // Return offset at index of type.
+    // -------------------------------
+    
+    snlCtrlPoint* ctrlPts;
+    
+    switch ( type )
+    {
+        case CHORD:
+                
+            ctrlPts = ( chord_offsetCurve -> controlPointNet().getCtrlPtsPtr() );
+            return ctrlPts [ index ].x() / ctrlPts [ index ].w();
+            break;
+            
+        case ANGLE:
+            
+            ctrlPts = ( angle_offsetCurve -> controlPointNet().getCtrlPtsPtr() );
+            return ctrlPts [ index ].x() / ctrlPts [ index ].w();
+            break;
+        
+        case TANGENT:
+        
+            ctrlPts = ( tangent_offsetCurve -> controlPointNet().getCtrlPtsPtr() );
+            return ctrlPts [ index ].x() / ctrlPts [ index ].w();
+            break;
+    }
+    
+    return 0.0;
+}
+
+void snlCircularOffsetCurve::vertexNet ( snlVertexNet* vNet, double tolerance, bool parametric )
+{
+    // Return approximation to curve.
+    // --------------------------------
+    // tolerance:    Tolerance to approximate to.
+    // parametric:   Do a parametric analysis as opposed to knot refinement.
+    // vNet:         Vertex net to fill with data.
+    
+    if ( parametric )
+    {
+        vertexNetParam ( vNet, tolerance );
+        return;
+    }
+    
+    snlCtrlPointNetCurve*    ctrlPtNet;
+    int                      size;
+    
+    const snlCtrlPoint*      chordOffsetPts;
+    const snlCtrlPoint*      angleOffsetPts;
+    const snlCtrlPoint*      tangentOffsetPts;
+    
+    // Get control point net to work with.
+    
+    snlCircularOffsetCurve* tmpCurve = 0;
+    
+    if ( tolerance > 0.0 )
+    {
+        tmpCurve = new snlCircularOffsetCurve ( *this );
+        tmpCurve -> refine ( tolerance );
+        ctrlPtNet = new snlCtrlPointNetCurve ( ( tmpCurve -> base_curve ) -> controlPointNet() );
+        size = ctrlPtNet -> getNumPts();
+        
+        chordOffsetPts = ( tmpCurve -> chord_offsetCurve ) -> controlPointNet().getCtrlPts();
+        angleOffsetPts = ( tmpCurve -> angle_offsetCurve ) -> controlPointNet().getCtrlPts();
+        tangentOffsetPts = ( tmpCurve -> tangent_offsetCurve ) -> controlPointNet().getCtrlPts();
+    }
+    else
+    {    
+        ctrlPtNet = new snlCtrlPointNetCurve ( base_curve -> controlPointNet() );
+        size = ctrlPtNet -> getNumPts();
+        
+        chordOffsetPts = chord_offsetCurve -> controlPointNet().getCtrlPts();
+        angleOffsetPts = angle_offsetCurve -> controlPointNet().getCtrlPts();
+        tangentOffsetPts = tangent_offsetCurve -> controlPointNet().getCtrlPts();
+    }
+    
+    snlCtrlPoint* ctrlPts = ctrlPtNet -> getCtrlPtsPtr();
+    
+    // Offset base curve control points.
+    
+    for ( int index = 0; index < size; index ++ )
+        applyOffset ( ctrlPts [ index ], chordOffsetPts [ index ], angleOffsetPts [ index ], tangentOffsetPts [ index ] );
+    
+    // Generate vertex net.
+    
+    vNet -> vertexNet ( ctrlPts, size );
+    
+    delete ctrlPtNet;
+    
+    if ( tmpCurve ) delete tmpCurve;
+}
+
+void snlCircularOffsetCurve::vertexNetParam ( snlVertexNet* vNet, double tolerance )
+{
+    // Generate an approximation to curve using a parametric analysis.
+    // ---------------------------------------------------------------
+    // vNet:         Vertex network to fill with data.
+    // tolerance:    Maximum error to curve.
+    
+    int              size;    
+    
+    snlPoint* pts = 0;
+    
+    if ( tolerance <= 0.0 )
+    {
+        size = base_curve -> controlPointNet().size();
+        
+        pts = new snlPoint [ size ];
+        
+        double minParam = base_curve -> minParam();
+        
+        double paramStep = ( base_curve -> maxParam() - minParam ) / (double) ( size - 1 );
+        
+        for ( int index = 0; index < size; index ++ )
+        {
+            double param = minParam + paramStep * (double) index;            
+            pts [ index ] = base_curve -> eval ( param );
+            
+            applyOffset ( pts [ index ],
+                          chord_offsetCurve -> eval ( param ),
+                          angle_offsetCurve -> eval ( param ),
+                          tangent_offsetCurve -> eval ( param ) );
+        }
+    }
+    
+    vNet -> vertexNet ( pts, size );
+    
+    if ( pts ) delete[] pts;
+}
+
+int snlCircularOffsetCurve::size()
+{
+    // Return number of control points in curve.
+    // -----------------------------------------
+    
+    return base_curve -> size();
+}
+
+double snlCircularOffsetCurve::maxParam() const
+{
+    return base_curve -> maxParam();
+}
+
+double snlCircularOffsetCurve::minParam() const
+{
+    return base_curve -> minParam();
+}
+
+snlPoint snlCircularOffsetCurve::eval ( knot param ) const
+{
+    // Eval curve at param.
+    // --------------------
+    // param:    Paramter to evaluate at.
+    
+    snlPoint retPoint = base_curve -> eval ( param );
+    
+    applyOffset ( retPoint,
+                  chord_offsetCurve -> eval ( param ),
+                  angle_offsetCurve -> eval ( param ),
+                  tangent_offsetCurve -> eval ( param ) );     
+    
+    return retPoint;
+}
+
diff --git a/src/snlCircularOffsetCurve.h b/src/snlCircularOffsetCurve.h
new file mode 100644
index 0000000..a03577a
--- /dev/null
+++ b/src/snlCircularOffsetCurve.h
@@ -0,0 +1,79 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** NURBS Curve of Circular Offset from Base Curve ***
+
+#ifndef SNL_CIRCULAROFFSET_CURVE_H
+#define SNL_CIRCULAROFFSET_CURVE_H
+
+#include "snlCurve.h"
+#include "snlCurveBase.h"
+#include "snlPoint.h"
+
+class snlCircularOffsetCurve : public snlCurveBase
+{
+    public:
+    
+        snlCircularOffsetCurve();
+        virtual ~snlCircularOffsetCurve();
+        
+        snlCircularOffsetCurve ( snlCircularOffsetCurve& copyFrom );
+        
+        snlCircularOffsetCurve ( snlCurve* baseCurve, snlPoint* axisStart, snlPoint* axisEnd );
+        
+        void refine ( double tolerance );
+        
+        virtual void applyOffset ( snlPoint& point, snlPoint chordOffset, snlPoint angleOffset, snlPoint tangentOffset ) const;
+        
+        int numOffsets();        
+        void generateOffsets ( int type, double startOffset, double endOffset );
+        void offset ( int index, int type, double val, double weight = 1.0 );
+        double offset ( int index, int type );
+        
+        void vertexNetParam ( snlVertexNet* vNet, double tolerance );        
+        
+        int size();
+        
+        double maxParam() const;
+        double minParam() const;
+        
+        enum offsetType
+        {
+            CHORD,
+            ANGLE,
+            TANGENT
+        };
+        
+        // Abstract Implementation.
+        
+        virtual void vertexNet ( snlVertexNet* vNet, double tolerance, bool parametric );        
+        
+        virtual snlPoint eval ( knot param ) const;
+        
+    protected:
+    
+        snlCurve*    base_curve;
+        
+        snlCurve*    chord_offsetCurve;  // Offsets that correspond to control points.
+        snlCurve*    angle_offsetCurve;
+        snlCurve*    tangent_offsetCurve;
+        
+        snlPoint*    axis_start;
+        snlPoint*    axis_end;
+};
+
+#endif
diff --git a/src/snlCtrlPoint.cpp b/src/snlCtrlPoint.cpp
new file mode 100644
index 0000000..f9a389d
--- /dev/null
+++ b/src/snlCtrlPoint.cpp
@@ -0,0 +1,77 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "snlCtrlPoint.h"
+
+snlCtrlPoint::snlCtrlPoint()
+{
+    selected = false;
+}
+
+snlCtrlPoint::snlCtrlPoint ( snlPoint& pt )
+    : snlPoint ( pt )
+{
+    selected = false;
+}
+
+void snlCtrlPoint::operator = ( const snlPoint& copyFrom )
+{
+    // Copy data from another point.
+    // -----------------------------
+    
+    elements [ 0 ] = copyFrom.elements [ 0 ];
+    elements [ 1 ] = copyFrom.elements [ 1 ];
+    elements [ 2 ] = copyFrom.elements [ 2 ];
+    elements [ 3 ] = copyFrom.elements [ 3 ];
+    
+    selected = false;
+}
+
+void snlCtrlPoint::select ( bool yesNo )
+{
+    // Set selection state of control point.
+    // -------------------------------------
+
+    selected = yesNo;
+}
+
+bool snlCtrlPoint::isSelected()
+{
+    return selected;
+}
+
+void snlCtrlPoint::weight ( double setTo )
+{
+    // Set control points weight.
+    // --------------------------
+    
+    if ( elements [ 3 ] == 0.0 )
+        elements [ 3 ] = setTo;
+    else
+    {    
+        double multFactor = setTo / elements [ 3 ];
+    
+        for ( int index = 0; index < 4; index ++ )
+            elements [ index ] *= multFactor;
+    }
+}
+
+double snlCtrlPoint::weight()
+{
+    return elements [ 3 ];
+}
+
diff --git a/src/snlCtrlPoint.h b/src/snlCtrlPoint.h
new file mode 100644
index 0000000..929027b
--- /dev/null
+++ b/src/snlCtrlPoint.h
@@ -0,0 +1,44 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef SNLCTRLPOINT_H
+#define SNLCTRLPOINT_H
+
+#include "snlPoint.h"
+
+class snlCtrlPoint : public snlPoint
+{
+    public:
+
+        snlCtrlPoint();        
+        
+        snlCtrlPoint ( snlPoint& );
+        
+        void operator = ( const snlPoint& copyFrom );
+
+        void select ( bool yesNo );
+        bool isSelected();
+        
+        void weight ( double setTo );
+        double weight();
+
+    private:
+
+        bool    selected;  // Point has been selected.
+};
+
+#endif
diff --git a/src/snlCtrlPointNet.cpp b/src/snlCtrlPointNet.cpp
new file mode 100644
index 0000000..424c752
--- /dev/null
+++ b/src/snlCtrlPointNet.cpp
@@ -0,0 +1,656 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** Control Point Network - Base Class ***
+
+#include "snlCtrlPointNet.h"
+#include "snlUtil.h"
+
+#ifdef SGI_MIPS
+
+    #include <math.h>
+    
+#else
+
+    #include <cmath>
+    using namespace std;
+    
+#endif
+
+
+snlCtrlPointNet::snlCtrlPointNet()
+{
+    ctrlPts = 0;
+    ctrlPtArraySize = 0;
+}
+
+snlCtrlPointNet::~snlCtrlPointNet()
+{
+    if ( ctrlPts ) delete[] ctrlPts;
+}
+
+snlCtrlPointNet::snlCtrlPointNet ( const snlCtrlPointNet& copyFrom )
+{
+    // Copy constructor.
+    // -----------------
+    
+    ctrlPtArraySize = copyFrom.ctrlPtArraySize;
+    
+    ctrlPts = new snlCtrlPoint [ ctrlPtArraySize ];
+    
+    for ( unsigned index = 0; index < ctrlPtArraySize; index ++ )
+        ctrlPts [ index ] = copyFrom.ctrlPts [ index ];
+}
+
+bool snlCtrlPointNet::checkBounds ( unsigned index )
+{
+    // Check index against array bounds.
+    // ---------------------------------
+
+    if ( index >= ctrlPtArraySize ) return false;
+
+    return true;
+}
+
+void snlCtrlPointNet::transform ( unsigned ptIndex, snlTransform& transf )
+{
+    // Transform a control point.
+    // --------------------------
+
+    if ( checkBounds ( ptIndex ) )
+        transf.transform ( ctrlPts + ptIndex );
+}
+
+void snlCtrlPointNet::transform ( snlTransform& transf )
+{
+    // Transform all control points.
+    // -----------------------------
+
+    for ( unsigned index = 0; index < ctrlPtArraySize; index ++ )        
+            transf.transform ( ctrlPts + index );    
+}
+
+void snlCtrlPointNet::transformSelected ( snlTransform& transf )
+{
+    // Transform all selected control points.
+    // --------------------------------------
+
+    for ( unsigned index = 0; index < ctrlPtArraySize; index ++ )
+    {
+        if ( ctrlPts [ index ].isSelected() )
+            transf.transform ( ctrlPts + index );
+    }
+}
+
+unsigned snlCtrlPointNet::getNumPts() const
+{
+    // Get number of control points that object holds.
+    // -----------------------------------------------
+
+    return ctrlPtArraySize;
+}
+
+const snlCtrlPoint* snlCtrlPointNet::getCtrlPts() const
+{
+    // Get pointer to array of control points.
+    // ---------------------------------------
+
+    return ctrlPts;
+}
+
+snlCtrlPoint* snlCtrlPointNet::getCtrlPtsPtr()
+{
+    // Return non-constant pointer to control points array.
+    // ----------------------------------------------------
+    
+    return ctrlPts;
+}
+
+snlCtrlPoint snlCtrlPointNet::getPoint ( unsigned ptIndex )
+{
+    // Return copy of control point.
+    // -----------------------------
+
+    if ( !checkBounds ( ptIndex ) ) return snlCtrlPoint();
+
+    return ctrlPts [ ptIndex ];
+}
+
+const snlCtrlPoint* snlCtrlPointNet::getPointPtr ( unsigned ptIndex )
+{
+    // Return pointer to control point.
+    // --------------------------------
+
+    if ( !checkBounds ( ptIndex ) ) return 0;
+
+    return ctrlPts + ptIndex;
+}
+
+double snlCtrlPointNet::getTransfZ ( unsigned index, snlTransform& trans )
+{
+    // Get transformed Z coordinate
+    // ----------------------------
+    // index:       Array index of control point to process.
+    // transMatrix: Transformation object.
+    //
+    // Note:        Does not modify control points.
+
+    snlCtrlPoint      point;
+
+    if ( index >= ctrlPtArraySize ) return 0.0;
+
+    point = ctrlPts [ index ];
+
+    trans.transform ( &point );
+
+    return point.z();
+}
+
+double snlCtrlPointNet::getMaxTransfZ ( snlTransform& trans )
+{
+    // Return maximum value of z element of control points after transformation.
+    // -------------------------------------------------------------------------
+
+    unsigned        index;
+    double          maxZ, transfZ;
+
+    for ( index = 0; index < ctrlPtArraySize; index ++ )
+    {
+        transfZ = getTransfZ ( index, trans );
+
+        if ( ! index || transfZ > maxZ )
+            maxZ = transfZ;
+    }
+
+    return maxZ;
+}
+
+double snlCtrlPointNet::getMinTransfZ ( snlTransform& trans )
+{
+    // Return minimum value of z element of control points after transformation.
+    // -------------------------------------------------------------------------
+
+    unsigned        index;
+    double          minZ, transfZ;
+
+    for ( index = 0; index < ctrlPtArraySize; index ++ )
+    {
+        transfZ = getTransfZ ( index, trans );
+
+        if ( ! index || transfZ < minZ )
+            minZ = transfZ;
+    }
+
+    return minZ;
+}
+
+bool snlCtrlPointNet::hasPointsSelected()
+{
+    for ( unsigned index = 0; index < ctrlPtArraySize; index ++ )
+    {
+        if ( ctrlPts [ index ].isSelected() ) return true;
+    }
+
+    return false;
+}
+
+unsigned snlCtrlPointNet::numPointsSelected()
+{
+    // Return number of control points that are currently selected.
+    // ------------------------------------------------------------
+
+    unsigned numSelect = 0;
+
+    for ( unsigned index = 0; index < ctrlPtArraySize; index ++ )
+    {
+        if ( ctrlPts [ index ].isSelected() ) numSelect ++;
+    }
+
+    return numSelect;
+}
+
+void snlCtrlPointNet::selectAllPoints ( bool yesNo )
+{
+    for ( unsigned index = 0; index < ctrlPtArraySize; index ++ )
+        ctrlPts [ index ].select ( yesNo );
+}
+
+void snlCtrlPointNet::selectPoint ( unsigned index, bool yesNo )
+{
+    if ( !checkBounds ( index ) ) return;
+
+    ctrlPts [ index ].select ( yesNo );
+}
+
+bool snlCtrlPointNet::isSelected ( unsigned index )
+{
+    if ( !checkBounds ( index ) ) return false;
+
+    return ( ctrlPts [ index ].isSelected() );
+}
+
+void snlCtrlPointNet::clearSelected()
+{
+    for ( unsigned index = 0; index < ctrlPtArraySize; index ++ )
+    {
+        ctrlPts [ index ].select ( false );
+    }
+}
+
+unsigned* snlCtrlPointNet::getSelectedIndexes()
+{
+    // Return array of indexes that corresponds to selected control points.
+    // --------------------------------------------------------------------
+    //
+    // NOTES:       First array element holds array size.
+
+    unsigned numSelect = numPointsSelected();
+
+    unsigned* retArray  = new unsigned [ numSelect + 1 ];
+
+    retArray [ 0 ] = numSelect;
+
+    unsigned cArrayPos = 1;
+
+    for ( unsigned index = 0; index < ctrlPtArraySize; index ++ )
+    {
+        if ( ctrlPts [ index ].isSelected() ) retArray [ cArrayPos ++ ] = index ;
+    }
+
+    return retArray;
+}
+
+double snlCtrlPointNet::calcFlatness ( snlPoint** points, unsigned size )
+{
+    // Calculate flatness.
+    // -------------------
+    // points:      Array of pointers to control points to evaluate.
+    // size:        Size of the array.
+    //
+    // returns: Largest of the flatness tests.
+
+    double      dotP, proj, testLength, normDist;
+    double      flatness = 0;
+
+    if ( size < 3 ) return 0;
+    
+    snlPoint pt1 ( *( points [ 0 ] ) );
+    snlPoint pt2 ( *( points [ size -1 ] ) );
+    
+    pt1.normalise();
+    pt2.normalise();
+    
+    snlVector lineV ( pt1, pt2 );
+
+    // If lineV is zero length then use distance to pt1 for flatness.
+
+    bool usePoint = false;
+
+    if ( lineV.length() == 0.0 )
+        usePoint = true;
+
+    for ( unsigned index = 1; index < ( size - 1 ); index ++ )
+    {
+        pt2 = * ( points [ index ] );
+        
+        pt2.normalise();
+
+        if ( usePoint )
+        {
+            normDist = sqrt ( pt1.distSqrd ( pt2 ) );
+        }
+        else
+        {
+            snlVector testV ( pt1, pt2 );
+
+            // Calculate dot product.
+            dotP = lineV.dot ( testV );
+    
+            // Project test vector onto baseline vector.
+            proj = dotP / lineV.length();
+    
+            // Length of test vector.
+            testLength = testV.length();
+    
+            // Length of normal from baseline to test point.
+            normDist = sqrt ( testLength * testLength - proj * proj );
+        }
+
+        // Update flatness
+        if ( normDist > flatness ) flatness = normDist;
+    }
+
+    return flatness;
+}
+
+double snlCtrlPointNet::calcDeg1Flatness ( snlPoint** points ) const
+{
+    // Specifically for caclulating degree 1 one flatness calculations
+    // ---------------------------------------------------------------
+    // Points:      Array of pointers to control points to evaluate. Size of array is 4.
+
+    snlPoint    vect;
+    snlPoint    nPoints [ 4 ];  // Normalised points.
+
+    double       flatness;
+
+    for ( int index = 0; index < 4; index ++ )
+    {
+        nPoints [ index ] = *( points [ index ] );
+        nPoints [ index ].normalise();
+    }
+
+    // Approximate distance between lines that span opposite corners.
+
+    vect.x ( nPoints [ 0 ].x() + ( ( nPoints [ 3 ].x() - nPoints [ 0 ].x() ) * 0.5 ) );
+    vect.y ( nPoints [ 0 ].y() + ( ( nPoints [ 3 ].y() - nPoints [ 0 ].y() ) * 0.5 ) );
+    vect.z ( nPoints [ 0 ].z() + ( ( nPoints [ 3 ].z() - nPoints [ 0 ].z() ) * 0.5 ) );
+
+    vect.x ( vect.x() - nPoints [ 1 ].x() + ( ( nPoints [ 2 ].x() - nPoints [ 1 ].x() ) * 0.5 ) );
+    vect.y ( vect.y() - nPoints [ 1 ].y() + ( ( nPoints [ 2 ].y() - nPoints [ 1 ].y() ) * 0.5 ) );
+    vect.z ( vect.z() - nPoints [ 1 ].z() + ( ( nPoints [ 2 ].z() - nPoints [ 1 ].z() ) * 0.5 ) );
+
+    flatness = sqrt ( vect.x() * vect.x() + vect.y() * vect.y() + vect.z() * vect.z() );
+
+    return flatness;
+}
+
+double snlCtrlPointNet::calcCurvature ( snlPoint** points )
+{
+    // Calculate Curvature.
+    // --------------------
+    // points:      Array of 3 pointers to control points to evaluate.     
+    //
+    // returns:     Curvature as angle between vectors.
+    
+    snlPoint pt1 ( *( points [ 0 ] ) );
+    snlPoint pt2 ( *( points [ 1 ] ) );
+    snlPoint pt3 ( *( points [ 2 ] ) );
+    
+    pt1.normalise();
+    pt2.normalise();
+    pt3.normalise();
+    
+    snlVector vect1 ( pt1, pt2 );
+    snlVector vect2 ( pt2, pt3 );
+    
+    if ( vect1.isNull() || vect2.isNull() ) return 0.0;    
+
+    return vect1.angle ( vect2 );
+}
+
+bool snlCtrlPointNet::isConvex ( snlPoint** points, int numPts, double sensitivity )
+{
+    // Test for the given points being in a convex pattern.
+    // ----------------------------------------------------
+    // points:      Points to test.
+    // numPts:      Number of points to test.
+    // sensitivity: Maximum concave angle allowed to be considered convex. Used to account for noise
+    //              in the curvature of a relatively flat section.
+    //
+    // Notes:       This function is primarily used for testing of Bezier patches
+    //              to determine if they are convex.
+
+    int midPoint = numPts / 2;
+
+    double angleAdjust = 1.0e-15 + cos ( sensitivity );
+
+    // Check first set of points against end point.
+
+    for ( int index = 1; index < midPoint; index ++ )
+    {
+        // Check for flatness of points being checked. If the three points are colinear
+        // then they are considered convex. This is done to account for round off error noise
+        // that is causing this function to go into an infinite loop.
+
+        snlVector chord ( **( points + index - 1 ), **( points + index + 1 ) );
+        snlVector compare ( **( points + index - 1 ), **( points + index ) );
+
+        double angle = chord.calcAbsCos ( compare ) + angleAdjust;
+
+        if ( angle < 1.0 )
+        {
+            // Project end point onto chord.
+
+            snlVector endProject = projectToLine ( **( points + index - 1 ), **( points + index + 1 ),
+                                               **( points + numPts - 1 ) );
+
+            // Project point to do convex test with, onto chord.
+
+            snlVector testProject = projectToLine ( **( points + index - 1 ), **( points + index + 1 ),
+                                                **( points + index ) );
+
+            // Do convex test.
+
+            if ( ! testProject.isNull() )
+            {
+                double dotP = testProject.dot ( endProject );
+                if ( dotP > 0 ) return false;
+            }
+        }
+    }
+
+    // Check last set of points against start point.
+
+    for ( int index = midPoint; index < numPts - 1; index ++ )
+    {
+        snlVector chord ( **( points + index - 1 ), **( points + index + 1 ) );
+        snlVector compare ( **( points + index - 1 ), **( points + index ) );
+
+        double angle = chord.calcAbsCos ( compare ) +  + 1.0e-15;
+
+        if ( angle < 1.0 )
+        {
+            // Project end point onto chord.
+    
+            snlVector endProject = projectToLine ( **( points + index - 1 ), **( points + index + 1 ),
+                                                **points );
+    
+            // Project point to do convex test with, onto chord.
+    
+            snlVector testProject = projectToLine ( **( points + index - 1 ), **( points + index + 1 ),
+                                                    **( points + index ) );
+    
+            // Do convex test.
+    
+            if ( ! testProject.isNull() )
+            {
+                double dotP = testProject.dot ( endProject );
+                if ( dotP > 0 ) return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+void snlCtrlPointNet::replacePoints ( snlCtrlPoint* newPoints )
+{
+    // Replace all control points with new ones.
+    // -----------------------------------------
+    // newPoints:   New points to use. This object owns them.
+    //
+    // Notes:       The caller is trusted to provide the correct size of control point array.
+ 
+    if ( ctrlPts ) delete[] ctrlPts;
+    
+    ctrlPts = newPoints;
+}
+
+void snlCtrlPointNet::replacePoints ( const snlCtrlPoint* newPoints, unsigned numNewPoints, unsigned replaceIndex,
+                                      unsigned numToReplace )
+{
+    // Replace a subset of control points with new ones.
+    // -------------------------------------------------
+    // newPoints:    New points to place into array.
+    // numNewPoint:  Number of new points to use.
+    // replaceIndex: Starting index in array where replacement should begin.
+    // numToReplace: Number of points to replace. Can be different from numNewPoints.    
+    //
+    // Notes:    Does not shrink or grow control point array allocation. That must be done seperately
+    //           at a higher level or memory errors _will_ occur.
+    
+    
+    // Account for the number of new points being more than the number being replaced.
+        
+    if ( numNewPoints > numToReplace )
+    {
+        unsigned diff = numNewPoints - numToReplace;
+        
+        for ( unsigned index = ctrlPtArraySize - 1; index >= replaceIndex + numNewPoints; index -- )
+            ctrlPts [ index ] = ctrlPts [ index - diff ];
+    }    
+    
+    // Copy new points into array.
+    
+    for ( unsigned index = 0; index < numNewPoints; index ++ )
+        ctrlPts [ replaceIndex + index ] = newPoints [ index ];
+    
+    // Account for the number of new points being less than the number being replaced.
+    
+    if ( numNewPoints < numToReplace )
+    {
+        unsigned diff = numToReplace - numNewPoints;
+        
+        for ( unsigned index = replaceIndex + numNewPoints; index < ctrlPtArraySize - diff; index ++ )
+            ctrlPts [ index ] = ctrlPts [ index + diff ];
+    }    
+}
+
+void snlCtrlPointNet::appendPointSpace ( unsigned numPoints )
+{
+    // Add extra space to end of control point array.
+    // ----------------------------------------------
+
+    if ( numPoints <= 0 ) return;
+    
+    snlCtrlPoint* newPts = new snlCtrlPoint [ ctrlPtArraySize + numPoints ];
+    
+    for ( unsigned index = 0; index < ctrlPtArraySize; index ++ )
+        newPts [ index ] = ctrlPts [ index ];        
+
+    ctrlPtArraySize += numPoints;
+
+    delete[] ctrlPts;
+    
+    ctrlPts = newPts;
+}
+
+void snlCtrlPointNet::truncatePointSpace ( unsigned numPoints )
+{
+    // Remove space from end of array.
+    // -------------------------------
+    
+    snlCtrlPoint* newPts = new snlCtrlPoint [ ctrlPtArraySize - numPoints ];
+    
+    for ( unsigned index = 0; index < ( ctrlPtArraySize - numPoints ); index ++ )
+        newPts [ index ] = ctrlPts [ index ];        
+
+    ctrlPtArraySize -= numPoints;
+
+    delete[] ctrlPts;
+    
+    ctrlPts = newPts;    
+}
+
+void snlCtrlPointNet::appendPoints ( const snlCtrlPoint* points, unsigned numPoints )
+{
+    // Append control points to this control point net.
+    // ------------------------------------------------
+    // points:    Points to append.
+    // numPoints: Number of points to append.
+    
+    unsigned oldSize = ctrlPtArraySize;
+    
+    appendPointSpace ( numPoints );
+    
+    unsigned pointsIndex = 0;
+    
+    for ( unsigned index = oldSize; index < ctrlPtArraySize; index ++ )
+        ctrlPts [ index ] = points [ pointsIndex ++ ];    
+}
+
+void snlCtrlPointNet::print()
+{
+    // Print all points.
+    // -----------------
+    
+    for ( unsigned index = 0; index < ctrlPtArraySize; index ++ )
+    {
+        cout << "[ " << index << " ]";
+        ctrlPts [ index ].print();
+        cout << "\n";
+    }
+}
+
+void snlCtrlPointNet::print ( unsigned fromIndex, unsigned toIndex )
+{
+    // Print control point information.
+    // --------------------------------
+    // fromIndex:        Starting control point index.
+    // toIndex:          Ending control point index.
+    
+    for ( unsigned index = fromIndex; index <= toIndex; index ++ )
+    {
+        cout << "[ " << index << " ]";
+        ctrlPts [ index ].print();
+        cout << "\n";
+    }
+}
+
+bool snlCtrlPointNet::hasConcurrentPoints() const
+{
+    // Return true if any concurrent points exist in net.
+    // --------------------------------------------------
+    
+    bool concurrent = false;
+    
+    for ( unsigned index = 0; index < ctrlPtArraySize; index ++ )
+    {
+        for ( unsigned index2 = 0; index2 < ctrlPtArraySize; index2 ++ )
+        {
+            if ( index != index2 )
+                if ( ctrlPts [ index ] == ctrlPts [ index2 ] )
+                    concurrent = true;
+        }
+    }
+    
+    return concurrent;
+}
+
+void snlCtrlPointNet::operator += ( const snlCtrlPointNet& ctrlPointNet )
+{
+    // Point wise addition of control point nets.
+    // ------------------------------------------
+
+    unsigned numElements = ctrlPtArraySize > ctrlPointNet.ctrlPtArraySize ? ctrlPtArraySize : ctrlPointNet.ctrlPtArraySize;
+
+    for ( unsigned index = 0; index < numElements; index ++ )
+        ctrlPts [ index ] += ctrlPointNet.ctrlPts [ index ];
+}
+
+void snlCtrlPointNet::operator -= ( const snlCtrlPointNet& ctrlPointNet )
+{
+    // Point wise subtraction of control point nets.
+    // ---------------------------------------------
+
+    unsigned numElements = ctrlPtArraySize > ctrlPointNet.ctrlPtArraySize ? ctrlPtArraySize : ctrlPointNet.ctrlPtArraySize;
+
+    for ( unsigned index = 0; index < numElements; index ++ )
+        ctrlPts [ index ] -= ctrlPointNet.ctrlPts [ index ];
+}
+
diff --git a/src/snlCtrlPointNet.h b/src/snlCtrlPointNet.h
new file mode 100644
index 0000000..8927d0d
--- /dev/null
+++ b/src/snlCtrlPointNet.h
@@ -0,0 +1,135 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** Control Point Network - Base Class ***
+
+#ifndef SNLCTRLPOINTNET_H
+#define SNLCTRLPOINTNET_H
+
+#include "snlCtrlPoint.h"
+#include "snlTransform.h"
+
+#ifdef SGI_MIPS
+
+    #include <iostream.h>
+    #include <math.h>
+    
+#else
+
+    #include <iostream>
+    #include <cmath>
+    
+    using namespace std;
+    
+#endif
+
+class snlCtrlPointNet
+{
+    // Abstract base class of all control point objects.
+
+    public:
+
+        snlCtrlPointNet();
+        virtual ~snlCtrlPointNet();
+        
+        snlCtrlPointNet ( const snlCtrlPointNet& );  // Copy constructor.
+    
+        virtual void transform ( unsigned ptIndex, snlTransform& transform );  // Transform a control point.
+        
+        virtual void transform ( snlTransform& transform );  // Transform all points.
+
+        virtual void transformSelected ( snlTransform& transform );
+
+        // Get number of control points that object holds.
+        virtual unsigned getNumPts() const;
+
+        // Get pointer to array of control points.
+        virtual const snlCtrlPoint* getCtrlPts() const;
+        
+        // Get pointer to array of control points. Non constant return.
+        virtual snlCtrlPoint* getCtrlPtsPtr();
+
+        // Return copy of control point.
+        snlCtrlPoint getPoint ( unsigned index );
+
+        // Return pointer to control point.
+        virtual const snlCtrlPoint* getPointPtr ( unsigned index );
+
+        // Get transformed Z. Does not modify control points.
+        virtual double getTransfZ ( unsigned index, snlTransform& );
+
+        virtual double getMaxTransfZ ( snlTransform& );
+
+        virtual double getMinTransfZ ( snlTransform& );
+
+        virtual bool hasPointsSelected();
+
+        virtual unsigned numPointsSelected();
+
+        virtual unsigned* getSelectedIndexes();
+        
+        virtual void selectAllPoints ( bool yesNo = true ); 
+
+        void selectPoint ( unsigned index, bool yesNo = true );
+
+        virtual bool isSelected ( unsigned index );
+
+        virtual void clearSelected();
+        
+        static double calcFlatness ( snlPoint** points, unsigned size );
+        double calcDeg1Flatness ( snlPoint** points ) const;
+        
+        double calcCurvature ( snlPoint** points );
+
+        bool isConvex ( snlPoint** points, int numPts, double sensitivity = 0.0 );
+        
+        virtual void replacePoints ( snlCtrlPoint* newPoints );  // Replace control points with new ones.
+        virtual void replacePoints ( const snlCtrlPoint* newPoints, unsigned numNewPoints, unsigned replaceIndex,
+                                     unsigned numToReplace );
+        
+        virtual void appendPointSpace ( unsigned numPoints );  // Add space to end of array.
+        virtual void truncatePointSpace ( unsigned numPoints );  // Remove space from end of array.
+        
+        virtual void appendPoints ( const snlCtrlPoint* points, unsigned numPoints );
+        
+        void print();
+        void print ( unsigned fromIndex, unsigned toIndex );
+        
+        bool hasConcurrentPoints() const;
+
+        // Operators
+
+        void operator += ( const snlCtrlPointNet& ctrlPointNet );
+        void operator -= ( const snlCtrlPointNet& ctrlPointNet );
+
+        // *** Abstract Interface ***
+
+        // Return list of connected control points.
+        virtual int getCnctPts ( unsigned index, snlCtrlPoint* retPts ) = 0;
+
+        // Return maximum number of connections a single control point can have.
+        virtual int maxConnections() const = 0;        
+
+    protected:
+
+        virtual bool checkBounds ( unsigned index );  // Check index against array bounds.
+        
+        snlCtrlPoint*       ctrlPts;
+        unsigned            ctrlPtArraySize;
+};
+
+#endif
diff --git a/src/snlCtrlPointNetCurve.cpp b/src/snlCtrlPointNetCurve.cpp
new file mode 100644
index 0000000..932eab5
--- /dev/null
+++ b/src/snlCtrlPointNetCurve.cpp
@@ -0,0 +1,238 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "snlCtrlPointNetCurve.h"
+
+snlCtrlPointNetCurve::snlCtrlPointNetCurve ( snlCtrlPoint* cPtArray, unsigned size, bool copy )
+{
+    // Control Points for a curve - Constructor
+    // ----------------------------------------
+    // cPtArray:    Array of points to copy.
+    // size_u:      Size of array to copy.
+    // copy:        Make a copy of cPtArray.
+
+    ctrlPtArraySize = size;
+    
+    if ( copy )
+    {
+        // Copy points into object.        
+        ctrlPts = new snlCtrlPoint [ ctrlPtArraySize ];
+        
+        for ( unsigned count = 0; count < ctrlPtArraySize; count ++ )
+            ctrlPts [ count ] = cPtArray [ count ];
+    }
+    else
+        ctrlPts = cPtArray;
+}
+        
+snlCtrlPointNetCurve::snlCtrlPointNetCurve ( unsigned size, snlPoint& start, snlPoint& end )
+{
+    // Construct a control point network.
+    // ----------------------------------
+    // size:    Number of control points.
+    // start:   Starting point of curve.
+    // end:     Ending point of curve.
+    
+    if ( ! size )
+    {
+        ctrlPts = 0;
+        return;
+    }
+    
+    ctrlPtArraySize = size;
+
+    ctrlPts = new snlCtrlPoint [ ctrlPtArraySize ];
+
+    snlVector lineVect ( start, end );    
+
+    lineVect *= ( 1.0 / ( size - 1 ) );
+    
+    for ( unsigned index = 0; index < size; index ++ )
+    {
+        snlPoint newPoint = start + ( lineVect * ( (double) index ) );
+        ctrlPts [ index ] = newPoint;  // Conversion to snlCtrlPoint
+    }
+}
+
+snlCtrlPointNetCurve::~snlCtrlPointNetCurve()
+{
+}
+
+unsigned snlCtrlPointNetCurve::size() const
+{
+    return ctrlPtArraySize;
+}
+
+snlCtrlPoint* snlCtrlPointNetCurve::grow()
+{
+    // Increase the control point net's size.
+    // --------------------------------------
+
+    if ( ! ctrlPts ) return 0;    
+
+    snlCtrlPoint* newPts = new snlCtrlPoint [ ctrlPtArraySize + 1 ];
+
+    // Copy points into new array.
+    for ( unsigned index = 0; index < ctrlPtArraySize; index ++ )
+        newPts [ index ] = ctrlPts [ index ];
+
+    // Delete old array and point to new one.
+    delete[] ctrlPts;
+
+    ctrlPts = newPts;
+    
+    ctrlPtArraySize ++;
+
+    return ctrlPts;
+}
+
+snlCtrlPoint* snlCtrlPointNetCurve::shrink()
+{
+    // Decrease the control point net's size.
+    // --------------------------------------
+    
+    if ( ! ctrlPts ) return 0;
+
+    snlCtrlPoint* newPts = new snlCtrlPoint [ ctrlPtArraySize - 1 ];
+
+    // Copy points into new array.
+    for ( unsigned index = 0; index < ( ctrlPtArraySize - 1 ); index ++ )
+        newPts [ index ] = ctrlPts [ index ];
+
+    // Delete old array and point to new one.
+
+    delete[] ctrlPts;
+
+    ctrlPts = newPts;    
+
+    ctrlPtArraySize --;
+
+    return ctrlPts;    
+}
+
+double snlCtrlPointNetCurve::calcFlatness ( int index, int numPoints ) const
+{
+    // Calculate flatness of a series of points.
+    // -----------------------------------------
+    // index:        Index to get points from.
+    // numPoints:    Number of points to evaluate.
+    
+    if ( (unsigned) ( index + numPoints ) > ctrlPtArraySize ) return 0;    
+    
+    snlPoint** testPoints  = new snlPoint* [ numPoints ];
+    
+    for ( int count = 0; count < numPoints; count ++ )
+    {
+        testPoints [ count ] = ctrlPts + index + count;
+    }
+    
+    double flatness = snlCtrlPointNet::calcFlatness ( testPoints, numPoints );
+    
+    delete[] testPoints;
+    
+    return flatness;
+}
+
+void snlCtrlPointNetCurve::truncate ( int atIndex, bool keepLast )
+{
+    // Truncate control point array.
+    // -----------------------------
+    // index:    Array index to truncate from.
+    // keepLast: Keep last part of array and truncate first part.
+    
+    snlCtrlPoint* newCtrlPts;
+    
+    unsigned newSize;
+    
+    if ( keepLast )
+    {
+        newSize = ctrlPtArraySize - atIndex;
+        
+        newCtrlPts = new snlCtrlPoint [ newSize ];
+        
+        for ( unsigned index = 0; index < newSize; index ++ )
+            newCtrlPts [ index ] = ctrlPts [ atIndex + index ];
+    }
+    else
+    {
+        newSize = atIndex + 1;
+        
+        newCtrlPts = new snlCtrlPoint [ newSize ];
+        
+        for ( unsigned index = 0; index < newSize; index ++ )
+            newCtrlPts [ index ] = ctrlPts [ index ];
+    }
+    
+    ctrlPtArraySize = newSize;
+    
+    delete[] ctrlPts;
+    
+    ctrlPts = newCtrlPts;
+}
+
+void snlCtrlPointNetCurve::reverse()
+{
+    // Reverse order of control point net array.
+    // -----------------------------------------
+    
+    unsigned midPoint = ctrlPtArraySize / 2;
+
+    unsigned swapIndex = ctrlPtArraySize - 1;
+
+    snlCtrlPoint trans;
+
+    for ( unsigned index = 0; index < midPoint; index ++ )
+    {
+        trans = ctrlPts [ index ];  // Transfer value.
+        ctrlPts [ index ] = ctrlPts [ swapIndex ];
+        ctrlPts [ swapIndex -- ] = trans;
+    }
+}
+
+int snlCtrlPointNetCurve::getCnctPts ( unsigned index, snlCtrlPoint* retPts )
+{
+    // Return connected control points.
+    // --------------------------------
+    // index:    Index of point to find connections of.
+    // retPts:   Array to return points in.
+    //
+    // Returns:  Number of connected points found.
+    
+    if ( index >= ctrlPtArraySize ) return 0;    
+    
+    int retIndex = 0;
+    
+    if ( index > 0 )
+    {
+        retPts [ retIndex ] = ctrlPts [ index - 1 ];
+        retIndex ++;
+    }    
+    
+    if ( index < ( ctrlPtArraySize - 1 ) )
+    {
+        retPts [ retIndex ] = ctrlPts [ index + 1 ];
+        retIndex ++;
+    }
+    
+    return retIndex;
+}
+
+int snlCtrlPointNetCurve::maxConnections() const
+{
+    return 2;
+}
+
diff --git a/src/snlCtrlPointNetCurve.h b/src/snlCtrlPointNetCurve.h
new file mode 100644
index 0000000..70f42e5
--- /dev/null
+++ b/src/snlCtrlPointNetCurve.h
@@ -0,0 +1,57 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef SNL_CTRLPTNETCURVE_H
+#define SNL_CTRLPTNETCURVE_H
+
+#include "snlCtrlPointNet.h"
+
+class snlCtrlPointNetCurve : public snlCtrlPointNet
+{
+    public:
+
+        snlCtrlPointNetCurve ( snlCtrlPoint* cPtArray, unsigned size, bool copy = false );
+        
+        snlCtrlPointNetCurve ( unsigned size, snlPoint& start, snlPoint& end );
+
+        virtual ~snlCtrlPointNetCurve();
+
+        unsigned size() const;        
+
+        // Increase the control point net's size.
+        snlCtrlPoint* grow();
+        
+        // Decrease the control point net's size.
+        snlCtrlPoint* shrink();        
+        
+        double calcFlatness ( int index, int numPoints ) const;
+        
+        void truncate ( int atIndex, bool keepLast );
+
+        void reverse();
+
+        // snlCtrlPointNet Abstract implementation.
+
+        virtual int getCnctPts ( unsigned index, snlCtrlPoint* retPts );
+
+        virtual int maxConnections() const;
+        
+    private:
+        
+};
+
+#endif
diff --git a/src/snlCtrlPointNetSurface.cpp b/src/snlCtrlPointNetSurface.cpp
new file mode 100644
index 0000000..01ab588
--- /dev/null
+++ b/src/snlCtrlPointNetSurface.cpp
@@ -0,0 +1,835 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "snlCtrlPointNetSurface.h"
+#include "snlUtil.h"
+
+snlCtrlPointNetSurface::snlCtrlPointNetSurface ( snlCtrlPoint* cPtArray, unsigned size_u, unsigned size_v, bool copy )
+{
+    // Control Points for a surface - Constructor
+    // ------------------------------------------
+    // cPtArray:    2-D Array of points to copy. Format [u][v]
+    // size_u:      Size in u parametric direction.
+    // size_v:      Size in v parametric direction.
+    // copy:        Make a copy of cPtArray.
+
+    sizeU = size_u;
+    sizeV = size_v;
+
+    ctrlPtArraySize = sizeU * sizeV;
+    
+    if ( copy )
+    {
+        // Copy points into object.        
+        ctrlPts = new snlCtrlPoint [ ctrlPtArraySize ];
+        
+        for ( unsigned count = 0; count < ctrlPtArraySize; count ++ )
+            ctrlPts [ count ] = cPtArray [ count ];
+    }
+    else
+        ctrlPts = cPtArray;
+}
+
+snlCtrlPointNetSurface::snlCtrlPointNetSurface ( unsigned size_u, unsigned size_v, snlPoint& origin,
+                                                 snlPoint& cornerX, snlPoint& cornerY )
+{
+    // Constructor.
+    // ------------
+    // size_u:      Number of points in U direction.
+    // size_v:      Number of points in V direction.
+    // origin:      First point in array
+    // cornerX:     Corner at ( x, 0 ).
+    // cornerY:     Corner at ( 0, y ).
+    //
+    // Notes:       Usage of X,Y coordinates is deprecated.
+
+    sizeU = size_u;
+    sizeV = size_v;
+
+    if ( ! sizeU || ! sizeV ) return;
+    
+    ctrlPtArraySize = size_u * size_v;
+
+    ctrlPts = new snlCtrlPoint [ ctrlPtArraySize ];
+
+    snlVector O_X ( origin, cornerX );
+    snlVector O_Y ( origin, cornerY );
+
+    snlVector stepX = O_X * ( 1.0 / (double) sizeU );
+    snlVector stepY = O_Y * ( 1.0 / (double) sizeV );
+
+    snlPoint currentX = origin;
+
+    unsigned cIndex = 0;
+
+    for ( int index_X = 0; index_X < sizeU; index_X ++ )
+    {
+        snlPoint currentY = currentX;
+
+        for ( int index_Y = 0; index_Y < sizeV; index_Y ++ )
+        {
+            // Add point to control points array.
+            ctrlPts [ cIndex ++ ] = currentY;
+
+            currentY = currentY + stepY;
+        }
+
+        currentX = currentX + stepX;
+    }
+}
+
+snlCtrlPointNetSurface::~snlCtrlPointNetSurface()
+{
+}
+
+unsigned snlCtrlPointNetSurface::getSizeU() const
+{
+    return sizeU;
+}
+
+unsigned snlCtrlPointNetSurface::getSizeV() const
+{
+    return sizeV;
+}
+
+void snlCtrlPointNetSurface::setSizeU ( unsigned size )
+{
+    // Set sizeU to size.
+    // ------------------
+    
+    sizeU = size;
+}
+
+void snlCtrlPointNetSurface::setSizeV ( unsigned size )
+{
+    // Set sizeV to size.
+    // ------------------
+    
+    sizeV = size;
+}
+
+snlCtrlPoint* snlCtrlPointNetSurface::getPoint ( unsigned indexU, unsigned indexV ) const
+{
+    // Return pointer to control point at [t][u]
+    // -----------------------------------------
+    // indexU:  u param.
+    // indexV:  v param.
+
+    return ( ctrlPts + ( indexU * sizeV ) + indexV );
+}
+
+snlCtrlPoint* snlCtrlPointNetSurface::growU ( int increaseBy, bool reallocate )
+{
+    // Grow control point net in U direction by 1.
+    // -------------------------------------------
+    // reallocate:      If false then extra space has already been allocated elsewhere.
+    // increaseBy:      Size U direction should be increased by.
+
+    if ( ! ctrlPts ) return 0;
+    
+    if ( reallocate )
+    {
+        snlCtrlPoint* newPts = new snlCtrlPoint [ ctrlPtArraySize + ( sizeV * increaseBy ) ];
+
+        // Copy points into new array.
+        for ( unsigned index = 0; index < ctrlPtArraySize; index ++ )
+           newPts [ index ] = ctrlPts [ index ];
+
+        // Delete old array and point to new one.
+        delete[] ctrlPts;
+
+        ctrlPts = newPts;
+        
+        ctrlPtArraySize += sizeV * increaseBy;
+    }
+
+    sizeU += increaseBy;
+
+    return ctrlPts;
+}
+
+snlCtrlPoint* snlCtrlPointNetSurface::growV ( int increaseBy, bool reallocate )
+{
+    // Grow control point net in V direction by 1.
+    // -------------------------------------------
+    // reallocate:      If false then extra space has already been allocated elsewhere.
+    // increaseBy:      Size V direction should be increased by.
+
+    if ( ! ctrlPts ) return 0;
+    
+    snlCtrlPoint*   newPts;
+    
+    if ( reallocate )
+        newPts = new snlCtrlPoint [ ctrlPtArraySize + ( sizeU * increaseBy )];
+    else
+        newPts = ctrlPts;
+
+    // Copy points into new array.
+
+    int oldPos = sizeU * sizeV - 1;  // Don't use ctrlPtArraySize. Realloc may have changed it.
+    int newPos = sizeU * ( sizeV + increaseBy ) - 1 - increaseBy;
+    
+    for ( int indexU = sizeU - 1; indexU > -1; indexU -- )
+    {
+        for ( int indexV = sizeV - 1; indexV > -1; indexV -- )
+            newPts [ newPos -- ] = ctrlPts [ oldPos -- ];
+
+        newPos -= increaseBy;
+    }
+
+    // Delete old array and point to new one.
+
+    if ( reallocate )
+    {
+        delete[] ctrlPts;
+
+        ctrlPts = newPts;
+        
+        ctrlPtArraySize += sizeU * increaseBy;
+    }
+
+    sizeV += increaseBy;
+
+    return ctrlPts;
+}
+
+snlCtrlPoint* snlCtrlPointNetSurface::shrinkU()
+{
+    // Shrink control point net in U direction by 1.
+    // ---------------------------------------------
+    
+   if ( ! ctrlPts ) return 0;
+
+    snlCtrlPoint* newPts = new snlCtrlPoint [ ctrlPtArraySize - sizeV ];
+
+    // Copy points into new array.
+    for ( unsigned index = 0; index < ( ctrlPtArraySize - sizeV ); index ++ )
+        newPts [ index ] = ctrlPts [ index ];
+
+    // Delete old array and point to new one.
+
+    delete[] ctrlPts;
+
+    ctrlPts = newPts;
+
+    sizeU --;
+
+    ctrlPtArraySize -= sizeV;
+
+    return ctrlPts;
+}
+
+snlCtrlPoint* snlCtrlPointNetSurface::shrinkV()
+{
+    // Shrink control point net in V direction by 1.
+    // ---------------------------------------------
+    
+    if ( ! ctrlPts ) return 0;
+
+    snlCtrlPoint* newPts = new snlCtrlPoint [ ctrlPtArraySize - sizeU ];
+
+    // Copy points into new array.
+    for ( int indexU = 0; indexU < sizeU; indexU ++ )
+        for ( int indexV = 0; indexV < ( sizeV - 1 ); indexV ++ )
+            newPts [ ( indexU * ( sizeV - 1 ) ) + indexV ] = ctrlPts [ indexU * sizeV + indexV ];
+
+    // Delete old array and point to new one.
+
+    delete[] ctrlPts;
+
+    ctrlPts = newPts;
+
+    sizeV --;
+
+    ctrlPtArraySize -= sizeU;
+
+    return ctrlPts;
+
+}
+
+double snlCtrlPointNetSurface::calcFlatness ( int indexU, int indexV, int numPointsU, int numPointsV )
+{
+    // Calculate flatness of a rectangular section of control points.
+    // --------------------------------------------------------------
+    // indexU:      U index of starting position in control point net.
+    // indexV:      V index of starting position in control point net.
+    // numPointsU:  Number of points in U direction, including starting point, to test.
+    // numPointsV:  Number of points in V direction, including starting point, to test.
+    //
+    // Notes:
+    //
+    //              Rectangle data orientation:
+    //
+    //                V     B--------D
+    //                      |        |
+    //                ^     |        |
+    //                |     |        |
+    //                |     A--------C
+    //
+    //                      ----->   U
+
+    // Locate rectangular array of points to process.
+
+    int numPoints = numPointsU * numPointsV;
+
+    snlCtrlPoint** ctrlPointPtrs = new snlCtrlPoint* [ numPoints ];
+
+    locatePoints ( indexU, indexV, numPointsU, numPointsV, ctrlPointPtrs );
+
+    // Pre-calculate vectors and normals.
+
+    // 4 Corners.
+    
+    int indexB = numPointsV - 1;
+    int indexC = numPoints - numPointsV;
+    int indexD = numPoints - 1;
+
+    snlCtrlPoint ptA = *(ctrlPointPtrs [ 0 ]);
+    ptA.normalise();
+    snlCtrlPoint ptB = *(ctrlPointPtrs [ numPointsV - 1 ]);
+    ptB.normalise();
+    snlCtrlPoint ptC = *(ctrlPointPtrs [ numPoints - numPointsV ]);
+    ptC.normalise();
+    snlCtrlPoint ptD = *(ctrlPointPtrs [ numPoints - 1 ]);
+    ptD.normalise();
+
+    // Side Vectors.
+    
+    snlVector ab ( ptA, ptB );
+    ab.unitise();
+    
+    snlVector ba = ab * -1.0;
+
+    snlVector ac ( ptA, ptC );
+    ac.unitise();
+
+    snlVector ca = ac * -1.0;
+
+    snlVector bd ( ptB, ptD );
+    bd.unitise();
+
+    snlVector db = bd * -1.0;
+
+    snlVector cd ( ptC, ptD );
+    cd.unitise();
+
+    snlVector dc = cd * -1.0;
+
+    // Diagonal Vectors.
+
+    snlVector bc ( ptB, ptC );
+    snlVector ad ( ptA, ptD );
+
+    // Normals.
+
+    snlVector normA ( ac, ab );
+    snlVector normB ( ba, bd );
+    snlVector normC ( cd, ca );
+    snlVector normD ( db, dc );
+
+    // Project each point of the rectangle, not on the corners, onto the 4 base triangles.
+    // If the projection does not lay on a triangle, it is not considered in any further flatness
+    // calculations. If a point does not have any projections that lay on one of the four triangles
+    // then the closest distance to one of the 6 vectors defining the 4 triangles is taken
+    // as the flatness.
+
+    double maxDistance = 0.0;  // Maximum distance or "flatness".
+
+    for ( int index = 0; index < indexD; index ++ )
+    {
+        if ( ! index || index == indexB || index == indexC ) continue;  // Don't process the corners.
+
+        bool interiorFound = false;  // True if at least one triangle contains projection.
+
+        snlCtrlPoint t = *(ctrlPointPtrs [ index ]);
+        t.normalise();
+
+        // Project point to and compare to triangle rooted at A.
+
+        snlVector toProj ( ptA, t );
+
+        snlVector projVect = toProj.project ( normA );
+
+        snlPoint projPoint = t - projVect;
+
+        double projDist = projVect.length();
+
+        bool isInterior = isInteriorToTriangle ( projPoint, ptA, ab, ac, ptB, ba, bc );
+
+        if ( isInterior ) interiorFound = true;
+
+        if ( isInterior &&  maxDistance < projDist ) maxDistance = projDist;
+
+        // Project point to and compare to triangle rooted at B.
+
+        toProj.calc ( ptB, t );
+
+        projVect = toProj.project ( normB );
+
+        projPoint = t - projVect;
+
+        projDist = projVect.length();
+
+        isInterior = isInteriorToTriangle ( projPoint, ptB, ba, bd, ptA, ab, ad );
+
+        if ( isInterior ) interiorFound = true;
+
+        if ( isInterior &&  maxDistance < projDist ) maxDistance = projDist;
+        
+        // Project point to and compare to triangle rooted at C.
+
+        toProj.calc ( ptC, t );
+
+        projVect = toProj.project ( normC );
+
+        projPoint = t - projVect;
+
+        projDist = projVect.length();
+
+        isInterior = isInteriorToTriangle ( projPoint, ptC, ca, cd, ptA, ac, ad );
+
+        if ( isInterior ) interiorFound = true;
+
+        if ( isInterior &&  maxDistance < projDist ) maxDistance = projDist;
+        
+        // Project point to and compare to triangle rooted at D.
+
+        toProj.calc ( ptD, t );
+
+        projVect = toProj.project ( normD );
+
+        projPoint = t - projVect;
+
+        projDist = projVect.length();
+
+        isInterior = isInteriorToTriangle ( projPoint, ptD, db, dc, ptB, bd, bc );
+
+        if ( isInterior ) interiorFound = true;
+
+        if ( isInterior &&  maxDistance < projDist ) maxDistance = projDist;
+        
+        // If no triangle contains a projection then project to the six triangle outlines and take _smallest_ value
+
+        if ( ! interiorFound )
+        {
+            toProj.calc ( ptA, t );
+
+            double projMaxDistance = ab.projectDist ( toProj );  // A -> B.
+
+            double projDist = ac.projectDist ( toProj );  // A -> C.
+
+            if ( projDist < projMaxDistance ) projMaxDistance = projDist;
+
+            projDist = ad.projectDist ( toProj );  // A -> D.
+
+            if ( projDist < projMaxDistance ) projMaxDistance = projDist;
+
+            toProj.calc ( ptD, t );
+
+            projDist = db.projectDist ( toProj );  // D -> B.
+
+            if ( projDist < projMaxDistance ) projMaxDistance = projDist;
+
+            projDist = dc.projectDist ( toProj );  // D -> C.
+
+            if ( projDist < projMaxDistance ) projMaxDistance = projDist;
+
+            toProj.calc ( ptB, t );
+
+            projDist = bc.projectDist ( toProj );  // B -> C.
+
+            if ( projDist < projMaxDistance ) projMaxDistance = projDist;
+
+            if ( maxDistance < projMaxDistance ) maxDistance = projMaxDistance;
+        }
+    }
+
+    return maxDistance;
+}
+
+double snlCtrlPointNetSurface::calcFlatnessU ( int indexU, int indexV, int numPoints, bool degree1 ) const
+{
+    // Test flatness in U direction.
+    // -----------------------------
+    // indexU:      U index of starting position in control point net.
+    // indexV:      V index of starting position in control point net.
+    // numPoints:   Number of points, including starting point, to test.
+    
+    snlCtrlPoint** testCtrlPoints  = new snlCtrlPoint* [ numPoints ];
+    
+    double flatness;
+    
+    if ( degree1 )
+    {
+        testCtrlPoints [ 0 ] = getPoint ( indexU, indexV );
+        testCtrlPoints [ 1 ] = getPoint ( indexU + 1, indexV );
+        testCtrlPoints [ 2 ] = getPoint ( indexU, indexV + 1 );
+        testCtrlPoints [ 3 ] = getPoint ( indexU + 1, indexV + 1 );
+        
+        flatness = calcDeg1Flatness ( (snlPoint**) testCtrlPoints );        
+    }
+    else
+    {
+        locatePointsU ( indexU, indexV, numPoints, testCtrlPoints );
+        
+        flatness = snlCtrlPointNet::calcFlatness ( (snlPoint**) testCtrlPoints, numPoints );
+    }
+    
+    delete[] testCtrlPoints;
+    
+    return flatness;
+}
+
+double snlCtrlPointNetSurface::calcFlatnessV ( int indexU, int indexV, int numPoints, bool degree1 ) const
+{
+    // Test flatness in V direction.
+    // -----------------------------
+    // indexU:      U index of starting position in control point net.
+    // indexV:      V index of starting position in control point net.
+    // numPoints:   Number of points, including starting point, to test.
+    
+    snlCtrlPoint** testCtrlPoints  = new snlCtrlPoint* [ numPoints ];
+    
+    double flatness;
+    
+    if ( degree1 )
+    {
+        testCtrlPoints [ 0 ] = getPoint ( indexU, indexV );
+        testCtrlPoints [ 1 ] = getPoint ( indexU, indexV + 1 );
+        testCtrlPoints [ 2 ] = getPoint ( indexU + 1, indexV );
+        testCtrlPoints [ 3 ] = getPoint ( indexU + 1, indexV + 1 );
+
+        flatness = calcDeg1Flatness ( (snlPoint**) testCtrlPoints );        
+    }
+    else
+    {    
+        locatePointsV ( indexU, indexV, numPoints, testCtrlPoints );
+            
+        flatness = snlCtrlPointNet::calcFlatness ( (snlPoint**) testCtrlPoints, numPoints );
+    }
+    
+    delete[] testCtrlPoints;
+    
+    return flatness;
+}
+
+double snlCtrlPointNetSurface::maxFlatnessU ( int span )
+{
+    // Calculate the maximum flatness of the control point net in the U direction
+    // --------------------------------------------------------------------------
+    // span:    Width of span to calculate flatness across.
+
+    double maxFlatness = 0.0;
+
+    int maxU = sizeU - span;
+    
+    for ( int indexV = 0; indexV < sizeV; indexV ++ )
+    {
+        for ( int indexU = 0; indexU < maxU; indexU ++ )
+        {
+            // Test for flatness
+
+            double flatness = calcFlatnessU ( indexU, indexV, span + 1, false );
+
+            if ( flatness > maxFlatness ) maxFlatness = flatness;
+        }
+    }
+
+    return maxFlatness;
+}
+
+double snlCtrlPointNetSurface::maxFlatnessV ( int span )
+{
+    // Calculate the maximum flatness of the control point net in the V direction
+    // --------------------------------------------------------------------------
+    // span:    Width of span to calculate flatness across.
+
+    double maxFlatness = 0.0;
+
+    int maxV = sizeV - span;
+    
+    for ( int indexU = 0; indexU < sizeU; indexU ++ )
+    {
+        for ( int indexV = 0; indexV < maxV; indexV ++ )
+        {
+            // Test for flatness
+
+            double flatness = calcFlatnessV ( indexU, indexV, span + 1, false );
+
+            if ( flatness > maxFlatness ) maxFlatness = flatness;
+        }
+    }
+
+    return maxFlatness;
+}
+
+double snlCtrlPointNetSurface::maxCurvatureU()
+{
+    // Calculate the maximum curvature of surface in U direction.
+    // ----------------------------------------------------------
+    // Returns:     Maximum curvature as an angle between 0 and PI.
+    
+    snlCtrlPoint** testCtrlPoints  = new snlCtrlPoint* [ 3 ];
+    snlPoint** testPoints  = new snlPoint* [ 3 ];
+    
+    double maxCurvature = 0.0;
+    
+    for ( int indexV = 0; indexV < sizeV; indexV ++ )
+    {
+        for ( int indexU = 0; indexU < sizeU - 2; indexU ++ )
+        {
+            locatePointsU ( indexU, indexV, 3, testCtrlPoints );    
+    
+            // Make sure pointer is converted correctly.
+        
+            for ( int index = 0; index < 3; index ++ )
+                testPoints [ index ] = testCtrlPoints [ index ];
+            
+             double curvature = calcCurvature ( testPoints );
+             
+             if ( curvature > maxCurvature ) maxCurvature = curvature;
+        }
+    } 
+    
+    delete[] testPoints;
+    delete[] testCtrlPoints;
+    
+    return maxCurvature;
+}
+
+double snlCtrlPointNetSurface::maxCurvatureV()
+{
+    // Calculate the maximum curvature of surface in V direction.
+    // ----------------------------------------------------------
+    // Returns:     Maximum curvature as an angle between 0 and PI.
+    
+    snlCtrlPoint** testCtrlPoints  = new snlCtrlPoint* [ 3 ];
+    snlPoint** testPoints  = new snlPoint* [ 3 ];
+    
+    double maxCurvature = 0.0;
+    
+    for ( int indexU = 0; indexU < sizeU; indexU ++ )
+    {
+        for ( int indexV = 0; indexV < sizeV - 2; indexV ++ )
+        {
+            locatePointsV ( indexU, indexV, 3, testCtrlPoints );    
+    
+            // Make sure pointer is converted correctly.
+        
+            for ( int index = 0; index < 3; index ++ )
+                testPoints [ index ] = testCtrlPoints [ index ];
+            
+             double curvature = calcCurvature ( testPoints );
+             
+             if ( curvature > maxCurvature ) maxCurvature = curvature;
+        }
+    } 
+    
+    delete[] testPoints;
+    delete[] testCtrlPoints;
+    
+    return maxCurvature;
+}
+
+void snlCtrlPointNetSurface::locatePoints ( int indexU, int indexV, int numPointsU, int numPointsV,
+                                            snlCtrlPoint** pointsLocated ) const
+{
+    // Get pointers to an array of points.
+    // -----------------------------------
+    // indexU:          U index of starting position in control point net.
+    // indexV:          V index of starting position in control point net.
+    // numPointsU:      Number of points in U direction to return.
+    // numPointsV:      Number of points in V direction to return.
+    // pointsLocated:   Pointer array to return pointers in. Must be size numPointsU * numPointsV.
+    //
+    // Notes:           There is no array bounds check in this function. So be carefull of the U and V indexes
+    //                  being passed to this function.
+    
+    int arrayIndex = indexU * sizeV + indexV;
+
+    int retArrayIndex = 0;
+
+    int uStep = sizeV - numPointsV;
+
+    for ( int uPtNum = 0; uPtNum < numPointsU; uPtNum ++ )
+    {
+        for ( int vPtNum = 0; vPtNum < numPointsV; vPtNum ++ )
+        {
+            pointsLocated [ retArrayIndex ++ ] = ctrlPts + arrayIndex ++;
+        }
+
+        arrayIndex += uStep;
+    }
+}
+
+void snlCtrlPointNetSurface::locatePointsU ( int indexU, int indexV, int numPoints, snlCtrlPoint** testPoints ) const
+{
+    // Get pointers to a series of points in the U direction.
+    // ------------------------------------------------------
+    // indexU:      U index of starting position in control point net.
+    // indexV:      V index of starting position in control point net.
+    // numPoints:   Number of points, including starting point.
+    // testPoints:  Pointer array to return pointers in.
+    
+    int arrayIndex = indexU * sizeV + indexV;
+    
+    for ( int ptNum = 0; ptNum < numPoints; ptNum ++ )
+    {
+        if ( (unsigned) arrayIndex < ctrlPtArraySize )
+        {
+            testPoints [ ptNum ] = ctrlPts + arrayIndex;
+            arrayIndex += sizeV;
+        }
+        else
+            break;
+    }            
+}
+
+void snlCtrlPointNetSurface::locatePointsV ( int indexU, int indexV, int numPoints, snlCtrlPoint** testPoints ) const
+{
+    // Get pointers to a series of points in the V direction.
+    // ------------------------------------------------------
+    // indexU:      U index of starting position in control point net.
+    // indexV:      V index of starting position in control point net.
+    // numPoints:   Number of points, including starting point.
+    // testPoints:  Pointer array to return pointers in.
+    
+    int arrayIndex = indexU * sizeV + indexV;
+    
+    for ( int ptNum = 0; ptNum < numPoints; ptNum ++ )
+    {
+        if ( (unsigned) arrayIndex < ctrlPtArraySize )
+        {
+            testPoints [ ptNum ] = ctrlPts + arrayIndex;
+            arrayIndex ++;
+        }
+        else
+            break;
+    }            
+}
+
+int snlCtrlPointNetSurface::maxConnections() const
+{
+    return 4;
+}
+
+int snlCtrlPointNetSurface::getCnctPts ( unsigned index, snlCtrlPoint* retPts )
+{
+    // Get connected control points to the one pointed to by index.
+    // ------------------------------------------------------------
+    // index:       Point index to look for.
+    // retPts:      Pre-allocated array to return points in.
+
+    // !@#$ NOT IMPLEMENTED YET!
+
+    cout << "Doh! snlCtrlPointNetSurface::getCnctPts hasn't been finished, and you are calling it!\n";
+
+    return 0;
+}
+
+void snlCtrlPointNetSurface::selectPoint ( int indexU, int indexV )
+{
+    // Select point at U, V.
+    // ---------------------
+    
+    int index = indexU * sizeV + indexV;
+    
+    ctrlPts [ index ].select ( true );
+}
+
+void snlCtrlPointNetSurface::selectLineConstU ( int indexU )
+{
+    // Select line in constant U direction.
+    // ------------------------------------
+    
+    for ( int indexV = 0; indexV < sizeV; indexV ++ )
+        selectPoint ( indexU, indexV );
+}
+
+void snlCtrlPointNetSurface::selectLineConstV ( int indexV )
+{
+    // Select line in constant V direction.
+    // ------------------------------------
+    
+    for ( int indexU = 0; indexU < sizeU; indexU ++ )
+        selectPoint ( indexU, indexV );
+}
+
+void snlCtrlPointNetSurface::print()
+{
+    // Print control points to std out.
+    // --------------------------------
+    
+    for ( int indexU = 0; indexU < sizeU; indexU ++ )
+    {
+        cout << indexU << ": ";
+        
+        for ( int indexV = 0; indexV < sizeV; indexV ++ )
+        {
+            ctrlPts [ indexU * sizeV + indexV ].print();
+            cout << "\n";
+        }
+        
+        cout << "\n";
+    }
+}
+
+void snlCtrlPointNetSurface::printCompare ( snlCtrlPointNetSurface& compareTo )
+{
+    // Print out comparison whith another control point net.
+    // -----------------------------------------------------
+    // Note:    Assumes both control point nets are _exactly_ the same size.
+
+    for ( int indexU = 0; indexU < sizeU; indexU ++ )
+    {
+        cout << indexU << ": ";
+        
+        for ( int indexV = 0; indexV < sizeV; indexV ++ )
+        {
+            snlPoint pt1 = ctrlPts [ indexU * sizeV + indexV ];
+            snlPoint pt2 = compareTo.ctrlPts [ indexU * sizeV + indexV ];
+            
+            if ( pt1 == pt2 )
+                cout << "E";
+            else
+                cout << "NE";
+                
+            cout << "\t";
+        }
+        
+        cout << "\n";
+    }
+}
+
+void snlCtrlPointNetSurface::print_cpp()
+{
+    // Print control points to std out.
+    // --------------------------------
+    // Notes:   Prints data that can be directly included in a c++ program.
+
+    cout << "snlCtrlPoint* points = new snlCtrlPoint [ " << sizeU * sizeV << " ];\n\n";
+
+    int totalSize = sizeU * sizeV;
+
+    for ( int index = 0; index < totalSize; index ++ )
+    {
+        cout << "points [ " << index << " ].components ";
+        ctrlPts [ index ].print();
+        cout << ";\n";
+    }
+}
+
diff --git a/src/snlCtrlPointNetSurface.h b/src/snlCtrlPointNetSurface.h
new file mode 100644
index 0000000..dc3289e
--- /dev/null
+++ b/src/snlCtrlPointNetSurface.h
@@ -0,0 +1,88 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef SNLCTRLPTNETSURFACE_H
+#define SNLCTRLPTNETSURFACE_H
+
+#include "snlCtrlPointNet.h"
+
+class snlCtrlPointNetSurface : public snlCtrlPointNet
+{
+    public:
+
+        snlCtrlPointNetSurface ( snlCtrlPoint* cPtArray, unsigned sizeU, unsigned sizeV, bool copy = false );
+        
+        snlCtrlPointNetSurface ( unsigned sizeU, unsigned sizeV, snlPoint& origin,
+                                 snlPoint& cornerU, snlPoint& cornerV );
+
+        virtual ~snlCtrlPointNetSurface();
+
+        unsigned getSizeU() const;
+        unsigned getSizeV() const;
+        
+        void setSizeU ( unsigned size );
+        void setSizeV ( unsigned size );
+
+        snlCtrlPoint* getPoint ( unsigned indexU, unsigned indexV ) const;
+
+        // Increase the control point net's size.
+        snlCtrlPoint* growU ( int increaseBy = 1, bool reallocate = true );
+        snlCtrlPoint* growV ( int increaseBy = 1, bool reallocate = true );
+        
+        // Decrease the control point net's size.
+        snlCtrlPoint* shrinkU();
+        snlCtrlPoint* shrinkV();
+
+        double calcFlatness ( int indexU, int indexV, int numPointsU, int numPointsV );
+        
+        double calcFlatnessU ( int indexU, int indexV, int numPoints, bool degree1 ) const;
+        double calcFlatnessV ( int indexU, int indexV, int numPoints, bool degree1 ) const;
+
+        double maxFlatnessU ( int span );
+        double maxFlatnessV ( int span );
+        
+        double maxCurvatureU();
+        double maxCurvatureV();
+
+        void locatePoints ( int indexU, int indexV, int numPointsU, int numPointsV, snlCtrlPoint** pointsLocated ) const;
+        
+        void locatePointsU ( int indexU, int indexV, int numPoints, snlCtrlPoint** testPoints ) const;
+        void locatePointsV ( int indexU, int indexV, int numPoints, snlCtrlPoint** testPoints ) const;
+        
+        void selectPoint ( int indexU, int indexV );
+        void selectLineConstU ( int indexU );  // Select line in constant U direction.
+        void selectLineConstV ( int indexV );  // Select line in constant V direction.        
+        
+        void print();
+        void printCompare ( snlCtrlPointNetSurface& compareTo );
+        void print_cpp();
+
+        // snlCtrlPointNet Abstract implementation.
+
+        virtual int getCnctPts ( unsigned index, snlCtrlPoint* retPts );
+
+        virtual int maxConnections() const;
+        
+    private:
+
+        int     sizeU, sizeV;  // Array size in U and V directions.
+
+};
+
+#endif
+
+
diff --git a/src/snlCurve.cpp b/src/snlCurve.cpp
new file mode 100644
index 0000000..a1ed1b9
--- /dev/null
+++ b/src/snlCurve.cpp
@@ -0,0 +1,1484 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// libSNL - Simple Nurbs Library
+// Copyright 2003 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** General NURBS Curve ***
+
+#include "snlCurve.h"
+#include "snlUtil.h"
+#include "snlSquareLinear.h"
+
+snlCurve::~snlCurve()
+{
+    if ( ctrlPtNet ) delete ctrlPtNet;
+    if ( knotVect ) delete knotVect;
+}
+
+snlCurve::snlCurve()
+{
+    deg = 0;
+    
+    ctrlPtNet = 0;
+    knotVect = 0;
+}
+
+snlCurve::snlCurve ( const snlCurve& copyFrom )
+{
+    // Copy constructor.
+    // -----------------
+    
+    ctrlPtNet = new snlCtrlPointNetCurve ( *(copyFrom.ctrlPtNet) );
+    
+    knotVect = new snlKnotVector ( *(copyFrom.knotVect) );    
+    
+    deg = copyFrom.deg;    
+}
+
+snlCurve::snlCurve ( int degree, unsigned size, snlPoint& start, snlPoint& end )
+{
+    // Generate new curve.
+    // -------------------
+    // deg:    Degree of curve.
+    // size:   Number of control points curve has.
+    // start:  Starting point of curve.
+    // end:    Ending point of curve.
+    //
+    // Notes:  Draws a straight line.
+    
+    deg = degree;
+    
+    ctrlPtNet = new snlCtrlPointNetCurve ( size, start, end );
+    
+    knotVect = new snlKnotVector ( 0.0, 1.0, size + degree + 1, degree );
+}
+                     
+snlCurve::snlCurve ( int degree, unsigned size, snlCtrlPoint* points, knot* knots )
+{
+    // Generate new curve.
+    // -------------------
+    // deg:        Degree of curve.
+    // size:       Number of control points curve has.
+    // points:     Points to use.
+    // knots:      Knots to use.
+    //
+    // Notes:      Does not copy points and knots. So don't delete them elsewhere.
+    
+    deg = degree;
+    
+    ctrlPtNet = new snlCtrlPointNetCurve ( points, size, false );
+    
+    if ( knots )
+        knotVect = new snlKnotVector ( knots, size + degree + 1, degree );
+    else
+        knotVect = new snlKnotVector ( 0.0, 1.0, size + degree + 1, degree );
+}
+
+snlCurve::snlCurve ( unsigned size, snlCtrlPoint* points, snlKnotVector* knotVector )
+{
+    // Generate new curve.
+    // -------------------
+    // size:        Size of control point array.
+    // points:      Control point array.
+    // knotVect:    Knot vector to use.
+    //
+    // Notes:       Does not copy points and knot vector. So don't delete them elsewhere.
+
+    deg = knotVector -> degree();
+
+    knotVect = knotVector;
+
+    ctrlPtNet = new snlCtrlPointNetCurve ( points, size, false );
+}
+
+snlCurve::snlCurve ( snlPoint* points, unsigned size, int fittingType, int degree, bool closedLoop, knot** retParams )
+{
+    // Interpolated / approximated curve.
+    // ----------------------------------
+    // points:      Points to interpolate between.
+    // size:        Number of points.
+    // fittingType: Type of interpolation from SNL_FITTING_TYPES.
+    // degree:      Resultant curve should be this degree.
+    // closedLoop:  The points specify a closed loop that should join smoothly.
+    // retParams:   Pointer to pointer that points to array of parameters that correspond to given points.
+    //
+    // Notes:       Array returned via retParams should be deleted by calling function.
+    
+    ctrlPtNet = 0;
+    knotVect = 0;
+    
+    switch ( fittingType )
+    {
+        case SNL_GLOBAL_INTERPOLATION:
+        case SNL_GLOBAL_INTERP_CENTRIFUGAL:
+        
+            if ( closedLoop )
+                globalInterpClosedLoop ( fittingType, points, size, degree, retParams );
+            else
+                globalInterp ( fittingType, points, size, degree, retParams );
+                
+            break;
+        
+        case SNL_LOCAL_INTERPOLATION:
+        
+            if ( degree == 2 )
+                localInterpQuadratic ( points, size );
+            else
+                localInterpCubic ( points, size );
+            break;
+    };    
+}
+
+snlCurve::snlCurve ( snlPoint& startPoint, snlPoint& endPoint, snlPoint& centrePoint, int numSections )
+{
+    // Create curve as circular arc.
+    // -----------------------------
+    // startPoint:      Starting point of arc.
+    // endPoint:        Ending point of arc.
+    // centrePoint:     Centre point of arc.
+    // numSections:     ( optional ) specify number of sections arc has.
+    //
+    // Notes:           Can not do an arc bigger than 180 degrees.
+
+    snlVector arcStart ( centrePoint, startPoint );
+    snlVector arcEnd ( centrePoint, endPoint );
+
+    double arcAngle = arcStart.angle ( arcEnd );
+
+    if ( numSections == -1 )
+        numSections = (int) ( ( arcAngle / ( M_PI / 2.0 ) ) + 1 );
+
+    double sectionAngle = arcAngle / (double) numSections;
+
+    double stepAngle = sectionAngle / 2.0;
+
+    double midPtWeight = cos ( stepAngle );
+
+    int numCtrlPts = ( numSections - 1 ) * 2 + 3;
+
+    snlCtrlPoint* ctrlPts = new snlCtrlPoint [ numCtrlPts ];
+
+    ctrlPts [ 0 ] = startPoint;
+    ctrlPts [ numCtrlPts - 1 ] = endPoint;
+
+    snlTransform rotation;
+
+    snlVector normal;
+
+    // Rotate points into place.
+
+    normal.crossProduct ( arcStart, arcEnd );
+
+    rotation.rotate ( stepAngle, centrePoint, normal );
+
+    for ( int ptNum = 1; ptNum < numCtrlPts - 1; ptNum ++ )
+    {
+        ctrlPts [ ptNum ] = ctrlPts [ ptNum - 1 ];
+
+        rotation.transform ( ctrlPts [ ptNum ] );
+    }
+
+    // Set mid point lengths and weights.
+
+    int index = 1;
+
+    // Calculate length of vector to add to mid point. midPtWeight is the cosine of the step angle.
+    double midPtVectLength = ( arcStart.length() / midPtWeight ) - arcStart.length();
+
+    for ( int sectNum = 0; sectNum < numSections; sectNum ++ )
+    {
+        snlVector midPtVect ( centrePoint, ctrlPts [ index ] );
+
+        midPtVect.length ( midPtVectLength );
+
+        ctrlPts [ index ] += midPtVect;
+
+        ctrlPts [ index ].multiplyWeight ( midPtWeight );
+    
+        index += 2;
+    }
+
+    // Generate control point net.
+
+    ctrlPtNet = new snlCtrlPointNetCurve ( ctrlPts, numCtrlPts );
+
+    // Generate knot vector. Degree 2.
+
+    int numKnots = numCtrlPts + 3;
+
+    knot* knots = new knot [ numKnots ];
+
+    // End clamps.
+
+    for ( index = 0; index < 3; index ++ )
+    {
+        knots [ index ] = 0.0;
+        knots [ numKnots - index - 1 ] = 1.0;
+    }
+
+    // Internal knots.
+
+    index = 3;
+
+    for ( int sectNum = 1; sectNum < numSections; sectNum ++ )
+    {
+        knot knotVal = ( 1.0 / (double) numSections ) * (double) sectNum;
+
+        knots [ index ++ ] = knotVal;
+        knots [ index ++ ] = knotVal;
+    }
+
+    // Generate knot vector.
+
+    knotVect = new snlKnotVector ( knots, numKnots, 2 );
+
+    deg = 2;
+}
+
+snlCurve& snlCurve::operator= ( const snlCurve& curveToCopy )
+{
+    if ( this != &curveToCopy )
+    {
+        if ( ctrlPtNet ) delete ctrlPtNet;
+        if ( knotVect ) delete knotVect;
+
+        ctrlPtNet = new snlCtrlPointNetCurve ( *(curveToCopy.ctrlPtNet) );
+    
+        knotVect = new snlKnotVector ( *(curveToCopy.knotVect) );
+    
+        deg = curveToCopy.deg;
+    }
+
+    return *this;
+}
+
+snlCtrlPointNetCurve& snlCurve::controlPointNet()
+{
+    // Return reference to control point network object for curve.
+    // -----------------------------------------------------------
+    
+    return *ctrlPtNet;
+}
+
+snlPoint snlCurve::evalHmg ( knot param ) const
+{
+    // Evaluate Non Rational Homogeneous Curve Point.
+    // ----------------------------------------------
+    // param:       Parameter to evaluate.    
+    //
+    // Returns:     Homogeneous point on curve.
+
+    snlPoint      rPoint;  // Return point.    
+    
+    unsigned span = knotVect -> findSpan ( param );    
+    
+    // Evaluate basis functions.
+    basis* bVals = knotVect -> evalBasis ( param );    
+
+    unsigned baseIndex = span - (unsigned) deg;
+    
+    // Get control point array.
+    const snlCtrlPoint* ctrlPts = ctrlPtNet -> getCtrlPts();
+    
+    rPoint.null();  // Set everything to zero.
+
+    for ( int index = 0; index <= deg; index ++ )
+        rPoint += ctrlPts [ baseIndex + index ] * bVals [ index ];    
+    
+    delete[] bVals;
+
+    return rPoint;
+}
+
+snlPoint snlCurve::eval ( knot param ) const
+{
+    // Evaluate rational non-homogeneous curve point.
+    // ----------------------------------------------
+    // param:       Parameter to evaluate.
+    //
+    // Returns:     Non-homogeneous point on curve.
+    
+    snlPoint retPoint = evalHmg ( param );
+    retPoint.normalise();
+    
+    return retPoint;
+}
+
+snlPoint* snlCurve::evalDerivsHmg ( knot param, unsigned deriv ) const
+{                                             
+    // Evaluate Non Rational Homogeneous Surface Derivatives.
+    // ------------------------------------------------------
+    // param:       Parameter to evaluate at.    
+    // deriv        Derivative order to evaluate.
+    //
+    // Returns:     Array of snlPoint [ deriv + 1 ]. Calling function
+    //              must delete[] this array.
+    
+    snlPoint* retPnts = new snlPoint [ deriv + 1 ];
+    
+    // Find spans
+    unsigned span = knotVect -> findSpan ( param );
+    
+    // Evaluate basis functions.
+    basis* bVals = knotVect -> evalBasisDeriv ( param, deriv );
+    
+    unsigned baseIndex = span - (unsigned) deg;
+    
+    // Get control point array.
+    const snlCtrlPoint* ctrlPts = ctrlPtNet -> getCtrlPts();
+    
+    for ( unsigned derivIndex = 0; derivIndex <= deriv; derivIndex ++ )
+    {   
+        retPnts [ derivIndex ].null();  // Set everything to zero.
+    
+        for ( int index = 0; index <= deg; index ++ )
+            retPnts [ derivIndex ] += ctrlPts [ baseIndex + index ] * bVals [ index + derivIndex * ( deg + 1 ) ];
+    }
+    
+    delete[] bVals;
+
+    return retPnts;
+}
+
+snlPoint* snlCurve::evalDerivs ( knot param, unsigned deriv ) const
+{
+    // Evaluate Rational Non-Homogeneous Surface Derivatives
+    // -----------------------------------------------------
+    // param:       Parameter to evaluate at.    
+    // deriv:       Derivative order to evaluate.
+    //
+    // Returns:     Array of snlPoint [ deriv + 1 ]. Calling function must
+    //              delete[] array.    
+
+    // Get homogeneous derivatives.
+    snlPoint* derivPts = evalDerivsHmg ( param, deriv );
+    
+    // Array for returning points in.
+    snlPoint* evalPts = new snlPoint [ deriv + 1 ];
+    
+    evalPts [ 0 ] = derivPts [ 0 ];  // First point in array is not a derivative.
+    
+    evalPts [ 0 ].normalise();
+    
+    double w0 = derivPts [ 0 ].w();
+    
+    snlPoint sum;
+    
+    for ( unsigned derivIndex = 1; derivIndex <= deriv; derivIndex ++ )
+    {
+        sum.null();
+        
+        for ( unsigned index = 1; index <= derivIndex; index ++ )        
+            sum += evalPts [ derivIndex - index ] * ( binCoefs::binCoefArray [ derivIndex ] [ index ] * derivPts [ index ].w() );
+        
+        evalPts [ derivIndex ] = derivPts [ derivIndex ] - sum;
+        evalPts [ derivIndex ] *= w0;
+        evalPts [ derivIndex ].w ( 0.0 );  // Point is actually a 3D vector so w must always be 0.
+    }
+      
+    delete[] derivPts;    
+    
+    return evalPts;
+}
+
+snlVector snlCurve::velocity ( knot param )
+{
+    // Velocity ( first derivative ) of curve.
+    // ---------------------------------------
+    // param:    Parameter to get velocity at.
+    
+    snlPoint* derivPoints = evalDerivs ( param, 1 );
+    
+    snlVector retVect ( derivPoints [ 1 ] );
+    
+    delete[] derivPoints;
+    
+    return retVect;
+}
+
+void snlCurve::insertKnot ( knot iParam, bool reallocate )
+{
+    // Insert a knot into knot vector and calculate new control points.
+    // ---------------------------------------------------------------
+    // iParam:      Parameter value to insert.
+    // reallocate:  Reallocate memory for control points.
+    //    
+    // Notes:       ctrlPts MUST have an additional point space allocated at the end of
+    //              each line in the array for the chosen direction.
+
+    unsigned        count, index;
+    snlCtrlPoint    pt1, pt2;
+
+    if ( reallocate )
+        ctrlPtNet -> grow();
+        
+    // Span new knot belongs to.
+    unsigned span = knotVect -> findSpan ( iParam );
+
+    // Pre calculate alphas.
+    double* alpha = new double [ deg ];
+
+    for ( count = 0; count < (unsigned) deg; count ++ )
+    {
+        index = span - deg + 1 + count;
+        alpha [ count ]  = ( iParam - ( knotVect -> val ( index ) ) )
+                           / ( knotVect -> val ( index + deg ) - knotVect -> val ( index ) );
+    }
+
+    // Build temp array to store new array of control points in.
+    snlCtrlPoint* tmpPts = new snlCtrlPoint [ deg ];
+    
+    // Get pointer to control points.
+    snlCtrlPoint* ctrlPts = ctrlPtNet -> getCtrlPtsPtr();
+
+    // Calculate new control points.
+    for ( count = 0; count < (unsigned) deg; count ++ )
+    {
+        index = span - deg + 1 + count;
+
+        // Get first and second ctrl points to process with        
+            
+        pt1 = ctrlPts [ index ];
+        pt2 = ctrlPts [ index - 1 ];        
+
+        pt1 *= alpha [ count ];
+        tmpPts [ count ] = pt1;
+        pt2 *= ( 1.0 - alpha [ count ] );
+        tmpPts [ count ] += pt2;
+    } 
+
+    // Place new points into array.    
+
+    // Copy non-altered control points forward one position at the end of the array.
+    for ( count = ( ctrlPtNet -> size() ) - 1; count > span; count -- )
+        ctrlPts [ count ] = ctrlPts [ count - 1 ];
+
+    // Copy new control points into array.
+    for ( count = 0; count < (unsigned) deg; count ++ )
+    {
+        index = span - deg + 1 + count;
+        ctrlPts [ index ] = tmpPts [ count ];
+    }    
+
+    // Insert new knot into knot vector
+    knotVect -> insertKnot ( iParam );
+
+    delete[] tmpPts;
+    delete[] alpha;
+}
+
+void snlCurve::insertKnots ( knot iParam, int numToInsert, bool reallocate )
+{
+    // Insert multiple knots.
+    // ----------------------
+    // iParam:        Parameter to insert.
+    // numToInsert:   Number of knots to insert.
+    // reallocate:    Reallocate memory for control points.
+    
+    for ( int index = 0; index < numToInsert; index ++ )
+        insertKnot ( iParam, reallocate );
+}
+
+double snlCurve::removeKnots ( int numKnots, unsigned removalIndex, double tolerance )
+{
+    // Remove multiple knots from index.
+    // ---------------------------------
+    // numKnots:            Number of knots to remove.
+    // removalIndex:        Index to remove knot from.
+    // tolerance:           Maximum error allowed before knot removal aborted.
+    //                      No tolerance if equals 0.
+    //
+    // Returns:             Tolerance achieved during knot removal whether successful or not.
+    //
+    // Notes:               Only removes multiples of the same parameter value initially at removal index.
+    
+    if ( numKnots < 1 ) return 0.0;
+    
+    double maxTol = 0.0;
+    
+    double param = knotVect -> val ( removalIndex );
+    
+    int multi = knotVect -> findMultiplicity ( removalIndex );
+    
+    int numToRemove = numKnots > multi ? multi : numKnots;
+    
+    for ( int count = 0; count < numToRemove; count ++ )
+    {
+        double tol = removeKnot ( removalIndex, tolerance );
+        
+        if ( tol > maxTol ) maxTol = tol;
+        
+        removalIndex = knotVect -> findSpan ( param );
+    }
+    
+    return maxTol;   
+}
+
+double snlCurve::removeKnot ( unsigned removalIndex, double tolerance )
+{
+    // Remove knot from curve.
+    // -----------------------
+    // removalIndex:        Index to remove knot from.
+    // tolerance:           Maximum error allowed before knot removal aborted.
+    //                      No tolerance if equals 0.
+    //
+    // Returns:             Tolerance achieved during knot removal whether successful or not.
+    
+    knot rParam = knotVect -> val ( removalIndex );
+
+    // Span knot to be removed belongs to. This will always adjust the removal index to
+    // point to a non-zero span. ie Multiplicity check.
+    unsigned rSpan = knotVect -> findSpan ( rParam );
+
+    // Find multiplicity of knot at index.
+    unsigned multi = knotVect -> findMultiplicity ( rSpan );
+
+    // Calculate the number of equations.
+    unsigned numEqns = deg - multi + 1;
+
+    // Pre calculate alphas.
+    double* alpha = knotVect -> calcRemovalAlphas ( rSpan );
+
+    // Build temp array to store new set of control points in.
+    // First and last points are not new.
+    snlCtrlPoint* tmpPts = new snlCtrlPoint [ numEqns + 1 ];
+    
+    // Get control point array and calculate starting point for processing new points within it.
+    
+    const snlCtrlPoint* ctrlPts = ctrlPtNet -> getCtrlPts();
+    
+    unsigned ctrlPtIndex = rSpan - deg - 1;
+    
+    // Seed temp array.
+    
+    tmpPts [ 0 ] = ctrlPts [ ctrlPtIndex ++ ];
+    
+    // Generate new points.
+
+    for ( unsigned index = 1; index <= numEqns; index ++ )
+    {
+        tmpPts [ index ] = ctrlPts [ ctrlPtIndex ++ ];
+        tmpPts [ index ] -= tmpPts [ index - 1 ] * ( 1.0 - alpha [ index - 1 ] );
+        tmpPts [ index ] /= alpha [ index - 1 ];
+    }
+    
+    // If error is under tolerance then copy new points into control point array.
+
+    double error = snlVector ( ctrlPts [ ctrlPtIndex ], tmpPts [ numEqns ] ).length();
+    
+    if ( error <= tolerance || tolerance == 0.0 )
+    {
+        // Use original curve control point instead of newly created one in last of equations.
+        tmpPts [ numEqns ] = ctrlPts [ ctrlPtIndex ];
+        
+        // Replace points in control point array.
+        
+        ctrlPtNet -> replacePoints ( tmpPts + 1, numEqns, rSpan - deg, numEqns + 1 );
+        
+        ctrlPtNet -> truncatePointSpace ( 1 );
+        
+        knotVect -> removeKnot ( rSpan );
+    }
+    
+    // Clean up.
+    
+    delete[] alpha;
+    delete[] tmpPts;
+    
+    return error;
+    
+}
+
+void snlCurve::refine ( double tolerance )
+{
+    // Refine control point net until tolerance is achieved.
+    // -----------------------------------------------------
+    
+    if ( deg <= 1 ) return;  // Degree 1 curves are straight lines.
+    
+    bool tolOk = false;
+    
+    while ( ! tolOk )
+    {
+        tolOk = true;        
+        
+        for ( int index = 0; (unsigned) index < ( ctrlPtNet -> size() ) - deg; index ++ )
+        {
+            // Test for flatness
+        
+            double  flatness;                
+            
+            flatness = ctrlPtNet -> calcFlatness ( index, deg + 1 );
+            
+            if ( flatness > tolerance )
+            {
+                // Insert knot into surface. Half way between existing knots.
+            
+                int insertIndex = index + deg;
+            
+                knot insertParam = ( ( knotVect -> val ( insertIndex + 1 )
+                                       - knotVect -> val ( insertIndex ) ) / 2 )
+                                       + knotVect -> val ( insertIndex );
+
+                insertKnot ( insertParam, true );
+
+                tolOk = false;
+
+                index ++;  // If this is not done then nothing converges if the curvature is too great.
+            }
+        }
+    }
+}
+
+double snlCurve::maxParam() const
+{
+    // Return maximum parameter value for curve.
+    // -----------------------------------------
+    
+    return knotVect -> max();
+}
+
+double snlCurve::minParam() const
+{
+    // Return minimum parameter value for curve.
+    // -----------------------------------------
+    
+    return knotVect -> min();
+}
+
+double snlCurve::param ( unsigned index ) const
+{
+    // Return parameter at specified knot index.
+    // -----------------------------------------
+    
+    return knotVect -> val ( index );
+}
+
+int snlCurve::size()
+{
+    return ctrlPtNet -> size();
+}
+
+void snlCurve::truncate ( knot param, bool keepLast, bool reparam )
+{
+    // Truncate curve.
+    // ---------------
+    // param:       Parameter to truncate at.
+    // keepLast:    Keep last part of curve instead of first part.
+    // reparam:     Reparameterise curve to original pararemeter boundaries.
+    
+    knot paramStart = knotVect -> min();
+    knot paramEnd = knotVect -> max();
+    
+    if ( param == paramStart || param == paramEnd ) return;
+    
+    insertPartition ( param );
+    
+    // Remove unwanted control points.
+    
+    unsigned span = knotVect -> findSpan ( param );
+    
+    if ( keepLast )
+        span -= deg;
+    else
+        span = knotVect -> getPreviousSpan ( span );
+        
+    ctrlPtNet -> truncate ( span, keepLast );    
+    
+    // Remove unwanted knots.
+    knotVect -> truncate ( param, keepLast );    
+    
+    // Reparameterise if required.
+    
+    if ( reparam )
+        reparameterise ( paramStart, paramEnd );
+}
+
+void snlCurve::insertPartition ( knot param )
+{
+    // Insert partition into curve.
+    // ----------------------------
+    // param:    Parameter to insert partition into.
+    //
+    // Notes:    Function basically makes sure degree knots are present
+    //           at supplied parameter.
+    
+    int numToInsert = deg - knotVect -> findMultiplicity ( param );
+    
+    for ( int index = 0; index < numToInsert; index ++ )
+        insertKnot ( param, true );    
+}
+
+void snlCurve::reparameterise ( knot startKnot, knot endKnot )
+{
+    // Do a linear Reparameterise on curve.
+    // ------------------------------------
+    // startKnot:    New starting knot value of knot vector.
+    // endKnot:      Ending knot value of knot vector.
+    //
+    // Notes:        Linear reparameterisations don't effect control points.
+    
+    knotVect -> reparameterise ( startKnot, endKnot );    
+}
+
+void snlCurve::reverseEvalDirection()
+{
+    // Reverse curves parametric evaluation direction.
+    // -----------------------------------------------
+
+    // Reverse knot vector.
+
+    knotVect -> reverse();
+
+    // Reverse control points.
+
+    ctrlPtNet -> reverse();
+}
+
+void snlCurve::globalInterpClosedLoop ( int type, snlPoint* points, unsigned size, int degree, knot** retParams )
+{
+    // Global interpolation as closed loop.
+    // ------------------------------------
+    // type:      Type of global interpolation from SNL_FITTING_TYPES.
+    // points:    Points to interpolate between.
+    // size:      Number of points.
+    // degree:    Resultant curve should be this degree.
+    // retParams: Pointer to pointer that points to array of parameters that correspond to given points.
+    //
+    // Notes:     Array returned via retParams should be deleted by calling function.
+
+    // Make sure first and last points aren't the same.
+
+    if ( points [ 0 ] == points [ size - 1 ] ) size --;
+
+    // Create new points array with overlap to interpolate with.
+
+    int newSize = size + degree * 2 + 1;
+
+    snlPoint* newPoints = new snlPoint [ newSize ];
+
+    // Starting overlap.
+
+    int newIndex = 0;
+
+    for ( unsigned index = size - degree; index < size; index ++ )
+        newPoints [ newIndex ++ ] = points [ index ];
+
+    // Middle points.
+
+    for ( unsigned index = 0; index < size; index ++ )
+        newPoints [ newIndex ++ ] = points [ index ];
+
+    // Ending join and overlap.
+
+    for ( int index = 0; index < degree + 1; index ++ )
+        newPoints [ newIndex ++ ] = points [ index ];
+
+    // Pass new points to global interpolation function.
+
+    knot* newRetParams;
+
+    globalInterp ( type, newPoints, newSize, degree, &newRetParams );
+
+    // Truncate curve.
+
+    knot paramStart = knotVect -> min();
+    knot paramEnd = knotVect -> max();
+
+    knot newParamStart = newRetParams [ degree ];
+    knot newParamEnd = newRetParams [ newSize - degree - 1 ];
+    
+    truncate ( newParamStart, true, false );
+    truncate ( newParamEnd, false, false );
+
+    // Reparameterise the curve and create new point return parameters.
+
+    reparameterise( paramStart, paramEnd );
+
+    if ( retParams )
+    {
+        knot* params = new knot [ size + 1 ];
+
+        int paramIndex = degree;  // Discard overlap params.
+
+        knot oldSpan = newParamEnd - newParamStart;
+        knot newSpan = paramEnd - paramStart;
+
+        // There is an additional parameter now because of the start and end points coinciding.
+
+        for ( unsigned index = 0; index <= size; index ++ )
+        {
+            params [ index ] = ( ( ( newRetParams [ paramIndex ] - newParamStart ) / oldSpan ) * newSpan ) + paramStart;
+            paramIndex ++;
+        }
+
+        *retParams = params;
+    }
+
+    // Clean up.
+
+    delete[] newRetParams;
+    delete[] newPoints;
+    
+}
+
+void snlCurve::globalInterp ( int type, snlPoint* points, unsigned size, int degree, knot** retParams )
+{
+    // Global interpolation.
+    // ---------------------
+    // type:      Type of global interpolation from SNL_FITTING_TYPES.
+    // points:    Points to interpolate between.
+    // size:      Number of points.
+    // degree:    Resultant curve should be this degree.
+    // retParams: Pointer to pointer that points to array of parameters that correspond to given points.
+    //
+    // Notes:     Array returned via retParams should be deleted by calling function.
+    
+    if ( knotVect ) delete knotVect;
+    if ( ctrlPtNet ) delete ctrlPtNet;
+    
+    deg = degree;
+    
+    // Generate parameters.
+    
+    knot* params = new knot [ size ];    
+    
+    knot totalChordLength = 0.0;
+    
+    snlVector chord;    
+    
+    // Intermediate step. Calculate (square root of) chord length.
+    
+    for ( unsigned index = 1; index < size; index ++ )
+    {
+        chord.calc ( points [ index - 1 ], points [ index ] );
+        
+        knot chordLength;
+        
+        if ( type == SNL_GLOBAL_INTERP_CENTRIFUGAL )
+            chordLength = sqrt ( chord.length() );
+        else        
+            chordLength = chord.length();
+            
+        totalChordLength += chordLength;
+        
+        params [ index ] = chordLength;
+    }    
+    
+    // Calculate final parameter values.
+    
+    params [ 0 ] = 0.0;
+    params [ size - 1 ] = 1.0;
+    
+    for ( unsigned index = 1; index < size - 1; index ++ )
+        params [ index ] = params [ index - 1 ] + params [ index ] / totalChordLength;
+        
+    // Generate knot vector.
+    
+    knot* knots = new knot [ size + degree + 1 ];
+    
+    unsigned index;
+    
+    // Start clamp.
+    for ( index = 0; index <= (unsigned) degree; index ++ )
+        knots [ index ] = 0.0;
+    
+    // End clamp.
+    for ( index = size; index < size + degree + 1; index ++ )
+        knots [ index ] = 1.0;
+    
+    // Internal knots.
+    for ( index = 1; index < size - degree; index ++ )
+    {
+        knot sum = 0.0;
+        
+        for ( unsigned paramIndex = index; paramIndex < index + degree; paramIndex ++ )
+            sum += params [ paramIndex ];        
+        
+        knots [ index + degree ] = sum / degree;
+    }
+    
+    knotVect = new snlKnotVector ( knots, size + degree + 1, degree );
+    
+    // Setup and solve linear equations.
+    
+    // Generate coefficient array.
+    
+    unsigned arraySize = size * size;
+    
+    double* coeffs = new double [ arraySize ];
+    
+    // Zero everything to begin with.
+    
+    for ( index = 0; index < arraySize; index ++ )
+        coeffs [ index ] = 0.0;
+    
+    // First and last rows just relfect clamps.
+    
+    coeffs [ 0 ] = 1.0;
+    coeffs [ arraySize - 1 ] = 1.0;
+    
+    // Fill middle rows with basis function values.
+    
+    for ( unsigned row = 1; row < size - 1; row ++ )
+    {
+        basis* basisVals = knotVect -> evalBasis ( params [ row ] );
+        
+        unsigned span = knotVect -> findSpan ( params [ row ] );
+        
+        unsigned rowStartIndex = row * size;
+        
+        index = 0;
+        
+        for ( unsigned col = span - degree; col <= span; col ++ )
+            coeffs [ rowStartIndex + col ] = basisVals [ index ++ ];
+        
+        delete[] basisVals;
+    }
+    
+    // Generate right hand sides.
+    
+    double* rhSides = new double [ size * 4 ];  // x, y, z and w.    
+    
+    for ( unsigned row = 0; row < size; row ++ )
+    {
+        index = row * 4;
+        
+        rhSides [ index ] = points [ row ].elements [ 0 ];  // x.
+        rhSides [ index + 1 ] = points [ row ].elements [ 1 ];  // y.
+        rhSides [ index + 2 ] = points [ row ].elements [ 2 ];  // z.
+        rhSides [ index + 3 ] = points [ row ].elements [ 3 ];  // w.
+    }    
+    
+    // Pass data to linear solver.
+    
+    snlSquareLinear solver ( size, 4, coeffs, rhSides );  // Constructor also calls snlSquareLinear::solve().
+    
+    // Copy solved points into new control points.
+    
+    snlCtrlPoint* ctrlPts = new snlCtrlPoint [ size ];
+    
+    for ( unsigned row = 0; row < size; row ++ )
+    {
+        index = row * 4;
+        
+        ctrlPts [ row ].elements [ 0 ] = rhSides [ index ];
+        ctrlPts [ row ].elements [ 1 ] = rhSides [ index + 1 ];
+        ctrlPts [ row ].elements [ 2 ] = rhSides [ index + 2 ];
+        ctrlPts [ row ].elements [ 3 ] = rhSides [ index + 3 ];
+    }
+    
+    // Create control point net.
+        
+    ctrlPtNet = new snlCtrlPointNetCurve ( ctrlPts, size, false );
+    
+    // Return parameters of given points if needed.
+    
+    if ( retParams )
+        *retParams = params;
+    else
+        delete[] params;    
+}
+
+snlCtrlPoint* snlCurve::genGlobalInterpPoints ( snlPoint* points, unsigned size, knot* params, snlKnotVector* knots )
+{
+    // Generate control points for global interpolation.
+    // -------------------------------------------------
+    // points:      Array of points to interpolate between.
+    // size:        Size of points array.
+    // params:      Parameters that correspond to points array.
+    // knots:       Knot vector to use.
+    //
+    // Returns:     Array of control points the same size as the given "points" array.
+
+    // *** Setup and solve linear equations ***
+    
+    // Generate coefficient array.
+
+    unsigned arraySize = size * size;
+    
+    double* coeffs = new double [ arraySize ];
+    
+    // Zero everything to begin with.
+
+    unsigned index;
+    
+    for ( index = 0; index < arraySize; index ++ )
+        coeffs [ index ] = 0.0;
+    
+    // First and last rows just relfect clamps.
+    
+    coeffs [ 0 ] = 1.0;
+    coeffs [ arraySize - 1 ] = 1.0;
+    
+    // Fill middle rows with basis function values.
+
+    int deg = knots -> degree();
+    
+    for ( unsigned row = 1; row < size - 1; row ++ )
+    {
+        basis* basisVals = knots -> evalBasis ( params [ row ] );
+
+        unsigned span = knots -> findSpan ( params [ row ] );
+
+        unsigned rowStartIndex = row * size;
+        
+        index = 0;
+        
+        for ( unsigned col = span - deg; col <= span; col ++ )
+            coeffs [ rowStartIndex + col ] = basisVals [ index ++ ];
+        
+        delete[] basisVals;
+    }
+
+    // Generate right hand sides.
+    
+    double* rhSides = new double [ size * 4 ];  // x, y, z and w.    
+    
+    for ( unsigned row = 0; row < size; row ++ )
+    {
+        index = row * 4;
+        
+        rhSides [ index ] = points [ row ].elements [ 0 ];  // x.
+        rhSides [ index + 1 ] = points [ row ].elements [ 1 ];  // y.
+        rhSides [ index + 2 ] = points [ row ].elements [ 2 ];  // z.
+        rhSides [ index + 3 ] = points [ row ].elements [ 3 ];  // w.
+    }    
+
+    // Pass data to linear solver.
+
+    snlSquareLinear solver ( size, 4, coeffs, rhSides );  // Constructor also calls snlSquareLinear::solve().
+
+    // Copy solved points into new control points.
+    
+    snlCtrlPoint* ctrlPts = new snlCtrlPoint [ size ];
+    
+    for ( unsigned row = 0; row < size; row ++ )
+    {
+        index = row * 4;
+        
+        ctrlPts [ row ].elements [ 0 ] = rhSides [ index ];
+        ctrlPts [ row ].elements [ 1 ] = rhSides [ index + 1 ];
+        ctrlPts [ row ].elements [ 2 ] = rhSides [ index + 2 ];
+        ctrlPts [ row ].elements [ 3 ] = rhSides [ index + 3 ];
+    }
+
+    return ctrlPts;
+}
+
+void snlCurve::localInterpQuadratic ( snlPoint* points, unsigned size )
+{
+    // Local quadratic interpolation.
+    // ------------------------------
+    
+    
+}
+
+void snlCurve::localInterpCubic ( snlPoint* points, unsigned size )
+{
+    // Local cubic interpolation.
+    // --------------------------
+    
+    
+}
+
+void snlCurve::synchronise ( snlCurve& curve )
+{
+    //! Synchronise this curves knot vector to given curve.
+    //  ---------------------------------------------------
+    //! @param curve Curve to snync to.
+    //!
+    //! @par Notes: Knots are only ever added NOT removed.
+    //!             So if curve has less multiplicity at a particular span index
+    //!             then true synchronisation will not occur and the caller
+    //!             should call the synchronise function on curve with this object
+    //!             as it's argument.
+    
+    if ( curve.deg != deg ) return;  // Curve to sync to must have same degree.
+    
+    unsigned numSpans = curve.knotVect -> getNumSpans();
+
+    if ( numSpans < 2 ) return;
+        
+    // If the degree is the same then the first span will always have the same multiplicity for both curves.
+    
+    unsigned spanIndex = curve.knotVect -> getFirstSpan();
+    spanIndex = curve.knotVect -> getNextSpan ( spanIndex );
+        
+    for ( unsigned index = 1; index < numSpans; index ++ )
+    {
+        knot param = curve.knotVect -> val ( spanIndex );
+        
+        int multi = curve.knotVect -> findMultiplicity ( spanIndex );
+        
+        unsigned insertSpan = knotVect -> findSpan ( param );  // Where param would be inserted in this object.
+        
+        // If knot already exists in this curve then reduce the number of knots inserted.
+        
+        if ( knotVect -> val ( insertSpan ) == param ) multi -= knotVect -> findMultiplicity ( insertSpan );
+        
+        if ( multi > 0 ) insertKnots ( param, multi, true );
+        
+        // Get next span.
+        
+        spanIndex = curve.knotVect -> getNextSpan ( spanIndex );
+    }
+}
+
+void snlCurve::makeCompatible ( snlCurve* curve )
+{
+    // Make this curve and given curve compatible.
+    // -------------------------------------------
+    // curve:   Curve to make this curve compatible with.
+
+    // Make sure the degree of each curve is the same.
+
+    if ( deg > curve -> deg )
+        curve -> elevateDegree ( deg - curve -> deg );
+
+    if ( curve -> deg > deg )
+        elevateDegree ( curve -> deg - deg );
+
+    // Make parametric ranges equal.
+
+    knot thisMin = knotVect -> min();
+    knot thisMax = knotVect -> max();
+    
+    knot compMin = ( curve -> knotVect ) -> min();  // Given curve.
+    knot compMax = ( curve -> knotVect ) -> max();
+
+    if ( thisMin != compMin || thisMax != compMax )
+    {
+        // Reparameterise both curves.
+
+        knot newMin = thisMin > compMin ? compMin : thisMin;
+        knot newMax = thisMax > compMax ? thisMax : compMax;
+
+        reparameterise ( newMin, newMax );
+        curve -> reparameterise ( newMin, newMax );
+    }
+
+    // Synchronise the knot vectors of each curve.
+
+    synchronise ( *curve );
+    curve -> synchronise ( *this );
+}
+
+unsigned snlCurve::createBezierSegments ( int** retNumKnotsAdded )
+{
+    // Create Bezier Segments over entire curve.
+    // -----------------------------------------
+    // retNumKnotsAdded:    Pointer to pointer to return array with number of inserted knots in it.
+    //                      Caller must delete this array. First index in array corresponds to second knot span.
+    //
+    // Returns:             Number of elements in returned array.
+    
+    // Find number of knots to be inserted and reallocate curve memory in one pass.
+    
+    // Find number of non-zero spans.
+    unsigned numSpans = knotVect -> getNumSpans();
+
+    // Find first spans knot index.
+    unsigned knotIndex = knotVect -> getFirstSpan();
+
+    // Resize control points array just once for all knot insertions.
+    
+    unsigned span = knotIndex;
+    unsigned extraKnots = 0;
+    
+    int* addedKnots = new int [ numSpans - 1 ];  // First index corresponds to second span as no knots are added to first span.  
+    
+    // Find amount to resize by.
+    for ( unsigned spanIndex = 1; spanIndex < numSpans; spanIndex ++ )
+    {
+        span = knotVect -> getNextSpan ( span );
+                
+        addedKnots [ spanIndex - 1 ] = deg - ( knotVect -> findMultiplicity ( span ) );
+        
+        extraKnots += addedKnots [ spanIndex - 1 ];
+    }
+    
+    // Append extra control point space to end of current control points.
+    ctrlPtNet -> appendPointSpace ( extraKnots );
+    
+    // Find knot index of second knot span.
+    span = knotVect -> getNextSpan ( knotIndex );
+    
+    for ( unsigned spanIndex = 0; spanIndex < numSpans - 1; spanIndex ++ )
+    {        
+        // Increase multiplicity of span to degree.       
+
+        knot insertParam = knotVect -> val ( span );
+                
+        insertKnots ( insertParam, addedKnots [ spanIndex ], false );
+        
+        // Re-adjust current span index to account for inserted knots.    
+        span = knotVect -> getNextSpan ( span + addedKnots [ spanIndex ] );
+    }
+    
+    *retNumKnotsAdded = addedKnots;
+    
+    return numSpans - 1;
+}
+
+void snlCurve::elevateBezierSegmentPointsDegree ( int origDegree, int byDegree, const snlCtrlPoint* origPoints,
+                                                  snlCtrlPoint* newPoints )
+{
+    // Calculate new control points for Bezier segment that is being degree elevated.
+    // ------------------------------------------------------------------------------
+    // origDegree:  Original degree of segment.
+    // byDegree:    Number of degrees segment is being elevated by.
+    // origPoints:  Original points of non-elevated segment. Expected size is origDegree + 1.
+    // newPoints:   Array to store new points in. Must be size origDegree + byDegree + 1.
+    
+    int numNewPts = origDegree + byDegree;
+    
+    for ( int index = 0; index <= numNewPts; index ++ )
+        newPoints [ index ].null();
+    
+    for ( int index = 0; index <= numNewPts; index ++ )
+    {
+        int sumStart = ( index - byDegree ) > 0 ? ( index - byDegree ) : 0;
+        int sumEnd = origDegree < index ? origDegree : index;
+        
+        for ( int index2 = sumStart; index2 <= sumEnd; index2 ++ )
+        {
+            double multiplier = ( (double) binCoefs::binCoefArray [ origDegree ] [ index2 ] ) *
+                                ( (double) binCoefs::binCoefArray [ byDegree ] [ index - index2 ] ) /
+                                ( (double) binCoefs::binCoefArray [ origDegree + byDegree ] [ index ] );        
+            
+            newPoints [ index ] += origPoints [ index2 ] * multiplier;
+        }            
+    }
+}
+
+void snlCurve::elevateDegree ( int byDegree )
+{
+    // Elevate degree of curve
+    // -----------------------
+    // byDegree:    Number of degrees to elevate by.
+    
+    // Convert curve into Bezier segments.
+    
+    int* addedKnots;
+    
+    unsigned numSegments = createBezierSegments ( &addedKnots );
+    
+    numSegments ++;  // Number returned is array size which is one less than number of segments.    
+    
+    // Grow control point net.
+    
+    ctrlPtNet -> appendPointSpace ( numSegments * byDegree );
+    
+    // Elevate degree of Bezier segments.
+    
+    int newSegmentSize = deg + byDegree + 1;
+    
+    snlCtrlPoint* tmpPts = new snlCtrlPoint [ newSegmentSize ];    
+    
+    const snlCtrlPoint* ctrlPts = ctrlPtNet -> getCtrlPts();
+    
+    int ptsIndex = 0;
+    
+    unsigned spanIndex = deg * 2;
+    
+    // Generate new points per segment.
+    
+    for ( unsigned segment = 0; segment < numSegments; segment ++ )
+    {        
+        elevateBezierSegmentPointsDegree ( deg, byDegree, ctrlPts + ptsIndex, tmpPts );
+        
+        // Replace points in control point array. First and last points are not altered.
+        ctrlPtNet -> replacePoints ( tmpPts + 1, newSegmentSize - 2, ptsIndex + 1, deg - 1 );        
+        
+        ptsIndex += deg + byDegree;
+        
+        // Add knots to knot vector.
+        knotVect -> increaseMultiplicity ( spanIndex, byDegree );
+        
+        spanIndex += deg + byDegree;
+    }
+    
+    // Make sure start clamp is of degree + 1 multiplicity.
+    
+    knotVect -> increaseMultiplicity ( deg, byDegree );
+    
+    // Increase degree indicator variables
+    
+    deg += byDegree;
+    
+    knotVect -> degree ( deg );
+    
+    // Remove number of knots that were added during knot insertion.
+    
+    spanIndex = knotVect -> getFirstSpan();
+    
+    spanIndex += deg;
+    
+    for ( unsigned segment = 0; segment < numSegments - 1; segment ++ )
+    {
+        removeKnots ( addedKnots [ segment ], spanIndex, 0.0 );
+        
+        spanIndex += deg - addedKnots [ segment ];
+    }
+    
+    // Clean up.
+    
+    delete[] addedKnots;
+    delete[] tmpPts;
+}
+
+void snlCurve::appendCurve ( snlCurve* curveToAppend, bool copy )
+{
+    // Append a curve to this curve.
+    // -----------------------------
+    // curveToAppend:    Curve to append to this curve.
+    // copy:             Copy curveToAppend and don't modify it in any way.
+    //
+    // Notes:            This curve may have it's degree elevated to accomodate new curve.
+    
+    
+    // Copy curve if required.
+    
+    snlCurve* curve;
+    
+    if ( copy )
+        curve = new snlCurve ( *curveToAppend );
+    else
+        curve = curveToAppend;
+        
+    // Elevate degree if needed.
+        
+    if ( deg > curve -> degree() )
+        curve -> elevateDegree ( deg - curve -> degree() );
+    else if ( deg < curve -> degree() )
+        elevateDegree ( curve -> degree() - deg );
+        
+    // Re-parameterise curve to append so that it's starting knot val is the same as this curves knot end val.
+    
+    double min = knotVect -> min();
+    double max = knotVect -> max();
+    
+    curve -> reparameterise ( max, max + 1.0 );
+    
+    // Join control points and knot vectors together.
+    
+    ctrlPtNet -> appendPoints ( ( curve -> ctrlPtNet ) -> getCtrlPts() + 1, ( curve -> ctrlPtNet ) -> getNumPts() - 1 );
+    
+    knotVect -> join ( curve -> knotVect );
+    
+    reparameterise ( min, max );
+        
+    // Clean up.        
+    
+    if ( copy ) delete curve;
+}
+
+void snlCurve::print()
+{
+    // Print curve to standard out.
+    // ----------------------------
+
+    ctrlPtNet -> print();
+    knotVect -> print();
+}
+
+void snlCurve::vertexNet ( snlVertexNet* vNet, double tolerance, bool parametric )
+{
+    // Return approximation to curve.
+    // --------------------------------
+    // tolerance:    Tolerance to approximate to.
+    // parametric:   Do a parametric analysis as opposed to knot refinement.
+    // vNet:         Vertex net to fill with data.
+    
+    if ( parametric )
+    {
+        vertexNetParam ( vNet, tolerance );
+        return;
+    }
+    
+    const snlCtrlPoint*   ctrlPts;    
+    int                   size;
+    
+    snlCurve* tmpCurve = 0;
+    
+    if ( tolerance > 0.0 )
+    {
+        tmpCurve = new snlCurve ( *this );
+        tmpCurve -> refine ( tolerance );
+        ctrlPts = ( tmpCurve -> ctrlPtNet ) -> getCtrlPts();
+        size = ( tmpCurve -> ctrlPtNet ) -> size();
+    }
+    else
+    {    
+        ctrlPts = ctrlPtNet -> getCtrlPts();
+        size = ctrlPtNet -> size();        
+    }
+    
+    vNet -> vertexNet ( ctrlPts, size );
+    
+    if ( tmpCurve ) delete tmpCurve;
+}
+
+void snlCurve::vertexNetParam ( snlVertexNet* vNet, double tolerance )
+{
+    // Generate vertex net based on evaluation of curve.
+    // -------------------------------------------------
+    // vNet:        Vertex network to load with points.
+    // tolerance:   Tolerance to actual surface.
+    
+    int              size;    
+    
+    snlPoint* pts = 0;
+    
+    if ( tolerance <= 0.0 )
+    {
+        size = ctrlPtNet -> size();
+        
+        pts = new snlPoint [ size ];
+        
+        double minP = minParam();
+        
+        double paramStep = ( maxParam() - minP ) / (double) ( size - 1 );
+        
+        for ( int index = 0; index < size; index ++ )
+        {
+            double param = minP + paramStep * (double) index;            
+            pts [ index ] = eval ( param );            
+        }
+    }
+    else
+    {
+        // !@#$ Not complete!!
+    }
+    
+    if ( pts ) vNet -> vertexNet ( pts, size );
+    
+    if ( pts ) delete[] pts;
+}
+
+const snlKnotVector& snlCurve::knotVector()
+{
+    return *knotVect;
+}
+
+int snlCurve::degree()
+{
+    return deg;
+}
+
diff --git a/src/snlCurve.h b/src/snlCurve.h
new file mode 100644
index 0000000..b50f2bd
--- /dev/null
+++ b/src/snlCurve.h
@@ -0,0 +1,140 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** General NURBS Curve ***
+
+#ifndef SNL_CURVE_H
+#define SNL_CURVE_H
+
+
+#include "snlCtrlPointNetCurve.h"
+#include "snlCurveBase.h"
+#include "snlKnotVector.h"
+#include "snlPoint.h"
+#include "snlVector.h"
+#include "snlVertex.h"
+#include "snlVertexNet.h"
+
+
+class snlCurve : public snlCurveBase
+{    
+    public:        
+
+        virtual ~snlCurve();
+        
+        snlCurve();
+        
+        snlCurve ( int degree, unsigned size, snlPoint& start, snlPoint& end );
+                     
+        snlCurve ( int degree, unsigned size, snlCtrlPoint* points, knot* knots = 0 );
+
+        snlCurve ( unsigned size, snlCtrlPoint* points, snlKnotVector* knotVector );
+        
+        snlCurve ( const snlCurve& copyFrom );  // Copy constructor.
+
+        snlCurve& operator= ( const snlCurve& curveToCopy );
+        
+        // Interpolated / approximated curve.
+        snlCurve ( snlPoint* points, unsigned size, int fittingType, int degree, bool closedLoop = false, knot** retParams = 0 );
+
+        // Circular Arc.
+        snlCurve ( snlPoint& startPoint, snlPoint& endPoint, snlPoint& centrePoint, int numSections = - 1 );
+        
+        snlCtrlPointNetCurve& controlPointNet();
+        
+        snlPoint evalHmg ( knot param ) const;
+        
+        virtual snlPoint eval ( knot param ) const;
+        
+        snlPoint* evalDerivsHmg ( knot param, unsigned deriv ) const;
+        
+        snlPoint* evalDerivs ( knot param, unsigned deriv ) const;
+        
+        snlVector velocity ( knot param );
+        
+        void insertKnot ( knot iParam, bool reallocate );
+        void insertKnots ( knot iParam, int numToInsert, bool reallocate );
+        
+        double removeKnots ( int numKnots, unsigned removalIndex, double tolerance );
+        double removeKnot ( unsigned removalIndex, double tolerance );        
+        
+        void refine ( double tolerance );
+        
+        double maxParam() const;
+        double minParam() const;
+        
+        double param ( unsigned index ) const;
+
+        const snlKnotVector& knotVector();
+        
+        int degree();
+        
+        int size();
+        
+        void truncate ( knot param, bool keepLast = false, bool reparameterise = false );
+        void insertPartition ( knot param );
+        void reparameterise ( knot startKnot, knot endKnot );
+        void reverseEvalDirection();
+        
+        void globalInterpClosedLoop ( int type, snlPoint* points, unsigned size, int degree, knot** retParams );
+        void globalInterp ( int type, snlPoint* points, unsigned size, int degree, knot** retParams );
+        static snlCtrlPoint* genGlobalInterpPoints ( snlPoint* points, unsigned size, knot* params, snlKnotVector* knots );
+        
+        void localInterpQuadratic ( snlPoint* points, unsigned size );  // Local quadratic interpolation.
+        void localInterpCubic ( snlPoint* points, unsigned size );  // Local cubic interpolation.
+        
+        void synchronise ( snlCurve& curve );
+        void makeCompatible ( snlCurve* curve );
+        
+        unsigned createBezierSegments ( int** retNumKnotsAdded );
+        
+        static void elevateBezierSegmentPointsDegree ( int origDegree, int byDegree, const snlCtrlPoint* origPoints,
+                                                       snlCtrlPoint* newPoints );
+        
+        void elevateDegree ( int byDegree );
+        
+        void appendCurve ( snlCurve* curve, bool copy = true );
+
+        void print();
+        
+        // Abstract Implementation.
+        
+        virtual void vertexNet ( snlVertexNet* vNet, double tolerance, bool parametric );
+        
+        // Enumerations.
+        
+        enum SNL_FITTING_TYPES
+        {
+            SNL_GLOBAL_INTERPOLATION,
+            SNL_GLOBAL_INTERP_CENTRIFUGAL,
+            SNL_LOCAL_INTERPOLATION
+        };
+        
+    protected:
+
+        void vertexNetParam ( snlVertexNet* vNet, double tolerance );
+    
+    private:
+    
+        int        deg;  // Degree of curve.
+    
+        snlCtrlPointNetCurve*       ctrlPtNet;
+                                    
+        snlKnotVector*              knotVect;
+};
+
+#endif
diff --git a/src/snlCurveBase.h b/src/snlCurveBase.h
new file mode 100644
index 0000000..2a61596
--- /dev/null
+++ b/src/snlCurveBase.h
@@ -0,0 +1,54 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2003 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** Curve Base Class ***
+
+#ifndef SNL_CURVE_BASE_H
+#define SNL_CURVE_BASE_H
+
+#include "snlKnotVector.h"
+#include "snlPoint.h"
+#include "snlVertexNet.h"
+
+#ifdef SGI_MIPS
+
+    #include <iostream.h>
+    #include <math.h>
+    #include <float.h>
+    
+#else
+
+    #include <iostream>
+    #include <cmath>
+    #include <cfloat>
+    
+    using namespace std;
+    
+#endif
+
+class snlCurveBase
+{
+    public:
+
+        virtual ~snlCurveBase(){};
+
+        virtual snlPoint eval ( knot param ) const = 0;
+    
+        virtual void vertexNet ( snlVertexNet* vNet, double tolerance, bool parametric ) = 0;
+};
+
+#endif
diff --git a/src/snlKnotVector.cpp b/src/snlKnotVector.cpp
new file mode 100644
index 0000000..d155a1b
--- /dev/null
+++ b/src/snlKnotVector.cpp
@@ -0,0 +1,866 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2003 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "snlKnotVector.h"
+
+snlKnotVector::~snlKnotVector ()
+{
+    if ( knots ) delete [ ] knots;
+}
+
+snlKnotVector::snlKnotVector ( const snlKnotVector& vector )
+{
+    // Copy constructor.
+    // -----------------
+
+    copyFrom ( vector );
+}
+
+snlKnotVector::snlKnotVector ( knot* knotArrayToUse, unsigned size, int degree, int snlKnotVectorType, bool copy )
+{
+    unsigned    index;
+
+    if ( copy )
+    {
+        // Create new knot vector array from supplied data of "size".
+        knots = new knot [ size ];
+
+        // Copy supplied data into array.
+        if ( knotArrayToUse)
+            for ( index = 0; index < size; index ++ )
+                *( knots + index ) = *( knotArrayToUse + index );
+    }
+    else
+        knots = knotArrayToUse;
+
+    vectorSize = size;
+    
+    deg = degree;
+
+    kvType = snlKnotVectorType;
+}
+
+snlKnotVector::snlKnotVector ( knot startVal, knot endVal, unsigned numKnots, int degree )
+{
+    
+    //  Generate a new Knot Vector
+    //  --------------------------
+    //
+    //  startVal:   Starting parametric knot value.
+    //  endVal:     End parametric knot value.
+    //  numKnots:   Number of knots in vector.
+    //  degree:     Degree to use during evaluation of basis functions.
+    //
+    //  Returns:    Constructor
+    //
+    //  Notes:      Assumes clamped (open) vector.     
+
+    unsigned    index;
+    
+    kvType = open;
+    
+    deg = degree;
+
+    // Calculate Spacing between knots.
+    knot step = (endVal - startVal) / (knot) ( numKnots - (2 * degree) - 1 );
+
+    // Create new array.
+    knots = new knot [ numKnots ];
+
+    // Fill knot vector with data.
+    for ( index = 0; index < numKnots; index ++ )
+    {
+        if ( index < (unsigned) degree )
+            knots [ index ] = startVal;
+
+        else if ( index > ( numKnots - 1 - degree ) )
+            knots [ index ] = endVal;
+
+        else
+            knots [ index ] = startVal + ( step * ((knot) ( index - degree )) );
+    }
+
+    vectorSize = numKnots;
+}
+
+snlKnotVector::snlKnotVector ( int size, int degree, knot* params )
+{
+    // Generate knot vector given existing parameters.
+    // -----------------------------------------------
+    // params:  Parameters to average between.
+    // size:    Size of parameters array.
+    // degree:  Degree of vector.
+    //
+    // Notes:   Used for interpolation.
+
+    deg = degree;
+
+    kvType = open;
+
+    vectorSize = size + degree + 1;
+
+    knots = new knot [ vectorSize ];
+    
+    int index;
+    
+    // Start clamp.
+    for ( index = 0; index <= degree; index ++ )
+        knots [ index ] = 0.0;
+    
+    // End clamp.
+    for ( index = size; index < (int) vectorSize; index ++ )
+        knots [ index ] = 1.0;
+    
+    // Internal knots.
+    for ( index = 1; index < size - degree; index ++ )
+    {
+        knot sum = 0.0;
+        
+        for ( int paramIndex = index; paramIndex < index + degree; paramIndex ++ )
+            sum += params [ paramIndex ];        
+        
+        knots [ index + degree ] = sum / (double) degree;
+    }
+}
+
+snlKnotVector::snlKnotVector ( int degree )
+{
+    // Generate knot vector for Bezier patch.
+    // --------------------------------------
+    // degree:      Degree of knot vector.
+
+    kvType = open;
+
+    deg = degree;
+
+    vectorSize = ( degree + 1 ) * 2;
+
+    knots = new knot [ vectorSize ];
+
+    int upperIndex = degree + 1;
+
+    for ( int index = 0; index <= degree; index ++ )
+    {
+        knots [ index ] = 0.0;
+        knots [ upperIndex ++ ] = 1.0;
+    }
+}
+
+snlKnotVector& snlKnotVector::operator= ( const snlKnotVector& knotVectToCopy )
+{
+    // Assignment Operator.
+    // --------------------
+
+    if ( this != &knotVectToCopy )
+    {
+        if ( knots ) delete [ ] knots;
+
+        copyFrom ( knotVectToCopy );
+    }
+
+    return *this;
+}
+
+void snlKnotVector::copyFrom ( const snlKnotVector& vector )
+{
+    vectorSize = vector.vectorSize;
+    
+    knots = new knot [ vectorSize ];
+    
+    deg = vector.deg;
+
+    for ( unsigned index = 0; index < vectorSize; index ++ )
+        knots [ index ] = vector.knots [ index ];   
+
+    kvType = vector.type();
+}
+
+unsigned snlKnotVector::findSpan ( knot param ) const
+{
+
+    //  Find Knot Span corresponding to parameter
+    //  -----------------------------------------
+    //  param:      Parameter to find span of.
+
+    unsigned    count;
+    unsigned    span = 0;
+
+    if ( param > knots [ vectorSize - 1 ] )
+        param = knots [ vectorSize - 1 ];
+
+    if ( param == knots [ vectorSize - 1 ] )
+    {
+        // Allow clamped end value to be a valid parameter.
+        // Not strictly correct but works better in practice.
+
+        // Step backwards through knot array until first non-zero length span found.
+        for( count = ( vectorSize - 1 ); count > 0 ; count -- )
+        {
+            if ( param <= knots [ count ] && param > knots [ count - 1 ] )
+                span = count - 1;
+        }
+    }
+    else
+    {
+        for( count = 0; count < ( vectorSize - 1 ); count ++ )
+        {
+            if ( param >= knots [ count ] && param < knots [ count + 1 ] )
+                span = count;
+        }
+    }
+
+    return span;
+}
+
+knot snlKnotVector::val ( unsigned index ) const
+{
+    return knots [ index ];
+}
+
+const knot* snlKnotVector::getKnotPtr ( unsigned index )
+{
+    return knots + index;
+}
+
+unsigned snlKnotVector::size() const
+{
+    // Return size of knot vector.
+
+    return vectorSize;
+}
+
+int snlKnotVector::degree()
+{
+    // Get degree of knot vector.
+    // --------------------------
+
+    return deg;
+}
+
+void snlKnotVector::degree ( int val )
+{
+    // Set degree of knot vector.
+    // --------------------------
+    
+    deg = val;
+}
+
+bool snlKnotVector::equals ( const snlKnotVector& knotVect ) const
+{
+    if ( deg != knotVect.deg ) return false;
+    
+    if ( vectorSize != knotVect.vectorSize ) return false;
+    
+    for ( unsigned index = 0; index < vectorSize; index ++ )
+        if ( knots [ index ] != knotVect.knots [ index ] ) return false;
+    
+    return true;
+}
+
+void snlKnotVector::insertKnot ( knot param, int numTimes )
+{
+    // Insert new knot into vector.
+    // ----------------------------
+    // param:       knot to insert.
+    // numTimes:    Number of times to insert knot into vector.
+
+    unsigned        index;
+
+    if ( ! knots ) return;
+
+    unsigned span = findSpan ( param );
+
+    knot* newKnots = new knot [ vectorSize + numTimes ];
+
+    // Copy up to insertion point.
+    for ( index = 0; index <= span; index ++ )
+        newKnots [ index ] = knots [ index ];
+
+    // Add in new knot.
+    for ( int count = 0; count < numTimes; count ++ )
+        newKnots [ span + count + 1 ] = param;
+
+    // Copy rest of old knots to new vector.
+    for ( index = span + numTimes + 1; index < vectorSize + numTimes; index ++ )
+        newKnots [ index ] = knots [ index - numTimes ];
+
+    delete[] knots;
+
+    knots = newKnots;
+
+    vectorSize += numTimes;
+}
+
+void snlKnotVector::removeKnot ( unsigned spanIndex )
+{
+    // Remove knot at spanIndex
+    // --------------------
+
+    unsigned        index;
+
+    knot * newKnots = new knot [ vectorSize - 1 ];
+
+    // Copy up to removal point.
+    for ( index = 0; index < spanIndex; index ++ )
+        newKnots [ index ] = knots [ index ];
+
+    // Copy remainder of knots. Skip knot to be removed.
+    for ( index = spanIndex; index < vectorSize - 1; index ++ )
+        newKnots [ index ] = knots [ index + 1 ];
+
+    delete[] knots;
+
+    knots = newKnots;
+
+    vectorSize --;
+}
+
+void snlKnotVector::grow ( unsigned bySize )
+{
+    // Increase size of knot vector array.
+    // -----------------------------------
+    // bySize:    Size to increase knot vector by.
+    
+    knot* newKnots = new knot [ vectorSize + bySize ];
+    
+    for ( unsigned index = 0; index < vectorSize; index ++ )
+        newKnots [ index ] = knots [ index ];
+    
+    delete[] knots;
+    
+    knots = newKnots;
+    
+    vectorSize += bySize;
+}
+
+void snlKnotVector::increaseMultiplicity ( unsigned spanIndex, int numKnotsToAdd )
+{
+    // Increase multiplicity of knots at span.
+    // ---------------------------------------
+    // spanIndex:        Index of knot span to process.
+    // numKnotsToAdd:    Number of knots to add at spanIndex.
+    
+    unsigned        index;
+
+    if ( ! knots ) return;
+    
+    knot param = knots [ spanIndex ];
+
+    knot* newKnots = new knot [ vectorSize + numKnotsToAdd ];
+
+    // Copy up to insertion point.
+    for ( index = 0; index <= spanIndex; index ++ )
+        newKnots [ index ] = knots [ index ];
+
+    // Add in new knots.
+    for ( index = spanIndex + 1; index <= spanIndex + numKnotsToAdd; index ++ )    
+        newKnots [ index ] = param;
+
+    // Copy rest of old knots to new vector.
+    for ( index = spanIndex + numKnotsToAdd + 1; index < vectorSize + numKnotsToAdd; index ++ )
+        newKnots [ index ] = knots [ index - numKnotsToAdd ];
+
+    delete[] knots;
+
+    knots = newKnots;
+
+    vectorSize += numKnotsToAdd;
+}
+
+knot* snlKnotVector::getKnotArray()
+{
+    return knots;
+}
+
+int snlKnotVector::type() const
+{
+    return kvType;
+}
+
+const knot* snlKnotVector::array()
+{
+    // Return pointer to array of knots
+    // --------------------------------
+
+    return knots;
+}
+
+knot snlKnotVector::max() const
+{
+    // Return maximum knot value in vector
+    // -----------------------------------
+
+    return knots [ vectorSize - 1 ];
+}
+
+knot snlKnotVector::min() const
+{
+    // Return minimum knot value in vector
+    // -----------------------------------
+
+    return knots [ 0 ];
+}
+
+unsigned snlKnotVector::getNumSpans() const
+{
+    // Return number of non-zero length spans
+    // --------------------------------------
+    
+    unsigned numSpans = 0;
+
+    for ( unsigned index = 0; index < ( vectorSize - 1 ); index ++ )
+    {
+        if ( knots [ index + 1 ] > knots [ index ] ) numSpans++;
+    }
+
+    return numSpans;
+}
+
+unsigned snlKnotVector::getFirstSpan() const
+{
+    // Get first non zero length span
+    // ------------------------------
+
+    for ( unsigned index = 0; index < ( vectorSize - 1 ); index ++ )
+    {
+        if ( knots [ index + 1 ] > knots [ index ] ) return index;
+    }
+
+    return 0;
+}
+
+unsigned snlKnotVector::getNextSpan ( unsigned spanIndex ) const
+{
+    // Get next non-zero length span given current spanIndex.
+    // ------------------------------------------------------
+    // Returns:     Next non-zero length span index.
+
+    for ( unsigned index = spanIndex + 1; index < ( vectorSize - 1 ); index ++ )
+    {
+        if ( knots [ index + 1 ] > knots [ index ] ) return index;
+    }
+
+    return 0;
+}
+
+unsigned snlKnotVector::getPreviousSpan ( unsigned spanIndex ) const
+{
+    // Get previous non-zero length span given current spanIndex.
+    // ----------------------------------------------------------
+    // Returns:     Previous non-zero length span index.
+    
+    for ( unsigned index = spanIndex - 1; index >= 0; index -- )
+    {
+        if ( knots [ index + 1 ] > knots [ index ] ) return index;
+    }
+
+    return 0;
+}
+
+int snlKnotVector::findMultiplicity ( unsigned index ) const
+{
+    // Find the knot multiplicity at index
+    // -----------------------------------
+    // index:       Index to search.
+    //
+    // Returns:     Number of knots found.
+
+    // Find last index that has required knot value.
+
+    unsigned cIndex = index;
+
+    while ( knots [ cIndex ] == knots [ cIndex + 1 ] ) cIndex ++;
+
+    // Count multiples backwards.
+
+    int multi = 1;
+
+    while ( knots [ cIndex ] == knots [ cIndex - 1 ] )
+    {
+        multi ++;
+
+        if ( cIndex == 1 )
+            break;
+        else
+            cIndex --;
+    }
+
+    return multi;
+}
+
+int snlKnotVector::findMultiplicity ( knot param ) const
+{
+    // Find the knot multiplicity at a particular knot value.
+    // ------------------------------------------------------
+    // param:       Parameter to evaluate.
+    //
+    // Returns:     Number of knots found.
+    
+    // Find span parameter belongs to.
+    unsigned span = findSpan ( param );
+    
+    // Find value of knot associated with span.
+    knot spanKnotVal = val ( span );
+    
+    // Return multiplicity.
+    if ( param == spanKnotVal )    
+        return findMultiplicity ( span );
+    
+    return 0;        
+}
+
+void snlKnotVector::truncate ( knot param, bool keepLast )
+{
+    // Truncate knot vector.
+    // ---------------------
+    // param:    Parameter to truncate at.
+    // keepLast: Keep last section of knot vector, discard first part.
+    //
+    // Notes:    Truncates at last of knots valued at param.
+    //           Assumes degree knots are present at param.
+    
+    unsigned start, end;    
+    
+    if ( keepLast )
+    {
+        start = findSpan ( param );
+        start = getPreviousSpan ( start ) + 1;  // Go to start of knots if more than one at param.
+        end = vectorSize - 1;        
+    }
+    else
+    {
+        start = 0;
+        end = findSpan ( param );        
+    }
+    
+    // Generate new knot vector and populate.
+    
+    unsigned newSize = end - start + 2;  // Add one point for correct clamping.
+    
+    knot* newKnots = new knot [ newSize ];
+    
+    unsigned copyFrom = start;
+    
+    if ( keepLast )
+    {
+        for ( unsigned index = 1; index < newSize; index ++, copyFrom ++ )
+            newKnots [ index ] = knots [ copyFrom ];
+            
+        newKnots [ 0 ] = param;
+    }
+    else
+    {
+        for ( unsigned index = 0; index < newSize - 1; index ++, copyFrom ++ )
+            newKnots [ index ] = knots [ copyFrom ];
+        
+        newKnots [ newSize - 1 ] = param;
+    }    
+        
+    delete[] knots;
+    
+    knots = newKnots;
+    
+    vectorSize = newSize;    
+}
+
+void snlKnotVector::reparameterise ( knot startKnot, knot endKnot )
+{
+    // Reparameterise knot vector using a linear reparameterisation.
+    // -------------------------------------------------------------
+    // startKnot:    Knot vectors new starting value.
+    // endKnot:      Knot vectors new ending value.
+    
+    double oldLength = knots [ vectorSize - 1 ] - knots [ 0 ];
+    double newLength = endKnot - startKnot;
+    
+    double oldStartKnot = knots [ 0 ];
+    
+    for ( unsigned index = 0; index < vectorSize; index ++ )
+        knots [ index ] = ( ( knots [ index ] - oldStartKnot ) / oldLength ) * newLength + startKnot;
+}
+
+void snlKnotVector::reverse()
+{
+    // Reverse knot vector.
+    // --------------------
+
+    unsigned midPoint = vectorSize / 2;
+
+    unsigned swapIndex = vectorSize - 1;
+
+    for ( unsigned index = 0; index < midPoint; index ++ )
+    {
+        knot trans = knots [ index ];  // Transfer value.
+        knots [ index ] = knots [ swapIndex ];
+        knots [ swapIndex -- ] = trans;
+    }
+}
+
+void snlKnotVector::join ( snlKnotVector* knotVector )
+{
+    // Join another knot vector to the end of this one.
+    // ------------------------------------------------
+    // knotVector:    Knot vector to join. Must be of same degree as this knot vector.
+    //
+    // Notes:         Assumes end clamp of this and start clamp values of other knot vector
+    //                are the same. This is _not_ just a join of arrays, it results in a properly
+    //                formed knot vector.
+    
+    if ( knotVector -> deg != deg ) return;
+    
+    unsigned newSize = vectorSize + ( knotVector -> vectorSize ) - deg - 2;
+    
+    unsigned oldSize = vectorSize;
+    
+    grow ( newSize - vectorSize );
+    
+    // Copy new points into knot array.    
+    
+    unsigned copyFromIndex = deg + 1;  // The appended vector loses deg + 1 knots from the start of it's array.
+    
+    // This knot vector looses one knot at end of the array
+    
+    for ( unsigned index = oldSize - 1; index < newSize; index ++ )
+        knots [ index ] = knotVector -> knots [ copyFromIndex ++ ];    
+}
+
+basis* snlKnotVector::evalBasis ( knot param )
+{
+
+    // Evaluate Basis Functions
+    // ------------------------
+    // param:       Paramater value to process at.
+    //
+    // Returns:     Array of basis function values, evaluated at param.
+    //
+    // Notes:       The calling function owns the returned array and is responsible for
+    //              deleting it using delete[]. The size of the array is always deg + 1.
+    
+    basis* bVals = new basis [ deg + 1 ];
+
+    if ( param == knots [ 0 ] && kvType == open )
+    {
+        // First basis function value is 1 the rest are zero.
+
+        bVals [ 0 ] = 1.0;
+
+        for ( int index = 1; index < deg + 1; index ++ )
+            bVals [ index ] = 0.0;
+
+        return bVals;
+    }
+
+    if ( param == knots [ vectorSize - 1 ] && kvType == open )
+    {
+        // Last basis function value is 1 the rest are zero.
+
+        bVals [ deg ] = 1.0;
+
+        for ( int index = 0; index < deg; index ++ )
+            bVals [ index ] = 0.0;
+
+        return bVals;
+    }
+    
+    unsigned spanIndex = findSpan ( param );
+
+    basis*      right = new basis [ deg + 1 ];
+    basis*      left = new basis  [ deg + 1 ];
+    basis       saved, temp;
+    unsigned    index, level;
+
+    bVals [ 0 ] = 1.0;
+
+    for ( level = 1; level <= (unsigned) deg; level ++ )
+    {
+        left [ level ] = param - knots [ spanIndex + 1 - level ];
+        right [ level ] = knots [ spanIndex + level ] - param;
+
+        saved = 0.0;
+
+        for ( index = 0; index < level; index ++ )
+        {
+            temp = bVals [ index ] / ( right [ index + 1 ] + left [ level - index ] );
+            bVals [ index ] = saved + right [ index + 1 ] * temp;
+            saved = left [ level - index ] * temp;
+        }
+
+        bVals [ level ] = saved;
+    }
+
+    delete[] right;
+    delete[] left;
+
+    return bVals;
+}
+
+basis* snlKnotVector::evalBasisDeriv ( knot param, int deriv )
+{
+    // Evaluate basis functions and their derivatives.
+    // -----------------------------------------------
+    //
+    // param:       Parameter to process at.
+    // deriv:       Which derivative to process to.
+    //
+    // Returns:     Two dimensional array of basis and basis derivative values.
+    //
+    // Notes:       The calling function is responsible for deleting the returned array
+    //              using delete[]. The size of the array is [ deriv + 1 ] [ deg + 1 ].    
+
+    basis*      right = new basis [ deg + 1 ];
+    basis*      left = new basis [ deg + 1 ];
+    basis       saved, temp;
+    basis*      derivSaved = new basis [ deriv ];
+    unsigned    index, level;
+    int         count;
+    
+    basis* bVals = new basis [ ( deriv + 1 ) * ( deg + 1 ) ];
+    
+    unsigned spanIndex = findSpan ( param );
+
+    unsigned         overFlow;  // Just in case deriv is bigger than degree.
+
+    if ( deriv > deg )
+    {
+        overFlow = deriv - deg;
+        deriv = deg;
+    }
+    else
+        overFlow = 0;
+
+    bVals [ 0 ] = 1.0;
+
+    for ( level = 1; level <= (unsigned) deg; level ++ )
+    {
+        left [ level ] = param - knots [ spanIndex + 1 - level ];
+        right [ level ] = knots [ spanIndex + level ] - param;
+
+        saved = 0.0;
+
+        for ( count = 0; count < deriv; count ++ )
+            derivSaved [ count ] = 0.0;
+
+        for ( index = 0; index < level; index ++ )
+        {
+            temp = bVals [ index ] / ( right [ index + 1 ] + left [ level - index ] );
+            bVals [ index ]  = saved + right [ index + 1 ] * temp;
+            saved = left [ level - index ] * temp;
+
+            // Process first order derivatives as needed.
+            if ( level > (unsigned) ( deg - deriv ) )
+            {
+                bVals [ index + ( ( deg - level + 1 ) * ( deg + 1 ) ) ] = level * ( derivSaved [ 0 ] - temp );
+                derivSaved [ 0 ] = temp;
+            }
+
+            // Process other order derivatives.
+            for ( count = deg - level + 2; count <= deriv; count ++ )
+            {
+                temp = bVals [ index + ( count * ( deg + 1 ) ) ] /
+                               ( right [ index + 1 ] + left [ level - index ] );
+
+                bVals [ index + ( count * ( deg + 1 ) ) ] = level * ( derivSaved [ count - 1 ] - temp );
+
+                derivSaved [ count - 1 ] = temp;
+            }
+        }
+
+        bVals [ level ] = saved;
+
+        // Add last first order derivative at this level.
+        if ( level > (unsigned) ( deg - deriv ) )
+            bVals [ level + ( ( deg - level + 1 ) * ( deg + 1 ) ) ] = level * derivSaved [ 0 ];
+
+        // Add last other order derivatives at this level.
+        for ( count = deg - level + 2; count <= deriv; count ++ )
+            bVals [ index + ( count * ( deg + 1 ) ) ] = level * derivSaved [ count - 1 ];
+    }
+
+    if ( overFlow )
+    {
+        for ( index = ( deriv + 1 ) * ( deg + 1 ); index < ( deriv + overFlow + 1 ) * ( deg + 1 ); index ++ )
+            bVals [ index ] = 0.0;
+    }
+
+    delete[] left;
+    delete[] right;
+    delete[] derivSaved;
+    
+    return bVals;
+}
+
+double* snlKnotVector::calcRemovalAlphas ( unsigned span )
+{
+    // Calculate alphas used for knot removal.
+    // ---------------------------------------
+    // span:    Span of knot where removal is taking place.
+    //
+    // Returns:    Array of alphas. Must be deleted by caller.
+    
+    // Find multiplicity of knot at index.
+    unsigned multi = findMultiplicity ( span );
+
+    // Calculate the number of equations.
+    unsigned numEqns = deg - multi + 1;
+    
+    knot rParam = val ( span );
+    
+    double* alpha = new double [ numEqns ];
+
+    unsigned count = 0;
+
+    for ( unsigned index = span - deg; index <= ( span - multi ) ; index ++ )
+    {
+        alpha [ count ++ ]  = ( rParam - ( knots [ index ] ) )
+                                / ( knots [ index + deg + 1 ] - knots [ index ] );
+    }
+    
+    return alpha;
+}
+
+void snlKnotVector::print()
+{
+    // Print knot vector to std out.
+    // -----------------------------
+
+    cout << "degree: " << deg << " Knots: ";
+
+    for ( unsigned index = 0; index < vectorSize; index ++ )
+        cout << knots [ index ] << "  ";
+
+    cout << "\n";
+}
+
+void snlKnotVector::print_cpp()
+{
+    // Print knot vector to std out.
+    // -----------------------------
+    // Notes:   Prints in c++ code format.
+
+    cout << "{ ";
+
+    for ( unsigned index = 0; index < vectorSize; index ++ )
+    {
+        cout << knots [ index ];
+
+        if ( index < vectorSize - 1 )
+            cout << ", ";
+    }
+
+    cout << " };";
+}
+
diff --git a/src/snlKnotVector.h b/src/snlKnotVector.h
new file mode 100644
index 0000000..20b7a8d
--- /dev/null
+++ b/src/snlKnotVector.h
@@ -0,0 +1,139 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2003 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** Single dimension array of knots. ***
+
+#ifndef SNLKNOTVECTOR_H
+#define SNLKNOTVECTOR_H
+
+#ifdef SGI_MIPS
+
+    #include <iostream.h>
+    #include <math.h>
+    
+#else
+
+    #include <iostream>
+    #include <cmath>
+    
+    using namespace std;
+    
+#endif
+
+typedef double knot;
+typedef double basis;
+
+class snlKnotVector
+{
+    public:
+
+        virtual ~snlKnotVector ();
+
+        snlKnotVector ( const snlKnotVector& );  // Copy constructor.
+    
+        // Use existing knot array.
+        snlKnotVector ( knot* knotArrayToUse, unsigned size, int degree, int knotVectType = 1, bool copy = false );
+        
+        // Generate new knot array.        
+        snlKnotVector ( knot startVal, knot endVal, unsigned numKnots, int degree );
+
+        // Generate knot vector given existing parameters. Used for interpolation.
+        snlKnotVector ( int size, int degree, knot* params );
+
+        // Generate knot vector for Bezier patch.
+        snlKnotVector ( int degree );
+
+        snlKnotVector& operator= ( const snlKnotVector& KnotVectToCopy );
+
+        enum knotVectorType
+        {
+            open = 1,       // Clamped
+            periodic = 2    // Periodic will not be supported for some time yet :-)
+        };
+
+        knot val ( unsigned index ) const;
+        
+        const knot* getKnotPtr ( unsigned index );
+
+        unsigned size() const;
+        
+        int degree();
+        void degree ( int val );
+        
+        bool equals ( const snlKnotVector& knotVect ) const;
+
+        void insertKnot ( knot param, int numTimes = 1 );
+        
+        void removeKnot ( unsigned spanIndex );
+        
+        void grow ( unsigned bySize );
+        
+        void increaseMultiplicity ( unsigned spanIndex, int numKnotsToAdd );
+
+        unsigned findSpan ( knot param ) const;
+
+        unsigned getNumSpans() const;  // Return number of non-zero length spans.
+
+        unsigned getFirstSpan() const;  // Return knot index of first non-zero span.
+
+        unsigned getNextSpan ( unsigned spanIndex ) const;
+        
+        unsigned getPreviousSpan ( unsigned spanIndex ) const;
+
+        int findMultiplicity ( unsigned index ) const;
+        int findMultiplicity ( knot param ) const;
+        
+        void truncate ( knot param, bool keepLast );
+        
+        void reparameterise ( knot startKnot, knot endKnot );
+
+        void reverse();
+        
+        void join ( snlKnotVector* knotVector );
+
+        const knot* array();  // Return pointer to array of knots.
+
+        knot max() const;  // Max knot val.
+        knot min() const;  // Min knot val.
+
+        int type() const;  // Return value from knotVectorType.
+        
+        basis* evalBasis ( knot param );
+        
+        basis* evalBasisDeriv ( knot param, int deriv );
+        
+        double* calcRemovalAlphas ( unsigned span );
+
+        void print();
+        void print_cpp();
+        
+    protected:
+
+        void copyFrom ( const snlKnotVector& vector );
+    
+        knot* getKnotArray();
+    
+        knot*       knots;
+        unsigned    vectorSize;
+        
+        int         deg; // Degree associated with vector.
+
+        // Type of knot vector
+        int         kvType;
+};
+
+#endif
diff --git a/src/snlMatrix_4x4.cpp b/src/snlMatrix_4x4.cpp
new file mode 100644
index 0000000..7c3c34a
--- /dev/null
+++ b/src/snlMatrix_4x4.cpp
@@ -0,0 +1,210 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** 4x4 Matrix of doubles ***
+
+#include "snlMatrix_4x4.h"
+
+snlMatrix_4X4::snlMatrix_4X4()
+{
+    element = new double [ 16 ];
+    scratch = new double [ 16 ];
+}
+
+snlMatrix_4X4::~snlMatrix_4X4()
+{
+    delete[] element;
+    delete[] scratch;
+}
+
+snlMatrix_4X4::snlMatrix_4X4 ( snlMatrix_4X4& copyFrom )
+{
+    element = new double [ 16 ];
+    scratch = new double [ 16 ];
+    
+    for ( int index = 0; index < 16; index ++ )
+        element [ index ] = copyFrom.element [ index ];
+}
+
+void snlMatrix_4X4::ident()
+{
+    // Set matrix to identity
+    // ----------------------
+
+    for ( int index = 0; index < 16; index ++ )
+    {
+        element [ index ] = 0;
+    }
+
+    element [ 0 ] = 1.0;
+    element [ 5 ] = 1.0;
+    element [ 10 ] = 1.0;
+    element [ 15 ] = 1.0;
+}
+
+void snlMatrix_4X4::multiply ( snlMatrix_4X4& multMatrix, bool pre )
+{
+    // multiply this matrix by given matrix
+    // ------------------------------------
+    // multMatrix:      Matrix to multiply.
+    // pre:             If true then pre multiply.
+
+    double      cVal;
+
+    double*     multA;
+    double*     multB;
+
+    int         pIndex = 0;  // Primary Index.
+    int         pRow, sRow;
+    int         sIndex = 0;  // Scratch Index.
+    int         multAIndex;  // Multiplier A matrix index.
+
+    // Multiply AB
+    if ( pre )
+    {
+        // Pre multiply.
+        multA = multMatrix.element;
+        multB = element;
+    }
+    else
+    {
+        // Post multiply.
+        multA = element;
+        multB = multMatrix.element;
+    }
+
+    // Zero scratch space
+    for ( int index = 0; index < 16; index ++ ) scratch [ index ] = 0.0;
+
+    for ( int pCol = 0; pCol < 4; pCol ++ )
+    {
+        multAIndex = 0;
+
+        for ( pRow = 0; pRow < 4; pRow ++ )
+        {
+            cVal = multB [ pIndex ++ ];
+
+            for ( sRow = 0; sRow < 4; sRow ++ )
+            {
+                scratch [ sIndex + sRow ] += cVal * multA [ multAIndex ++ ];
+            }
+        }
+
+        sIndex += 4;
+    }
+
+    // Swap scratch and element matrices.
+    multA = element;  // multA is simply used as a temp variable.
+    element = scratch;
+    scratch = multA;
+}
+
+void snlMatrix_4X4::translateIdent ( double x, double y, double z )
+{
+    // Setup matrix as translation identity
+    // ------------------------------------
+
+    ident();
+
+    element [ 12 ] = x;
+    element [ 13 ] = y;
+    element [ 14 ] = z;
+}
+
+
+void snlMatrix_4X4::rotateXIdent ( double yy, double yz, double zy, double zz )
+{
+    // Create rotation identity for rotation about x axis
+    // --------------------------------------------------
+    // yy:      y coordinate scalar for new y.
+    // yz:      z coordinate scalar for new y.
+    // zy:      y coordinate scalar for new z.
+    // zz:      z coordinate scalar for new z.
+
+    ident();
+
+    element [ 5 ] = yy;
+    element [ 9 ] = yz;
+    element [ 6 ] = zy;
+    element [ 10 ] = zz;
+}
+
+void snlMatrix_4X4::rotateYIdent ( double xx, double xz, double zx, double zz )
+{
+    // Create rotation identity for rotation about y axis
+    // --------------------------------------------------
+    // xx:      x coordinate scalar for new x.
+    // xz:      z coordinate scalar for new x.
+    // zx:      x coordinate scalar for new z.
+    // zz:      z coordinate scalar for new z.
+
+    ident();
+
+    element [ 0 ] = xx;
+    element [ 8 ] = xz;
+    element [ 2 ] = zx;
+    element [ 10 ] = zz;
+}
+
+void snlMatrix_4X4::rotateZIdent ( double xx, double xy, double yx, double yy )
+{
+    // Create rotation identity for rotation about y axis
+    // --------------------------------------------------
+    // xx:      x coordinate scalar for new x.
+    // xy:      y coordinate scalar for new x.
+    // yx:      x coordinate scalar for new y.
+    // yy:      y coordinate scalar for new y.
+
+    ident();
+
+    element [ 0 ] = xx;
+    element [ 4 ] = xy;
+    element [ 1 ] = yx;
+    element [ 5 ] = yy;
+}
+
+void snlMatrix_4X4::scaleIdent ( double x, double y, double z )
+{
+    // Create scaling identity for scaling operation.
+    // ----------------------------------------------
+    // x, y, z:     Scaling factors.
+
+    ident();
+
+    element [ 0 ] = x;
+    element [ 5 ] = y;
+    element [ 10 ] = z;
+}
+
+double* snlMatrix_4X4::elements()
+{
+    return element;
+}
+
+void snlMatrix_4X4::print()
+{
+    for ( int row = 0; row < 4; row ++ )
+    {
+        for ( int col = 0; col < 4; col ++ )
+        {
+            cout << element [ row + ( col * 4 ) ] << "  ";
+        }
+
+        cout << "\n";
+    }
+}
+
diff --git a/src/snlMatrix_4x4.h b/src/snlMatrix_4x4.h
new file mode 100644
index 0000000..578155e
--- /dev/null
+++ b/src/snlMatrix_4x4.h
@@ -0,0 +1,74 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** 4x4 Matrix of doubles ***
+
+#ifndef SNLMATRIX4X4_H
+#define SNLMATRIX4X4_H
+
+#include "snlPoint.h"
+
+#ifdef SGI_MIPS
+
+    #include <iostream.h>
+    #include <math.h>
+    
+#else
+
+    #include <iostream>
+    #include <cmath>
+    
+    using namespace std;
+    
+#endif
+
+class snlMatrix_4X4
+{
+    // Matrix optimised for size 4 X 4 of double
+
+    public:
+
+        snlMatrix_4X4();
+        virtual ~snlMatrix_4X4();
+        
+        snlMatrix_4X4 ( snlMatrix_4X4& copyFrom );
+
+        void ident();  // Set matrix to identity.
+
+        void translateIdent ( double x, double y, double z );  // Setup matrix as translation identity.
+
+        void rotateXIdent ( double yy, double yz, double zy, double zz );
+        void rotateYIdent ( double xx, double xz, double zx, double zz );
+        void rotateZIdent ( double xx, double xy, double yx, double yy );
+
+        void scaleIdent ( double x, double y, double z );
+
+        void multiply ( snlMatrix_4X4&, bool pre = false );  // Multiply this matrix by given matrix.
+
+        void transform ( snlPoint* );  // Transform given point with matrix.
+        
+        double* elements();
+
+        void print();  //!< Print matrice to standard out.
+
+    protected:
+
+        double*     element;  // OpenGL style array. NOT c/c++.
+        double*     scratch;  // Scratch space for matrix multiplication etc.
+};
+
+#endif
diff --git a/src/snlMeshable.cpp b/src/snlMeshable.cpp
new file mode 100644
index 0000000..b9c49c4
--- /dev/null
+++ b/src/snlMeshable.cpp
@@ -0,0 +1,33 @@
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** Base class of renderable objects ***
+
+#include "snlMeshable.h"
+
+snlMeshable::~snlMeshable()
+{
+}
+
+snlMeshable::snlMeshable()
+{
+}
+
+void snlMeshable::triangleMesh ( snlTriangleMesh* triMesh, double tolerance )
+{
+    // This function is selectively implemented in sub classes.
+    // The Default is to do nothing.
+
+    return;
+}
diff --git a/src/snlMeshable.h b/src/snlMeshable.h
new file mode 100644
index 0000000..36c749f
--- /dev/null
+++ b/src/snlMeshable.h
@@ -0,0 +1,66 @@
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** Base class of renderable objects ***
+
+#ifndef SNL_MESHABLE_H
+#define SNL_MESHABLE_H
+
+#include "snlKnotVector.h"
+#include "snlPoint.h"
+#include "snlTriangleMesh.h"
+#include "snlVertexNet.h"
+
+#ifdef SGI_MIPS
+
+    #include <iostream.h>
+    #include <math.h>
+    #include <float.h>
+    
+#else
+
+    #include <iostream>
+    #include <cmath>
+    #include <cfloat>
+    
+    using namespace std;
+    
+#endif
+
+// Notes:   A curve is not strictly meshable but is included anyway.
+
+class snlMeshable
+{
+    public:
+    
+        snlMeshable();
+
+        virtual ~snlMeshable();
+
+        virtual snlPoint eval ( knot paramU, knot paramV ) const = 0;
+    
+        virtual void vertexNet ( snlVertexNet* vNet, double tolerance, bool parametric ) = 0;
+        
+        virtual void triangleMesh ( snlTriangleMesh* triMesh, double tolerance );
+
+        enum meshClass
+        {
+            SNL_CURVE,
+            SNL_SURFACE
+        };
+    
+    protected:
+};
+
+#endif
diff --git a/src/snlNurbsCommon.cpp b/src/snlNurbsCommon.cpp
new file mode 100644
index 0000000..b12cc61
--- /dev/null
+++ b/src/snlNurbsCommon.cpp
@@ -0,0 +1,829 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** NURBS routines that don't belong in classes ***
+
+// *** Legacy OpenCDSM routines are included ***
+
+// *** NOTE: OpenCDSM functions still use t,u for surface parameters ***
+
+#include "snlNurbsCommon.h"
+#include "snlVector.h"
+
+//#define PROJ_COMMENT
+
+static int maxIterations = 0;
+static unsigned maxPasses = 0;
+
+bool newtonIterStepSurf ( snlPoint* derivPts, snlPoint* projPt, knot* deltaU, knot* deltaV )
+{
+    // Evaluate a single step in a newton iteration over a surface
+    // -----------------------------------------------------------
+    // derivPts:        Pre calculated rational 0th to 2nd derivatives at desired paramters.
+    //                  This functions expectes a 3 X 3 array.
+    // projPt:          Point being projected onto surface.
+    // deltaU:          Change in u parameter to return.
+    // deltaV:          Change in v parameter to return.
+    //
+    // returns:         True if processing was succesfull. False if deltas would be infinite.
+    //
+    // Notes:   Theory from "The NURBS Book 2nd Ed" pages 232 - 234.
+    
+    basis       jcbn [ 2 ] [ 2 ];  // 2 X 2 Jacobian matrix.
+    
+    snlVector dist ( *projPt, derivPts [ 0 ] );  // Distance from point to be projected to surface.
+
+    jcbn [ 0 ] [ 0 ] = derivPts [ 3 ].lengthSqrd() + dist.dot ( derivPts [ 6 ] );
+    jcbn [ 0 ] [ 1 ] = snlVector ( derivPts [ 3 ] ).dot ( derivPts [ 1 ] )
+                       + dist.dot ( derivPts [ 4 ] );
+    jcbn [ 1 ] [ 0 ] = jcbn [ 0 ] [ 1 ];
+    jcbn [ 1 ] [ 1 ] = derivPts [ 1 ].lengthSqrd( ) + dist.dot ( derivPts [ 2 ] );
+
+    return solve2X2LinEqn ( jcbn [ 0 ] [ 0 ], jcbn [ 1 ] [ 0 ], jcbn [ 0 ] [ 1 ], jcbn [ 1 ] [ 1 ],
+                            - ( dist.dot ( derivPts [ 3 ] ) ),
+                            - ( dist.dot ( derivPts [ 1 ] ) ),
+                            deltaU, deltaV );
+}
+
+bool newtonIterStepCurve ( snlPoint* derivPts, snlPoint* projPt, knot* paramDelta )
+{
+    // Evaluate a single step in a newton iteration over a curve
+    // ---------------------------------------------------------
+    // derivPts:        Pre calculated rational 0th to 2nd derivatives at desired paramter. Size 3.
+    // projPt:          Point being projected onto curve.
+    // paramDelta:      Change in t parameter to return.
+    //
+    // returns:         True if processing was succesfull. False if paramDelta would be infinite.
+    //
+    // Notes:   Theory from "The NURBS Book 2nd Ed" pages 230 - 231.
+    
+    snlVector dist ( *projPt, derivPts [ 0 ] );  // Distance from point to be projected to surface.
+
+    if ( ( dist.dot ( derivPts [ 2 ] ) + derivPts [ 1 ].lengthSqrd() ) == 0.0 ) return false;
+
+    *paramDelta = - ( dist.dot ( derivPts [ 1 ] ) /
+                  ( dist.dot ( derivPts [ 2 ] ) + derivPts [ 1 ].lengthSqrd() ) );
+
+    return true;
+}
+
+bool lineIterStepCurve ( snlPoint* derivPts, snlPoint* projPt, knot* paramDelta )
+{
+    // Evaluate a single step in a line iteration over a degree 1 curve
+    // ----------------------------------------------------------------
+    // derivPts:        Pre calculated rational 0th to 2nd derivatives at desired paramter. Size 3.
+    // projPt:          Point being projected onto curve.
+    // paramDelta:      Change in t parameter to return.
+    //
+    // returns:         True if processing was succesfull. False if paramDelta would be infinite.
+        
+    basis       length;
+
+    // Check for divide by zero error.
+    length = derivPts [ 1 ].lengthSqrd();
+
+    if ( length == 0.0 ) return false;
+    
+    snlVector dist ( *projPt, derivPts [ 0 ] );  // Distance from point to be projected to surface.
+
+    // Vectors are nose to tail so have to negate. Look at definition of dot product.
+    *paramDelta = -( dist.dot ( derivPts [ 1 ] ) ) / length;
+
+    return true;
+}
+
+int  solve2X2LinEqn ( double a1, double a2, double a3, double a4,
+                      double b1, double b2, double* x1, double* x2 )
+{
+    /* Solve a 2 X 2 system of linear equations
+    // ----------------------------------------
+    //
+    // a1 to a4:        Coefficients of the system.
+    // b1, b2:          If the system is represented as Ax = b, these are the b's.
+    //
+    //                   _            _     _  _
+    //                  |              |   |    |
+    //                  | a1.x1  a3.x2 |   | b1 |
+    //                  |              | = |    |
+    //                  | a2.x1  a4.x2 |   | b2 |
+    //                  |_            _|   |_  _|
+    //
+    //  x1, x2:         Pointers to return solutions in.
+    //
+    //  Returns:        False if det is 0, otherwise True. ie Returned values would be infinite.
+    */
+
+    double      det, det1, det2;
+
+    det = a1 * a4 - a2 * a3;
+
+    det1 = b1 * a4 - b2 * a3;
+
+    det2 = a1 * b2 - a2 * b1;
+    
+    if ( det != 0 )
+    {
+        *x1 = det1 / det;
+        *x2 = det2 / det;
+        
+        return 1;
+    }
+    
+    return 0;
+}
+
+ptrList < sLocn >* projPtSurf ( snlSurface& surface, snlPoint* toProj, int toProjNum, sLocn* best,
+                                double iterTol, double cosTol, unsigned maxPass )
+{
+    // Project points onto given surface
+    // ---------------------------------
+    // surface:     Surface to project to.
+    // toProj:      Point to project.
+    // toProjNum:   Number of points to project
+    // best:        Array to return best evaluted locations in. Must be toProjNum in size.
+    // iterTol:     Iteration tolerance. Maximum distance between surface points after
+    //              successive iterations. If below this value then processing stops.
+    // cosTol:      Cosine Tolerance. Maxium allowable deviation of cosine from zero.
+    //              i.e. How close to a surface normal it is.
+    // maxPass:     Maximum number of passes to be made. Each subsequent pass doubles the number of
+    //              sections each span is divided into. For ordinary cases this only needs to be 1.
+    //
+    //
+    // Notes:       Evaluates more than one point at once.
+    //              Mostly OpenCDSM legacy code and uses OpenCDSM parameter names.
+    //
+    //              This is messy inefficient code! But it mostly works ;-)
+
+    knot        paramT, paramU;  // Evaluation parameters.
+    knot        pTStart, pTEnd;  // Param T start and end for span.
+    knot        pUStart, pUEnd;  // Param U start and end for span.
+    knot        pTStep, pUStep;  // Param T and U incremental steps.
+    knot        pTStepDenom, pUStepDenom;  // PxStep denominators.
+    
+    snlPoint    evalPt;  // Current evaled non-homogeneous point.
+
+    snlPoint*   evalDerivs = 0;  // Derivatives of point needed for newton iterations.
+    snlPoint*   newEvalDerivs = 0;
+
+    bool        withinTol = false;  // Found a point within tolerance.
+
+    bool        noBest = true;  // No best results have been initialised yet.
+    int         index;
+
+    unsigned    cPass;
+
+    bool*       withinTols = new bool [ toProjNum ];
+
+    ptrList < projLocn >* bestLists = new ptrList < projLocn > [ toProjNum ];
+    ptrList < projLocn >* cList;
+
+    projLocn*       cBest;
+
+    basis           bestDist;  // Used for point selection with multiple hits.
+
+    ptrList < sLocn >* ambig = new ptrList < sLocn >;
+
+    unsigned degT = surface.degreeU();
+    unsigned degU = surface.degreeV();
+
+    bool        degT1 = ( degT == 1 );  // Degree T is 1.
+    bool        degU1 = ( degU == 1 );  // Degree U is 1.
+
+    for ( index = 0; index < toProjNum; index ++ ) withinTols [ index ] = false;
+
+    #ifdef PROJ_COMMENT
+
+        cout << "\nNew Projection\n";
+        cout.precision ( 15 );
+
+    #endif
+    
+    const snlKnotVector& kntsT = surface.knotVectorU();
+    const snlKnotVector& kntsU = surface.knotVectorV();
+    
+    // Calculate bisection step multipliers used to account for surfaces with both
+    // high curvature and high discontinuity ( small number of non-zero spans, large number of knots ).
+    
+    int stepMultiT = (int) ( surface.maxCurvatureU() * 2.0 ) + ( surface.sizeU() / kntsT.getNumSpans() );
+    int stepMultiU = (int) ( surface.maxCurvatureV() * 2.0 ) + ( surface.sizeV() / kntsU.getNumSpans() );    
+    
+    #ifdef PROJ_COMMENT
+        cout << "stepMultiT: " << stepMultiT << "  stepMultiU: " << stepMultiU << "\n";
+    #endif
+    
+    // Evaluate surface points that correspond to beginning and end of knot spans
+    // and store point with least distance from surface points.
+
+    for ( cPass = 1; cPass <= maxPass; cPass ++ )
+    {
+
+        #ifdef PROJ_COMMENT
+
+            cout << "Pass: " << cPass << "\n";
+
+        #endif        
+
+        unsigned minSpanT = kntsT.findSpan ( kntsT.min() );
+        unsigned maxSpanT = kntsT.findSpan ( kntsT.max() );
+
+        unsigned minSpanU = kntsU.findSpan ( kntsU.min() );
+        unsigned maxSpanU = kntsU.findSpan ( kntsU.max() );
+
+        // Seed best point.
+        paramT = kntsT.val ( minSpanT );
+        paramU = kntsU.val ( minSpanU );
+
+        evalPt = surface.eval ( paramT, paramU );        
+
+        // On the first loop the value found is the best.
+        if ( noBest )
+        {
+            // Initialise pointer lists
+            for ( index = 0; index < toProjNum; index ++ )
+            {
+                cBest = new projLocn;
+
+                cBest -> paramT = paramT;
+                cBest -> paramU = paramU;
+                cBest -> pt = evalPt;
+
+                bestLists [ index ].append ( cBest, true );
+            }
+
+            noBest = false;
+        }
+
+        for ( unsigned spanT = minSpanT; spanT <= maxSpanT; spanT ++ )
+        {
+            pTStart = kntsT.val ( spanT );
+            pTEnd = kntsT.val ( spanT + 1 );
+
+            // Only work with non-zero length knot spans.
+            if ( pTStart < pTEnd )
+            {
+                // Set T bisection step value.
+                pTStepDenom = ( ( degT + 1 ) * cPass * stepMultiT );
+                pTStep = ( pTEnd - pTStart ) / pTStepDenom;
+
+                for ( unsigned spanU = minSpanU; spanU <= maxSpanU; spanU ++ )
+                {
+                    pUStart = kntsU.val ( spanU );
+                    pUEnd = kntsU.val ( spanU + 1 );
+
+                    // Only work with non-zero length knot spans.
+                    if ( pUStart < pUEnd )
+                    {
+                        // Set U bisection step value.
+                        pUStepDenom = ( ( degU + 1 ) * cPass * stepMultiU );
+                        pUStep = ( pUEnd - pUStart ) / pUStepDenom;
+
+                        // Bisect span "rectangle", spanT -> spanT + 1, spanU -> spanU + 1.
+                        // Step through each section. Some points are checked twice for the sake
+                        // of code simplicity.
+
+                        for ( unsigned sectT = 0; sectT <= (unsigned) pTStepDenom; sectT ++ )
+                        {
+                            paramT = pTStart + pTStep * sectT;
+
+                            for ( unsigned sectU = 0; sectU <= (unsigned) pUStepDenom; sectU ++ )
+                            {
+                                paramU = pUStart + pUStep * sectU;
+
+                                // Evaluate
+                                evalPt = surface.eval ( paramT, paramU );
+            
+                                // Iterate through each point to be projected.
+                                for ( index = 0; index < toProjNum; index ++ )
+                                {
+                                    if ( withinTols [ index ] ) continue;
+
+                                    cList = bestLists + index;
+
+                                    cBest = cList -> first();
+
+                                    // See if evalStart [ 0 ] is the best fit so far.
+                                    if ( snlVector ( toProj [ index ], cBest -> pt ).length()
+                                         > snlVector ( toProj [ index ], evalPt ).length() )
+                                    {                
+                                        // If the evaluated point is closer to the point to be projected
+                                        // then it is the new best.
+                                        cBest -> paramT = paramT;
+                                        cBest -> paramU = paramU;
+                                        cBest -> pt = evalPt;
+                                        
+                                        cList -> truncate();
+                                    }
+                                    else if ( ( evalPt == ( cBest -> pt ) ) &&
+                                              ( cBest -> paramT != paramT ||
+                                                cBest -> paramU != paramU ) )
+                                    {                
+                                        // Coincident point found.
+                                        
+                                        // Make sure point hasn't been found before.
+                                        
+                                        bool exists = false;
+                                        
+                                        while ( cBest )
+                                        {                                            
+                                            if ( paramT == cBest -> paramT && paramU == cBest -> paramU )
+                                                exists = true;
+                                                
+                                            cBest = cList -> next();
+                                        }
+                                        
+                                        if ( ! exists )
+                                        {
+                                            cBest = new projLocn;
+                                            cBest -> paramT = paramT;
+                                            cBest -> paramU = paramU;
+                                            cBest -> pt = evalPt;
+    
+                                            cList -> append ( cBest, true );
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }        
+
+        // Whoa! that is far too many nested for loops *grin*.
+
+        // Okey dokey. Now it is time to try a newton iteration or two ( thousand ) on the point of best fit.
+
+        for ( index = 0; index < toProjNum; index ++ )
+        {
+            if ( withinTols [ index] ) continue;
+
+            cBest = bestLists [ index ].first();
+
+            double guessDistance = snlVector ( cBest -> pt, toProj [ index ] ).length();
+
+            #ifdef PROJ_COMMENT
+                cout << "Processing Index: " << index << "\n";
+                cout << "Number in bestList: " << bestLists [ index ].count() << "\n";
+                cout << "Original Point: "; toProj [ index ].print(); cout << "\n";
+            #endif
+
+            while ( cBest )
+            {
+                #ifdef PROJ_COMMENT                    
+                    cout << "Guess: ";cBest -> pt.print(); cout << "\n";
+                #endif
+
+                for ( int count = 0; count < MAX_PROJ_ITER; count ++ )
+                {
+                    withinTol = false;
+
+                    if ( maxIterations < count ) maxIterations = count;
+
+                    #ifdef PROJ_COMMENT
+
+                        cout << "Iteration Number: " << count << "\n";                        
+
+                    #endif
+
+                    // If proj point is within iteration tolerance from evaluated point then stop.
+
+                    cBest -> dist = snlVector ( cBest -> pt, toProj [ index ] ).length();
+
+                    if ( ( cBest -> dist ) < iterTol )
+                    {
+                        withinTol = true;
+                        break;
+                    }
+
+                    // Get 1st and 2nd derivatives of point.
+                    if ( ! evalDerivs )
+                        evalDerivs = surface.evalDerivs ( cBest -> paramT, cBest -> paramU, 2, 2 );
+
+                    // Calc projPt to surface vector.
+                    snlVector projToSurf ( toProj [ index ], evalDerivs [ 0 ] );
+
+                    // Check to see if cosine of determination function is under tolerance ( cosTol ).
+
+                    snlVector tVect ( evalDerivs [ 3 ] );
+                    basis cosT = projToSurf.calcAbsCos ( tVect );
+                    
+                    snlVector uVect ( evalDerivs [ 1 ] );
+                    basis cosU = projToSurf.calcAbsCos ( uVect );
+
+                    #ifdef PROJ_COMMENT
+                        
+                        cout << "Iteration: " << count << "  DotT: " << projToSurf.dot ( tVect ) << "  DotU: " << projToSurf.dot ( uVect ) << "\n";
+                        cout << "Cosine T: " << cosT << "\n";
+                        cout << "Cosine U: " << cosU << "\n";
+
+                    #endif
+
+                    if ( cosU <= cosTol && cosT <= cosTol )
+                    {
+                        withinTol = true;
+                        break;
+                    }
+
+                    knot    deltaT;
+                    knot    deltaU;
+
+                    knot    newT;
+                    knot    newU;
+
+                    // Get new paramaters.
+                    if ( !degT1 && !degU1 )
+                    {
+                        if ( ! newtonIterStepSurf ( evalDerivs, toProj + index, &deltaT, &deltaU ) )
+                        {
+                            break;  // Param deltas would have gone to infinity.
+                        }
+                    }
+                    else
+                    {
+                        // At least one of the degrees is 1.
+
+                        snlPoint tDerivs [ 3 ];
+
+                        tDerivs [ 0 ] = evalDerivs [ 0 ];
+                        tDerivs [ 1 ] = evalDerivs [ 3 ];
+                        tDerivs [ 2 ] = evalDerivs [ 6 ];
+
+                        if ( degT1 )
+                        {
+                            if ( ! lineIterStepCurve ( tDerivs, toProj + index, &deltaT ) )
+                                break;
+
+                        }
+                        else
+                        {
+                            if ( ! newtonIterStepCurve ( tDerivs, toProj + index, &deltaT ) )
+                                break;
+                        }
+
+                        if ( degU1 )
+                        {
+                            if ( ! lineIterStepCurve ( evalDerivs, toProj + index, &deltaU ) )
+                                break;
+                        }
+                        else
+                        {
+                            if ( ! newtonIterStepCurve ( evalDerivs, toProj + index, &deltaU ) )
+                                break;
+                        }
+                    }
+
+                    // Clamp params to knot bounds.
+                    newT = cBest -> paramT + deltaT;
+                    newU = cBest -> paramU + deltaU;
+
+                    if ( newT > ( kntsT.max() ) )
+                    {
+                        newT = kntsT.max();
+                        deltaT = newT - cBest -> paramT;
+                    }
+
+                    if ( newT < ( kntsT.min() ) )
+                    {
+                        newT = kntsT.min();
+                        deltaT = newT - cBest -> paramT;
+                    }
+
+                    if ( newU > ( kntsU.max() ) )
+                    {
+                        newU = kntsU.max();
+                        deltaU = newU - cBest -> paramU;
+                    }
+
+                    if ( newU < ( kntsU.min() ) )
+                    {
+                        newU = kntsU.min();
+                        deltaU = newU - cBest -> paramU;
+                    }
+
+                    #ifdef PROJ_COMMENT
+                        cout << "OldT: " << cBest -> paramT << "\n";
+                        cout << "OldU: " << cBest -> paramU << "\n";
+                        cout << "NewT: " << newT << "\n";
+                        cout << "NewU: " << newU << "\n";
+                        cout << "deltaT: " << deltaT << "\n";
+                        cout << "deltaU: " << deltaT << "\n";
+                    #endif
+
+                    // If parameters haven't changed then break.
+                    if ( deltaT == 0.0 && deltaU == 0.0 )
+                        break;
+
+                    // Evaluate new parameters.
+
+                    newEvalDerivs = surface.evalDerivs ( newT, newU, 2, 2 );
+
+                    // Generate new best.
+                    cBest -> pt = newEvalDerivs [ 0 ];
+                    cBest -> paramT = newT;
+                    cBest -> paramU = newU;
+
+                    // Calculate new distance to surface.
+                    cBest -> dist = snlVector ( cBest -> pt, toProj [ index ] ).length();
+
+                    // Check for below tolerance changes in surface point.
+                    snlVector chgT ( evalDerivs [ 3 ] );
+                    chgT *= deltaT;
+                    
+                    snlVector chgU ( evalDerivs [ 1 ] );
+                    chgU *= deltaU;
+
+                    // Change vector.
+                    snlVector chgV = chgT + chgU;
+
+                    #ifdef PROJ_COMMENT
+                        cout << "Change in T direction: " << chgT.length() << "\n";
+                        cout << "Change in U direction: " << chgT.length() << "\n";
+                        cout << "Change in point: " << chgV.length() << "\n";
+                    #endif
+
+                    if ( chgV.length() <= iterTol )
+                    {
+                        withinTol = true;
+                        break;
+                    }
+
+                    // Reorganise evaluation arrays.
+                    if ( evalDerivs ) delete[] evalDerivs;
+                    evalDerivs = newEvalDerivs;
+                    newEvalDerivs = 0;
+                }
+
+                // Detect whether the point to be projected is within tolerances
+                // Projected points that are further from surface than original
+                // guess are not within tolerance.
+                
+                if ( withinTol && ( cBest -> dist <= guessDistance ) )
+                {                
+                    withinTols [ index ] = withinTol;
+                    cBest -> flag = true;                    
+                }
+                else
+                    cBest -> flag = false;
+
+                cBest = bestLists [ index ].next();
+            }
+
+            if ( withinTols [ index ] )
+            {
+                // ** Process best points **
+
+                // Get best distance.
+
+                cBest = bestLists [ index ].first();
+
+                bestDist = cBest -> dist;
+
+                cBest = bestLists [ index ].next();
+
+                while ( cBest )
+                {
+                    if ( bestDist > ( cBest -> dist ) ) bestDist = ( cBest -> dist );
+
+                    cBest = bestLists [ index ].next();
+                }
+                
+                if ( bestDist < iterTol ) bestDist = iterTol;  // Accounts for discrepencies in Newton convergence.
+
+                cBest = bestLists [ index ].first();
+
+                int numBest = 0;
+
+                while ( cBest )
+                {
+                    // Get the point closest to the surface.
+
+                    if ( ( cBest -> flag ) && ( ( cBest -> dist ) <= bestDist ) )
+                    {
+                        if ( ! numBest )
+                        {
+                            best [ index ].paramT = cBest -> paramT;
+                            best [ index ].paramU = cBest -> paramU;
+                            best [ index ].pt = cBest -> pt;
+                        }
+                        else
+                        {
+                            // Add item to ambiguity list.
+                            sLocn* ambLocn = new sLocn;
+                            ambLocn -> paramT = cBest -> paramT;
+                            ambLocn -> paramU = cBest -> paramU;
+                            ambLocn -> pt = cBest -> pt;
+                            ambLocn -> flag = index;
+
+                            ambig -> append ( ambLocn, true );
+                        }
+
+                        numBest ++;
+                    }
+                    else
+                        cBest -> flag = false;
+
+                    cBest = bestLists [ index ].next();
+                }
+
+                best [ index ].flag = numBest;
+            }
+            else
+            {
+                // Make sure at least something is in the best array.
+                cBest = bestLists [ index ].first();
+
+                if ( cBest )
+                {
+                    best [ index ].paramT = cBest -> paramT;
+                    best [ index ].paramU = cBest -> paramU;
+                    best [ index ].pt = cBest -> pt;
+                    best [ index ].flag = 1;
+                }
+            }
+
+            // Clean up.
+            if ( evalDerivs )
+            {
+                delete[] evalDerivs;
+                evalDerivs = 0;
+            }
+
+            if ( newEvalDerivs )
+            {
+                delete[] newEvalDerivs;
+                newEvalDerivs = 0;
+            }
+            
+            #ifdef PROJ_COMMENT
+                cout << "Best Found: "; best [ index ].pt.print(); cout << "\n";
+            #endif
+        }
+
+        // All point tolerances must be within threshold to break out of pass loop.
+
+        withinTol = true;
+
+        for ( index = 0; index < toProjNum; index ++ )
+        {
+            if ( ! withinTols [ index ] ) withinTol = false;
+        }        
+
+        if ( withinTol ) break;
+    }
+
+    delete[] withinTols;
+
+    if ( maxPasses < cPass ) maxPasses = cPass;
+
+    #ifdef PROJ_COMMENT
+
+        cout << "Max Passes: " << maxPasses << "\n";
+        cout << "Max Iterations: " << maxIterations << "\n";
+
+    #endif
+
+    if ( ambig -> count() )
+        resolveAmbig ( best, toProjNum, ambig );
+
+    return ambig;
+}
+
+void resolveAmbig ( sLocn* projns, int projSize, ptrList < sLocn >* ambig )
+{
+    // Attempt to resolve projection ambiguities.
+    // ------------------------------------------
+    // projns:      Projections.
+    // projSize:    Size of projns array.
+    // ambig:       Ambiguities.
+
+    // Generate array so that ambiguous points before and after
+    // current point being considered can be easily checked.
+
+    sLocn*      locn;
+
+    if ( projSize < 3 ) return;  // Can't get a trend with less than three points.
+
+    bool* ambigIndexes = new bool [ projSize ];
+
+    for ( int index = 0; index < projSize; index ++ )
+        ambigIndexes [ index ] = false;
+
+    for ( locn = ambig -> first(); locn; locn = ambig -> next() )
+        ambigIndexes [ locn -> flag ] = true;
+
+    bool reverse = false;
+
+    // Process projection ambiguities.
+
+    // If parametric distance of ambiguous point is closer to
+    // neighbours then this point is considered a better match.
+
+    // Process forwards.
+
+    for ( locn = ambig -> first(); locn; locn = ambig -> next() )
+    {
+        // Don't compare to ambiguous neighbours.
+
+        if ( ! ( locn -> flag ) )
+        {
+            reverse = true;
+            continue;
+        }
+
+        if ( ambigIndexes [ ( locn -> flag ) - 1 ] )
+        {
+            reverse = true;
+            continue;
+        }
+
+        ambigIndexes [ ( locn -> flag ) ] = false;  // Don't process in reverse.
+
+        knot cDist = paramDistSqrd ( projns [ ( locn -> flag ) - 1  ].paramT,
+                                     projns [ ( locn -> flag ) - 1  ].paramU,
+                                     projns [ ( locn -> flag ) ].paramT,
+                                     projns [ ( locn -> flag ) ].paramU );
+
+        knot tDist = paramDistSqrd ( projns [ ( locn -> flag ) - 1  ].paramT,
+                                     projns [ ( locn -> flag ) - 1  ].paramU,
+                                     locn -> paramT,
+                                     locn -> paramU );
+
+        // If the sLocn being tested is a better match then swap data.
+
+        if ( tDist < cDist )
+        {
+            sLocn tmpLocn = *locn;
+            *locn = projns [ tmpLocn.flag ];
+            projns [ tmpLocn.flag ] = tmpLocn;
+
+            // Make sure flags aren't swapped.
+            projns [ tmpLocn.flag ].flag = locn -> flag;
+            locn -> flag = tmpLocn.flag;
+        }
+    }
+
+    // Process Backwards. Try and pick up points missed during forward processing.
+
+    if ( reverse )
+    {
+        for ( locn = ambig -> last(); locn; locn = ambig -> prev() )
+        {
+            if ( ! ambigIndexes [ ( locn -> flag ) ] )
+                continue;  // Has already been processed.
+
+            // Don't compare to ambiguous neighbours.
+
+            if ( ( locn -> flag ) >= projSize - 1 )  // Is last point.
+                continue;
+
+            if ( ambigIndexes [ ( locn -> flag ) + 1 ] )
+                continue;
+
+            knot cDist = paramDistSqrd ( projns [ ( locn -> flag ) + 1  ].paramT,
+                                         projns [ ( locn -> flag ) + 1  ].paramU,
+                                         projns [ ( locn -> flag ) ].paramT,
+                                         projns [ ( locn -> flag ) ].paramU );
+
+            knot tDist = paramDistSqrd ( projns [ ( locn -> flag ) + 1  ].paramT,
+                                         projns [ ( locn -> flag ) + 1  ].paramU,
+                                         locn -> paramT,
+                                         locn -> paramU );
+
+            // If the sLocn being tested is a better match then swap data.
+
+            if ( tDist < cDist )
+            {
+                sLocn tmpLocn = *locn;
+                *locn = projns [ tmpLocn.flag ];
+                projns [ tmpLocn.flag ] = tmpLocn;
+
+                // Make sure flags aren't swapped.
+                projns [ tmpLocn.flag ].flag = locn -> flag;
+                locn -> flag = tmpLocn.flag;
+            }
+        }
+
+    }
+
+    delete[] ambigIndexes;
+}
+
+knot paramDistSqrd ( knot t1, knot u1, knot t2, knot u2 )
+{
+    return ( t2 - t1 ) * ( t2 - t1 ) + ( u2 - u1 ) * ( u2 - u1 );
+}
+
diff --git a/src/snlNurbsCommon.h b/src/snlNurbsCommon.h
new file mode 100644
index 0000000..9302f14
--- /dev/null
+++ b/src/snlNurbsCommon.h
@@ -0,0 +1,83 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** NURBS routines that don't belong in classes ***
+
+// *** Legacy opencdsm routines are included ***
+
+
+#include "snlPoint.h"
+#include "snlKnotVector.h"
+#include "snlSurface.h"
+#include "ptrList.h"
+
+#ifdef SGI_MIPS
+
+    #include <iostream.h>
+    #include <math.h>
+    
+#else
+
+    #include <iostream>
+    #include <cmath>
+    
+    using namespace std;
+    
+#endif
+
+const int MAX_PROJ_ITER = 64;  // Maximum number of newton iterations for projection functions.
+
+typedef struct
+{
+    /* surface location */
+
+    knot        paramT;  // Parameter in t direction.
+    knot        paramU;  // Parameter in u direction.
+    snlPoint    pt;  // Point corresponding to parameters.
+    int         flag;  // Indicates whatever you want ;-)
+
+} sLocn;
+
+typedef struct
+{
+    /* surface location */
+
+    knot        paramT;  // Parameter in t direction.
+    knot        paramU;  // Parameter in u direction.
+    snlPoint    pt;  // Point corresponding to parameters.
+    int         flag;
+    basis       dist;  // Distance from point to surface.
+
+} projLocn;
+
+bool newtonIterStepSurf ( snlPoint* derivPts, snlPoint* projPt, knot* deltaU, knot* deltaV );
+
+bool newtonIterStepCurve ( snlPoint* derivPts, snlPoint* projPt, knot* paramDelta );
+
+bool lineIterStepCurve ( snlPoint* derivPts, snlPoint* projPt, knot* paramDelta );
+
+int solve2X2LinEqn ( double a1, double a2, double a3, double a4,
+                     double b1, double b2, double* x1, double* x2 );
+
+knot paramDistSqrd ( knot t1, knot u1, knot t2, knot u2 );
+
+void resolveAmbig ( sLocn* projns, int projSize, ptrList < sLocn >* ambig );
+
+ptrList < sLocn >* projPtSurf ( snlSurface& surface, snlPoint* toProj, int toProjNum, sLocn* best,
+                                double iterTol, double cosTol, unsigned maxPass );
+
+
diff --git a/src/snlPoint.cpp b/src/snlPoint.cpp
new file mode 100644
index 0000000..be0ecfd
--- /dev/null
+++ b/src/snlPoint.cpp
@@ -0,0 +1,424 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2003 Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "snlPoint.h"
+
+snlPoint::snlPoint()
+{
+    elements [ 0 ] = 0;
+    elements [ 1 ] = 0;
+    elements [ 2 ] = 0;
+    elements [ 3 ] = 1;
+}
+
+snlPoint::snlPoint ( const snlPoint& copyFrom )
+{
+    // Copy constructor.
+    // -----------------
+    
+    for ( unsigned index = 0; index < 4; index ++ )
+        elements [ index ] = copyFrom.elements [ index ];
+}
+
+snlPoint::snlPoint ( double x, double y, double z, double w )
+{
+    elements [ 0 ] = x;
+    elements [ 1 ] = y;
+    elements [ 2 ] = z;
+    elements [ 3 ] = w;
+}
+
+void snlPoint::components ( double x, double y, double z, double w )
+{
+    elements [ 0 ] = x;
+    elements [ 1 ] = y;
+    elements [ 2 ] = z;
+    elements [ 3 ] = w;
+}
+
+void snlPoint::components ( double x, double y, double z )
+{
+    elements [ 0 ] = x;
+    elements [ 1 ] = y;
+    elements [ 2 ] = z;
+}
+       
+void snlPoint::components ( double* x, double* y, double* z, double* w )
+{
+    *x = elements [ 0 ];
+    *y = elements [ 1 ];
+    *z = elements [ 2 ];
+    *w = elements [ 3 ];
+}
+
+void snlPoint::components ( double* x, double* y, double* z )
+{
+    *x = elements [ 0 ];
+    *y = elements [ 1 ];
+    *z = elements [ 2 ];
+}
+
+double snlPoint::x() const
+{
+    return elements [ 0 ];
+}
+
+double snlPoint::y() const
+{
+    return elements [ 1 ];
+}
+
+double snlPoint::z() const
+{
+    return elements [ 2 ];
+}
+
+double snlPoint::w() const
+{
+    return elements [ 3 ];
+}
+
+void snlPoint::multiplyWeight ( double multiplier )
+{
+    // Multiply this points weight by multiplier.
+    // ------------------------------------------
+
+    for ( int index = 0; index < 4; index ++ )
+        elements [ index ] *= multiplier;
+}
+
+void snlPoint::normalise()
+{
+    // Normalise point. ie Divide by w.
+    // --------------------------------
+    
+    if ( elements [ 3 ] == 0.0 ) return;  // Stop divide by zero error.
+
+    double w = elements [ 3 ];
+
+    elements [ 0 ] /= w;
+    elements [ 1 ] /= w;
+    elements [ 2 ] /= w;
+    elements [ 3 ] = 1.0;
+}
+
+void snlPoint::null()
+{
+    // Set everything to zero.
+    // -----------------------
+    
+    elements [ 0 ] = 0;   
+    elements [ 1 ] = 0;   
+    elements [ 2 ] = 0;   
+    elements [ 3 ] = 0;   
+}
+
+bool snlPoint::isNull()
+{
+    // Return true if point is null.
+    // -----------------------------
+
+    return elements [ 3 ] == 0;
+}
+
+void snlPoint::zero()
+{
+    // Set everything to zero.
+    // -----------------------
+    
+    elements [ 0 ] = 0;   
+    elements [ 1 ] = 0;   
+    elements [ 2 ] = 0;   
+    elements [ 3 ] = 1;   
+}
+
+snlPoint snlPoint::operator + ( const snlVector& vect ) const
+{
+    // Add a vector to this point
+    // --------------------------
+
+    snlPoint    retPt;
+    int         index;
+
+    if ( vect.homogeneous )
+    {
+        for ( index = 0; index < 4; index ++ )
+            retPt.elements [ index ] = elements [ index ] + vect.elements [ index ];
+    }
+    else
+    {
+        for ( index = 0; index < 3; index ++ )
+            retPt.elements [ index ] = elements [ index ] + vect.elements [ index ] * elements [ 3 ];
+        
+        retPt.elements [ 3 ] = elements [ 3 ];
+    }
+
+    return retPt;
+}
+
+snlPoint snlPoint::operator + ( const snlPoint& point ) const
+{
+    // Add a point to this point
+    // -------------------------
+    
+    snlPoint    retPt;
+    int         index;
+
+    for ( index = 0; index < 4; index ++ )
+        retPt.elements [ index ] = elements [ index ] + point.elements [ index ];
+
+    return retPt;
+}
+
+snlPoint snlPoint::operator - ( const snlVector& vect ) const
+{
+    // Subtract a vector from this point
+    // ---------------------------------
+    
+    snlPoint    retPt;
+    int         index;
+    
+    if ( vect.homogeneous )
+    {
+        for ( index = 0; index < 4; index ++ )
+            retPt.elements [ index ] = elements [ index ] - vect.elements [ index ];
+    }
+    else
+    {
+        for ( index = 0; index < 3; index ++ )
+            retPt.elements [ index ] = elements [ index ] - vect.elements [ index ] * elements [ 3 ];
+        
+        retPt.elements [ 3 ] = elements [ 3 ];
+    }
+    
+    return retPt;
+}
+
+snlPoint snlPoint::operator - ( const snlPoint& point ) const
+{
+    // Subtract a point from this point.
+    // ---------------------------------
+    
+    snlPoint    retPt;
+    int         index;
+
+    for ( index = 0; index < 4; index ++ )
+        retPt.elements [ index ] = elements [ index ] - point.elements [ index ];
+
+    return retPt;
+}
+
+snlPoint snlPoint::operator * ( double scalar ) const
+{
+    // Multiply scalar to this point.
+    // ------------------------------
+    // scalar:      Scalar to multiply to point.
+    
+    snlPoint    retPt;
+    
+    for ( int index = 0; index < 4; index ++ )
+        retPt.elements [ index ] = elements [ index ] * scalar;
+    
+    return retPt;
+}
+
+snlPoint snlPoint::operator / ( double scalar ) const
+{
+    // Divide this point by a scalar.
+    // ------------------------------
+    // scalar:      Scalar to divide point by.
+    
+    snlPoint    retPt;
+    
+    for ( int index = 0; index < 4; index ++ )
+        retPt.elements [ index ] = elements [ index ] / scalar;
+    
+    return retPt;
+}
+
+void snlPoint::operator = ( const snlPoint& copyFrom )
+{
+    // Copy data from another point.
+    // -----------------------------
+    
+    elements [ 0 ] = copyFrom.elements [ 0 ];
+    elements [ 1 ] = copyFrom.elements [ 1 ];
+    elements [ 2 ] = copyFrom.elements [ 2 ];
+    elements [ 3 ] = copyFrom.elements [ 3 ];
+}
+
+void snlPoint::operator += ( const snlPoint& point )
+{
+    // Add a point to this point.
+    // --------------------------
+    
+    elements [ 0 ] += point.elements [ 0 ];
+    elements [ 1 ] += point.elements [ 1 ];
+    elements [ 2 ] += point.elements [ 2 ];
+    elements [ 3 ] += point.elements [ 3 ];
+}
+
+void snlPoint::operator += ( const snlVector& vect )
+{
+    // Add a vector to this point.
+    // ---------------------------
+    
+    if ( vect.homogeneous )
+    {
+        elements [ 0 ] += vect.elements [ 0 ];
+        elements [ 1 ] += vect.elements [ 1 ];
+        elements [ 2 ] += vect.elements [ 2 ];
+        elements [ 3 ] += vect.elements [ 3 ];
+    }
+    else
+    {
+        elements [ 0 ] += vect.elements [ 0 ] * elements [ 3 ];
+        elements [ 1 ] += vect.elements [ 1 ] * elements [ 3 ];
+        elements [ 2 ] += vect.elements [ 2 ] * elements [ 3 ];
+    }
+}
+
+void snlPoint::operator -= ( const snlPoint& point )
+{
+    // Subtract a point from this point.
+    // ---------------------------------
+    
+    elements [ 0 ] -= point.elements [ 0 ];
+    elements [ 1 ] -= point.elements [ 1 ];
+    elements [ 2 ] -= point.elements [ 2 ];
+    elements [ 3 ] -= point.elements [ 3 ];
+}
+
+void snlPoint::operator *= ( double scalar )
+{
+    // Mulitply a scalar to this point.
+    // --------------------------------
+    
+    for ( int index = 0; index < 4; index ++ )
+        elements [ index ] *= scalar;
+}
+
+void snlPoint::operator /= ( double scalar )
+{
+    // Divide this point by a scalar.
+    // ------------------------------
+    
+    for ( int index = 0; index < 4; index ++ )
+        elements [ index ] /= scalar;
+}
+
+void snlPoint::x ( double val )
+{
+    // Set x component.
+    // ----------------
+
+    elements [ 0 ] = val;
+}
+
+void snlPoint::y ( double val )
+{
+    // Set y component.
+    // ----------------
+
+    elements [ 1 ] = val;
+}
+
+void snlPoint::z ( double val )
+{
+    // Set z component.
+    // ----------------
+
+    elements [ 2 ] = val;
+}
+
+void snlPoint::w ( double val )
+{
+    // Set w component.
+    // ----------------
+
+    elements [ 3 ] = val;
+}
+
+double snlPoint::lengthSqrd() const
+{
+    // Return length of this point, treated as a vector, squared.
+    // ----------------------------------------------------------
+
+    double sum = 0;
+
+    for ( int index = 0; index < 4; index ++ )
+        sum += elements [ index ] * elements [ index ];
+        
+    return sum;
+}
+
+double snlPoint::distSqrd ( const snlPoint& toPoint ) const
+{
+    // Return squared distance from this point to given point.
+    // -------------------------------------------------------
+    // toPoint:     Point to calculate distance to.
+    //
+    // Notes:       This is a 3D distance only.
+
+    double      diff;
+    
+    double retVal = 0;
+
+    if ( elements [ 3 ] == 1.0 && toPoint.elements [ 3 ] == 1.0 )
+    {
+        for ( int index = 0; index < 3; index ++ )
+        {
+            diff = elements [ index ] - toPoint.elements [ index ];
+            retVal += diff * diff;
+        }
+    }
+    else
+    {
+        for ( int index = 0; index < 3; index ++ )
+        {
+            diff = ( elements [ index ] / elements [ 3 ] )
+                   - ( toPoint.elements [ index ] / toPoint.elements [ 3 ] );
+                   
+            retVal += diff * diff;
+        }
+    }
+
+    return retVal;
+}
+
+bool snlPoint::operator == ( snlPoint& compare )
+{
+    // Return true if compare is eqivalent to this point.
+    // --------------------------------------------------
+    
+    bool retVal = true;
+    
+    for ( int index = 0; index < 4; index ++ )
+        if ( elements [ index ] != compare.elements [ index ] )
+            retVal = false;
+            
+    return retVal;
+}
+
+void snlPoint::print() const
+{
+    // Print to std out.
+    // -----------------
+ 
+    cout << "( " << elements [ 0 ] << ", " <<  elements [ 1 ] << ", " << elements [ 2 ] << ", " << elements [ 3 ] << " )";
+}
+
diff --git a/src/snlPoint.h b/src/snlPoint.h
new file mode 100644
index 0000000..5050fe3
--- /dev/null
+++ b/src/snlPoint.h
@@ -0,0 +1,85 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2003 Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef SNLPOINT_H
+#define SNLPOINT_H
+
+class snlVector;
+
+#include "snlVector.h"
+
+class snlPoint
+{
+    public:
+
+        snlPoint();        
+        
+        snlPoint ( const snlPoint& copyFrom );
+        
+        snlPoint ( double x, double y, double z, double w = 1.0 );
+        
+        void components ( double x, double y, double z, double w );
+        void components ( double x, double y, double z );
+        
+        void components ( double* x, double* y, double* z, double* w );
+        void components ( double* x, double* y, double* z );
+
+        double x() const;
+        double y() const;
+        double z() const;
+        double w() const;
+
+        void x ( double );
+        void y ( double );
+        void z ( double );
+        void w ( double );
+
+        void multiplyWeight ( double multiplier );
+
+        void normalise();
+        
+        void null();  // Set everything to zero.
+
+        bool isNull();
+        
+        void zero();  // Set everything to zero and w to 1.
+        
+        double lengthSqrd() const;  // Treat point as vector.
+        double distSqrd ( const snlPoint& toPoint ) const;
+
+        snlPoint operator + ( const snlVector& vect ) const;
+        snlPoint operator + ( const snlPoint& point ) const;
+        snlPoint operator - ( const snlVector& vect ) const;
+        snlPoint operator - ( const snlPoint& point ) const;
+        snlPoint operator * ( double scalar ) const;
+        snlPoint operator / ( double scalar ) const;
+        
+        void operator = ( const snlPoint& copyFrom );
+        void operator += ( const snlPoint& point );
+        void operator += ( const snlVector& vect );
+        void operator -= ( const snlPoint& point );        
+        void operator *= ( double scalar );
+        void operator /= ( double scalar );
+        
+        bool operator == ( snlPoint& compare );
+        
+        void print() const;
+
+        double elements [ 4 ];  // x, y, z, w.
+};
+
+#endif
diff --git a/src/snlSquareLinear.cpp b/src/snlSquareLinear.cpp
new file mode 100644
index 0000000..b7c8aa5
--- /dev/null
+++ b/src/snlSquareLinear.cpp
@@ -0,0 +1,184 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** Class for the solution of square linear algebraic equations ***
+
+#include "snlSquareLinear.h"
+
+snlSquareLinear::snlSquareLinear()
+{
+    num_unknowns = 0;
+    num_rhSides = 0;
+    coeffs = 0;
+    rhSides = 0;
+}
+
+snlSquareLinear::~snlSquareLinear()
+{    
+    if ( coeffs ) delete[] coeffs;
+    if ( rhSides ) delete[] rhSides;
+}
+
+snlSquareLinear::snlSquareLinear ( int numUnknowns, int numRightHandSides, double* coefficients, double* rightHandSides )
+{
+    // Setup and solve square system of linear equations.
+    // --------------------------------------------------
+    // numUnknowns:        Number of unknowns in each equation.
+    // numRightHandSides:  Number of right hand sides.
+    // coefficients:       Array of size [numUnkowns][numUnknowns] holding unknown coefficients.
+    // rightHandSides:     Array of size [numRightHandSides][numUnknowns] holding each right hand sides in a column.
+    //
+    // Notes:              All arrays use c style ordering. ( This is different from snlMatrix_4X4 ).
+    //                     This object owns the arrays passed to it.
+    
+    num_unknowns = numUnknowns;
+    num_rhSides = numRightHandSides;
+    
+    coeffs = coefficients;
+    rhSides = rightHandSides;
+    
+    solve();
+}
+
+void snlSquareLinear::solve()
+{
+    // Solve linear equations.
+    // -----------------------
+    //
+    // Notes:    This method does not use row or column interchanges.    
+    
+    if ( ! coeffs || ! rhSides ) return;
+    
+    // Use coefficient array to calculate and then store LU decomposition.
+    // L is stored in the lower diagonal part of the coef array without utilising the diagonal.
+    // U is stored in the upper diagonal part of the coef array.
+    
+    // Perform Gauss elimination and store negatives of the multipliers m(j,i) in L matrix.    
+    
+    for ( int pivot = 0; pivot < num_unknowns - 1; pivot ++ )
+    {        
+        int pivotRowIndex = pivot * num_unknowns;
+        int pivotIndex = pivotRowIndex + pivot;
+        
+        double pivotVal = coeffs [ pivotIndex ];        
+        
+        for ( int row = pivot + 1; row < num_unknowns; row ++ )
+        {
+            int index = row * num_unknowns + pivot;  // Current index in coef array being processed.
+            
+            double multiplier = coeffs [ index ] / pivotVal;
+            
+            coeffs [ index ++ ] = multiplier;  // Pivot is subtracted instead of added during elimination.
+            
+            for ( int col = pivot + 1; col < num_unknowns; col ++ )            
+                coeffs [ index ++ ] -= multiplier * coeffs [ pivotRowIndex + col ];            
+        }
+    }    
+    
+    // Calculate intermediate right hand values using forward substitution and L.
+    
+    for ( int coefRow = 1; coefRow < num_unknowns; coefRow ++ )
+    {
+        int multIndex = coefRow * num_unknowns;
+        
+        int rhsIndex = coefRow * num_rhSides;
+        
+        int rhsMultIndex = 0;
+        
+        for ( int coefCol = 0; coefCol < coefRow; coefCol ++ )
+        {        
+            double multiplier = coeffs [ multIndex ++ ];            
+            
+            for ( int rhsCol = 0; rhsCol < num_rhSides; rhsCol ++ )            
+                rhSides [ rhsIndex + rhsCol ] -= multiplier * rhSides [ rhsMultIndex ++ ];
+        }     
+    }    
+    
+    // Calculate final right hand values using back substitution and U.
+    
+    for ( int coefRow = num_unknowns - 1; coefRow >= 0; coefRow -- )
+    {
+        int multIndex = ( coefRow + 1 ) * num_unknowns - 1;
+        
+        int rhsIndex = ( coefRow + 1 ) * num_rhSides - 1;
+        
+        int rhsMultIndex = num_unknowns * num_rhSides - 1;
+        
+        for ( int coefCol = num_unknowns - 1; coefCol > coefRow; coefCol -- )
+        {        
+            double multiplier = coeffs [ multIndex -- ];
+            
+            for ( int rhsCol = 0; rhsCol < num_rhSides; rhsCol ++ )
+                rhSides [ rhsIndex - rhsCol ] -= multiplier * rhSides [ rhsMultIndex -- ];
+        }
+        
+        // Divide right hand sides through by coef diagonal values.
+        
+        double divisor = coeffs [ multIndex ];
+        
+        for ( int rhsCol = 0; rhsCol < num_rhSides; rhsCol ++ )
+            rhSides [ rhsIndex - rhsCol ] /= divisor;
+    }    
+}
+
+
+void snlSquareLinear::print()
+{
+    // Print current coefficients and right hand sides to standard out.
+    // ----------------------------------------------------------------
+    
+    cout << "\n\nCoefficients:\n\n";
+    
+    printCoeffs();    
+    
+    cout << "\n\nRight Hand Sides:\n\n";
+    
+    printRhSides();
+}
+
+void snlSquareLinear::printCoeffs()
+{
+    // Print coefficents to standard out.
+    // ----------------------------------
+    
+    int index = 0;    
+    
+    for ( int row = 0; row < num_unknowns; row ++ )
+    {
+        for ( int col = 0; col < num_unknowns; col ++ )
+            cout << coeffs [ index ++ ] << "   ";
+        
+        cout << "\n";
+    }
+}
+
+void snlSquareLinear::printRhSides()
+{
+    // Print right hand sides to standard out.
+    // ---------------------------------------
+    
+    int index = 0;    
+    
+    for ( int row = 0; row < num_unknowns; row ++ )
+    {
+        for ( int col = 0; col < num_rhSides; col ++ )
+            cout << rhSides [ index ++ ] << "   ";
+        
+        cout << "\n";
+    }
+}
+
diff --git a/src/snlSquareLinear.h b/src/snlSquareLinear.h
new file mode 100644
index 0000000..191e8fc
--- /dev/null
+++ b/src/snlSquareLinear.h
@@ -0,0 +1,62 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** Class for the solution of square linear algebraic equations ***
+
+#ifndef SNL_SQUARELINEAR_H
+#define SNL_SQUARELINEAR_H
+
+#include "snlPoint.h"
+
+#ifdef SGI_MIPS
+
+    #include <iostream.h>
+    #include <math.h>
+    
+#else
+
+    #include <iostream>
+    #include <cmath>
+    
+    using namespace std;
+    
+#endif
+
+class snlSquareLinear
+{
+    public:
+        
+        snlSquareLinear();
+        ~snlSquareLinear();
+        snlSquareLinear ( int numUnknowns, int numRightHandSides, double* coefficients, double* rightHandSides );
+        
+        void solve();
+        
+        void print();
+        void printCoeffs();
+        void printRhSides();
+    
+    private:
+    
+        int        num_unknowns; // Number of unknowns corresponding to coefficient matrix.
+        int        num_rhSides;  // Number of right hand sides.
+        
+        double*    coeffs;  // Coefficient matrix array.
+        double*    rhSides;  // Array of right hands sides.            
+};
+
+#endif
diff --git a/src/snlSurface.cpp b/src/snlSurface.cpp
new file mode 100644
index 0000000..dc2fd4c
--- /dev/null
+++ b/src/snlSurface.cpp
@@ -0,0 +1,3925 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2003 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+
+// *** General NURBS Surface ***
+
+#include "snlSurface.h"
+#include "snlUtil.h"
+
+#include "snlNurbsCommon.h"
+
+snlSurface::~snlSurface()
+{
+    if ( ctrlPtNet ) delete ctrlPtNet;
+    
+    if ( knotVectU ) delete knotVectU;
+    if ( knotVectV ) delete knotVectV;
+
+    if ( trim_curves ) delete trim_curves;
+}
+
+snlSurface::snlSurface()
+{
+    init();
+}
+
+void snlSurface::init()
+{
+    //! Standard Initialisation.
+    //  ------------------------
+
+    ctrlPtNet = 0;
+
+    knotVectU = 0;
+    knotVectV = 0;
+
+    trim_curves = new ptrList< snlCurve >;
+}
+
+snlSurface::snlSurface ( const snlSurface& surfaceToCopy )
+{
+    //! Copy constructor.
+    //  -----------------
+
+    init();
+
+    copyFrom ( surfaceToCopy );
+}
+
+snlSurface::snlSurface ( int degreeU, int degreeV, unsigned sizeU, unsigned sizeV, 
+                         snlPoint& origin, snlPoint& cornerMaxU, snlPoint& cornerMaxV )
+{
+    //! Construct a new NURBS surface.
+    //  ------------------------------
+    //! @param degreeU Degree of surface in U direction.
+    //! @param degreeV Degree of surface in V direction.
+    //! @param sizeU Number of control points in the U dimension.
+    //! @param sizeV Number of control points in the V dimension.
+    //! @param origin Point at (u,v) = (0,0).
+    //! @param cornerMaxU Point at (u,v) = (MaxU,0).
+    //! @param cornerMaxV Point at (u,v) = (0,MaxV).
+
+    init();
+    
+    ctrlPtNet = new snlCtrlPointNetSurface ( sizeU, sizeV, origin, cornerMaxU, cornerMaxV );
+    
+    knotVectU = new snlKnotVector ( 0.0, 1.0, sizeU + degreeU + 1, degreeU );
+    knotVectV = new snlKnotVector ( 0.0, 1.0, sizeV + degreeV + 1, degreeV );
+    
+    degU = degreeU;
+    degV = degreeV;
+}
+
+snlSurface::snlSurface ( int degreeU, int degreeV, unsigned sizeU, unsigned sizeV, snlCtrlPoint* points,
+                         knot* knotsU, knot* knotsV )
+{
+    //! Create surface from existing data.
+    //  ----------------------------------
+    //! @param degreeU Degree in U direction.
+    //! @param degreeV Degree in V direction.
+    //! @param sizeU Size of U dimension.
+    //! @param sizeV Size of V dimension.
+    //! @param points Control points to use.
+    //! @param knotsU Array of knots for U direction.
+    //! @param knotsV Array of knots for V direction.
+    //!
+    //! @par   Notes:
+    //!        Assumes a clamped (open) knot vector.
+    //! @attention Does NOT COPY point and knot data. So don't delete them elsewhere.
+
+    init();
+    
+    degU = degreeU;
+    degV = degreeV;
+
+    ctrlPtNet = new snlCtrlPointNetSurface ( points, sizeU, sizeV, false );
+    
+    if ( knotsU )
+        knotVectU = new snlKnotVector (  knotsU, sizeU + degU + 1, degreeU );
+    else
+        knotVectU = new snlKnotVector (  0.0, 1.0, sizeU + degU + 1, degreeU );
+    
+    if ( knotsV )    
+        knotVectV = new snlKnotVector (  knotsV, sizeV + degV + 1, degreeV );
+    else
+        knotVectV = new snlKnotVector (  0.0, 1.0, sizeV + degV + 1, degreeV );
+}
+
+snlSurface::snlSurface ( snlCurve& curve1, snlCurve& curve2, int direction )
+{
+    //! Generate ruled surface.
+    //  -----------------------
+    //! @param curve1 First side of surface.
+    //! @param curve2 Second side of surface.
+    //! @param direction Parmetric direction defining curves lay in.
+
+    init();
+
+    // Curves may be modified so copy them.
+
+    snlCurve* curve_1 = new snlCurve ( curve1 );
+    snlCurve* curve_2 = new snlCurve ( curve2 );
+
+    // Make sure curves are compatible.
+
+    curve_1 -> makeCompatible ( curve_2 );
+
+    // Generate knot vectors.
+
+    if ( direction == SNL_U_DIR )
+    {
+        knotVectU = new snlKnotVector ( curve_1 -> knotVector() );
+        knotVectV = new snlKnotVector ( 0.0, 1.0, 4, 1 );
+    }
+    else
+    {
+        knotVectU = new snlKnotVector ( 0.0, 1.0, 4, 1 );
+        knotVectV = new snlKnotVector ( curve_1 -> knotVector() );
+    }
+    
+    // Generate control points.
+    
+    int size = curve_1 -> size();
+    int arraySize = size * 2;
+    
+    snlCtrlPoint* ctrlPts = new snlCtrlPoint [ arraySize ];
+    
+    const snlCtrlPoint* copyNet1 = curve_1 -> controlPointNet().getCtrlPts();
+    const snlCtrlPoint* copyNet2 = curve_2 -> controlPointNet().getCtrlPts();
+    
+    int ctrlPtsIndex = 0;
+
+    if ( direction == SNL_U_DIR )
+    {
+        for ( int index = 0; index < size; index ++ )
+        {
+            ctrlPts [ ctrlPtsIndex ++ ] = copyNet1 [ index ];
+            ctrlPts [ ctrlPtsIndex ++ ] = copyNet2 [ index ];
+        }
+        
+        ctrlPtNet = new snlCtrlPointNetSurface ( ctrlPts, size, 2, false );
+        
+        degU = curve_1 -> degree();
+        degV = 1;
+    }
+    else
+    {
+        int ctrlPtsIndex2 = size;
+        
+        for ( int index = 0; index < size; index ++ )
+        {
+            ctrlPts [ ctrlPtsIndex ++ ] = copyNet1 [ index ];
+            ctrlPts [ ctrlPtsIndex2 ++ ] = copyNet2 [ index ];
+        }
+
+        ctrlPtNet = new snlCtrlPointNetSurface ( ctrlPts, 2, size, false );
+        
+        degU = 1;
+        degV = curve_1 -> degree();
+    }
+
+    // Clean up.
+
+    delete curve_1;
+    delete curve_2;
+}
+
+snlSurface::snlSurface ( snlCurve& generator, snlPoint& axisStart, snlPoint& axisEnd, double angle )
+{
+    //! Construct surface of revolution.
+    //  --------------------------------
+    //! @param generator Generating curve.
+    //! @param axisStart Starting point of axis generator is revolved about.
+    //! @param axisEnd Ending point of axis.
+    //! @param angle Angle in degrees to revolve generator about axis. Angle is in degrees so that
+    //!              different precisions of PI do not affect surface closure.
+    //!
+    //! @par Notes:
+    //!      Rotation is counter clockwise about axis vector ie Right hand rule. Curve defines V
+    //!      direction.
+
+    init();
+
+    genSurfRevolution ( generator, axisStart, axisEnd, angle );
+}
+
+snlSurface::snlSurface ( snlCurve** curves, int numCurves, int dir )
+{
+    //! Construct skinned surface.
+    //  --------------------------
+    //! @param curves Curves to skin with.
+    //! @param numCurves Number of curves in curves array.
+    //! @param dir Parametric direction to skin over.
+    //!
+    //! @par Notes:
+    //!      The skinned direction is degree 2. \n
+    //!      It is assumed the curves are exactly the same size with the same knot vector.
+
+    init();
+    
+    // Assemble points to be interpolated.
+
+    int numCurvePts = curves [ 0 ] -> size();
+    int numPts = numCurvePts * numCurves;
+
+    int sizeU = ( dir == SNL_U_DIR ? numCurves : numCurvePts );
+    int sizeV = ( dir == SNL_U_DIR ? numCurvePts : numCurves );
+
+    snlPoint* points = new snlPoint [ numPts ];
+
+    for ( int curveIndex = 0; curveIndex < numCurves; curveIndex ++ )
+    {
+        int index, step;
+
+        if ( dir == SNL_U_DIR )
+        {
+            index = curveIndex * numCurvePts;
+            step = 1;
+        }
+        else
+        {
+            index = curveIndex;
+            step = numCurves;
+        }
+
+        const snlCtrlPoint* curvePts = ( curves [ curveIndex ] -> controlPointNet() ).getCtrlPts();
+        
+        for ( int ptIndex = 0; ptIndex < numCurvePts; ptIndex ++ )
+        {
+            points [ index ] = curvePts [ ptIndex ];
+
+            index += step;
+        }
+    }
+
+    // Generate parameters
+
+    knot* params = globalInterpGenParams ( SNL_GLOBAL_INTERP_CENTRIFUGAL, points, sizeU, sizeV, dir );
+                                           
+    // Generate knot vectors.
+
+    if ( dir == SNL_U_DIR )
+    {
+        knotVectU = new snlKnotVector ( numCurves, 2, params );
+        knotVectV = new snlKnotVector ( curves [ 0 ] -> knotVector() );
+    }
+    else
+    {
+        knotVectU = new snlKnotVector ( curves [ 0 ] -> knotVector() );
+        knotVectV = new snlKnotVector ( numCurves, 2, params );
+    }
+
+    // Generate control points.
+
+    snlPoint* linePts = new snlPoint [ numCurves ];
+
+    snlCtrlPoint* ctrlPts = new snlCtrlPoint [ numPts ];
+
+    for ( int lineIndex = 0; lineIndex < numCurvePts; lineIndex ++ )
+    {
+        // Assemble points.
+
+        int index, step;
+
+        if ( dir == SNL_U_DIR )
+        {
+            index = lineIndex;
+            step = numCurvePts;
+        }
+        else
+        {
+            index = lineIndex * numCurves;
+            step = 1;
+        }
+
+        for ( int ptIndex = 0; ptIndex < numCurves; ptIndex ++ )
+        {
+            linePts [ ptIndex ] = points [ index ];
+            index += step;
+        }
+
+        // Interpolate between assembled points.
+
+        snlCtrlPoint* finalPts = snlCurve::genGlobalInterpPoints ( linePts, numCurves, params,
+                                                                   dir == SNL_U_DIR ? knotVectU : knotVectV );
+
+        // Copy points into surface control point array.
+
+        if ( dir == SNL_U_DIR )
+        {
+            index = lineIndex;
+            step = numCurvePts;
+        }
+        else
+        {
+            index = lineIndex * numCurves;
+            step = 1;
+        }
+
+        for ( int ptIndex = 0; ptIndex < numCurves; ptIndex ++ )
+        {
+            ctrlPts [ index ] = finalPts [ ptIndex ];
+            index += step;
+        }
+
+        delete[] finalPts;
+    }
+
+    ctrlPtNet = new snlCtrlPointNetSurface ( ctrlPts, sizeU, sizeV );
+
+    // Set other variables.
+    
+    if ( dir == SNL_U_DIR )
+    {
+        degU = 2;
+        degV = curves [ 0 ] -> degree();
+    }
+    else
+    {
+        degU = curves [ 0 ] -> degree();
+        degV = 2;
+    }
+
+    // Clean up.
+
+    delete[] points;
+    delete[] params;
+    delete[] linePts;
+}
+
+snlSurface::snlSurface ( int interpType, snlPoint* pointsInterp, int sizeU, int sizeV, int degreeU, int degreeV )
+{
+    //! Generate surface by interpolating point data.
+    //  ---------------------------------------------
+    //! @param interpType Type of interpolation. Comes from enum SNL_INTERP_TYPES.
+    //! @param pointsInterp Points to interpolate. Point array.
+    //! @param sizeU Size of point array dimension in U direction.
+    //! @param sizeV Size of point array dimension in V direction.
+    //! @param degreeU Degree of U direction.
+    //! @param degreeV Degree of V direction.
+
+    init();
+
+    if ( interpType == SNL_GLOBAL_INTERP_CHORDLENGTH || interpType == SNL_GLOBAL_INTERP_CENTRIFUGAL )
+        genGlobalInterpSurf ( interpType, pointsInterp, sizeU, sizeV, degreeU, degreeV );
+}
+
+snlSurface::snlSurface ( snlCurve* U1, snlCurve* U2, snlCurve* V1, snlCurve* V2 )
+{
+    //! Generate Coons Patch from given curves.
+    //  ---------------------------------------
+    //! @param U1 First curve in U direction.
+    //! @param U2 Second curve in U direction.
+    //! @param V1 First curve in V direction.
+    //! @param V2 Second curve in V direction.
+
+    init();
+
+    genBilinearCoons( U1, U2, V1, V2 );
+}
+
+void snlSurface::copyFrom ( const snlSurface& surfaceToCopy )
+{
+    //! Copy contents of another surface into this.
+    //  -------------------------------------------
+
+    ctrlPtNet = new snlCtrlPointNetSurface ( *(surfaceToCopy.ctrlPtNet) );
+    
+    knotVectU = new snlKnotVector ( *(surfaceToCopy.knotVectU) );
+    knotVectV = new snlKnotVector ( *(surfaceToCopy.knotVectV) );
+    
+    degU = surfaceToCopy.degU;
+    degV = surfaceToCopy.degV;
+
+    snlCurve* trimCurve = ( surfaceToCopy.trim_curves ) -> first();
+
+    snlCurve* curveCopy;
+
+    while ( trimCurve )
+    {
+        curveCopy = new snlCurve ( *trimCurve );
+
+        addTrimCurve ( curveCopy );
+
+        trimCurve = ( surfaceToCopy.trim_curves ) -> next();
+    }
+}
+
+snlSurface& snlSurface::operator= ( const snlSurface& surfaceToCopy )
+{
+    //! Assignment Operator.
+    //  --------------------
+
+    if ( this != &surfaceToCopy )
+    {
+        // If surface already contains data then will have to delete it.
+
+        if ( ctrlPtNet ) delete ctrlPtNet;
+        if ( knotVectU ) delete knotVectU;
+        if ( knotVectV ) delete knotVectV;
+
+        trim_curves -> clear();
+
+        copyFrom ( surfaceToCopy );
+    }
+
+    return *this;
+}
+
+void snlSurface::genBilinearCoons ( snlCurve* curve_U1, snlCurve* curve_U2, snlCurve* curve_V1, snlCurve* curve_V2 )
+{
+    //! Generate Bilinear Coons Patch.
+    //  ------------------------------
+    //! @param curve_U1 First curve in U direction.
+    //! @param curve_U2 Second curve in U direction.
+    //! @param curve_V1 First curve in V direction.
+    //! @param curve_V2 Second curve in V direction.
+    //!
+    //! @par Notes:
+    //!      The curves must form a closed but non-sequential loop.
+    //! @verbatim
+    //!         V1
+    //!      -------->
+    //!     |         |
+    //! U1  |         |  U2
+    //!     |         |
+    //!     V         V
+    //!      -------->
+    //!         V2              @endverbatim
+
+    // Create Bilinear surface as base of this surface.
+
+    knotVectV = new snlKnotVector ( 0.0, 1.0, 4, 1 );
+    knotVectU = new snlKnotVector ( 0.0, 1.0, 4, 1 );
+
+    snlCtrlPoint* ctrlPts = new snlCtrlPoint [ 4 ];
+
+    ctrlPts [ 0 ] = ( curve_V1 -> controlPointNet() ).getPoint ( 0 );
+    ctrlPts [ 1 ] = ( curve_V1 -> controlPointNet() ).getPoint ( ( curve_V1 -> controlPointNet() ).size() - 1 );
+    ctrlPts [ 2 ] = ( curve_V2 -> controlPointNet() ).getPoint ( 0 );
+    ctrlPts [ 3 ] = ( curve_V2 -> controlPointNet() ).getPoint ( ( curve_V2 -> controlPointNet() ).size() - 1 );
+
+    degU = 1;
+    degV = 1;
+
+    ctrlPtNet = new snlCtrlPointNetSurface ( ctrlPts, 2, 2, false );
+
+    // Make sure curve pairs are compatible.
+
+    curve_U1 -> makeCompatible ( curve_U2 );
+    curve_V1 -> makeCompatible ( curve_V2 );
+
+    // Create U and V direction surfaces.
+
+    snlSurface* surf_U = new snlSurface ( *curve_U1, *curve_U2, (int) SNL_U_DIR );
+    snlSurface* surf_V = new snlSurface ( *curve_V1, *curve_V2, (int) SNL_V_DIR );
+
+    // Surfaces need to be compatible with each other.
+
+    surf_U -> makeCompatible ( surf_V, SNL_U_DIR );
+    surf_U -> makeCompatible ( surf_V, SNL_V_DIR );
+
+    makeCompatible ( surf_V, SNL_U_DIR );
+    makeCompatible ( surf_V, SNL_V_DIR );
+
+    makeCompatible ( surf_U, SNL_U_DIR );
+    makeCompatible ( surf_U, SNL_V_DIR );
+
+    // Generate new surface as addition of control points; surf_U + surf_V - bilinear ( this ).
+
+    *( surf_U -> ctrlPtNet ) += *( surf_V -> ctrlPtNet );
+    *( surf_U -> ctrlPtNet ) -= *ctrlPtNet;
+
+    snlCtrlPointNetSurface* newCtrlPtNet = surf_U -> ctrlPtNet;  // Swap control point net into this.
+    surf_U -> ctrlPtNet = ctrlPtNet;
+    ctrlPtNet = newCtrlPtNet;
+
+    // Clean Up.
+
+    delete surf_U;
+    delete surf_V;
+    
+}
+
+knot* snlSurface::globalInterpGenParams ( int type, snlPoint* points, int sizeU, int sizeV,
+                                          int dir )
+{
+    //! Generate parameters for global interpolation.
+    //  ---------------------------------------------
+    //! @param type Type of global interpolation.
+    //! @param points Array points to interpolate. [ U ][ V ].
+    //! @param sizeU Array size in U direction.
+    //! @param sizeV Array size in V direction.
+    //! @param dir Point array direction to generate parameters for. See enum parametricDirections.
+    //!
+    //! @return 1D Array of knots that holds parametric positions in chosen direction. All
+    //!         lines in dir have exactly the same parametric sequence. Hence this array is 1D.
+
+    int     size, oSize;
+
+    if ( dir == SNL_U_DIR )
+    {
+        size = sizeU;
+        oSize = sizeV;
+    }
+    else
+    {
+        size = sizeV;
+        oSize = sizeU;
+    }
+
+    knot* chordLengths = new knot [ ( size - 1 ) * oSize ];
+    
+    knot* totalChordLengths = new knot [ oSize ];
+
+    for ( int index = 0; index < oSize; index ++ )
+        totalChordLengths [ index ] = 0.0;
+    
+    snlVector chord;
+
+    chord.homogeneous = true;
+
+    // Intermediate step. Calculate (square root of) chord length.
+
+    int chordIndex = 0;
+    
+    for ( int cIndex = 1; cIndex < size; cIndex ++ )
+    {
+        int     index;
+        
+        if ( dir == SNL_U_DIR )
+            index = cIndex * sizeV;
+        else
+            index = cIndex;
+        
+        for ( int oIndex = 0; oIndex < oSize; oIndex ++ )
+        {
+            if ( dir == SNL_U_DIR )
+            {
+                chord.calc ( points [ index - sizeV ], points [ index ] );
+                index ++;
+            }
+            else
+            {
+                chord.calc ( points [ index - 1 ], points [ index ] );
+                index += sizeV;
+            }
+            
+            knot chordLength;
+            
+            if ( type == SNL_GLOBAL_INTERP_CENTRIFUGAL )
+                chordLength = sqrt ( chord.length() );
+            else        
+                chordLength = chord.length();
+                
+            totalChordLengths [ oIndex ] += chordLength;
+
+if ( chordLength == 0.0 )
+{
+    cout << "Zero Chord Length @: " << cIndex << ", " << oIndex << "\n";
+    cout << "start pt: "; points [ index - sizeV - 1 ].print(); cout << "\n";
+    cout << "end pt: "; points [ index - 1 ].print(); cout << "\n";
+}
+            
+            chordLengths [ chordIndex ++ ] = chordLength;
+        }
+    }
+
+    // Calculate final parameter values.
+
+    knot* params = new knot [ size ];
+    
+    params [ 0 ] = 0.0;
+    params [ size - 1 ] = 1.0;
+
+    chordIndex = 0;
+    
+    for ( int cIndex = 1; cIndex < size - 1; cIndex ++ )
+    {
+        params [ cIndex ] = 0.0;
+        
+        for ( int oIndex = 0; oIndex < oSize; oIndex ++ )
+        {
+            // Sum across all chords.
+            params [ cIndex ] += chordLengths [ chordIndex ++ ] / totalChordLengths [ oIndex ];
+        }
+
+        params [ cIndex ] /= (double) oSize;
+
+        params [ cIndex ] += params [ cIndex - 1 ];
+    }
+
+    delete[] totalChordLengths;
+
+    delete[] chordLengths;
+
+    return params;
+}
+
+void snlSurface::genGlobalInterpSurf ( int interpType, snlPoint* pointsInterp, int sizeU, int sizeV,
+                                       int degreeU, int degreeV )
+{
+    //! Generate surface by globally interpolating point data.
+    //  ------------------------------------------------------
+    //! @param interpType Type of interpolation. Comes from enum SNL_INTERP_TYPES.
+    //! @param pointsInterp Points to interpolate. Point array.
+    //! @param sizeU Size of point array dimension in U direction.
+    //! @param sizeV Size of point array dimension in V direction.
+    //! @param degreeU Degree of U direction.
+    //! @param degreeV Degree of V direction.
+
+    degU = degreeU;
+    degV = degreeV;
+
+    // Generate parameters that correspond to given points.
+
+    knot* paramsU = globalInterpGenParams ( interpType, pointsInterp, sizeU, sizeV, SNL_U_DIR );
+    knot* paramsV = globalInterpGenParams ( interpType, pointsInterp, sizeU, sizeV, SNL_V_DIR );
+
+    // Generate knot vectors.
+    
+    knotVectU = new snlKnotVector ( sizeU, degreeU, paramsU );
+    knotVectV = new snlKnotVector ( sizeV, degreeV, paramsV );
+
+    // Generate intermediate control points using U direction.
+
+    int numPts = sizeU * sizeV;
+
+    snlPoint* intermPts = new snlPoint [ numPts ];  // Intermediate points.
+
+    snlPoint* curvePtsU = new snlPoint [ sizeU ];
+
+    for ( int indexV = 0; indexV < sizeV; indexV ++ )
+    {
+        // Find points for intermediate curve.
+
+        int curvePtsIndex = 0;
+
+        for ( int index = indexV; index < numPts; index += sizeV )
+            curvePtsU [ curvePtsIndex ++ ] = pointsInterp [ index ];
+
+        snlCtrlPoint* interpPoints = snlCurve::genGlobalInterpPoints ( curvePtsU, sizeU, paramsU, knotVectU );
+
+        // Place interpolted points into intermediate array.
+
+        curvePtsIndex = 0;
+
+        for ( int index = indexV; index < numPts; index += sizeV )
+            intermPts [ index ] = interpPoints [ curvePtsIndex ++ ];
+
+        delete[] interpPoints;
+    }
+
+    // Generate final control points.
+
+    snlCtrlPoint* finalPts = new snlCtrlPoint [ numPts ];
+
+    snlPoint* curvePtsV = new snlPoint [ sizeV ];
+
+    for ( int baseIndex = 0; baseIndex < numPts; baseIndex += sizeV )
+    {
+        // Each line in V direction is based at baseIndex.
+        
+        int maxVLineIndex = baseIndex + sizeV;
+
+        // Find isoparametric curve points to evaluate.
+
+        int curvePtsIndex = 0;
+        
+        for ( int vLineIndex = baseIndex; vLineIndex < maxVLineIndex; vLineIndex ++ )
+            curvePtsV [ curvePtsIndex ++ ] = intermPts [ vLineIndex ];
+
+        snlCtrlPoint* interpPoints = snlCurve::genGlobalInterpPoints ( curvePtsV, sizeV, paramsV, knotVectV );
+
+        // Place interpolated points into final control point array.
+
+        curvePtsIndex = 0;
+
+        for ( int vLineIndex = baseIndex; vLineIndex < maxVLineIndex; vLineIndex ++ )
+            finalPts [ vLineIndex ] = interpPoints [ curvePtsIndex ++ ];
+
+        delete[] interpPoints;
+    }
+
+    // Generate Control Point Net.
+
+    ctrlPtNet = new snlCtrlPointNetSurface ( finalPts, sizeU, sizeV, false );  // CtrlPtNet _owns_ finalPts array.
+
+    // Clean up.
+
+    delete[] intermPts;
+    delete[] curvePtsU;
+    delete[] curvePtsV;
+    delete[] paramsU;
+    delete[] paramsV;
+}
+
+int snlSurface::degreeU() const
+{
+    //! Return degree of surface in U direction.
+    //  ----------------------------------------
+    
+    return degU;
+}
+
+int snlSurface::degreeV() const
+{
+    //! Return degree of surface in V direction.
+    //  ----------------------------------------
+    
+    return degV;
+}
+        
+unsigned snlSurface::sizeU() const
+{
+    //! Return size of control point array in U direction.
+    //  --------------------------------------------------
+    
+    return ctrlPtNet -> getSizeU();
+}
+
+unsigned snlSurface::sizeV() const
+{
+    //! Return size of control point array in V direction.
+    //  --------------------------------------------------
+    
+    return ctrlPtNet -> getSizeV();
+}
+
+
+knot snlSurface::minU()
+{
+    //! Return minimum U parameter.
+    //  ---------------------------
+
+    return knotVectU -> min();
+}
+
+knot snlSurface::maxU()
+{
+    //! Return maximum U parameter.
+    //  ---------------------------
+
+    return knotVectU -> max();
+}
+
+knot snlSurface::minV()
+{
+    //! Return minimum V parameter.
+    //  ---------------------------
+
+    return knotVectV -> min();
+}
+
+knot snlSurface::maxV()
+{
+    //! Return maximum V parameter.
+    //  ---------------------------
+
+    return knotVectV -> max();
+}
+        
+const snlCtrlPoint* snlSurface::controlPoints()
+{
+    //! Return pointer to array of surfaces' control points.
+    //  ----------------------------------------------------
+    
+    return ctrlPtNet -> getCtrlPts();
+}
+        
+const knot* snlSurface::knotsU()
+{
+    //! Return pointer to array of knots in U direction.
+    //  ------------------------------------------------
+    
+    return knotVectU -> array();
+}
+
+const knot* snlSurface::knotsV()
+{
+    //! Return pointer to array of knots in V direction.
+    //  ------------------------------------------------
+    
+    return knotVectV -> array();
+}
+
+snlCtrlPointNetSurface& snlSurface::controlPointNet()
+{
+    //! Return reference to control point network object for surface.
+    //  -------------------------------------------------------------
+    
+    return *ctrlPtNet;
+}
+
+const snlKnotVector& snlSurface::knotVectorU()
+{
+    //! Return pointer to Knot vector in U direction.
+    //  ---------------------------------------------
+    
+    return *knotVectU;
+}
+
+const snlKnotVector& snlSurface::knotVectorV()
+{
+    //! Return pointer to Knot vector in U direction.
+    //  ---------------------------------------------
+    
+    return *knotVectV;
+}
+
+snlPoint snlSurface::evalHmg ( knot paramU, knot paramV, basis* basisU, basis* basisV ) const
+{
+    //! Evaluate Non Rational Homogeneous Surface Point.
+    //  ------------------------------------------------
+    //! @param paramU Parameter in U direction to evaluate.
+    //! @param paramV Parameter in V direction to evaluate.
+    //! @param basisU Supplied basis function values. Must be 0 if not supplied.
+    //! @param basisV Supplied basis function values. Must be 0 if not supplied.
+    //!
+    //! @return Homogeneous point on surface.
+
+
+    snlPoint      rPoint;  // Return point.
+    snlPoint      iPoint;  // Intermediate point.
+    
+    unsigned    vStart;  // The index where the V "line" starts in the control point array.
+    
+    unsigned spanU = knotVectU -> findSpan ( paramU );
+    unsigned spanV = knotVectV -> findSpan ( paramV );    
+    
+    // Evaluate basis functions.
+
+    basis* bValsU;
+
+    if ( basisU )
+        bValsU = basisU;
+    else
+        bValsU = knotVectU -> evalBasis ( paramU );
+
+    basis* bValsV;
+        
+    if ( basisV )
+        bValsV = basisV;
+    else
+        bValsV = knotVectV -> evalBasis ( paramV );
+
+    unsigned baseVIndex = spanV - (unsigned) degV;  // Where in the V dimension the processing starts.
+
+    rPoint.w ( 0 );
+    
+    // Get control point array.
+    const snlCtrlPoint* ctrlPts = ctrlPtNet -> getCtrlPts();
+
+    for ( int indexU = 0; indexU <= degU; indexU ++ )
+    {
+        iPoint.null();  // Set everything to zero.
+
+        vStart = ( spanU - (unsigned) degU + indexU ) * sizeV();
+
+        for ( int indexV = 0; indexV <= degV; indexV ++ )
+        {
+            snlPoint tmpPoint = ( ctrlPts [ vStart + baseVIndex + indexV ] ) * bValsV [ indexV ];
+            iPoint += tmpPoint;
+        }
+        
+        snlPoint tmpPoint = iPoint * bValsU [ indexU ];
+        rPoint += tmpPoint;
+    }
+    
+    if ( ! basisU ) delete[] bValsU;
+    if ( ! basisV ) delete[] bValsV;
+
+    return rPoint;
+}
+
+snlPoint snlSurface::eval ( knot paramU, knot paramV, basis* basisU, basis* basisV ) const
+{
+    //! Evaluate rational non-homogeneous surface point.
+    //  ------------------------------------------------
+    //! @param paramU Parameter in U direction to evaluate.
+    //! @param paramV Parameter in V direction to evaluate.
+    //! @param basisU Supplied basis function values. Must be 0 if not supplied.
+    //! @param basisV Supplied basis function values. Must be 0 if not supplied.
+    //!
+    //! @return Non-homogeneous point on surface.
+
+    knot minU = knotVectU -> min();
+    knot maxU = knotVectU -> max();
+    knot minV = knotVectV -> min();
+    knot maxV = knotVectV -> max();
+
+    // Clamp parameters.
+
+    if ( paramU > maxU )
+    {
+        cout.precision ( 16 );
+        cout << "Surface Eval. Out of bounds U: " << paramU << "  Min: " << minU << "  Max: " << maxU << "\n";
+        paramU = maxU;
+    }
+     
+    if ( paramU < minU )
+    {
+        cout.precision ( 16 );
+        cout << "Surface Eval. Out of bounds U: " << paramU << "  Min: " << minU << "  Max: " << maxU << "\n";
+        paramU = minU;
+    }
+
+    if ( paramV > maxV )
+    {
+        cout.precision ( 16 );
+        cout << "Surface Eval. Out of bounds U: " << paramV << "  Min: " << minU << "  Max: " << maxU << "\n";
+        paramV = maxV;
+    }
+     
+    if ( paramV < minV )
+    {
+        cout.precision ( 16 );
+        cout << "Surface Eval. Out of bounds U: " << paramV << "  Min: " << minU << "  Max: " << maxU << "\n";
+        paramV = minV;
+    }
+    
+    snlPoint retPoint = evalHmg ( paramU, paramV, basisU, basisV );
+    retPoint.normalise();
+    
+    return retPoint;
+}
+
+snlPoint snlSurface::eval ( knot paramU, knot paramV ) const
+{
+    //! Evaluate rational non-homogeneous surface point.
+    //  ------------------------------------------------
+    //! @param paramU Parameter in U direction to evaluate.
+    //! @param paramV Parameter in V direction to evaluate.
+    //!
+    //! @return Non-homogeneous point on surface.
+    //!
+    //! @par Notes:
+    //!      Function duplication necessary to satisfy parent abstract class.
+
+    knot minU = knotVectU -> min();
+    knot maxU = knotVectU -> max();
+    knot minV = knotVectV -> min();
+    knot maxV = knotVectV -> max();
+
+    // Clamp parameters.
+
+    if ( paramU > maxU )
+    {
+        cout.precision ( 16 );
+        cout << "Surface Eval. Out of bounds U: " << paramU << "  Min: " << minU << "  Max: " << maxU << "\n";
+        paramU = maxU;
+    }
+     
+    if ( paramU < minU )
+    {
+        cout.precision ( 16 );
+        cout << "Surface Eval. Out of bounds U: " << paramU << "  Min: " << minU << "  Max: " << maxU << "\n";
+        paramU = minU;
+    }
+
+    if ( paramV > maxV )
+    {
+        cout.precision ( 16 );
+        cout << "Surface Eval. Out of bounds U: " << paramV << "  Min: " << minU << "  Max: " << maxU << "\n";
+        paramV = maxV;
+    }
+     
+    if ( paramV < minV )
+    {
+        cout.precision ( 16 );
+        cout << "Surface Eval. Out of bounds U: " << paramV << "  Min: " << minU << "  Max: " << maxU << "\n";
+        paramV = minV;
+    }
+
+    snlPoint retPoint = evalHmg ( paramU, paramV );
+    retPoint.normalise();
+    
+    return retPoint;
+}
+
+snlPoint* snlSurface::evalDerivsHmg ( knot paramU, knot paramV, unsigned derivU, unsigned derivV,
+                                      basis* basisU, basis* basisV )
+{                                             
+    //! Evaluate Non Rational Homogeneous Surface Derivatives.
+    //  ------------------------------------------------------
+    //! @param paramU Parameter in U direction to evaluate at.
+    //! @param paramV Parameter in V direction to evaluate at.
+    //! @param derivU Derivative order to evaluate in U direction.
+    //! @param derivV Derivative order to evaluate in V direction.
+    //! @param basisU Pre-computed basis functions values.
+    //! @param basisV Pre-computed basis functions values.
+    //!
+    //! @return Array of snlPoint [ derivU + 1 ] [ derivV + 1 ]. Calling function
+    //!         must delete[] this array.
+
+    const snlPoint* cPnt;
+    snlPoint*       vPnts = new snlPoint [ derivV + 1 ];
+    
+    // Find spans
+    unsigned spanU = knotVectU -> findSpan ( paramU );
+    unsigned spanV = knotVectV -> findSpan ( paramV );    
+    
+    // Evaluate basis functions.
+
+    basis* bValsU;
+    basis* bValsV;
+    
+    if ( basisU )
+        bValsU = basisU;
+    else
+        bValsU = knotVectU -> evalBasisDeriv ( paramU, derivU );
+        
+    if ( basisV )
+        bValsV = basisV;
+    else
+        bValsV = knotVectV -> evalBasisDeriv ( paramV, derivV );
+
+    unsigned baseVIndex = spanV - degV;  // Where in the V dimension the processing starts.
+    
+    unsigned evalPtsSize = ( derivU + 1 ) * ( derivV + 1 );
+    
+    snlPoint* evalPts = new snlPoint [ evalPtsSize ];
+
+    // Set eval points net to 0.
+    for ( unsigned index = 0; index < evalPtsSize; index ++ )    
+        evalPts [ index ].null();  // Set x, y, z and w to zero.
+        
+    // Get control point array.
+    const snlCtrlPoint* ctrlPts = ctrlPtNet -> getCtrlPts();
+
+    // Just loop through control points that match non-zero basis funtions.
+
+    for ( unsigned indexU = 0; indexU <= (unsigned) degU; indexU ++ )
+    {
+        unsigned vStart = ( spanU - degU + indexU ) * sizeV();
+
+        // Zero vPnts array.
+        for ( unsigned index = 0; index <= derivV; index ++ )        
+            vPnts [ index ].null();
+
+        // Utilise V direction basis values.
+        for ( unsigned indexV = 0; indexV <= (unsigned) degV; indexV ++ )
+        {
+            // Find control point only once.
+            cPnt = ctrlPts + vStart + baseVIndex + indexV;
+
+            // Calculate all V deriv values based on this point.
+            for ( unsigned dIndexV = 0; dIndexV <= derivV; dIndexV ++ )
+            {
+                // Calc index into basis derivs for V direction.
+                unsigned vDerivStart = ( dIndexV * ( degV + 1 ) ) + indexV;
+                
+                snlPoint tmpPoint = ( *cPnt ) * bValsV [ vDerivStart ];
+                vPnts [ dIndexV ] += tmpPoint;
+            }
+        }
+
+        // Multiply U deriv basis values and add to eval array.
+
+        for ( unsigned dIndexU = 0; dIndexU <= derivU; dIndexU ++ )
+        {
+            // Get basis value for current u deriv level.
+            basis cBValU = bValsU [ dIndexU * ( degU + 1 ) + indexU ];
+
+            for ( unsigned dIndexV = 0; dIndexV <= derivV; dIndexV ++ )
+            {
+                unsigned evalIndex = dIndexU * ( derivV + 1 ) + dIndexV;
+                
+                snlPoint tmpPoint = vPnts [ dIndexV ] * cBValU;
+                evalPts [ evalIndex ] += tmpPoint;
+            }
+        }
+    }
+
+    delete[] vPnts;
+
+    if ( ! basisU ) delete[] bValsU;
+    if ( ! basisV ) delete[] bValsV;
+    
+    return evalPts;
+}
+
+snlPoint* snlSurface::evalDerivs ( knot paramU, knot paramV, unsigned derivU, unsigned derivV )
+{
+    //! Evaluate Rational Non-Homogeneous Surface Derivatives
+    //  -----------------------------------------------------
+    //! @param paramU Parameter in U direction to evaluate at.
+    //! @param paramV Parameter in V direction to evaluate at.
+    //! @param derivU Derivative order to evaluate in U direction.
+    //! @param derivV Derivative order to evaluate in V direction.
+    //!
+    //! @return Array of snlPoint [ derivU + 1 ] [ derivV + 1 ]. Calling function must
+    //!         delete[] array.
+    //!
+    //! @par Notes:
+    //!      Follows theory, "The NURBS Book 2nd ed", page 136 equation 4.20.
+
+    unsigned    kIndex, lIndex;  // Partial derivs in t and u directions respectively.
+    unsigned    iIndex, jIndex;  // Current partial deriv levels for t and u directions respectively.
+    unsigned    cDIndex;  // Current derivPts index.
+    unsigned    kBaseIndex;  // Base k index into evalPts array.
+    unsigned    kBaseIndex2;
+
+    snlPoint    iPoint;  // Intermediate point. Holds x, y and z of homogeneous point.
+    snlPoint    iPoint2;  // Another intermediate point.    
+
+    // Get homogeneous derivatives.
+    snlPoint* derivPts = evalDerivsHmg ( paramU, paramV, derivU, derivV );
+    
+    // Array for returning points in.
+    snlPoint* evalPts = new snlPoint [ ( derivU + 1 ) * ( derivV + 1 ) ];    
+
+    unsigned dSizeV = derivV + 1;
+
+    for ( kIndex = 0; kIndex <= derivU; kIndex ++ )
+    {
+        kBaseIndex = kIndex * ( derivV + 1 );
+
+        for ( lIndex = 0; lIndex <= derivV; lIndex ++ )
+        {
+            cDIndex = kIndex * dSizeV + lIndex;
+
+            iPoint = derivPts [ cDIndex ];
+
+            // 0th k order derivatives.
+            for ( jIndex = 1; jIndex <= lIndex; jIndex ++ )
+            {
+                iPoint.x ( iPoint.x() - binCoefs::binCoefArray [ lIndex ] [ jIndex ] * derivPts [ jIndex ].w() *
+                           evalPts [ kBaseIndex + lIndex - jIndex ].x() );
+
+                iPoint.y ( iPoint.y() - binCoefs::binCoefArray [ lIndex ] [ jIndex ] * derivPts [ jIndex ].w() *
+                           evalPts [ kBaseIndex + lIndex - jIndex ].y() );
+
+                iPoint.z ( iPoint.z() - binCoefs::binCoefArray [ lIndex ] [ jIndex ] * derivPts [ jIndex ].w() *
+                           evalPts [ kBaseIndex + lIndex - jIndex ].z() );
+            }
+
+            // 0th j order and ( k, j ) order derivatives.
+            for ( iIndex = 1; iIndex <= kIndex; iIndex ++ )
+            {
+                kBaseIndex2 = ( kIndex - iIndex ) * ( derivV + 1 );
+
+                cDIndex = iIndex * dSizeV;
+
+                iPoint.x ( iPoint.x() - binCoefs::binCoefArray [ kIndex ] [ iIndex ] * derivPts [ cDIndex ].w() *
+                           evalPts [ kBaseIndex2 + lIndex ].x() );
+                           
+                iPoint.y ( iPoint.y() - binCoefs::binCoefArray [ kIndex ] [ iIndex ] * derivPts [ cDIndex ].w() *
+                           evalPts [ kBaseIndex2 + lIndex ].y() );
+                           
+                iPoint.z ( iPoint.z() - binCoefs::binCoefArray [ kIndex ] [ iIndex ] * derivPts [ cDIndex ].w() *
+                           evalPts [ kBaseIndex2 + lIndex ].z() );
+
+                iPoint2.null();
+
+                for ( jIndex = 1; jIndex <= lIndex; jIndex ++ )
+                {
+                    iPoint2.x ( iPoint2.x() + binCoefs::binCoefArray [ lIndex ] [ jIndex ] *
+                                derivPts [ cDIndex + jIndex ].w() *
+                                evalPts [ kBaseIndex2 + lIndex - jIndex ].x() );
+                                
+                    iPoint2.y ( iPoint2.y() + binCoefs::binCoefArray [ lIndex ] [ jIndex ] *
+                                derivPts [ cDIndex + jIndex ].w() *
+                                evalPts [ kBaseIndex2 + lIndex - jIndex ].y() );
+                                
+                    iPoint2.z ( iPoint2.z() + binCoefs::binCoefArray [ lIndex ] [ jIndex ] *
+                                derivPts [ cDIndex + jIndex ].w() *
+                                evalPts [ kBaseIndex2 + lIndex - jIndex ].z() );
+                }
+
+                snlPoint tmpPoint = iPoint2 * binCoefs::binCoefArray [ kIndex ] [ iIndex ];
+                iPoint -= tmpPoint;
+            }
+            
+            evalPts [ kBaseIndex + lIndex ] = iPoint / ( derivPts [ 0 ].w() );
+            evalPts [ kBaseIndex + lIndex ].w ( 0 );  // No delta in w component at any time.
+        }
+    }
+    
+    delete[] derivPts;
+    
+    evalPts [ 0 ].w ( 1.0 );  // First entry in array is not a derivative.
+    
+    return evalPts;
+}
+
+void snlSurface::velocities ( knot paramU, knot paramV, snlPoint& evalPoint, snlVector& velocityU, snlVector& velocityV,
+                              basis* basisU, basis* basisV )
+{
+    //! Compute velocities in U and V directions.
+    //  -----------------------------------------
+    //! @param paramU U parameter to evaluate at.
+    //! @param paramV V parameter to evaluate at.
+    //! @param evalPoint Variable to return surface point at which velocity was evaluated.
+    //! @param velocityU Varibale to return U directon velocity in.
+    //! @param velocityV Varibale to return V directon velocity in.
+    //! @param basisU Pre-computed basis function values.
+    //! @param basisV Pre-computed basis funciton values.
+    //!
+    //! @par Notes:
+    //!      This function is designed for speed not correctness.
+
+    knot minU = knotVectU -> min();
+    knot maxU = knotVectU -> max();
+    knot minV = knotVectV -> min();
+    knot maxV = knotVectV -> max();
+
+    // Clamp parameters.
+
+    if ( paramU > maxU )
+    {
+        cout.precision ( 16 );
+        cout << "Surface Velocity Eval. Out of bounds U: " << paramU << "  Min: "
+             << minU << "  Max: " << maxU << "\n";
+        paramU = maxU;
+    }
+     
+    if ( paramU < minU )
+    {
+        cout.precision ( 16 );
+        cout << "Surface Velocity Eval. Out of bounds U: " << paramU << "  Min: " << minU
+             << "  Max: " << maxU << "\n";
+        paramU = minU;
+    }
+
+    if ( paramV > maxV )
+    {
+        cout.precision ( 16 );
+        cout << "Surface Velocity Eval. Out of bounds U: " << paramV << "  Min: " << minU
+             << "  Max: " << maxU << "\n";
+        paramV = maxV;
+    }
+     
+    if ( paramV < minV )
+    {
+        cout.precision ( 16 );
+        cout << "Surface Velocity Eval. Out of bounds U: " << paramV << "  Min: " << minU
+             << "  Max: " << maxU << "\n";
+        paramV = minV;
+    }
+
+    // Get homogeneous velocities.
+    
+    snlPoint* derivPts = evalDerivsHmg ( paramU, paramV, 1, 1, basisU, basisV );
+
+    basis wVal = derivPts [ 0 ].elements [ 3 ];
+
+    derivPts [ 0 ].normalise();
+
+    evalPoint = derivPts [ 0 ];
+
+    basis uWVal = derivPts [ 2 ].elements [ 3 ];
+
+    velocityU.elements [ 0 ] = ( derivPts [ 2 ].elements [ 0 ] - uWVal
+                               * derivPts [ 0 ].elements [ 0 ] ) / wVal;
+    velocityU.elements [ 1 ] = ( derivPts [ 2 ].elements [ 1 ] - uWVal
+                               * derivPts [ 0 ].elements [ 1 ] ) / wVal;
+    velocityU.elements [ 2 ] = ( derivPts [ 2 ].elements [ 2 ] - uWVal
+                               * derivPts [ 0 ].elements [ 2 ] ) / wVal;
+    
+    basis vWVal = derivPts [ 1 ].elements [ 3 ];
+
+    velocityV.elements [ 0 ] = ( derivPts [ 1 ].elements [ 0 ] - vWVal
+                               * derivPts [ 0 ].elements [ 0 ] ) / wVal;
+    velocityV.elements [ 1 ] = ( derivPts [ 1 ].elements [ 1 ] - vWVal
+                               * derivPts [ 0 ].elements [ 1 ] ) / wVal;
+    velocityV.elements [ 2 ] = ( derivPts [ 1 ].elements [ 2 ] - vWVal
+                               * derivPts [ 0 ].elements [ 2 ] ) / wVal;
+
+    // Clean up.
+
+    delete[] derivPts;
+}
+
+void snlSurface::insertKnot ( knot iParam, int dir, bool reallocate )
+{
+    //! For a Surface: Insert a Knot into Knot Vector and Calculate new Control Points
+    //  ------------------------------------------------------------------------------
+    //! @param iParam Parameter value to insert.
+    //! @param dir Direction to evaluate in. 0 = u, 1 = v.
+    //! @param reallocate Reallocate memory for control points.
+    //!
+    //! @par Notes:
+    //!      ctrlPts MUST have an additional point space allocated at the end of
+    //!      each line in the array for the chosen direction.
+
+    unsigned        count, index, lineIndex, offset;
+    unsigned        cDeg, oDeg;   // Degree to be processed.
+    snlKnotVector*  cKnts;  // Current knots
+    snlKnotVector*  oKnts;  // Other knots.
+    snlCtrlPoint    pt1, pt2;
+
+    if ( dir == SNL_V_DIR )
+    {
+        cDeg = degV;
+        oDeg = degU;
+        cKnts = knotVectV;
+        oKnts = knotVectU;
+        
+        ctrlPtNet -> growV ( 1, reallocate );
+    }
+    else
+    {
+        cDeg = degU;
+        oDeg = degV;
+        cKnts = knotVectU;
+        oKnts = knotVectV;
+        
+        ctrlPtNet -> growU ( 1, reallocate );
+    }
+    
+    // Span new knot belongs to.
+    unsigned span = cKnts -> findSpan ( iParam );
+
+    // Pre calculate alphas.
+    double* alpha = new double [ cDeg ];
+
+    for ( count = 0; count < cDeg; count ++ )
+    {
+        index = span - cDeg + 1 + count;
+        alpha [ count ]  = ( iParam - ( cKnts -> val ( index ) ) )
+                           / ( cKnts -> val ( index + cDeg ) - cKnts -> val ( index ) );
+    }
+
+    // Build temp array to store new array of control points in.
+    snlCtrlPoint* tmpPts = new snlCtrlPoint [ cDeg ];
+    
+    // Get pointer to control points.
+    snlCtrlPoint* ctrlPts = ctrlPtNet -> getCtrlPtsPtr();
+
+    // Do for each "line" in direction of insertion.
+    for ( lineIndex = 0; lineIndex < ( oKnts -> size() - oDeg - 1 ); lineIndex ++ )
+    {
+        // Calculate new control points.
+        for ( count = 0; count < cDeg; count ++ )
+        {
+            index = span - cDeg + 1 + count;
+
+            // Get first and second ctrl points to process with
+            if ( dir == SNL_V_DIR )
+            {
+                // V direction.
+                pt1 = ctrlPts [ ( lineIndex * sizeV() ) + index ];
+                pt2 = ctrlPts [ ( lineIndex * sizeV() ) + index - 1 ];
+            }
+            else
+            {
+                // U direction.
+                pt1 = ctrlPts [ ( index * sizeV() ) + lineIndex ];
+                pt2 = ctrlPts [ ( ( index - 1 ) * sizeV() ) + lineIndex ];
+            }
+
+            tmpPts [ count ].x ( ( alpha[count] * pt1.x() ) + ( ( 1.0 - alpha[count] ) * pt2.x() ) );
+            tmpPts [ count ].y ( ( alpha[count] * pt1.y() ) + ( ( 1.0 - alpha[count] ) * pt2.y() ) );
+            tmpPts [ count ].z ( ( alpha[count] * pt1.z() ) + ( ( 1.0 - alpha[count] ) * pt2.z() ) );
+            tmpPts [ count ].w ( ( alpha[count] * pt1.w() ) + ( ( 1.0 - alpha[count] ) * pt2.w() ) );
+        }
+
+        // Place new points into array.
+
+        if ( dir == SNL_V_DIR )
+        {
+            // V direction insert
+            offset = lineIndex * sizeV();
+
+            // Copy non-altered control points forward one position at the end of the array.
+            for ( count = sizeV() - 1; count > span; count -- )
+                ctrlPts [ offset + count ] = ctrlPts [ offset + count - 1 ];
+
+            // Copy new control points into array.
+            for ( count = 0; count < cDeg; count ++ )
+            {
+                index = span - cDeg + 1 + count + offset;
+                ctrlPts [ index ] = tmpPts [ count ];
+            }
+        }
+        else
+        {
+            // U direction insert.
+
+            // Copy non-altered control points forward one position at the end of the array.
+            for ( count = sizeU() - 1; count > span; count -- )
+            {
+                index = count * sizeV() + lineIndex;
+                ctrlPts [ index ] = ctrlPts [ index - sizeV() ];
+            }
+
+            // Copy new control points into array.
+            for ( count = 0; count < cDeg; count ++ )
+            {
+                index = ( span - cDeg + 1 + count ) * sizeV() + lineIndex;
+                ctrlPts [ index ] = tmpPts [ count ];
+            }
+        }
+    }
+
+    // Insert new knot into knot vector
+    cKnts -> insertKnot ( iParam );
+
+    delete[] tmpPts;
+    delete[] alpha;
+}
+
+void snlSurface::insertKnot ( knot iParam, int dir, int numToInsert, bool reallocate )
+{
+    //! For a Surface: Insert a Knot into Knot Vector and Calculate new Control Points
+    //  ------------------------------------------------------------------------------
+    //! @param iParam Parameter value to insert.
+    //! @param dir Direction to evaluate in. 0 = u, 1 = v.
+    //! @param numToInsert Number of knots to insert into location.
+    //! @param reallocate Reallocate memory for control points.
+    //!
+    //! @par Notes:
+    //!      ctrlPts MUST have an additional point space allocated at the end of
+    //!      each line in the array for the chosen direction.
+
+    if ( numToInsert <= 0 )
+    {
+        cout << "Bad use of function: snlSurface::insertKnot. Can't insert " << numToInsert << " knots.\n";
+        return;
+    }
+    
+    int             count, index, lineIndex, offset;
+    unsigned        cDeg, oDeg;   // Degree to be processed.
+    snlKnotVector*  cKnts;  // Current knots
+    snlKnotVector*  oKnts;  // Other knots.
+    snlCtrlPoint    pt1, pt2;
+
+    if ( dir == SNL_V_DIR )
+    {
+        cDeg = degV;
+        oDeg = degU;
+        cKnts = knotVectV;
+        oKnts = knotVectU;
+        
+        ctrlPtNet -> growV ( numToInsert, reallocate );
+    }
+    else
+    {
+        cDeg = degU;
+        oDeg = degV;
+        cKnts = knotVectU;
+        oKnts = knotVectV;
+        
+        ctrlPtNet -> growU ( numToInsert, reallocate );
+    }
+    
+    // Span new knot belongs to.
+    unsigned span = cKnts -> findSpan ( iParam );
+
+    int multi = cKnts -> findMultiplicity ( iParam );  // Multiplicity of knot vector at param.
+
+    // Pre calculate alphas.
+    
+    int numAlphas = cDeg - multi;
+    
+    double* alpha = new double [ numAlphas * numToInsert ];
+
+    for ( int insertCount = 1; insertCount <= numToInsert; insertCount ++ )
+    {
+        for ( count = 0; count < numAlphas - insertCount + 1; count ++ )
+        {
+            index = span - cDeg + insertCount + count;
+
+            int alphaIndex = count + ( ( insertCount - 1 ) * numAlphas );
+            
+            alpha [ alphaIndex ]  = ( iParam - ( cKnts -> val ( index ) ) )
+                                    / ( cKnts -> val ( index + cDeg - insertCount + 1 )
+                                      - cKnts -> val ( index ) );
+        }
+    }
+    
+    // Build temp array to store new array of control points in.
+
+    int numNewPts = cDeg - multi + numToInsert - 1;
+    
+    snlCtrlPoint* tmpPts = new snlCtrlPoint [ numNewPts ];
+    
+    // Get pointer to control points.
+    snlCtrlPoint* ctrlPts = ctrlPtNet -> getCtrlPtsPtr();
+
+    // Do for each "line" in direction of insertion.
+    for ( lineIndex = 0; lineIndex < (int)( oKnts -> size() - oDeg - 1 ); lineIndex ++ )
+    {
+        // Calculate new control points for first insertion.
+        for ( count = 0; count < numAlphas; count ++ )
+        {
+            index = span - cDeg + 1 + count;
+
+            // Get first and second ctrl points to process with
+            if ( dir == SNL_V_DIR )
+            {
+                // V direction.
+                pt1 = ctrlPts [ ( lineIndex * sizeV() ) + index ];
+                pt2 = ctrlPts [ ( lineIndex * sizeV() ) + index - 1 ];
+            }
+            else
+            {
+                // U direction.
+                pt1 = ctrlPts [ ( index * sizeV() ) + lineIndex ];
+                pt2 = ctrlPts [ ( ( index - 1 ) * sizeV() ) + lineIndex ];
+            }
+
+            tmpPts [ count ].x ( ( alpha[count] * pt1.x() ) + ( ( 1.0 - alpha[count] ) * pt2.x() ) );
+            tmpPts [ count ].y ( ( alpha[count] * pt1.y() ) + ( ( 1.0 - alpha[count] ) * pt2.y() ) );
+            tmpPts [ count ].z ( ( alpha[count] * pt1.z() ) + ( ( 1.0 - alpha[count] ) * pt2.z() ) );
+            tmpPts [ count ].w ( ( alpha[count] * pt1.w() ) + ( ( 1.0 - alpha[count] ) * pt2.w() ) );
+        }
+
+        // Calculate subsequent points for each insertion.
+
+        for ( int insertCount = 0; insertCount < numToInsert - 1; insertCount ++ )
+        {
+            // Copy some last points calculated forward one position.
+
+            for ( int copyCount = 0; copyCount < insertCount + 1; copyCount ++ )
+                tmpPts [ numAlphas + insertCount - copyCount ] = tmpPts [ numAlphas + insertCount - copyCount - 1 ];
+            
+            // Calculate new points
+
+            int alphaOffset = ( insertCount + 1 ) * numAlphas;
+
+            index = numAlphas - 1;
+            
+            for ( count = numAlphas - insertCount - 2; count >= 0 ; count -- )
+            {
+                tmpPts [ index ].x ( ( alpha [ alphaOffset + count ] * tmpPts [ index ].x() )
+                                     + ( ( 1.0 - alpha [ alphaOffset + count ] ) * tmpPts [ index - 1 ].x() ) );
+                                     
+                tmpPts [ index ].y ( ( alpha [ alphaOffset + count ] * tmpPts [ index ].y() )
+                                     + ( ( 1.0 - alpha [ alphaOffset + count ] ) * tmpPts [ index - 1 ].y() ) );
+                                     
+                tmpPts [ index ].z ( ( alpha [ alphaOffset + count ] * tmpPts [ index ].z() )
+                                     + ( ( 1.0 - alpha [ alphaOffset + count ] ) * tmpPts [ index - 1 ].z() ) );
+                                     
+                tmpPts [ index ].w ( ( alpha [ alphaOffset + count ] * tmpPts [ index ].w() )
+                                     + ( ( 1.0 - alpha [ alphaOffset + count ] ) * tmpPts [ index - 1 ].w() ) );
+
+                -- index;
+            }
+        }
+
+        // Place new points into array.
+
+        if ( dir == SNL_V_DIR )
+        {
+            // V direction insert
+            offset = lineIndex * sizeV();
+
+            // Copy non-altered control points forward.
+            for ( count = sizeV() - 1; count > (int) ( span - multi ); count -- )
+                ctrlPts [ offset + count ] = ctrlPts [ offset + count - numToInsert ];
+
+            index = span - cDeg + 1 + offset;
+
+            // Copy new control points into array.
+            for ( count = 0; count < numNewPts; count ++ )
+            {
+                ctrlPts [ index ] = tmpPts [ count ];
+                ++ index;
+            }
+        }
+        else
+        {
+            // U direction insert.
+
+            int backIndexOffset = sizeV() * numToInsert;
+
+            index = ( sizeU() - 1 ) * sizeV() + lineIndex;
+
+            // Copy non-altered control points forward one position at the end of the array.
+            for ( count = sizeU() - 1; count > (int) ( span - multi ); count -- )
+            {
+                ctrlPts [ index ] = ctrlPts [ index - backIndexOffset ];
+                index -= sizeV();
+            }
+
+            // Copy new control points into array.
+
+            index = ( span - cDeg + 1 ) * sizeV() + lineIndex;
+            
+            for ( count = 0; count < numNewPts; count ++ )
+            {
+                ctrlPts [ index ] = tmpPts [ count ];
+                index += sizeV();
+            }
+        }
+    }
+
+    // Insert new knot into knot vector
+    cKnts -> insertKnot ( iParam, numToInsert );
+
+    delete[] tmpPts;
+    delete[] alpha;
+}
+
+double snlSurface::removeKnots ( int numKnots, unsigned removalIndex, int direction,
+                                 double tolerance, bool reallocate )
+{
+    //! For a Surface: Remove multiple Knots from Knot Vector and Calculate new Control Points
+    //  --------------------------------------------------------------------------------------
+    //! @param numKnots Number of knots to remove.
+    //! @param removalIndex Knot at index to remove.
+    //! @param direction Direction to evaluate in. 0 = u, 1 = v.
+    //! @param tolerance Maximum allowable error. A value of 0.0 means ignore.
+    //! @param reallocate Reallocate memory for control points. If false then de-allocation of
+    //!                   memory is left up to caller.
+    //!
+    //! @return Maximum error encountered.
+
+    if ( numKnots < 1 ) return 0.0;
+    
+    double maxTol = 0.0;
+
+    snlKnotVector* knotVect;
+
+    if ( direction == SNL_U_DIR )
+    {
+        knotVect = knotVectU;
+    }
+    else
+    {
+        knotVect = knotVectV;
+    }
+    
+    double param = knotVect -> val ( removalIndex );
+    
+    int multi = knotVect -> findMultiplicity ( removalIndex );
+    
+    int numToRemove = numKnots > multi ? multi : numKnots;
+    
+    for ( int count = 0; count < numToRemove; count ++ )
+    {
+        double tol = removeKnot ( removalIndex, direction, tolerance, reallocate );
+        
+        if ( tol > maxTol ) maxTol = tol;
+        
+        removalIndex = knotVect -> findSpan ( param );
+    }
+    
+    return maxTol;   
+}
+
+double snlSurface::removeKnot ( unsigned rIndex, int dir, double tolerance, bool reallocate )
+{
+    //! For a Surface: Remove a Knot from Knot Vector and Calculate new Control Points
+    //  ------------------------------------------------------------------------------
+    //! @param rIndex Knot at index to remove.
+    //! @param dir Direction to evaluate in. 0 = u, 1 = v.
+    //! @param tolerance Maximum allowable error. A value of 0.0 means ignore.
+    //! @param reallocate Reallocate memory for control points.
+    //!
+    //! @return Maximum error encountered.
+
+    unsigned        count, index, lineIndex, offset;
+    unsigned        cDeg, oDeg;   // Degree to be processed.
+    snlKnotVector*  cKnts;  // Current knots
+    snlKnotVector*  oKnts;  // Other knots.
+    snlCtrlPoint    pt1;
+
+    if ( dir == SNL_V_DIR )
+    {
+        cDeg = degV;
+        oDeg = degU;
+        cKnts = knotVectV;
+        oKnts = knotVectU;
+    }
+    else
+    {
+        cDeg = degU;
+        oDeg = degV;
+        cKnts = knotVectU;
+        oKnts = knotVectV;
+    }
+
+    knot rParam = cKnts -> val ( rIndex );
+
+    // Span knot to be removed belongs to. This will always adjust the removal index to
+    // point to a non-zero span. ie Multiplicity check.
+    unsigned rSpan = cKnts -> findSpan ( rParam );
+
+    // Find multiplicity of knot at index.
+    unsigned multi = cKnts -> findMultiplicity ( rSpan );
+
+    // Calculate the number of equations.
+    unsigned numEqns = cDeg - multi + 1;
+
+    // Pre calculate alphas.
+    double* alpha = cKnts -> calcRemovalAlphas ( rSpan );    
+
+    // Maximum error variable.
+    double maxError = 0;
+
+    // Build temp array to store new array of control points in.
+    // First and last points are not new.
+    snlCtrlPoint* tmpPts = new snlCtrlPoint [ numEqns + 1 ];
+    
+    // Copy control points. Only copy back if the tolerance is ok.
+    
+    const snlCtrlPoint* ctrlPtsToCopy = controlPoints();
+    
+    snlCtrlPoint* ctrlPts = new snlCtrlPoint [ sizeU() * sizeV() ];
+    
+    for ( unsigned index = 0; index < ( sizeU() * sizeV() ); index ++ )
+        ctrlPts [ index ] = ctrlPtsToCopy [ index ];
+        
+    // Do for each "line" in direction of removal.
+    for ( lineIndex = 0; lineIndex < ( oKnts -> size() - oDeg - 1 ); lineIndex ++ )
+    {
+        // Seed new points array.
+        if ( dir == SNL_V_DIR )
+            tmpPts [ 0 ] = ctrlPts [ ( lineIndex * sizeV() ) + ( rSpan - cDeg - 1 ) ];
+        else
+            tmpPts [ 0 ] = ctrlPts [ ( ( rSpan - cDeg - 1 ) * sizeV() ) + lineIndex ];
+
+        // Calculate new control points.
+        for ( count = 1; count <= numEqns; count ++ )
+        {
+            index = rSpan - cDeg + count - 1;
+
+            // Get ctrl point to process with.
+            if ( dir == SNL_V_DIR )
+            {
+                // U direction.
+                pt1 = ctrlPts [ ( lineIndex * ( sizeV() ) ) + index ];
+            }
+            else
+            {
+                // T direction.
+                pt1 = ctrlPts [ ( index * sizeV() ) + lineIndex ];
+            }
+
+            // Calculate new control point.
+
+            tmpPts [ count ] =  pt1;
+            tmpPts [ count ] -= tmpPts [ count - 1 ] * ( 1.0 - alpha [ count - 1 ] );
+            tmpPts [ count ] /= alpha [ count - 1 ];
+        }
+
+        // Place new points into array.
+
+        if ( dir == SNL_V_DIR )
+        {
+            // V direction removal.
+            offset = lineIndex * sizeV();
+
+            // Calculate maximum error.
+
+            snlCtrlPoint original = ctrlPts [ rSpan - cDeg + offset + numEqns - 1 ];
+
+            original.normalise();
+
+            tmpPts [ numEqns ].normalise();
+
+            snlVector errorVect ( original, tmpPts [ numEqns ] );
+
+            double error = errorVect.length();
+
+            if ( error > maxError ) maxError = error;
+
+            // Copy non-altered control points backward one position at the end of the array.
+            for ( count = rSpan - multi; count < sizeV(); count ++ )
+                ctrlPts [ offset + count ] = ctrlPts [ offset + count + 1 ];
+
+            // Copy new control points into array.
+            for ( count = 0; count < ( numEqns - 1 ); count ++ )
+            {
+                index = rSpan - cDeg + count + offset;
+                ctrlPts [ index ] = tmpPts [ count + 1 ];
+            }
+        }
+        else
+        {
+            // U direction removal.
+
+            // Calculate maximum error.
+
+            snlCtrlPoint original = ctrlPts [ ( rSpan - cDeg + numEqns - 1 )  * sizeV() + lineIndex ];
+
+            original.normalise();
+
+            tmpPts [ numEqns ].normalise();
+
+            snlVector errorVect ( original, tmpPts [ numEqns ] );
+
+            double error = errorVect.length();
+
+            if ( error > maxError ) maxError = error;
+
+            // Copy non-altered control points backwards one position at the end of the line.
+            for ( count = rSpan - multi; count > sizeU(); count ++ )
+            {
+                index = count * sizeV() + lineIndex;
+                ctrlPts [ index ] = ctrlPts [ index + sizeV() ];
+            }
+
+            // Copy new control points into array.
+            for ( count = 0; count < ( numEqns - 1 ); count ++ )
+            {
+                index = ( rSpan - cDeg + count ) * sizeV() + lineIndex;
+                ctrlPts [ index ] = tmpPts [ count + 1 ];
+            }
+        }
+    }
+
+    // If maximum error was under tolerance then go ahead with removal.
+    if ( maxError < tolerance || tolerance == 0.0 )
+    {    
+        // Remove knot from knot vector
+        cKnts -> removeKnot ( rSpan );
+        
+        ctrlPtNet -> replacePoints ( ctrlPts );
+        
+        if ( dir == SNL_V_DIR )
+            ctrlPtNet -> shrinkV();
+        else
+            ctrlPtNet -> shrinkU();        
+    }
+
+    delete[] tmpPts;
+    delete[] alpha;
+
+    return maxError;
+}
+
+unsigned snlSurface::createBezierSegments ( int dir, int** numKnotsAdded )
+{
+    //! Convert surface into Bezier segments.
+    //  -------------------------------------
+    //! @param dir Direction to process in. u = 0, v = 1.
+    //! @param numKnotsAdded Pointer to pointer that should hold the number of knots added to
+    //!                      each span.
+    //!
+    //! @return Number of Bezier segments present.
+
+    unsigned        cDeg;   // Degree to be processed. Current degree, other degree.
+    unsigned        cSize, oSize; // Number of control points in current and other direction.
+    snlKnotVector*  cKnts;  // Current knots
+
+    if ( dir == SNL_V_DIR )
+    {
+        cDeg = degV;
+        cKnts = knotVectV;
+        cSize = sizeV();
+        oSize = sizeU();
+    }
+    else
+    {
+        cDeg = degU;
+        cKnts = knotVectU;
+        cSize = sizeU();
+        oSize = sizeV();
+    }
+
+    // Find number of non-zero spans.
+    unsigned numSpans = cKnts -> getNumSpans();
+
+    if ( cDeg <= 1 ) return numSpans;  // 1st degree curve sections are already Bezier segments.
+    
+    int* knotsAdded = new int [ numSpans ];  // Last array element is unused and is left here so that a zero length array doesn't occur.
+
+    // Find first spans knot index.
+    unsigned knotIndex = cKnts -> getFirstSpan();
+
+    // Resize control points array just once for all knot insertions.
+    
+    unsigned nextSpan = knotIndex;
+    unsigned extraKnots = 0;
+    
+    // Find amount to resize by.
+    for ( unsigned spanIndex = 1; spanIndex < numSpans; spanIndex ++ )
+    {
+        nextSpan = cKnts -> getNextSpan ( nextSpan );
+        
+        extraKnots += cDeg - ( cKnts -> findMultiplicity ( nextSpan ) );
+    }    
+    
+    // Append extra control point space to end of current control points.
+    ctrlPtNet -> appendPointSpace ( oSize * extraKnots );
+
+    // *** Create Bezier segments ***
+    
+    // Find knot index of second knot span.
+    nextSpan = cKnts -> getNextSpan ( knotIndex );
+    
+    for ( unsigned spanIndex = 0; spanIndex < numSpans - 1; spanIndex ++ )
+    {        
+        // Increase multiplicity of span to degree.
+
+        unsigned multi = cKnts -> findMultiplicity ( nextSpan );
+
+        if ( cDeg - multi > 0 )
+        {
+            knot insertParam = cKnts -> val ( nextSpan );
+
+            insertKnot ( insertParam, dir, cDeg - multi, false );
+
+            // Re-adjust current span index to account for inserted knots.
+            nextSpan = cKnts -> getNextSpan ( cKnts -> findSpan ( insertParam ) );
+
+            // Populate number of knots added array elements.
+            knotsAdded [ spanIndex ] = cDeg - multi;
+        }
+        else
+            nextSpan = cKnts -> getNextSpan ( nextSpan );
+    }
+
+    if ( numKnotsAdded )
+        *numKnotsAdded = knotsAdded;
+    else
+        delete[] knotsAdded;
+    
+    return numSpans;
+}
+
+void snlSurface::createBezierSegments ( int* numU, int* numV )
+{
+    //! Convert surface into Bezier segments.
+    //  -------------------------------------
+
+    int numUSegments = createBezierSegments ( SNL_U_DIR );
+    int numVSegments = createBezierSegments ( SNL_V_DIR );
+
+    if ( numU ) *numU = numUSegments;
+    if ( numV ) *numV = numVSegments;
+}
+
+void snlSurface::createConvexBezierSegments ( int* numU, int* numV, double sensitivity )
+{
+    //! Convert surface into convex Bezier segments.
+    //  --------------------------------------------
+    //! @param numU Pointer to variable to return number of U segments in.
+    //! @param numV Pointer to variable to return number of V segments in.
+    //! @param sensitivity Maximum concave angle allowed to be considered convex. Used to account
+    //!                    for noise in the curvature of a relatively flat section.
+
+    int numUSegments = createBezierSegments ( SNL_U_DIR );
+    int numVSegments = createBezierSegments ( SNL_V_DIR );
+
+    // Subdivide U direction until all segments are convex.
+
+    int maxDeg = degU > degV ? degU : degV;
+
+    snlCtrlPoint** testPoints = new snlCtrlPoint* [ maxDeg + 1 ];
+
+    bool keepChecking = true;
+
+    while ( keepChecking )
+    {
+        keepChecking = false;
+
+        int sizeV = ctrlPtNet -> getSizeV();
+    
+        for ( int segment = 0; segment < numUSegments; segment ++ )
+        {
+            int indexU = segment * degU;
+    
+            bool convex = true;
+    
+            // Check all constant V lines for concave control point combinations.
+    
+            for ( int indexV = 0; indexV < sizeV; indexV ++ )
+            {
+                ctrlPtNet -> locatePointsU ( indexU, indexV, degU + 1, testPoints );
+    
+                if ( ! ( ctrlPtNet -> isConvex ( (snlPoint**) testPoints, degU + 1, sensitivity ) ) )
+                {
+                    convex = false;
+                    break;
+                }
+            }
+    
+            // If any part in U direction is concave then subdivide all patches in range.
+    
+            if ( ! convex )
+            {
+                knot startKnot = knotVectU -> val ( ( segment + 1 ) * degU );
+                knot endKnot = knotVectU -> val ( ( segment + 2 ) * degU );
+    
+                // Insert multiple knots into middle of patch to create two new Bezier segments.
+                
+                insertKnot ( ( ( endKnot - startKnot ) / 2.0 ) + startKnot, SNL_U_DIR, degU, true );
+    
+                numUSegments ++;
+                segment ++;
+
+                keepChecking = true;
+            }
+        }
+    }
+
+    // Subdivide V direction until all segments are convex.
+
+    keepChecking = true;
+
+    while ( keepChecking )
+    {
+        keepChecking = false;
+        
+        int sizeU = ctrlPtNet -> getSizeU();
+    
+        for ( int segment = 0; segment < numVSegments; segment ++ )
+        {
+            int indexV = segment * degV;
+    
+            bool convex = true;
+    
+            // Check all U lines for concave control point combinations.
+
+            for ( int indexU = 0; indexU < sizeU; indexU ++ )
+            {
+                ctrlPtNet -> locatePointsV ( indexU, indexV, degV + 1, testPoints );
+
+                if ( ! ( ctrlPtNet -> isConvex ( (snlPoint**) testPoints, degV + 1, sensitivity ) ) )
+                {
+                    convex = false;
+                    break;
+                }
+            }
+
+            // If any part in V direction is concave then subdivide all patches in range.
+    
+            if ( ! convex )
+            {
+                knot startKnot = knotVectV -> val ( ( segment + 1 ) * degV );
+                knot endKnot = knotVectV -> val ( ( segment + 2 ) * degV );
+    
+                // Insert multiple knots into middle of patch to create two new Bezier segments.
+                
+                insertKnot ( ( ( endKnot - startKnot ) / 2.0 ) + startKnot, SNL_V_DIR, degV, true );
+    
+                numVSegments ++;
+                segment ++;
+
+                keepChecking = true;
+            }
+        }
+    }
+
+    if ( numU ) *numU = numUSegments;
+    if ( numV ) *numV = numVSegments;
+
+    delete[] testPoints;
+}
+
+void snlSurface::elevateDegree ( int direction, int byDegree )
+{
+    //! Elevate degree of surface.
+    //  --------------------------
+    //! @param direction Parametric direction to elevate degree in. ( enum parametricDirections ).
+    //! @param byDegree Number of degrees to elevate direction by.
+    //!                 Convert curve into Bezier segments.
+    
+    int* addedKnots = 0;
+    
+    unsigned numSegments = createBezierSegments ( direction, &addedKnots );
+
+    // Grow control point net.
+
+    if ( direction == SNL_U_DIR )
+        ctrlPtNet -> growU ( numSegments * byDegree, true );
+    else
+        ctrlPtNet -> growV ( numSegments * byDegree, true );
+
+    // Setup direction specific variables.
+
+    int cDeg, numLines, lineSize;
+    snlKnotVector* cKnotVect;
+
+    if ( direction == SNL_U_DIR )
+    {
+        cDeg = degU;
+        numLines = ctrlPtNet -> getSizeV();
+        lineSize = ctrlPtNet -> getSizeU();
+        cKnotVect = knotVectU;
+    }
+    else
+    {
+        cDeg = degV;
+        numLines = ctrlPtNet -> getSizeU();
+        lineSize = ctrlPtNet -> getSizeV();
+        cKnotVect = knotVectV;
+    }
+
+    // Elevate degree of Bezier segments.
+    
+    int segmentSize = cDeg + 1;
+    int newSegmentSize = segmentSize + byDegree;
+    
+    snlCtrlPoint* tmpPts = new snlCtrlPoint [ newSegmentSize ];
+
+    snlCtrlPoint** linePtPtrs = new snlCtrlPoint* [ lineSize ];
+
+    snlCtrlPoint* linePts = new snlCtrlPoint [ lineSize ];
+    
+    for ( int lineIndex = 0; lineIndex < numLines; lineIndex ++ )
+    {
+        // Generate new points per segment.
+        
+        int ptsIndex = 0;
+        
+        unsigned spanIndex = cDeg * 2;
+
+        if ( direction == SNL_U_DIR )
+        {
+            // Get line in U direction.
+            ctrlPtNet -> locatePointsU ( 0, lineIndex, lineSize, linePtPtrs );
+        }
+        else
+        {
+            // Get line in V direction.
+            ctrlPtNet -> locatePointsV ( lineIndex, 0, lineSize, linePtPtrs );
+        }
+
+        // Elevate segments.
+        
+        for ( unsigned segment = 0; segment < numSegments; segment ++ )
+        {
+            // Populate control points to process for line.
+
+            int segmentStartIndex = segment * ( segmentSize - 1 );
+    
+            for ( int index = 0; index < segmentSize; index ++ )
+            {
+                linePts [ ptsIndex + index ] = *( linePtPtrs [ segmentStartIndex + index ] );
+            }
+
+            // Process segment.
+        
+            snlCurve::elevateBezierSegmentPointsDegree ( cDeg, byDegree, linePts + ptsIndex, tmpPts );
+        
+            // Replace points in temp control point array. Index 0 remains unchanged.
+            for ( int index = 1; index < newSegmentSize; index ++ )
+                linePts [ ptsIndex + index ] = tmpPts [ index ];
+        
+            ptsIndex += cDeg + byDegree;
+        
+            if ( lineIndex == 0 )
+            {
+                // Add knots to knot vector. This is only done _once_.
+                cKnotVect -> increaseMultiplicity ( spanIndex, byDegree );
+            }
+        
+            spanIndex += cDeg + byDegree;
+        }
+
+        // Copy temp points into control point net.
+
+        for ( int index = 0; index < lineSize; index ++ )
+            *( linePtPtrs [ index ] ) = linePts [ index ];
+    }
+
+    // Make sure start clamp is of degree + 1 multiplicity.
+    
+    cKnotVect -> increaseMultiplicity ( cDeg, byDegree );
+    
+    // Increase degree indicator variables
+    
+    cDeg += byDegree;
+    
+    cKnotVect -> degree ( cDeg );
+
+    if ( direction == SNL_U_DIR )
+        degU = cDeg;
+    else
+        degV = cDeg;
+    
+    // Remove number of knots that were added during knot insertion.
+    // No knots were added to degree one sections.
+
+    if ( cDeg - byDegree > 1 )
+    {
+        unsigned spanIndex = cKnotVect -> getFirstSpan();
+        
+        spanIndex += cDeg;
+        
+        for ( unsigned segment = 0; segment < numSegments - 1; segment ++ )
+        {
+            removeKnots ( addedKnots [ segment ], spanIndex, direction, 0.0, true );
+        
+            spanIndex += cDeg - addedKnots [ segment ];
+        }
+    }
+
+    // Clean up.
+
+    if ( addedKnots ) delete[] addedKnots;
+    delete[] tmpPts;
+    delete[] linePtPtrs;
+    delete[] linePts;
+}
+
+double snlSurface::reduceDegree ( int dir, unsigned numDeg, double tolerance )
+{
+    //! Reduce Surface Degree
+    //  ---------------------
+    //! @param dir Direction to evaluate in. 0 = u, 1 = v.
+    //! @param numDeg Number of degrees to reduce by.
+    //! @param tolerance Maximum error. A value of 0.0 means no tolerance specified.
+    //!
+    //! @return Maximum error encountered.
+    //!
+    //! @par Notes:
+    //!      This function has not been optimised.
+
+    unsigned        cDeg, oDeg;   // Degree to be processed. Current degree, other degree.
+    unsigned        cSize, oSize; // Number of control points in current and other direction.
+    snlKnotVector*  cKnts;  // Current knots
+    snlKnotVector*  oKnts;  // Other knots.
+
+    double maxError = 0.0;
+        
+    // Save knots and control points of original surface.
+    
+    snlCtrlPointNetSurface* ctrlPtNetCopy = new snlCtrlPointNetSurface ( *ctrlPtNet );
+       
+    snlKnotVector* knotVectUCopy = new snlKnotVector ( *knotVectU );
+    snlKnotVector* knotVectVCopy = new snlKnotVector ( *knotVectV );
+
+    // Convert into Bezier segments.    
+    unsigned numSpans = createBezierSegments ( dir );
+    
+    if ( dir == SNL_V_DIR )
+    {
+        cDeg = degV;
+        oDeg = degU;
+        cKnts = knotVectV;
+        oKnts = knotVectU;
+        cSize = sizeV();
+        oSize = sizeU();
+    }
+    else
+    {
+        cDeg = degU;
+        oDeg = degV;
+        cKnts = knotVectU;
+        oKnts = knotVectV;
+        cSize = sizeU();
+        oSize = sizeV();
+    }
+
+    // *** Reduce degree of Bezier segments ***
+    
+    snlCtrlPoint* ctrlPts = ctrlPtNet -> getCtrlPtsPtr();
+    
+    for ( unsigned count = 0; count < numDeg; count ++ )
+    {    
+        unsigned rDeg = cDeg - count;  // Current degree during reduction.
+        
+        // Pre-calculate alphas.
+        double* alpha = new double [ rDeg - 1 ];
+                
+        for ( unsigned index = 1; index < rDeg; index ++ )
+            alpha [ index - 1 ] = (double) index / (double) rDeg;        
+        
+        for ( unsigned patchIndex = 0; patchIndex < numSpans; patchIndex ++ )
+        {
+            // Process up each "line" in the direction to be reduced.
+            for ( unsigned lineIndex = 0; lineIndex < oSize; lineIndex ++ )        
+            {                
+                // Reduce patch.
+                
+                snlCtrlPoint*   cPnt;  // Current point.
+                snlCtrlPoint*   pPnt;  // Previous point.
+                
+                // Get starting and previous point's pointer.
+                
+                if ( dir == SNL_V_DIR )
+                {
+                    // V Direction.
+                    pPnt = ctrlPts + (unsigned)( ( lineIndex * cSize ) + ( patchIndex * rDeg ) );
+                    cPnt = pPnt + 1;
+                }
+                else
+                {
+                    // U Direction.
+                    pPnt = ctrlPts + (unsigned)( ( patchIndex * rDeg * oSize ) + lineIndex );
+                    cPnt = pPnt + sizeV();
+                }
+                
+                snlCtrlPoint newPoint;
+                
+                // Caculate new internal patch points. ie First and last points aren't modified.
+                
+                for ( unsigned pIndex = 1; pIndex < rDeg; pIndex ++ )
+                {
+                    if ( pIndex > 1 )
+                        *pPnt = newPoint;
+                    
+                    newPoint.x ( ( cPnt -> x() - ( ( pPnt -> x() ) * alpha [ pIndex - 1 ] ) )
+                                 / ( 1 - alpha [ pIndex - 1 ] ) );
+                    newPoint.y ( ( cPnt -> y() - ( ( pPnt -> y() ) * alpha [ pIndex - 1 ] ) )
+                                 / ( 1 - alpha [ pIndex - 1 ] ) );
+                    newPoint.z ( ( cPnt -> z() - ( ( pPnt -> z() ) * alpha [ pIndex - 1 ] ) )
+                                 / ( 1 - alpha [ pIndex - 1 ] ) );
+                    newPoint.w ( ( cPnt -> w() - ( ( pPnt -> w() ) * alpha [ pIndex - 1 ] ) )
+                                 / ( 1 - alpha [ pIndex - 1 ] ) );                    
+                    
+                    // Find next points.
+                    pPnt = cPnt;
+                    
+                    if ( dir == SNL_V_DIR )
+                        // V direction.
+                        cPnt ++;
+                    else
+                        // U direction
+                        cPnt += sizeV();
+                }                
+                
+                // Calculate error.
+                                
+                snlCtrlPoint cPntCopy ( *cPnt );
+                cPntCopy.normalise();
+                
+                newPoint.normalise();
+                
+                double cError = ( snlVector ( cPntCopy, newPoint ).length() );
+                
+                if ( cError > maxError ) maxError = cError;        
+            }
+            
+            // Adjust knot vector.
+            cKnts -> removeKnot ( ( patchIndex * ( rDeg - 1 ) ) + 1 );
+        }
+        
+        // Reduce end clamp multiplicity.
+        cKnts -> removeKnot ( ( cKnts -> size() ) - 1 );
+        
+        // Rearrange array to facilitate removal of redundant control points.
+        
+        snlCtrlPoint* copyFrom = ctrlPts;
+        snlCtrlPoint* copyTo = ctrlPts;
+        
+        if ( dir == SNL_V_DIR )
+        {        
+            for ( unsigned lineIndex = 0; lineIndex < oSize; lineIndex ++ )
+            {
+                for ( unsigned patchIndex = 0; patchIndex < numSpans; patchIndex ++ )
+                {
+                    for ( unsigned count = 0; count < rDeg - 1; count ++ )
+                    {
+                        *copyTo = *copyFrom;
+                        copyTo++;
+                        copyFrom++;
+                    }
+                
+                    copyFrom++;                                
+                }
+            
+                *copyTo = *copyFrom;
+                copyTo++;
+                copyFrom++;
+            }
+        }
+        else
+        {
+            // U direction.
+            
+            for ( unsigned patchIndex = 0; patchIndex < numSpans; patchIndex ++ )            
+            {
+                for ( unsigned lineIndex = 0; lineIndex < rDeg - 1; lineIndex ++ )
+                {
+                    for ( unsigned count = 0; count < oSize; count ++ )
+                    {
+                        *copyTo = *copyFrom;
+                        copyTo++;
+                        copyFrom++;
+                    }
+                }
+                
+                copyFrom += oSize;  // Skip a line.            
+            }
+            
+            // Copy last line.            
+            
+            for ( unsigned count = 0; count < oSize; count ++ )
+            {
+                *copyTo = *copyFrom;
+                copyTo++;
+                copyFrom++;
+            }            
+        }
+        
+        // Adjust current new size to reflect control point removals.
+        cSize -= numSpans;        
+        
+        // Clean up.        
+        delete[] alpha;        
+    }
+        
+    // Set control point net to correct size.
+    ctrlPtNet -> truncatePointSpace ( numDeg * numSpans );
+    
+    if ( dir == SNL_V_DIR )
+    {
+        ctrlPtNet -> setSizeV ( cSize );
+        degV -= numDeg;
+    }
+    else
+    {
+        ctrlPtNet -> setSizeU ( cSize );
+        degU -= numDeg;
+    }
+    
+    // Remove additional knots only if under tolerance.
+        
+    if ( ( tolerance != 0.0 ) && ( tolerance > maxError ) )
+    {
+        // !@#$ Simplify surface here.
+    }
+    
+    if ( ( maxError < tolerance ) || ( tolerance == 0.0 ) )
+    {
+        // Can keep new knots and control points so delete original copies.
+            
+        delete ctrlPtNetCopy;   
+        delete knotVectUCopy;
+        delete knotVectVCopy;        
+    }
+    else
+    {
+        // Reinstate old knots and control points.
+        
+        delete ctrlPtNet;
+        delete knotVectV;
+        delete knotVectU;
+        
+        ctrlPtNet = ctrlPtNetCopy;
+        knotVectV = knotVectVCopy;
+        knotVectU = knotVectUCopy;
+
+        if ( dir == SNL_V_DIR )
+            degV += numDeg;
+        else
+            degU += numDeg;
+    }
+    
+    return maxError;
+}
+
+void snlSurface::refine ( double tolerance )
+{
+    //! Refine surface control point net until tolerance is achieved.
+    //  -------------------------------------------------------------
+    //! @par Notes:
+    //!      Guarantees that if a span is approximated by it's end points then the error will be no
+    //!      greater than tolerance.
+
+    // Refine in individual parametric directions on pass in each direction at a time.
+
+    bool tolOk = false;
+
+    while ( ! tolOk )
+    {
+        tolOk = true;
+
+        if ( ! refineHull_U ( tolerance, true ) ) tolOk = false;
+        if ( ! refineHull_V ( tolerance, true ) ) tolOk = false;
+    }
+}
+
+void snlSurface::refineHull_UV ( double tolerance )
+{
+    //! Refine control point net until it is guaranteed be within tolerance to surface.
+    //  -------------------------------------------------------------------------------
+    //! @par Notes:
+    //!      Processes rectangular sections which is more accurate than doing each parametric
+    //!      direction in turn. This is also results in this funtion being VERY slow and extremely
+    //!      memory hungry.
+
+    if ( degU < 2 && degV < 2 ) return;
+
+    if ( degU < 2 )
+    {
+        refineHull_V ( tolerance );
+        return;
+    }
+
+    if ( degV < 2 )
+    {
+        refineHull_U ( tolerance );
+        return;
+    }
+
+    bool tolOk = false;  // Tolerance is okay.
+
+    while ( ! tolOk )
+    {
+        tolOk = true;
+
+        // Control point net sizes must be obtained at each loop as they may change during the loop.
+
+        // Only process non-zero knot vector spans.
+
+        int numSpansU = knotVectU -> getNumSpans();
+        int numSpansV = knotVectV -> getNumSpans();
+
+        bool* spanSubU = new bool [ numSpansU ];  // Span subdivision indicators.
+        bool* spanSubV = new bool [ numSpansV ];  // If true then subdivide span.
+
+        knot* spanSubUVal = new double [ numSpansU ];  // Knot insertion value for span.
+        knot* spanSubVVal = new double [ numSpansV ];
+
+        for ( int index = 0; index < numSpansU; index ++ )
+            spanSubU [ index ] = false;
+
+        for ( int index = 0; index < numSpansV; index ++ )
+            spanSubV [ index ] = false;
+
+        int spanU = 0;
+
+        int indexU = knotVectU -> getFirstSpan();
+
+        while ( indexU )
+        {
+            int indexV = knotVectV -> getFirstSpan();
+
+            int spanV = 0;
+
+            while ( indexV )
+            {
+                // Don't check rectangular span if both and U and V are already marked for subdivision.
+                
+                if ( ! ( spanSubU [ spanU ] && spanSubV [ spanV ] ) )
+                {
+                    double flatness = ctrlPtNet -> calcFlatness ( indexU - degU, indexV - degV, degU + 1, degV + 1 );
+    
+                    if ( flatness > tolerance )
+                    {
+                        if ( ! spanSubU [ spanU ] )
+                        {
+                            spanSubUVal [ spanU ]  = ( ( knotVectU -> val ( indexU + 1 )
+                                                       - knotVectU -> val ( indexU ) ) / 2 )
+                                                       + knotVectU -> val ( indexU );
+                        }
+
+                        if ( ! spanSubV [ spanV ] )
+                        {
+                            spanSubVVal [ spanV ] = ( ( knotVectV -> val ( indexV + 1 )
+                                                      - knotVectV -> val ( indexV ) ) / 2 )
+                                                      + knotVectV -> val ( indexV );
+                        }
+                        
+                        spanSubU [ spanU ] = true;
+                        spanSubV [ spanV ] = true;
+                        
+                        tolOk = false;
+                    }
+                }
+
+                indexV = knotVectV -> getNextSpan ( indexV );
+
+                spanV ++;
+            }
+
+            indexU = knotVectU -> getNextSpan ( indexU );
+
+            spanU ++;
+        }
+
+        // Insert Knots where needed.
+
+        // Insert U knots.
+
+        for ( int spanIndex = 0; spanIndex < numSpansU; spanIndex ++ )
+        {
+            if ( spanSubU [ spanIndex ] )
+                insertKnot ( spanSubUVal [ spanIndex ], SNL_U_DIR );
+        }
+
+        // Insert V knots.
+
+        for ( int spanIndex = 0; spanIndex < numSpansV; spanIndex ++ )
+        {
+            if ( spanSubV [ spanIndex ] )
+                insertKnot ( spanSubVVal [ spanIndex ], SNL_V_DIR );
+        }
+
+        delete[] spanSubU;
+        delete[] spanSubV;
+
+        delete[] spanSubUVal;
+        delete[] spanSubVVal;
+    }
+}
+
+bool snlSurface::refineHull_U ( double tolerance, bool singlePass )
+{
+    //! Refine control point net in U direction until it is
+    //! guaranteed be within tolerance to surface.
+    //  ---------------------------------------------------
+    //! @param tolerance Convex Hull of surface in U direction must be within tolerance of surface.
+    //! @param singlePass Only do a single refinement pass.
+    //!
+    //! @return If tolerance is was okay. Should be true unless single pass is specified.
+    
+    if ( degU < 2 ) return true;
+    
+    bool tolOk = false;  // Tolerance is okay.
+    
+    while ( ! tolOk )
+    {
+        tolOk = true;
+
+        // Only process non-zero knot vector spans.
+
+        int numSpans = knotVectU -> getNumSpans();
+
+        bool* spanSub = new bool [ numSpans ];  // Span subdivision indicators. If true then subdivide span.
+
+        knot* spanSubVal = new double [ numSpans ];  // Knot insertion value for span.
+
+        for ( int index = 0; index < numSpans; index ++ )
+            spanSub [ index ] = false;
+
+        for ( int indexV = 0; (unsigned) indexV < ( ctrlPtNet -> getSizeV() ); indexV ++ )
+        {
+            int indexU = knotVectU -> getFirstSpan();
+
+            int spanNum = 0;
+            
+            while ( indexU )
+            {
+                // Test for flatness
+            
+                double  flatness;
+                
+                flatness = ctrlPtNet -> calcFlatnessU ( indexU - degU, indexV, degU + 1, false );
+
+                if ( flatness > tolerance )
+                {
+                    // Insert knot into surface. Half way between existing knots.
+                
+                    int insertIndex = indexU;
+                
+                    knot insertParam = ( ( knotVectU -> val ( insertIndex + 1 )
+                                         - knotVectU -> val ( insertIndex ) ) / 2 )
+                                         + knotVectU -> val ( insertIndex );
+
+                    spanSubVal [ spanNum ] = insertParam;
+
+                    spanSub [ spanNum ] = true;
+
+                    tolOk = false;
+                }
+
+                indexU = knotVectU -> getNextSpan ( indexU );
+
+                spanNum ++;
+            }
+        }
+
+        // Insert knots where needed to subdivide span.
+        
+        for ( int spanNum = 0; spanNum < numSpans; spanNum ++ )
+        {
+            if ( spanSub [ spanNum ] )
+                insertKnot ( spanSubVal [ spanNum ], SNL_U_DIR );
+        }
+        
+        delete[] spanSub;
+        delete[] spanSubVal;
+
+        if ( singlePass ) break;
+    }
+
+    return tolOk;
+}
+
+bool snlSurface::refineHull_V ( double tolerance, bool singlePass )
+{
+    //! Refine control point net in V direction until it is
+    //! guaranteed be within tolerance to surface.
+    // ---------------------------------------------------
+    
+    if ( degV < 2 ) return true;
+    
+    bool tolOk = false;
+    
+    while ( ! tolOk )
+    {
+        tolOk = true;
+
+        // Only process non-zero knot vector spans.
+
+        int numSpans = knotVectV -> getNumSpans();
+
+        bool* spanSub = new bool [ numSpans ];  // Span subdivision indicators. If true then subdivide span.
+
+        knot* spanSubVal = new double [ numSpans ];  // Knot insertion value for span.
+
+        for ( int index = 0; index < numSpans; index ++ )
+            spanSub [ index ] = false;
+
+        for ( int indexU = 0; (unsigned) indexU < ( ctrlPtNet -> getSizeU() ); indexU ++ )
+        {            
+            int indexV = knotVectV -> getFirstSpan();
+
+            int spanNum = 0;
+            
+            while ( indexV )
+            {
+                // Test for flatness
+            
+                double  flatness;
+                
+                flatness = ctrlPtNet -> calcFlatnessV ( indexU, indexV - degV, degV + 1, false );
+
+                if ( flatness > tolerance )
+                {
+                    // Insert knot into surface. Half way between existing knots.
+                
+                    int insertIndex = indexV;
+                
+                    knot insertParam = ( ( knotVectV -> val ( insertIndex + 1 )
+                                         - knotVectV -> val ( insertIndex ) ) / 2 )
+                                         + knotVectV -> val ( insertIndex );
+
+                    spanSubVal [ spanNum ] = insertParam;
+
+                    spanSub [ spanNum ] = true;
+
+                    tolOk = false;
+                }
+                
+                indexV = knotVectV -> getNextSpan ( indexV );
+
+                spanNum ++;
+            }
+        }
+
+        // Insert knots where needed to subdivide span.
+        
+        for ( int spanNum = 0; spanNum < numSpans; spanNum ++ )
+        {
+            if ( spanSub [ spanNum ] )
+                insertKnot ( spanSubVal [ spanNum ], SNL_V_DIR );
+        }
+
+        delete[] spanSub;
+        delete[] spanSubVal;
+
+        if ( singlePass ) break;
+    }
+
+    return tolOk;
+}
+
+void snlSurface::refineHullBezier ( double tolerance )
+{
+    //! Refine control point net using Bezier patch subdivision.
+    //  --------------------------------------------------------
+    //! @param tolerance Control point net should be within this tolerance of surface.
+
+    // Break surface into Bezier segments ( patches ).
+
+    int numUSegments;
+    int numVSegments;
+
+    createBezierSegments ( &numUSegments, &numVSegments );
+
+    // Walk through each segment and calculate it's flatness.
+
+    bool tolOk = false;  // Tolerance has been achieved.
+
+    while ( ! tolOk )
+    {
+        tolOk = true;
+
+        numUSegments = knotVectU -> getNumSpans();
+        numVSegments = knotVectV -> getNumSpans();
+
+        bool* splitSpanU = new bool [ numUSegments ];  // Split knot span corresponding to array index if true.
+        bool* splitSpanV = new bool [ numVSegments ];
+    
+        knot* splitKnotU = new knot [ numUSegments ];  // Knot value within span that span will be split at.
+        knot* splitKnotV = new knot [ numVSegments ];
+            
+        for ( int index = 0; index < numUSegments; index ++ )
+            splitSpanU [ index ] = false;
+
+        for ( int index = 0; index < numVSegments; index ++ )
+            splitSpanV [ index ] = false;
+
+        // Step through each segment and calculate control point distance to surface.
+    
+        for ( int segU = 0; segU < numUSegments; segU ++ )
+        {
+            for ( int segV = 0; segV < numVSegments; segV ++ )
+            {
+
+if ( segU == 159 && segV == 2 )
+{
+    cout << "stop\n";
+}
+                // Don't test a segment if it has already being split.
+                
+                if ( ! splitSpanU [ segU ] || ! splitSpanV [ segV ] )
+                {
+                    double flatness = ctrlPtNet -> calcFlatness ( segU * degU, segV * degV, degU + 1, degV + 1 );
+
+                    if ( flatness > tolerance )
+                    {
+                        tolOk = false;
+                        splitSpanU [ segU ] = true;
+                        splitSpanV [ segV ] = true;
+cout << "SegU: " << segU << " SegV: " << segV << " Flatness: " << flatness << "\n";
+                    }
+                }
+            }
+        }
+
+        if ( ! tolOk )
+        {
+            // Split spans. First find parameters to insert.
+    
+            int spanIndex = knotVectU -> getFirstSpan();  // Current span index.
+    
+            for ( int spanNum = 0; spanNum < numUSegments; spanNum ++ )
+            {
+                if ( splitSpanU [ spanNum ] )
+                {
+                    splitKnotU [ spanNum ] = ( ( knotVectU -> val ( spanIndex + 1 )
+                                               - knotVectU -> val ( spanIndex ) ) / 2 )
+                                               + knotVectU -> val ( spanIndex );
+                }
+    
+                spanIndex = knotVectU -> getNextSpan ( spanIndex );
+            }
+    
+            spanIndex = knotVectV -> getFirstSpan();
+    
+            for ( int spanNum = 0; spanNum < numVSegments; spanNum ++ )
+            {
+                if ( splitSpanV [ spanNum ] )
+                {
+                    splitKnotV [ spanNum ] = ( ( knotVectV -> val ( spanIndex + 1 )
+                                               - knotVectV -> val ( spanIndex ) ) / 2 )
+                                               + knotVectV -> val ( spanIndex );
+                }
+    
+                spanIndex = knotVectV -> getNextSpan ( spanIndex );
+            }
+    
+            // Insert knots into each vector.
+    
+            for ( int spanNum = 0; spanNum < numUSegments; spanNum ++ )
+            {
+                if ( splitSpanU [ spanNum ] )
+                    insertKnot ( splitKnotU [ spanNum ], SNL_U_DIR, degU );
+            }
+    
+            for ( int spanNum = 0; spanNum < numVSegments; spanNum ++ )
+            {
+                if ( splitSpanV [ spanNum ] )
+                    insertKnot ( splitKnotV [ spanNum ], SNL_V_DIR, degV );
+            }
+        }
+
+        // Number of Bezier segments may have increased. If arrays are to be used again their sizes
+        // will be invalid.
+
+        delete[] splitSpanU;
+        delete[] splitSpanV;
+
+        delete[] splitKnotU;
+        delete[] splitKnotV;
+    }
+}
+
+double snlSurface::maxCurvatureU()
+{
+    //! Return maximum curvature of surface in U direction.
+    //  ---------------------------------------------------
+    //! @return Value between 0 and PI.
+    
+    return ctrlPtNet -> maxCurvatureU();
+}
+
+double snlSurface::maxCurvatureV()
+{
+    //! Return maximum curvature of surface in V direction.
+    //  ---------------------------------------------------
+    //! @return Value between 0 and PI.
+    
+    return ctrlPtNet -> maxCurvatureV();
+}
+
+snlVector snlSurface::calcNormal ( knot paramU, knot paramV, snlPoint* evalPt )
+{
+    //! Calculate surface normal.
+    //  -------------------------
+    //! @param paramU U parameter of location to generate normal from.
+    //! @param paramV V parameter of location to generate normal from.
+    //! @param evalPt Pointer to point that holds point evaluted at paramter. Optional.
+    //!
+    //! @return Pointer to normal to surface. Caller owns pointer.
+
+    snlVector       velocityU;
+    snlVector       velocityV;
+    snlPoint        evalPoint;
+
+    if ( ! evalPt )
+        velocities ( paramU, paramV, evalPoint, velocityU, velocityV );
+    else
+        velocities ( paramU, paramV, *evalPt, velocityU, velocityV );
+
+    snlVector normal;
+
+    normal.crossProduct ( velocityU, velocityV );
+
+    return normal;
+}
+
+snlSCtrlPtLocn* snlSurface::findClosestCtrlPt ( snlPoint* points, int numPoints )
+{
+    //! Find closest control point to a given point.
+    //  --------------------------------------------
+    //! @param points Array of points to process.
+    //! @param numPoints Number of points in array to process.
+    //!
+    //! @return Array of found control points that best match given points.
+    //!          Returned array indexes correspond to given array indexes.
+    //!          Caller owns array and should delete it once no longer needed.
+
+    int     index;
+
+    // Initialise return array.
+
+    snlSCtrlPtLocn* retArray = new snlSCtrlPtLocn [ numPoints ];
+
+    double maxDouble = DBL_MAX;
+
+    for ( index = 0; index < numPoints; index ++ )
+        retArray [ index ].dist = maxDouble;  // Make this very large.
+
+    const snlCtrlPoint* ctrlPts = controlPoints();
+
+    // Check test points against control points.
+
+    int uSize = sizeU();
+    int vSize = sizeV();
+
+    index = 0;
+
+    double dist;
+    
+    for ( int indexU = 0; indexU < uSize; indexU ++ )
+    {
+        for ( int indexV = 0; indexV < vSize; indexV ++ )
+        {
+            for ( int ptIndex = 0; ptIndex < numPoints; ptIndex ++ )
+            {
+                // Calculate distance to point.
+
+                dist = ctrlPts [ index ].distSqrd ( points [ ptIndex ] );
+
+                if ( retArray [ ptIndex ].dist > dist )
+                {
+                    retArray [ ptIndex ].dist = dist;
+                    retArray [ ptIndex ].uIndex = indexU;
+                    retArray [ ptIndex ].vIndex = indexV;
+                }
+            }
+
+            index ++;
+        }
+    }
+
+    return retArray;
+}
+
+snlCurve* snlSurface::extractEdge ( int edge )
+{
+    //! Extract surface edge as curve.
+    //  ------------------------------
+    //! @param edge enum surfaceEdges value that indicates which edge to return.
+
+    snlCtrlPoint** ctrlPtPtrs;
+    snlKnotVector* knotVect;
+
+    int size;
+
+    switch ( edge )
+    {
+        case SNL_EDGE_UMIN:
+
+            size = ctrlPtNet -> getSizeV();
+            ctrlPtPtrs = new snlCtrlPoint* [ size ];
+            ctrlPtNet -> locatePointsV ( 0, 0, size, ctrlPtPtrs );
+            knotVect = new snlKnotVector ( *knotVectV );
+            
+            break;
+            
+        case SNL_EDGE_VMIN:
+
+            size = ctrlPtNet -> getSizeU();
+            ctrlPtPtrs = new snlCtrlPoint* [ size ];
+            ctrlPtNet -> locatePointsU ( 0, 0, size, ctrlPtPtrs );
+            knotVect = new snlKnotVector ( *knotVectU );
+
+            break;
+        
+        case SNL_EDGE_UMAX:
+        
+            size = ctrlPtNet -> getSizeV();
+            ctrlPtPtrs = new snlCtrlPoint* [ size ];
+            ctrlPtNet -> locatePointsV ( ctrlPtNet -> getSizeU() - 1, 0, size, ctrlPtPtrs );
+            knotVect = new snlKnotVector ( *knotVectV );
+            
+            break;
+
+        case SNL_EDGE_VMAX:
+        
+            size = ctrlPtNet -> getSizeU();
+            ctrlPtPtrs = new snlCtrlPoint* [ size ];
+            ctrlPtNet -> locatePointsU ( 0, ctrlPtNet -> getSizeV() - 1, size, ctrlPtPtrs );
+            knotVect = new snlKnotVector ( *knotVectU );
+            
+            break;
+    };
+
+    // Generate new control point array.
+
+    snlCtrlPoint* ctrlPts = new snlCtrlPoint [ size ];
+
+    for ( int index = 0; index < size; index ++ )
+        ctrlPts [ index ] = * ctrlPtPtrs [ index ];
+
+    delete[] ctrlPtPtrs;
+
+    // Generate curve to return.
+
+    return new snlCurve ( size, ctrlPts, knotVect );
+}
+
+snlSurface* snlSurface::fillet ( int edge, snlVector& frontFaceNormal,
+                                 snlSurface& surface2, snlVector& frontFaceNormal2,
+                                 double tolerance, double radius, bool trim1, bool trim2 )
+{
+    //! Create a fillet between this and another surface.
+    //  -------------------------------------------------
+    //! @param edge Edge of this surface to fillet.
+    //! @param frontFaceNormal Normal that defines side of this surface to place fillet on.
+    //! @param surface2 surface to fillet.
+    //! @param frontFaceNormal2 Normal that defines side of surface to place fillet on.
+    //! @param tolerance Tolerance to surfaces fillet should comply with.
+    //! @param radius Radius of fillet.
+    //! @param trim1 Trim surface1 ( controlling surface ).
+    //! @param trim2 Trim surface2 ( matching surface ).
+    //!
+    //! @par Notes:
+    //!               The normals used for orientation should always be relative
+    //!               to the first corner of the respective surface.
+    //! @verbatim
+    //!                      Edge orientation -
+    //!
+    //!                             uMin
+    //!                       --------------------> V
+    //!                      |                |
+    //!                      |                |
+    //!                      |                |
+    //!                vMin  |                |  vMax
+    //!                      |                |
+    //!                      |                |
+    //!                      |________________|
+    //!                      |      uMax
+    //!                      |
+    //!                      v
+    //!
+    //!                      U                          @endverbatim
+
+cout.precision ( 16 );
+
+    // Calculate normal orientation.
+
+    snlVector refNorm = calcNormal ( minU(), minV() );
+
+    double orientation1 = 1.0;
+
+    if ( refNorm.dot ( frontFaceNormal ) < 0.0 )
+        orientation1 = - 1.0;
+
+    refNorm = surface2.calcNormal ( surface2.minU(), surface2.minV() );
+
+    double orientation2 = 1.0;
+
+    if ( refNorm.dot ( frontFaceNormal2 ) < 0.0 )
+        orientation2 = - 1.0;
+
+    // Generate list of starting fillet locations.
+
+    ptrList < arcLocn > arcLocnList;
+
+    knot min_u = minU();
+    knot max_u = maxU();
+    knot min_v = minV();
+    knot max_v = maxV();
+
+    knot paramU;
+    knot paramV;
+
+    knot constParam;
+
+    bool stepU = false;
+
+    switch ( edge )
+    {
+        case SNL_EDGE_UMIN:
+
+            paramU = min_u;
+            constParam = paramU;
+            paramV = min_v;
+            break;
+            
+        case SNL_EDGE_VMIN:
+
+            paramU = min_u;
+            paramV = min_v;
+            constParam = paramV;
+            stepU = true;
+            break;
+        
+        case SNL_EDGE_UMAX:
+        
+            paramU = max_u;
+            constParam = paramU;
+            paramV = min_v;
+            break;
+
+        case SNL_EDGE_VMAX:
+
+            paramU = min_u;
+            paramV = max_v;
+            constParam = paramV;
+            stepU = true;
+            break;
+    };
+
+    snlCurve* edgeCurve = extractEdge ( edge );
+
+    edgeCurve -> refine ( tolerance );
+
+    snlVector normal;
+
+    // Generate seed arc locations.
+
+    snlCtrlPoint* edgePoints = ( edgeCurve -> controlPointNet() ).getCtrlPtsPtr();
+
+    int edgeCurveSize = edgeCurve -> size();
+
+    snlPoint* ptsToProj = new snlPoint [ edgeCurveSize ];
+
+    for ( int index = 0; index < edgeCurveSize; index ++ )
+        ptsToProj [ index ] = edgePoints [ index ];
+
+    int numProjLocns;
+
+    snlSurfLocn* initialLocns = fastProject ( ptsToProj, edgeCurveSize, &numProjLocns, tolerance, 1.0e-8, 10, 1, 1 );
+
+    // Make sure first point is min param.
+
+    int arcIndex = 0;
+
+    knot lastParam;
+    
+    for ( int index = -1; index < edgeCurveSize; index ++ )
+    {
+        if ( index == edgeCurveSize - 1 )
+        {
+            // Make sure max param is obtained for last point.
+
+            if ( stepU )
+            {
+                paramU = max_u;
+                paramV = constParam;
+            }
+            else
+            {
+                paramU = constParam;
+                paramV = max_v;
+            }
+        }
+        else if ( index > - 1 )
+        {
+            paramU = initialLocns [ index ].paramU;
+            paramV = initialLocns [ index ].paramV;
+        }
+
+        // Check for duplicate parameter.
+
+        if ( index > -1 )
+        {
+            if ( ( stepU && paramU == lastParam ) || ( ! stepU && paramV == lastParam ) )
+                continue;
+        }
+       
+        if ( stepU )
+            lastParam = paramU;
+        else
+            lastParam = paramV;
+        
+        arcLocn* locn = new arcLocn;
+
+        locn -> cParamU = paramU;
+        locn -> cParamV = paramV;
+        
+        velocities ( paramU, paramV, locn -> cPt, locn -> velU, locn -> velV );
+
+        // Controlling surface
+
+        normal.crossProduct ( locn -> velU, locn -> velV );
+
+        normal *= orientation1;
+
+        normal.length ( radius );
+
+        locn -> normPt = locn -> cPt + normal;
+
+        locn -> converged = false;
+        locn -> stalled = false;
+
+        locn -> arcIndex = arcIndex ++;
+
+        arcLocnList.append ( locn, true );
+
+    }
+
+    delete[] initialLocns;
+    
+cout << "Finished stepping out. Num Locations: " << arcLocnList.count() << "\n";
+    // Project control points to matching surface then calculate new control points
+
+    double normTol = tolerance / sqrt ( tolerance * tolerance + radius * radius );  // Normal tolerance for projection.
+
+    int arraySize = arcLocnList.count();
+
+    snlPoint* points = new snlPoint [ arraySize ];
+
+    while ( 1 )
+    {
+        // Assemble points to send to the projection function.
+
+        arcLocn* locn = arcLocnList.first();
+
+        int index = 0;
+
+        while ( locn )
+        {
+            if ( ! locn -> converged && ! locn -> stalled )
+                points [ index ++ ] = locn -> normPt;
+            
+            locn = arcLocnList.next();
+        }
+
+        // Project to matching surface.
+
+        int numToSend = index;
+
+        snlSurfLocn* surfLocns = surface2.fastProject ( points, numToSend, 0, tolerance, normTol, 10, 1, 1 );
+
+        // Test for convergence and create next arc location to try if needed.
+
+        locn = arcLocnList.first();
+
+        while ( locn && ( locn -> converged || locn -> stalled ) )
+            locn = arcLocnList.next();
+
+        bool complete = true;
+
+
+        for ( index = 0; index < numToSend; index ++ )
+        {
+            // If distance to matching surface is within tolerance of arc radius then
+            // arc location has converged to an answer.
+
+            if ( surfLocns [ index ].dist > radius - tolerance && surfLocns [ index ].dist < radius + tolerance )
+            {
+                locn -> converged = true;
+                locn -> mParamU = surfLocns [ index ].paramU;
+                locn -> mParamV = surfLocns [ index ].paramV;
+                locn -> mPt = surfLocns [ index ].pt;
+            }
+            else
+            {
+                // Calculate new guess for arc location on controlling surface.
+
+                double distDelta; 
+
+                snlVector mRadius ( locn -> normPt, surfLocns [ index ].pt );
+
+                // Calculate normal to matching surface at projected point.
+
+                snlVector mNormal = surface2.calcNormal ( surfLocns [ index ].paramU, surfLocns [ index ].paramV );
+
+                mNormal *= orientation2;
+
+                if ( mRadius.dot ( mNormal ) > 0.0 )
+                    distDelta = radius + mRadius.length();
+                else
+                    distDelta = mRadius.length() - radius;
+
+                mRadius.length ( distDelta );
+
+                // Get component of delta in direction we are moving.
+
+                if ( stepU )
+                {
+                    // U is fixed.
+                    
+                    double length = locn -> velV.length();
+                    knot deltaV = mRadius.dot ( locn -> velV ) / ( length * length );
+
+                    knot newV = locn -> cParamV + deltaV;
+
+                    if ( newV > max_v ) newV = max_v;
+                    if ( newV < min_v ) newV = min_v;
+
+                    if ( newV < locn -> cParamV + SNL_NUMERIC_NOISE && newV > locn -> cParamV - SNL_NUMERIC_NOISE )
+                        locn -> stalled = true;
+                    else
+                    {
+                        locn -> cParamV = newV;
+                        complete = false;
+                    }
+                }
+                else
+                {
+                    // V is fixed.
+                    
+                    double length = locn -> velU.length();
+                    knot deltaU = mRadius.dot ( locn -> velU ) / ( length * length );
+
+                    knot newU = locn -> cParamU + deltaU;
+                    
+                    if ( newU > max_u ) newU = max_u;
+                    if ( newU < min_u ) newU = min_u;
+
+                    if ( newU < locn -> cParamU + SNL_NUMERIC_NOISE && newU > locn -> cParamU - SNL_NUMERIC_NOISE )
+                        locn -> stalled = true;
+                    else
+                    {
+                        locn -> cParamU = newU;
+                        complete = false;
+                    }
+                }
+
+                // Recalculate controlling surface data.
+
+                velocities ( locn -> cParamU, locn -> cParamV, locn -> cPt, locn -> velU, locn -> velV );
+
+                normal.crossProduct ( locn -> velU, locn -> velV );
+
+                normal *= orientation1;
+
+                normal.length ( radius );
+
+                locn -> normPt = locn -> cPt + normal;
+            }
+
+            locn = arcLocnList.next();
+
+            while ( locn && ( locn -> converged || locn -> stalled ) )
+                locn = arcLocnList.next();
+        }
+
+        delete[] surfLocns;
+
+        if ( complete ) break;
+    }
+
+    // Generate surface from found arcs.
+
+    int arcCount = 0;
+
+    arcLocn* locn = arcLocnList.first();
+
+    // Get number of arcs to create surface from and calculate
+    // the maximum arc angle.
+
+    double maxAngle = 0.0;
+
+    while ( locn )
+    {
+        if ( locn -> converged )
+        {
+            arcCount ++;
+
+            snlVector startArc ( locn -> normPt, locn -> cPt );
+            snlVector endArc ( locn -> normPt, locn -> mPt );
+
+            double angle = startArc.angle ( endArc );
+
+            if ( angle > maxAngle ) maxAngle = angle;
+
+            locn -> arcAngle = angle;
+        }
+
+        locn = arcLocnList.next();
+    }
+
+    if ( ! arcCount ) return 0;  // A surface couldn't be generated.
+
+    // Calculate number of sections. Must be common to all arcs so that knot vector is the same across them all.
+
+    int numSections = (int) ( ( maxAngle / ( M_PI / 2.0 ) ) + 1 );
+
+    // Generate array of arcs as curves.
+
+    snlCurve** curves = new snlCurve* [ arcCount ];
+
+    locn = arcLocnList.first();
+
+    int index = 0;
+
+    while ( locn )
+    {
+        if ( locn -> converged )
+            curves [ index ++ ] = new snlCurve ( locn -> cPt, locn -> mPt, locn -> normPt, numSections );
+
+        locn = arcLocnList.next();
+    }
+
+    // Generate skinned surface from arcs.
+
+    snlSurface* retSurf = new snlSurface ( curves, arcCount );
+
+    // Clean up and return.
+    
+    for ( index = 0; index < arcCount; index ++ )
+        delete curves [ index ];
+
+    delete[] curves;
+
+    delete edgeCurve;
+
+    return retSurf;
+}
+
+void snlSurface::transform ( snlTransform& transf )
+{
+    //! Apply transformation to surface.
+    //  --------------------------------
+
+    ctrlPtNet -> transform ( transf );
+}
+
+void snlSurface::makeCompatible ( snlSurface* surfaceToMatch, int direction )
+{
+    //! Make surfaces compatible in one parametric direction.
+    //  -----------------------------------------------------
+    //! @param surfaceToMatch Surface to make this surface compatible with.
+    //! @param direction Parametric direction. ( enum parametricDirections ).
+
+    // Synchronise degree.
+
+    if ( direction == SNL_U_DIR )
+    {
+        if ( degU > ( surfaceToMatch -> degU ) )
+            surfaceToMatch -> elevateDegree ( direction, degU - ( surfaceToMatch -> degU ) );
+
+        if ( degU < ( surfaceToMatch -> degU ) )
+            elevateDegree ( direction, ( surfaceToMatch -> degU ) - degU );
+    }
+    else
+    {
+        // SNL_V_DIR.
+        
+        if ( degV > ( surfaceToMatch -> degV ) )
+            surfaceToMatch -> elevateDegree ( direction, degV - ( surfaceToMatch -> degV ) );
+
+        if ( degV < ( surfaceToMatch -> degV ) )
+            elevateDegree ( direction, ( surfaceToMatch -> degV ) - degV );
+    }
+
+    // Sync knot vectors.
+
+    synchronise ( *surfaceToMatch, direction );
+    surfaceToMatch -> synchronise ( *this, direction );
+    
+}
+
+void snlSurface::synchronise ( snlSurface& surface, int direction )
+{
+    //! In the specified direction, synchronise this surfaces knot vector to given surfaces knot
+    //! vector.
+    //  ----------------------------------------------------------------------------------------
+    //! @param surface Surface to snync to.
+    //! @param direction Parametric direction. ( enum parametricDirections ).
+    //!
+    //! @par Notes:
+    //!           Knots are only ever added NOT removed.
+    //!           So if surface has less multiplicity at a particular span index
+    //!           then true synchronisation will not occur and the caller
+    //!           should call the synchronise function on surface with this object
+    //!           as it's argument.
+
+    int tDeg, oDeg;  // This degree, other surfaces degree.
+    
+    snlKnotVector* tKnotVect;  // This surfaces knot vector in direction.
+    snlKnotVector* oKnotVect;  // Other surfaces knot vector in direction.
+    
+    if ( direction == SNL_U_DIR )
+    {
+        tDeg = degU;
+        oDeg = surface.degU;
+        tKnotVect = knotVectU;
+        oKnotVect = surface.knotVectU;
+    }
+    else
+    {
+        tDeg = degV;
+        oDeg = surface.degV;
+        tKnotVect = knotVectV;
+        oKnotVect = surface.knotVectV;
+    }
+
+    if ( tDeg != oDeg ) return;  // Surfaces to sync to must have same degree.
+
+    // Make parametric ranges equal.
+
+    knot thisMin = tKnotVect -> min();
+    knot thisMax = tKnotVect -> max();
+    
+    knot compMin = oKnotVect -> min();
+    knot compMax = oKnotVect -> max();
+
+    if ( thisMin != compMin || thisMax != compMax )
+    {
+        // Reparameterise both curves.
+
+        knot newMin = thisMin > compMin ? compMin : thisMin;
+        knot newMax = thisMax > compMax ? thisMax : compMax;
+
+        tKnotVect -> reparameterise ( newMin, newMax );
+        oKnotVect -> reparameterise ( newMin, newMax );
+    }
+
+    // Sync knots.
+    
+    unsigned numSpans = oKnotVect -> getNumSpans();
+        
+    unsigned spanIndex = oKnotVect -> getFirstSpan();
+        
+    for ( unsigned index = 0; index < numSpans; index ++ )
+    {
+        knot param = oKnotVect -> val ( spanIndex );
+        
+        int multi = oKnotVect -> findMultiplicity ( spanIndex );
+        
+        unsigned insertSpan = tKnotVect -> findSpan ( param );  // Where param would be inserted in this object.
+        
+        // If knot already exists in this surface then reduce multiplicity to add.
+        
+        if ( tKnotVect -> val ( insertSpan ) == param )
+            multi -= tKnotVect -> findMultiplicity ( insertSpan );
+        
+        if ( multi > 0 )
+            insertKnot ( param, direction, multi, true );
+        
+        // Get next span.
+        
+        spanIndex = oKnotVect -> getNextSpan ( spanIndex );
+    }    
+}
+
+void snlSurface::addTrimCurve ( snlCurve* curve )
+{
+    //! Add trimming curve to this surface.
+    //  -----------------------------------
+    //! @param curve Trimming curve to add. This object owns the curve.
+
+    trim_curves -> append ( curve, true );
+}
+
+bool snlSurface::removeTrimCurve ( snlCurve* curve )
+{
+    //! Delete trimming curve from this surface.
+    //  ----------------------------------------
+    //! @param curve Trimming curve to remove.
+
+    if ( ! trim_curves -> hasItem ( curve ) ) return false;
+
+    trim_curves -> remove ( curve );
+
+    return true;
+}
+
+void snlSurface::print()
+{
+    //! Print surfaces control points and knot vectors to std out.
+    //  ----------------------------------------------------------
+    
+    ctrlPtNet -> print();
+
+    cout << "Knotvector U - ";
+    knotVectU -> print();
+
+    cout << "Knotvector V - ";
+    knotVectV -> print();
+}
+
+void snlSurface::print_cpp()
+{
+    //! Print surfaces control points and knot vectors to std out.
+    //  ----------------------------------------------------------
+    //! @par Notes:
+    //!      Prints data to be used for direct inclusion into a c++ program.
+
+    cout << "int degreeU = " << degU << ";\n";
+    cout << "int degreeV = " << degV << ";\n\n";
+
+    cout << "int sizeU = " << sizeU() << ";\n";
+    cout << "int sizeV = " << sizeV() << ";\n\n";
+
+    ctrlPtNet -> print_cpp();
+
+    cout << "\n";
+    
+    cout << "knot knotVectorU [ " << knotVectU -> size() << " ] = ";
+    knotVectU -> print_cpp();
+    cout << "\n\n";
+    
+    cout << "knot knotVectorV [ " << knotVectV -> size() << " ] = ";
+    knotVectV -> print_cpp();
+    cout << "\n";
+}
+
+void snlSurface::vertexNet ( snlVertexNet* vNet, double tolerance, bool parametric )
+{
+    //! Return approximation to surface.
+    //  --------------------------------
+    //! @param tolerance Tolerance to surface of approximation.
+    //! @param parametric Do a parametric analysis of the surface as opposed to knot refinement.
+    //! @param vNet Vertex net to fill with data.
+        
+    
+    const snlCtrlPoint*   ctrlPts;
+    int                   sizeU;
+    int                   sizeV;
+    
+    snlSurface* tmpSurf = 0;
+    
+    if ( tolerance > 0.0 )
+    {
+        tmpSurf = new snlSurface ( *this );
+        tmpSurf -> refine ( tolerance );
+        ctrlPts = ( tmpSurf -> ctrlPtNet ) -> getCtrlPts();
+        sizeU = ( tmpSurf -> ctrlPtNet ) -> getSizeU();
+        sizeV = ( tmpSurf -> ctrlPtNet ) -> getSizeV();
+    }
+    else
+    {    
+        ctrlPts = ctrlPtNet -> getCtrlPts();
+        sizeU = ctrlPtNet -> getSizeU();
+        sizeV = ctrlPtNet -> getSizeV();
+    }
+    
+    vNet -> vertexNet ( ctrlPts, sizeU, sizeV );    
+    
+    if ( tmpSurf ) delete tmpSurf;
+}
+
+void snlSurface::triangleMesh ( snlTriangleMesh* triMesh, int toleranceType, double tolerance )
+{
+    //! Return approximation to surface as triangle mesh.
+    //  -------------------------------------------------
+    //! @param triMesh Triangle mesh to populate with data.
+    //! @param toleranceType How the tolerance is applied. See snlSurfaceBase::meshToleranceType.
+    //! @param tolerance Mesh must be within tolerance to surface.
+
+    // !@#$ TEMP CODE - Just triangulate a vertexNet so that higher level stuff can be built and tested first.
+
+    snlVertexNet* vNet = new snlVertexNet;
+
+    vertexNet ( vNet, tolerance, false );
+
+    const snlVertex* vertexes = vNet -> vertexes();
+
+    int sizeU = vNet -> sizeU();
+    int sizeV = vNet -> sizeV();
+
+    triMesh -> addVertexes ( vertexes, sizeU * sizeV );
+
+    int leftEdge, rightEdge, diagonalEdge, bottomEdge, topEdge;
+
+    int* nextLeftEdges = new int [ sizeV - 1 ];
+
+    for ( int indexU = 0; indexU < sizeU - 1; indexU ++ )
+    {
+        int vertIndex = indexU * sizeV;
+        
+        for ( int indexV = 0; indexV < sizeV - 1; indexV ++ )
+        {
+            // Create edges and triangles.
+
+            if ( indexU == 0 )
+                leftEdge = triMesh -> addEdge ( vertIndex, vertIndex + 1 );  // Left Edge.
+            else
+                leftEdge = nextLeftEdges [ indexV ];
+                
+            if ( indexV == 0 )
+                bottomEdge = triMesh -> addEdge ( vertIndex, vertIndex + sizeV );  // Bottom Edge.
+            
+            diagonalEdge = triMesh -> addEdge ( vertIndex + 1, vertIndex + sizeV );  // Diagonal.
+
+            topEdge = triMesh -> addEdge ( vertIndex + 1, vertIndex + sizeV + 1 );  // Top Edge.
+
+            rightEdge = triMesh -> addEdge ( vertIndex + sizeV, vertIndex + sizeV + 1 );  // Right Edge.
+
+            nextLeftEdges [ indexV ] = rightEdge;
+
+            // Create two triangles per quad.
+
+            triMesh -> addTriangle ( leftEdge, bottomEdge, diagonalEdge );
+            triMesh -> addTriangle ( rightEdge, topEdge, diagonalEdge );
+
+            // Set edge for next quad.
+
+            bottomEdge = topEdge;
+        }
+    }
+
+    // Clean up.
+
+    delete[] nextLeftEdges;
+}
+
+void snlSurface::genSurfRevolution ( snlCurve& generator, snlPoint& axisStart, snlPoint& axisEnd, double angle )
+{
+    //! Construct surface of revolution.
+    //  --------------------------------
+    //! @param generator Generating curve.
+    //! @param axisStart Starting point of axis generator is revolved about.
+    //! @param axisEnd Ending point of axis.
+    //! @param angle Angle in degrees to revolve generator about axis. Angle is in degrees so that
+    //!              different precisions of PI do not affect surface closure.
+    //!
+    //! @par Notes:
+    //!      Rotation is counter clockwise about axis vector. Right hand rule. Curve defines V
+    //!      direction.
+
+    // Clamp angle to 360 degrees.
+    
+    if ( angle > 360.0 ) angle = 360.0;
+
+    double radAngle = ( angle / 180.0 ) * M_PI;
+
+    // Calculate number of sections and section angle.
+
+    int numSections = (int) ( ( angle / 90.0 ) + 1 );
+
+    double sectionAngle = radAngle / (double) numSections;
+
+    double stepAngle = sectionAngle / 2.0;
+
+    // Calculate mid point weight.
+
+    double midPtWeight = cos ( stepAngle );
+
+    // Setup rotation transforms.
+
+    int numRotations = numSections * 2;
+
+    snlTransform* rotations = new snlTransform [ numRotations ];
+
+    for ( int index = 0; index < numRotations; index ++ )
+        rotations [ index ].rotate ( stepAngle * (double) ( index + 1 ), axisStart, axisEnd );
+
+    // Generate non rotated mid point control points.
+
+    int sizeV = generator.size();
+
+    const snlCtrlPoint* curvePts = ( generator.controlPointNet() ).getCtrlPts();
+
+    snlCtrlPoint* midPoints = new snlCtrlPoint [ sizeV ];
+
+    for ( int index = 0; index < sizeV; index ++ )
+    {
+        // Get vector from point to axis.
+
+        snlVector projection = projectToLine ( axisStart, axisEnd, curvePts [ index ] );
+
+        projection *= - 1.0;  // Vector is pointing into the axis, we want it pointing out from it.
+
+        double projDist = projection.length();
+
+        if ( projDist != 0.0 )
+        {
+            // Calculate mid point to axis distance.
+    
+            double dist = projDist / midPtWeight;  // Mid point weight is the cosine of the step angle.
+    
+            // Generate new control point.
+    
+            projection.length ( dist - projDist );
+        }
+
+        midPoints [ index ] = curvePts [ index ] + projection;
+
+        // Multiply by mid point weight.
+        
+        midPoints [ index ].multiplyWeight ( midPtWeight );
+    }
+
+    // Generate surface control points.
+
+    int sizeU = ( numSections * 2 ) + 1;
+
+    int totalSize = sizeU * sizeV;
+
+    snlCtrlPoint* surfPts = new snlCtrlPoint [ totalSize ];
+
+    int index = 0;
+    int stepIndex = -1;
+
+    for ( int indexU = 0; indexU < sizeU; indexU ++ )
+    {
+        for ( int indexV = 0; indexV < sizeV; indexV ++ )
+        {
+            if ( ! indexU || ( indexU == ( sizeU - 1 ) && angle == 360.0 ) )
+                // If this is the first U index then curve points are taken as is.
+                surfPts [ index ] = curvePts [ indexV ];
+                
+            else
+            {
+                // Calculate rotated control point.
+
+                if ( stepIndex % 2 )
+                {
+                    // Not a mid point.
+                    surfPts [ index ] = curvePts [ indexV ];
+                }
+                else
+                {
+                    // Is a mid point.
+                    surfPts [ index ] = midPoints [ indexV ];
+                }   
+                
+                rotations [ stepIndex ].transform ( surfPts [ index ] );
+            }
+        
+            index ++;
+        }
+
+        stepIndex ++;
+    }
+
+    ctrlPtNet = new snlCtrlPointNetSurface ( surfPts, sizeU, sizeV );
+
+    // Generate Knot Vectors.
+
+    // U Knot Vector.
+    
+    knot* uKnots = new knot [ sizeU + 3 ];  // Degree 2 knot vector.
+
+    for ( index = 0; index < 3; index ++ )
+    {
+        // End clamps.
+
+        uKnots [ index ] = 0.0;
+        uKnots [ sizeU + index ] = 1.0;
+    }
+
+    // Internal knots.
+
+    index = 3;
+
+    knot knotStep = 1.0 / (double) ( numSections );
+    knot knotVal = knotStep;
+
+    for ( int step = 0; step < numSections - 1; step ++ )
+    {
+        uKnots [ index ++ ] = knotVal;  // Multiplicity 2.
+        uKnots [ index ++ ] = knotVal;
+
+        knotVal += knotStep; 
+    }
+
+    knotVectU = new snlKnotVector ( uKnots, sizeU + 3, 2 );
+
+    // V Knot Vector.
+
+    knotVectV = new snlKnotVector ( generator.knotVector() );
+
+    // Setup remaining variables.
+
+    degU = 2;
+    degV = generator.degree();
+
+    // Clean up.
+
+    delete[] rotations;
+    delete[] midPoints;
+}
+
diff --git a/src/snlSurface.h b/src/snlSurface.h
new file mode 100644
index 0000000..000f345
--- /dev/null
+++ b/src/snlSurface.h
@@ -0,0 +1,370 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2003 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+
+// *** General NURBS Surface ***
+
+#ifndef SNLSURFACE_H
+#define SNLSURFACE_H
+
+#include "snlCtrlPointNetSurface.h"
+#include "snlCurve.h"
+#include "snlKnotVector.h"
+#include "snlPoint.h"
+#include "snlSurfaceBase.h"
+#include "snlVertex.h"
+#include "snlVertexNet.h"
+
+#include "ptrList.h"
+
+#define SNL_INV_ITER_VP 8  // Number of iterations for velocity pass.
+#define SNL_INV_ITER_NP 8  // Number of iterations for newton pass.
+
+#define SNL_NUMERIC_NOISE 1.0e-14
+
+typedef struct
+{
+    /* Surface Edge */
+
+    int     direction;  // u = 0, v = 1.
+    knot    pVal;  // Parameter value.
+
+} sEdge;
+
+typedef struct
+{
+    // surface location.
+
+    knot        paramU;  // Evaluated parameter in u direction. Point found.
+    knot        paramV;  // Evaluated parameter in v direction.
+    snlPoint    pt;  // Point corresponding to parameters.
+    basis       dist;  // Distance from point being inverted / projected to point found.
+    double      cos;  // Cos of angle to normal.
+
+    int         origPtIndex;  // For inversion and projection, the index of the original point handed to the
+                              // relevant function.
+
+} snlSurfLocn;
+
+typedef struct
+{
+    // surface location.
+
+    knot        paramU;  // Evaluated parameter in u direction. Point found.
+    knot        paramV;  // Evaluated parameter in v direction.
+    snlPoint    pt;  // Point corresponding to parameters.
+    basis       dist;  // Distance squared from point being inverted to point found.
+    double      cos;  // Cos of angle to normal.
+    
+    int         origPtIndex;  // For inversion and projection, the index of the original point handed to the
+                              // relevant function.
+
+    int         spanNumber;  // Absolute span number guess belongs to.
+
+    knot        minU;  // Parametric boundaries used for culling.
+    knot        maxU;
+    knot        minV;
+    knot        maxV;
+
+    bool        culled;
+    bool        ignoreParamBounds;  // Don't cull this point if it goes out of bounds.
+    bool        converged;
+    
+} snlSurfLocnGuess;
+
+typedef struct
+{
+    // Control point location for surface
+
+    int             uIndex;  // Array indexes.
+    int             vIndex;
+
+    snlCtrlPoint*   ctrlPt;  // Pointer to control point found.
+
+    double          dist;  // Distance to control point from comparison point.
+    
+} snlSCtrlPtLocn;
+
+typedef struct
+{
+    bool        converged;
+    bool        stalled;  // Can't converge
+
+    int         arcIndex;
+
+    double      arcAngle;
+
+    // Controlling Surface.
+
+    knot        cParamU;
+    knot        cParamV;
+
+    snlPoint    cPt;  // Evaluated point.
+    snlPoint    normPt;  // Normal point.
+
+    snlVector   velU;  // U direction velocity.
+    snlVector   velV;  // V direction velocity.
+
+    // Matching Surface.
+
+    knot        mParamU;
+    knot        mParamV;
+
+    snlPoint    mPt;  // Evaluated point.
+
+} arcLocn;
+
+class snlSurface : public snlSurfaceBase
+{
+    public:
+        
+        
+        virtual ~snlSurface();
+        
+        snlSurface();
+
+        void init();  // Standard Initialisation.
+        
+        snlSurface ( const snlSurface& surfaceToCopy );  // Copy constructor.
+    
+        snlSurface ( int degreeU, int degreeV, unsigned sizeU, unsigned sizeV,
+                     snlPoint& origin, snlPoint& cornerMaxU, snlPoint& cornerMaxV );
+                     
+        snlSurface ( int degreeU, int degreeV, unsigned sizeU, unsigned sizeV, snlCtrlPoint* points,
+                     knot* knotsU, knot* knotsV );
+        
+        snlSurface ( snlCurve& curve1, snlCurve& curve2, int direction = SNL_U_DIR );  // Generate ruled surface.
+
+        snlSurface ( snlCurve& generator, snlPoint& axisStart, snlPoint& axisEnd, double angle );  // Surface of revolution.
+
+        snlSurface ( snlCurve** curves, int numCurves, int dir = SNL_U_DIR );  // Skinned surface.
+
+        snlSurface ( int interpType, snlPoint* pointsInterp, int sizeU, int sizeV,
+                     int degreeU, int degreeV );  // Interpolated surface.
+
+        typedef enum
+        {
+            //SNL_PRIM_AUTO,
+            SNL_PRIM_PLANE,
+            SNL_PRIM_SPHERE,
+            SNL_PRIM_CYLINDER,
+            SNL_PRIM_CONE,
+            SNL_BILINEAR_COONS
+            
+        } snlPrimType;
+
+        // Construct surface that fits a closed loop as described by points.
+        snlSurface ( snlPoint* points, int numPoints );  // Unknown topology.
+        snlSurface ( snlPoint* points, int numPoints, snlPrimType primType, snlPoint* axisStart = 0, snlPoint* axisEnd = 0 );
+
+        snlSurface ( snlCurve* U1, snlCurve* U2, snlCurve* V1, snlCurve* V2 );  // Bilinear Coons patch.
+
+        // Operators
+
+        snlSurface& operator= ( const snlSurface& surface );
+
+        // Interpolation Functions.
+
+        enum SNL_INTERP_TYPES
+        {
+            SNL_GLOBAL_INTERP_CHORDLENGTH,
+            SNL_GLOBAL_INTERP_CENTRIFUGAL
+        };
+        
+        // Data functions.
+        
+        int degreeU() const;
+        int degreeV() const;
+        
+        unsigned sizeU() const;
+        unsigned sizeV() const;
+
+        knot minU();
+        knot maxU();
+        
+        knot minV();
+        knot maxV();
+        
+        const snlCtrlPoint* controlPoints();
+        
+        const knot* knotsU();
+        const knot* knotsV();
+        
+        snlCtrlPointNetSurface& controlPointNet();
+        
+        const snlKnotVector& knotVectorU();
+        const snlKnotVector& knotVectorV();
+        
+        // Evaluation functions.
+
+        // Non-rational homogeneous surface point.        
+        snlPoint evalHmg ( knot paramU, knot paramV, basis* basisU = 0, basis* basisV = 0 ) const;
+        
+        // Rational non-homogeneous surface point.
+        virtual snlPoint eval ( knot paramU, knot paramV, basis* basisU, basis* basisV ) const;
+        virtual snlPoint eval ( knot paramU, knot paramV ) const;
+
+        // Derivatives.
+
+        snlPoint* evalDerivsHmg ( knot paramU, knot paramV, unsigned derivU, unsigned derivV,
+                                  basis* basisU = 0, basis* basisV = 0 );
+                                  
+        snlPoint* evalDerivs ( knot paramU, knot paramV, unsigned derivU, unsigned derivV );
+
+        void velocities ( knot paramU, knot paramV, snlPoint& evalPoint, snlVector& velocityU, snlVector& velocityV,
+                          basis* basisU = 0, basis* basisV = 0 );
+
+        // Knot manipulation.
+        
+        void insertKnot ( knot iParam, int dir, bool reallocate = true );
+        void insertKnot ( knot iParam, int dir, int numToInsert, bool reallocate = true );
+        double removeKnots ( int numKnots, unsigned removalIndex, int direction, double tolerance, bool reallocate = true );
+        double removeKnot ( unsigned removalIndex, int direction, double tolerance, bool reallocate = true );
+
+        // Projection.
+
+        snlVertex* project_depr ( snlPoint* toProject, int numPoints, double convergTol, double
+                                  normTol, int maxPass );
+
+        snlSurfLocn* invert ( snlPoint* toInvert, int numPoints, int* retArraySize,
+                              double convergTol, double normTol, int maxPass );
+
+        snlSurfLocn* project ( snlPoint* toProject, int numPoints, int* retArraySize,
+                               double convergTol, double normTol, int maxPass );
+
+        snlSurfLocn* fastProject ( snlPoint* toProject, int numPoints, int* retArraySize,
+                                   double convergTol, double normTol, int maxPass,
+                                   int sensitivity, int maxLocns );
+        
+        snlSCtrlPtLocn* findClosestCtrlPt ( snlPoint* points, int numPoints );
+        
+        // Try to predict edges that may have ambiguities during projection.
+        int hasAmbigEdges ( sEdge* results, double tolerance = 1.0e-6 );
+        int hasAmbigEdges_depr ( sEdge* results );
+
+        // Surface decomposition.
+        
+        unsigned createBezierSegments ( int dir, int** numKnotsAdded = 0);
+        void createBezierSegments ( int* numU = 0, int* numV = 0 );
+        void createConvexBezierSegments ( int* numU = 0, int* numV = 0, double sensitivity = 0.0 );
+
+        void elevateDegree ( int direction, int byDegree );
+        double reduceDegree ( int dir, unsigned numDeg, double tolerance );
+        
+        void refine ( double tolerance );  // Refine control point net.
+
+        void refineHull_UV ( double tolerance );  // Refine control point net using convex hull methods.
+        bool refineHull_U ( double tolerance, bool singlePass = false );
+        bool refineHull_V ( double tolerance, bool singlePass = false );
+
+        void refineHullBezier ( double tolerance );  // Refine control point net by Bezier patch subdivision.
+        
+        double maxCurvatureU();
+        double maxCurvatureV();
+
+        snlVector calcNormal ( knot paramU, knot paramV, snlPoint* evalPt = 0 );
+
+        snlCurve* extractEdge ( int edge );
+
+        snlSurface* fillet ( int edge, snlVector& frontFaceNormal,
+                             snlSurface& surface2, snlVector& frontFaceNormal2,
+                             double tolerance, double radius, bool trim1, bool trim2 );
+
+        void transform ( snlTransform& transf );
+
+        void makeCompatible ( snlSurface* surfaceToMatch, int direction );
+        void synchronise ( snlSurface& surface, int direction );
+
+        // Trimming Functions.
+
+        void addTrimCurve ( snlCurve* curve );
+        bool removeTrimCurve ( snlCurve* curve );
+
+        // Misc Functions.
+        
+        void print();
+
+        void print_cpp();
+        
+        // Abstract Implementation.
+        
+        virtual void vertexNet ( snlVertexNet* vNet, double tolerance, bool parametric );
+
+        virtual void triangleMesh ( snlTriangleMesh* triMesh, int toleranceType, double tolerance );
+        
+        enum parametricDirections
+        {
+            SNL_U_DIR = 0,
+            SNL_V_DIR = 1
+        };
+
+        enum surfaceEdges
+        {
+            SNL_EDGE_UMIN,
+            SNL_EDGE_UMAX,
+            SNL_EDGE_VMIN,
+            SNL_EDGE_VMAX
+        };
+        
+    protected:
+
+        void copyFrom ( const snlSurface& surfaceToCopy );
+
+        void fitBilinearCoons ( snlPoint* points, int numPoints );
+        void genBilinearCoons ( snlCurve* curve_U1, snlCurve* curve_U2, snlCurve* curve_V1, snlCurve* curve_V2 );
+        void fitPlane ( snlPoint* points, int numPoints );
+        void fitCylinder ( snlPoint* points, int numPoints, snlPoint* axisStart, snlPoint* axisEnd );
+        void fitCone ( snlPoint* points, int numPoints, snlPoint* axisStart, snlPoint* axisEnd );
+        void fitSphere ( snlPoint* points, int numPoints, snlPoint* sphereCentre );
+
+        knot* globalInterpGenParams ( int type, snlPoint* points, int sizeU, int sizeV, int dir );
+
+        void genGlobalInterpSurf ( int interpType, snlPoint* pointsInterp, int sizeU, int sizeV, int degreeU, int degreeV );
+
+        snlSurfLocn* processGuesses ( snlPoint* points, int numPoints, int* retArraySize,
+                                      ptrList <snlSurfLocnGuess>* guesses, double convergTol,
+                                      double normTol, int maxPass, bool retNonConverged = false, bool noCull = false,
+                                      int numVelocity = SNL_INV_ITER_VP, int numNewton = SNL_INV_ITER_NP );
+
+        bool convergeVelocity ( snlPoint* convergToPts, ptrList <snlSurfLocnGuess>* guesses,
+                                int numIterations, double convergTol, double normTol );
+
+        bool convergeNewton ( snlPoint* convergToPts, ptrList <snlSurfLocnGuess>* guesses,
+                              int numIterations, double convergTol, double normTol );
+
+        ptrList <snlSurfLocnGuess>* guessInvLocation ( snlPoint* points, int numPoints, bool* pointMask,
+                                                       int granU, int granV );
+
+        ptrList <snlSurfLocnGuess>* guessProjLocation ( snlPoint* points, int numPoints, bool* pointMask );
+
+        ptrList <snlSurfLocnGuess>* guessFastProjLocation ( snlPoint* points, int numPoints, int maxGuessPerPt,
+                                                            int granU, int granV );
+
+        ptrList <snlSurfLocnGuess>* guessProjLocation_triMethod ( snlPoint* points, int numPoints,
+                                                                  bool* pointMask );
+
+        void genSurfRevolution ( snlCurve& generator, snlPoint& axisStart, snlPoint& axisEnd, double angle );
+                                                            
+        // Data Section
+        
+        int     degU;  // Degree of surface in U direction.
+        int     degV;  // Degree of surface in V direction.
+    
+        snlCtrlPointNetSurface*     ctrlPtNet;
+        
+        snlKnotVector*              knotVectU;
+        snlKnotVector*              knotVectV;
+
+        ptrList< snlCurve >*        trim_curves;
+};
+
+#endif
diff --git a/src/snlSurfaceBase.h b/src/snlSurfaceBase.h
new file mode 100644
index 0000000..7479d60
--- /dev/null
+++ b/src/snlSurfaceBase.h
@@ -0,0 +1,59 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2005 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+
+// *** Base Class Of All Surfaces ***
+
+#ifndef SNL_SURFACE_BASE_H
+#define SNL_SURFACE_BASE_H
+
+#include "snlKnotVector.h"
+#include "snlPoint.h"
+#include "snlTriangleMesh.h"
+#include "snlVertexNet.h"
+
+#ifdef SGI_MIPS
+
+    #include <iostream.h>
+    #include <math.h>
+    #include <float.h>
+
+#else
+
+    #include <iostream>
+    #include <cmath>
+    #include <cfloat>
+
+    using namespace std;
+
+#endif
+
+class snlSurfaceBase
+{
+    public:
+
+        virtual ~snlSurfaceBase(){};
+
+        virtual snlPoint eval ( knot paramU, knot paramV ) const = 0;
+
+        virtual void vertexNet ( snlVertexNet* vNet, double tolerance, bool parametric ) = 0;
+
+        virtual void triangleMesh ( snlTriangleMesh* triMesh, int toleranceType, double tolerance ) = 0;
+
+        enum meshToleranceType
+        {
+            SNL_TOL_DISTANCE,  // Must be within distance tolerance to surface.
+            SNL_TOL_ANGLE      // Angle between successive sections must be less than tolerance.
+        };
+};
+
+#endif
diff --git a/src/snlSurfaceOfRevolution.cpp b/src/snlSurfaceOfRevolution.cpp
new file mode 100644
index 0000000..56c60ca
--- /dev/null
+++ b/src/snlSurfaceOfRevolution.cpp
@@ -0,0 +1,182 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** Surface of Revolution ***
+
+#include "snlSurfaceOfRevolution.h"
+#include "snlUtil.h"
+
+snlSurfaceOfRevolution::snlSurfaceOfRevolution()
+{
+    profile = 0;
+    axis_start = 0;
+    axis_end = 0;
+    
+    rot_angle = M_PI * 2.0;  
+}
+
+snlSurfaceOfRevolution::~snlSurfaceOfRevolution()
+{
+    if ( profile ) delete profile;
+    if ( axis_start ) delete axis_start;
+    if ( axis_end ) delete axis_end;
+}
+        
+snlSurfaceOfRevolution::snlSurfaceOfRevolution ( snlCurve* profileCurve, snlPoint* axisStart, snlPoint* axisEnd, double rotationAngle )
+{
+    // Construct new surface of revolution.
+    // ------------------------------------
+    // profileCurve:    Curve to revolve about axis.
+    // axisStart:       Start of revolution axis.
+    // axisEnd:         End of revolution axis.
+    // rotationAngle:   Angle to revolve about axis.
+    //
+    // Notes:    All objects are owned by this object.
+    //           Revolution about axis is right handed.
+    
+    profile = profileCurve;
+    axis_start = axisStart;
+    axis_end = axisEnd;
+    
+    if ( rotationAngle > M_PI * 2.0 || rotationAngle == 0.0)
+        rot_angle = M_PI * 2.0;
+    else if ( rotationAngle < ( - M_PI * 2.0 ) )
+        rot_angle = - M_PI * 2.0;
+    else
+        rot_angle = rotationAngle;        
+}
+
+snlCurve& snlSurfaceOfRevolution::profileCurve()
+{
+    return * profile;
+}
+
+void snlSurfaceOfRevolution::profileCurve ( snlCurve* profileCurve )
+{
+    if ( profile ) delete profile;
+    
+    profile = profileCurve;
+}
+
+snlPoint& snlSurfaceOfRevolution::axisStart()
+{
+    return *axis_start;
+}
+
+void snlSurfaceOfRevolution::axisStart ( snlPoint* startPoint )
+{
+    if ( axis_start ) delete axis_start;
+    
+    axis_start = startPoint;
+}
+
+snlPoint& snlSurfaceOfRevolution::axisEnd()
+{
+    return *axis_end;
+}
+
+void snlSurfaceOfRevolution::axisEnd ( snlPoint* endPoint )
+{
+    if ( axis_end ) delete axis_end;
+    
+    axis_end = endPoint;
+}
+
+double snlSurfaceOfRevolution::rotationAngle()
+{
+    return rot_angle;
+}
+
+void snlSurfaceOfRevolution::rotationAngle ( double angle )
+{
+    rot_angle = angle;
+}
+
+void snlSurfaceOfRevolution::vertexNet ( snlVertexNet* vNet, double tolerance, bool parametric )
+{
+    // Return approximation to surface.
+    // --------------------------------
+    // tolerance:    Tolerance to approximate to.
+    // parametric:   Do a parametric analysis as opposed to knot refinement.
+    // vNet:         Vertex net to fill with data.
+    
+    snlCurve* profileCopy = new snlCurve ( *profile );
+    
+    if ( tolerance > 0.0 )
+        profileCopy -> refine ( tolerance );    
+    
+    const snlCtrlPoint* ctrlPts = profileCopy -> controlPointNet().getCtrlPts();
+    
+    int numPts = profileCopy -> controlPointNet().size();
+    
+    vNet -> vertexNet ( ctrlPts, numPts );
+    
+    // Find angle step to use.
+    
+    snlPoint axis_start_norm ( *axis_start );
+    axis_start_norm.normalise();
+    
+    snlPoint axis_end_norm ( *axis_end );
+    axis_end_norm.normalise();    
+    
+    // Get largest radius
+    
+    double maxRadius = 0.0;
+    
+    for ( int index = 0; index < numPts; index ++ )
+    {        
+        double dist = distToLine ( axis_start_norm, axis_end_norm, ctrlPts [ index ] );
+        
+        if ( dist > maxRadius )
+            maxRadius = dist;        
+    }
+    
+    // Calculate steps based on tolerance and maximum radius.
+    
+    double angleStep = 2 * acos ( 1.0 - ( tolerance / maxRadius ) );
+    
+    int numSteps = (int ) ( rot_angle / angleStep ) + 1;
+    
+    angleStep = rot_angle / (double) numSteps;
+    
+    // Rotate and append points at discrete angle steps.   
+    
+    snlTransform transf;
+    
+    transf.rotate ( angleStep, axis_start_norm, axis_end_norm );
+    
+    for ( int step = 0; step < numSteps; step ++ )
+    {
+        profileCopy -> controlPointNet().transform ( transf );
+        
+        vNet -> appendRow ( ctrlPts );
+    }
+}
+
+snlPoint snlSurfaceOfRevolution::eval ( knot paramU, knot paramV ) const
+{
+    snlPoint retPoint;
+    
+    // !@#$ Incomplete
+    
+    return retPoint;
+}
+
+void snlSurfaceOfRevolution::triangleMesh ( snlTriangleMesh* triMesh, double tolerance )
+{
+}
+
diff --git a/src/snlSurfaceOfRevolution.h b/src/snlSurfaceOfRevolution.h
new file mode 100644
index 0000000..724c01c
--- /dev/null
+++ b/src/snlSurfaceOfRevolution.h
@@ -0,0 +1,68 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// ***!! Deprecated - Use snlSurface Instead !!****
+
+// *** Surface of Revolution ***
+
+#ifndef SNL_SURFACEOFREVOLUTION_H
+#define SNL_SURFACEOFREVOLUTION_H
+
+#include "snlCurve.h"
+#include "snlPoint.h"
+#include "snlSurfaceBase.h"
+
+class snlSurfaceOfRevolution : public snlSurfaceBase
+{
+    public:
+    
+        snlSurfaceOfRevolution();
+        virtual ~snlSurfaceOfRevolution();
+        
+        snlSurfaceOfRevolution ( snlCurve* profileCurve, snlPoint* axisStart, snlPoint* axisEnd, double rotationAngle );
+        
+        snlCurve& profileCurve();
+        void profileCurve ( snlCurve* profileCurve );
+        
+        snlPoint& axisStart();
+        void axisStart ( snlPoint* startPoint );
+        
+        snlPoint& axisEnd();
+        void axisEnd ( snlPoint* endPoint );
+        
+        double rotationAngle();
+        void rotationAngle ( double angle );
+        
+        // Abstract Implementation.
+        
+        virtual void vertexNet ( snlVertexNet* vNet, double tolerance, bool parametric );
+        
+        virtual snlPoint eval ( knot paramU, knot paramV ) const;
+
+        virtual void triangleMesh ( snlTriangleMesh* triMesh, double tolerance );
+    
+    private:
+    
+        snlCurve*    profile;
+        
+        snlPoint*    axis_start;
+        snlPoint*    axis_end;
+        
+        double       rot_angle;
+};
+
+#endif
diff --git a/src/snlSurface_pointLoop.cpp b/src/snlSurface_pointLoop.cpp
new file mode 100644
index 0000000..20a3750
--- /dev/null
+++ b/src/snlSurface_pointLoop.cpp
@@ -0,0 +1,794 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2003 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+
+// Point loop related definitions for snlSurface. ( snlSurface.cpp has been segmented to reduce size ).
+
+#include "snlSurface_pointLoop.h"
+
+snlSurface::snlSurface ( snlPoint* points, int numPoints )
+{
+    // Create surface that fits a closed loop as described by points.
+    // --------------------------------------------------------------
+    // points:      Points that describe a closed loop. The end point should not be a duplicate of the start point.
+    // numPoints:   Number of points in points array.
+
+    init();
+
+    // Make sure first and last points aren't the same.
+
+    if ( points [ 0 ] == points [ numPoints - 1 ] )
+        numPoints --;
+
+    // Generate surface.
+
+    fitBilinearCoons ( points, numPoints );
+}
+
+snlSurface::snlSurface ( snlPoint* points, int numPoints, snlPrimType primType, snlPoint* axisStart, snlPoint* axisEnd )
+{
+    // Create surface that fits a closed loop as described by points.
+    // --------------------------------------------------------------
+    // points:      Points that describe a closed loop. The end point should not be a duplicate of the start point.
+    // numPoints:   Number of points in points array.
+    // primType:    Type of primitive surface to use to fit points to.
+    // axisStart:   Starting point of axis for Cone and Cylinder. Centre point for sphere. ( Not required for plane ).
+    // axisEnd:     Ending point of axis for Cone and Cylinder. ( Not required for Sphere ).
+    
+    init();
+
+    // Make sure first and last points are compatible with primitive being built.
+
+    snlPoint* pointsToUse = points;
+
+    if ( primType == SNL_BILINEAR_COONS && points [ 0 ] == points [ numPoints - 1 ] )
+    {
+        numPoints --;
+    }
+    else if ( ! ( points [ 0 ] == points [ numPoints - 1 ] ) )
+    {
+        numPoints ++;
+
+        pointsToUse = new snlPoint [ numPoints ];
+
+        for ( int index = 0; index < numPoints - 1; index ++ )
+            pointsToUse [ index ] = points [ index ];
+
+        pointsToUse [ numPoints - 1 ] = points [ 0 ];
+    }
+
+    switch ( primType )
+    {
+        case SNL_PRIM_PLANE:
+
+            fitPlane ( pointsToUse, numPoints );
+            break;
+        
+        case SNL_PRIM_SPHERE:
+
+            fitSphere ( pointsToUse, numPoints, axisStart );
+            break;
+        
+        case SNL_PRIM_CYLINDER:
+        
+            fitCylinder ( pointsToUse, numPoints, axisStart, axisEnd );
+            break;
+        
+        case SNL_PRIM_CONE:
+
+            fitCone ( pointsToUse, numPoints, axisStart, axisEnd );
+            break;
+            
+        case SNL_BILINEAR_COONS:
+        default:
+
+            fitBilinearCoons ( pointsToUse, numPoints );
+            break;
+    }
+
+    if ( pointsToUse != points ) delete[] pointsToUse;
+}
+
+void snlSurface::fitBilinearCoons ( snlPoint* points, int numPoints )
+{
+    // Construct bilinear Coons surface that fits a closed loop as described by points.
+    // -----------------------------------------------------------------------------
+    // points:      Points that describe a closed loop. The end point should not be a duplicate of the start point.
+    // numPoints:   Number of points in points array.
+
+    // The resultant Coons patch is easier if the given points are not homogeneous.
+
+    snlPoint* normPoints = new snlPoint [ numPoints ];
+
+    for ( int index = 0; index < numPoints; index ++ )
+    {
+        normPoints [ index ] = points [ index ];
+        normPoints [ index ].normalise();
+    }
+
+    // Create an interpolated curve of degree 2.
+
+    knot* retParams;
+
+    snlCurve* initialCurve = new snlCurve ( normPoints, numPoints, snlCurve::SNL_GLOBAL_INTERP_CENTRIFUGAL, 2, true, &retParams );
+
+    // Split curve into four pieces using a basic approximation with given points.
+
+    double curveLength = 0;  // Total curve length.
+
+    double* sectionLengths = new double [ numPoints ];  // Save distance between points.
+
+    for ( int index = 0; index < numPoints; index ++ )
+    {
+        if ( index < numPoints - 1 )
+            sectionLengths [ index ] = sqrt ( normPoints [ index ].distSqrd ( normPoints [ index + 1 ] ) );
+        else
+            sectionLengths [ index ] = sqrt ( normPoints [ index ].distSqrd ( normPoints [ 0 ] ) );  // Loop join.
+
+        curveLength += sectionLengths [ index ];
+    }
+
+    // Step through section lengths to find points to split curve at.
+
+    double splitLength = curveLength / 4.0;
+
+    double splitParams [ 3 ];   // Three internal split positions gives four curve segments.
+
+    int sectionIndex = 0;
+    double distance = 0.0;
+
+    for ( int splitNumber = 0; splitNumber < 3; splitNumber ++ )
+    {
+        double splitDistance = splitLength * (double) ( splitNumber + 1 );
+        
+        while ( ( distance + sectionLengths [ sectionIndex ] ) < splitDistance )
+        {
+            distance += sectionLengths [ sectionIndex ++ ];
+        }
+
+        // Calculate parameter to split at.
+
+        double sectionFraction = ( splitDistance - distance ) / sectionLengths [ sectionIndex ];
+
+        double paramDelta = retParams [ sectionIndex + 1 ] - retParams [ sectionIndex ];
+
+        splitParams [ splitNumber ] = retParams [ sectionIndex ] + paramDelta * sectionFraction;
+    }
+
+    // Split curve into four pieces.
+
+    snlCurve* curve1;
+    snlCurve* curve2;
+    snlCurve* curve3;
+    snlCurve* curve4;
+    
+    curve1 = new snlCurve ( *initialCurve );
+    curve1 -> truncate ( splitParams [ 0 ], false, false );
+    curve1 -> reparameterise ( 0.0, 1.0 );  // All knot vectors should be clamped to [ 0.0, 1.0 ].
+    
+    curve2 = new snlCurve ( *initialCurve );
+    curve2 -> truncate ( splitParams [ 0 ], true, false );
+    curve2 -> truncate ( splitParams [ 1 ], false, false );
+    curve2 -> reparameterise ( 0.0, 1.0 );
+
+    curve3 = new snlCurve ( *initialCurve );
+    curve3 -> truncate ( splitParams [ 1 ], true, false );
+    curve3 -> truncate ( splitParams [ 2 ], false, false );
+    curve3 -> reparameterise ( 0.0, 1.0 );
+
+    curve4 = new snlCurve ( *initialCurve );
+    curve4 -> truncate ( splitParams [ 2 ], true, false );
+    curve4 -> reparameterise ( 0.0, 1.0 );
+
+    // Curves 3 and 4 are going the wrong direction.
+
+    curve3 -> reverseEvalDirection();
+    curve4 -> reverseEvalDirection();
+
+    // Generate Coons Patch.
+
+    genBilinearCoons ( curve4, curve2, curve1, curve3 );
+
+    // Clean Up.
+
+    delete[] normPoints;
+    delete[] sectionLengths;
+
+    delete curve1;
+    delete curve2;
+    delete curve3;
+    delete curve4;
+}
+
+void snlSurface::fitPlane ( snlPoint* points, int numPoints )
+{
+    // Fit ( and generate ) a planar surface to given points.
+    // ------------------------------------------------------
+    // points:      Given points to fit plane to.
+    // numPoints:   Number of points in points array.
+
+    // Using first point in array as base find point furtherest from that point.
+
+    double distToEndPoint = 0.0;  // Largest distance found.
+    int baseLineEndIndex;
+
+    for ( int index = 1; index < numPoints; index ++ )
+    {
+        double dist = points [ 0 ].distSqrd ( points [ index ] );
+
+        if ( dist > distToEndPoint )
+        {
+            distToEndPoint = dist;
+            baseLineEndIndex = index;
+        }
+    }
+
+    // Use furtherest point and base point as line. Project points to line. Re-asses line starting point.
+
+    double maxProjDist = 0.0;  // Maximum projection distance to line.
+
+    snlVector maxProjVector;  // Vector associated with maximum projection distance.
+
+    snlPoint newStartPoint = points [ 0 ];
+
+    for ( int index = 1; index < numPoints; index ++ )
+    {
+        snlVector projVect = projectToLine ( points [ 0 ], points [ baseLineEndIndex ], points [ index ] );
+
+        double dist = projVect.length();
+
+        if ( dist > maxProjDist )
+        {
+            maxProjDist = dist;
+            maxProjVector = projVect;
+        }
+        
+        snlPoint projPoint = points [ index ] + projVect;  // Project point onto base line.
+
+        // Determine if point should be new base line starting point.
+
+        dist = points [ baseLineEndIndex ].distSqrd ( projPoint );
+
+        if ( dist > distToEndPoint )
+        {
+            // New start point has been found.
+            newStartPoint = projPoint;
+            distToEndPoint = dist;
+        }
+    }
+
+    // Generate points that will delineate a rectangular NURBS patch.
+
+    snlPoint patchPt1 = newStartPoint + maxProjVector;
+    snlPoint patchPt2 = newStartPoint - maxProjVector;
+    snlPoint patchPt3 = points [ baseLineEndIndex ] + maxProjVector;
+    snlPoint patchPt4 = points [ baseLineEndIndex ] - maxProjVector;
+
+    // Generate planar NURBS surface.
+
+    knotVectV = new snlKnotVector ( 0.0, 1.0, 4, 1 );
+    knotVectU = new snlKnotVector ( 0.0, 1.0, 4, 1 );
+
+    snlCtrlPoint* ctrlPts = new snlCtrlPoint [ 4 ];
+
+    ctrlPts [ 0 ] = patchPt1;
+    ctrlPts [ 1 ] = patchPt2;
+    ctrlPts [ 2 ] = patchPt3;
+    ctrlPts [ 3 ] = patchPt4;
+
+    degU = 1;
+    degV = 1;
+
+    ctrlPtNet = new snlCtrlPointNetSurface ( ctrlPts, 2, 2, false );    
+}
+
+void snlSurface::fitCylinder ( snlPoint* points, int numPoints, snlPoint* axisStart, snlPoint* axisEnd )
+{
+    // Fit ( and generate ) a cylindrical surface to given points.
+    // -----------------------------------------------------------
+    // points:      Given points to fit cylinder to.
+    // numPoints:   Number of points in points array.
+    // axisStart:   Starting point of cylinder axis.
+    // axisEnd:     Ending point of cylinder axis.
+
+    // Determine start and end bounds of axis as well as angular bounds of cylinder.
+
+    snlVector axis ( *axisStart, *axisEnd );
+
+    // Calculate initial projections that are used for cylinder angle determination.
+    
+    snlVector angleProjMax = projectToLine ( *axisStart, *axisEnd, points [ 0 ] );  // Projection associated with maximum angle.
+
+    snlPoint newAxisStart = points [ 0 ] + angleProjMax;  // New axis bound points.
+    snlPoint newAxisEnd = newAxisStart;
+    
+    angleProjMax *= -1.0;  // These projections must be pointing from axis outwards.
+    
+    snlVector angleProjMin = angleProjMax;  // Projection associated with minimum angle.
+
+    snlVector lastProj;  // Projection vector that next projection is compared to.
+
+    // All angles are rooted at the start point projection. Positive angles are anti-clockwise ( right hand rule ).
+
+    double accumAngle = 0.0;  // Accumulated angle.
+    double maxAngle = 0.0;
+    double minAngle = 0.0;
+
+    // Step through each point from point loop.
+
+    for ( int index = 0; index < numPoints; index ++ )
+    {
+        snlVector projVect = projectToLine ( *axisStart, *axisEnd, points [ index ] );
+
+        snlPoint projPt = points [ index ] + projVect;
+
+        // See if projected point is new start or end axis point.
+
+        if ( snlVector ( newAxisStart, projPt ).dot ( axis ) < 0.0 )
+            newAxisStart = projPt;
+
+        if ( snlVector ( newAxisEnd, projPt ).dot ( axis ) > 0.0 )
+            newAxisEnd = projPt;
+
+        // Process angular bounds.
+
+        projVect *= -1.0;  // Make sure vector is pointing outwards from axis.
+
+        if ( index > 0 )
+        {
+            snlVector crossProd;
+
+            crossProd.crossProduct ( lastProj, projVect );
+
+            if ( ! crossProd.isNull() )
+            {
+                // If normal vector ( cross product ) is opposite direction to axis then angle is negative.
+    
+                double angle = lastProj.angle ( projVect );
+    
+                if ( crossProd.dot ( axis ) > 0.0 )
+                {
+                    // Positive angle.
+                    accumAngle += angle;
+
+                    if ( accumAngle > maxAngle )
+                    {
+                        maxAngle = accumAngle;
+                        angleProjMax = projVect;
+                    }
+                }
+                else
+                {
+                    // Negative angle.
+                    accumAngle -= angle;
+
+                    if ( accumAngle < minAngle )
+                    {
+                        minAngle = accumAngle;
+                        angleProjMin = projVect;
+                    }
+                }
+            }
+        }
+
+        lastProj = projVect;
+    }
+
+    // Cylinder bounds should have been calculated. Now build a cylinder.
+
+    double absAngle = maxAngle - minAngle;  // minAngle must be below or equal to 0.
+
+    // Recalculate more exact absAngle from vectors but using previously calculated absAngle as guide.
+
+    double angle = angleProjMax.angle ( angleProjMin );
+
+    if ( absAngle > M_PI )
+        absAngle = ( M_PI * 2.0 ) - angle;
+    else
+        absAngle = angle;
+
+    // Convert absolute angle to degrees.
+
+    absAngle = ( absAngle / ( 2.0 * M_PI ) ) * 360;
+
+    if ( absAngle > 360.0 )
+        absAngle = 360.0;
+
+    snlPoint curveStart = newAxisStart + angleProjMin;
+    snlPoint curveEnd = newAxisEnd + angleProjMin;
+
+    snlCurve generator ( 2, 3, curveStart, curveEnd );
+
+    genSurfRevolution ( generator, *axisStart, *axisEnd, absAngle );
+}
+
+void snlSurface::fitCone ( snlPoint* points, int numPoints, snlPoint* axisStart, snlPoint* axisEnd )
+{
+    // Fit ( and generate ) a conic surface to given points.
+    // -----------------------------------------------------
+    // points:      Given points to fit cone to.
+    // numPoints:   Number of points in points array.
+    // axisStart:   Starting point of cone axis.
+    // axisEnd:     Ending point of cone axis.
+
+    // Determine start and end bounds of axis as well as angular bounds of cone.
+
+    snlVector axis ( *axisStart, *axisEnd );
+
+    // Calculate initial projections that are used for angle determination.
+    
+    snlVector angleProjMax = projectToLine ( *axisStart, *axisEnd, points [ 0 ] );  // Projection associated with maximum angle.
+
+    snlPoint newAxisStart = points [ 0 ] + angleProjMax;  // New axis bound points.
+    snlPoint newAxisEnd = newAxisStart;
+    
+    angleProjMax *= -1.0;  // These projections must be pointing from axis outwards.
+    
+    snlVector angleProjMin = angleProjMax;  // Projection associated with minimum angle.
+
+    snlVector lastProj;  // Projection vector that next projection is compared to.
+
+    // All angles are rooted at the start point projection. Positive angles are anti-clockwise ( right hand rule ).
+
+    double accumAngle = 0.0;  // Accumulated angle.
+    double maxAngle = 0.0;
+    double minAngle = 0.0;
+
+    // Radii at start and end of axis.
+
+    double axisStartRadius = angleProjMax.length();
+    double axisEndRadius = axisStartRadius;
+
+    bool intersectsAxis = false;  // Only true if point loop intersects axis.
+
+    double lastAbsAngle, curAbsAngle;  // Absolute angles as based from first given point.
+
+    bool circleSect [ 16 ];  // Circle is divided into 16 sections. True if section is used.
+
+    for ( int index = 0; index < 16; index ++ )
+        circleSect [ index ] = false;
+
+    double circleSectSliceSize = ( M_PI* 2.0 ) / 16.0;  // Size of section in radians.
+
+    snlVector baseProj;  // Baseline projection. Used as zero angle reference.
+    baseProj.zero();
+
+    bool negTraverse;  // Negative traversal.
+
+    // Step through each point from point loop.
+
+    for ( int index = 0; index < numPoints; index ++ )
+    {
+        snlVector projVect = projectToLine ( *axisStart, *axisEnd, points [ index ] );
+
+        if ( projVect.isNull() )
+            intersectsAxis = true;
+
+        snlPoint projPt = points [ index ] + projVect;
+
+        // See if projected point is new start or end axis point.
+
+        if ( snlVector ( newAxisStart, projPt ).dot ( axis ) < 0.0 )
+        {
+            newAxisStart = projPt;
+            axisStartRadius = projVect.length();
+        }
+
+        if ( snlVector ( newAxisEnd, projPt ).dot ( axis ) > 0.0 )
+        {
+            newAxisEnd = projPt;
+            axisEndRadius = projVect.length();
+        }
+
+        // *** Process angular bounds.
+
+        projVect *= -1.0;  // Make sure vector is pointing outwards from axis.
+
+        // Make sure base projection is not a null vector.
+        
+        if ( baseProj.isNull() && ! projVect.isNull() )
+            baseProj = projVect;
+
+        // Calculate absolute angle.
+
+        if ( ! projVect.isNull() )
+        {
+            curAbsAngle = baseProj.angle ( projVect );
+
+            // Check to see if angle went past 180 degrees.
+
+            snlVector crossProd;
+
+            crossProd.crossProduct ( baseProj, projVect );
+
+            if ( ! crossProd.isNull() && crossProd.dot ( axis ) < 0.0 )
+            {
+                // Adjust for negative angle.
+
+                curAbsAngle = M_PI * 2.0 - curAbsAngle;
+            }
+        }
+
+        if ( index > 0 )
+        {
+            snlVector crossProd;
+
+            crossProd.crossProduct ( lastProj, projVect );
+
+            if ( ! crossProd.isNull() )
+            {
+                // If normal vector ( cross product ) is opposite direction to axis then angle is negative.
+    
+                double angle = lastProj.angle ( projVect );
+    
+                if ( crossProd.dot ( axis ) > 0.0 )
+                {
+                    // Positive angle.
+                    accumAngle += angle;
+
+                    negTraverse = false;
+
+                    if ( accumAngle > maxAngle )
+                    
+                    {
+                        maxAngle = accumAngle;
+                        angleProjMax = projVect;
+                    }
+                }
+                else
+                {
+                    // Negative angle.
+                    accumAngle -= angle;
+
+                    negTraverse = true;
+
+                    if ( accumAngle < minAngle )
+                    {
+                        minAngle = accumAngle;
+                        angleProjMin = projVect;
+                    }
+                }
+
+                // Calculate which circle sections are used. Only used if axis is intersected.
+
+                int sectEnd;
+
+                if ( lastAbsAngle > curAbsAngle && ! negTraverse )
+                    sectEnd = 15;  // Account for going over 360 degrees during positive traversal.
+                else
+                    sectEnd = (int) ( curAbsAngle / circleSectSliceSize );
+
+                int sectStart = (int) ( lastAbsAngle / circleSectSliceSize );
+
+                if ( sectStart > sectEnd )
+                {
+                    int intermediate = sectStart;
+                    sectStart = sectEnd;
+                    sectEnd = intermediate;
+                }
+
+                for ( int sectIndex = sectStart; sectIndex <= sectEnd; sectIndex ++ )
+                    circleSect [ sectIndex ] = true;
+            }
+        }
+
+        lastProj = projVect;
+
+        lastAbsAngle = curAbsAngle;
+    }
+
+    double absAngle;  // Absolute angle.
+
+    // If axis was intersected then angular properties need to be recalculated.
+
+    if ( intersectsAxis )
+    {
+        // Find largest gap.
+
+        int startGap = -1;
+        int endGap;
+        int curGap = -1;  // Current maximum gap start index.
+        double gapAngle = 0.0;
+        double maxGap = 0.0;
+
+        bool overlapped = false;
+
+        for ( int sectIndex = 0; sectIndex < 16; sectIndex ++ )
+        {
+            if ( ! circleSect [ sectIndex ]  )
+            {
+                if ( startGap == -1 )
+                    startGap = sectIndex;
+
+                gapAngle += circleSectSliceSize;
+            }
+
+            if ( circleSect [ sectIndex ] && startGap != -1 )
+            {
+                if ( maxGap < gapAngle )
+                {
+                    maxGap = gapAngle;
+                    curGap = startGap;
+                }
+
+                startGap = -1;
+                endGap = sectIndex;  // Deliberately points to first non-gap index.
+                gapAngle = 0.0;
+            }
+
+            if ( startGap != -1 && sectIndex == 15  && ! overlapped )
+            {
+                // Gap overlaps starting point so start from beginning again.
+                sectIndex = 0;
+                overlapped = true;
+            }
+        }
+
+        // Calculate new cone parameters.
+        
+        absAngle = ( M_PI * 2.0 ) - maxGap;
+
+        double rotAngle = endGap * circleSectSliceSize;
+
+        snlTransform rotation;
+
+        rotation.rotate ( rotAngle, *axisStart, *axisEnd );
+
+        angleProjMin = baseProj;
+
+        rotation.transform ( angleProjMin );
+    }
+
+    else
+
+    {
+        absAngle = maxAngle - minAngle;  // minAngle must be below or equal to 0.
+
+        // Recalculate more exact absAngle from vectors but using previously calculated absAngle as guide.
+    
+        double angle = angleProjMax.angle ( angleProjMin );
+    
+        if ( absAngle > M_PI )
+            absAngle = ( M_PI * 2.0 ) - angle;
+        else
+            absAngle = angle;
+    }
+
+    // Cone bounds should have been calculated. Now build a cone.
+
+    // Convert absolute angle to degrees.
+
+    absAngle = ( absAngle / ( 2.0 * M_PI ) ) * 360;
+
+    if ( absAngle > 360.0 )
+        absAngle = 360.0;
+
+    snlVector axisProj = angleProjMin;
+    axisProj.length ( axisStartRadius );
+    snlPoint curveStart = newAxisStart + axisProj;
+    
+    axisProj = angleProjMin;
+    axisProj.length ( axisEndRadius );
+    snlPoint curveEnd = newAxisEnd + axisProj;
+
+    snlCurve generator ( 2, 3, curveStart, curveEnd );
+
+    genSurfRevolution ( generator, *axisStart, *axisEnd, absAngle );
+}
+
+void snlSurface::fitSphere ( snlPoint* points, int numPoints, snlPoint* sphereCentre )
+{
+    // Fit ( and generate ) a spherical surface to given points.
+    // ---------------------------------------------------------
+    // points:          Given points to fit sphere to.
+    // numPoints:       Number of points in points array.
+    // sphereCentre:    Centre of sphere.
+
+    // Use first and second points with sphere centre as basis plane.
+
+    snlVector baseVect ( *sphereCentre, points [ 0 ] );  // Basis vector associated with point 0. Stays constant.
+    snlVector refVect ( *sphereCentre, points [ 1 ] );  // Reference vector. Calculated per point other than point 0.
+
+    snlVector basisPlaneNormal;  // Used to define plane as basis for angle calculations.
+
+    basisPlaneNormal.crossProduct ( baseVect, refVect );
+
+    snlVector lonBasisNormal;  // Used as basis for determining rotation angle about basis plane normal. Longitude.
+
+    lonBasisNormal.crossProduct ( basisPlaneNormal, baseVect );
+
+    double latMin = M_PI / 2.0;  // Max and min latitude with basis plane normal being 0 degrees. Point 0 is at 90 degrees.
+    double latMax = latMin;
+
+    snlVector refNorm;  // Normal vector used for longitudinal angle determination.
+    snlVector angleCheckNorm;
+
+    double lonAngle = 0.0;  // "Running" longitudinal angle.
+    double lonMin = 0.0;  // Longitudinal bounds.
+    double lonMax = 0.0;
+
+    // Determine patch boundaries in terms of rotation angles.
+
+    for ( int ptIndex = 1; ptIndex < numPoints; ptIndex ++ )
+    {
+        // Calculate angle to basis plane normal. Latitude.
+        
+        refVect.calc ( *sphereCentre, points [ ptIndex ] );
+        
+        double normAngle = basisPlaneNormal.angle ( refVect );
+
+        if ( normAngle > latMax ) latMax = normAngle;
+        if ( normAngle < latMin ) latMin = normAngle;
+
+        // Calculate longitudinal angle delta.
+
+        refNorm.crossProduct ( basisPlaneNormal, refVect );
+
+        double relLonAngle = lonBasisNormal.angle ( refNorm );  // Relative angle.
+
+        // Adjust for movement in negative direction.
+
+        angleCheckNorm.crossProduct ( lonBasisNormal, refNorm );
+
+        if ( basisPlaneNormal.dot ( angleCheckNorm ) < 0.0 )
+            lonAngle -= relLonAngle;  // Angle is negative.
+        else
+            lonAngle += relLonAngle;
+            
+        if ( lonAngle > lonMax ) lonMax = lonAngle;
+        if ( lonAngle < lonMin ) lonMin = lonAngle;
+
+        lonBasisNormal = refNorm;
+    }
+
+    // Angle bounds have been determined. Create spherical patch.
+
+    // First create a circular arc.
+    
+    lonBasisNormal.crossProduct ( basisPlaneNormal, baseVect );
+
+    snlTransform latTransf;
+    snlTransform lonTransf;
+    
+    lonTransf.rotate ( lonMin, *sphereCentre, basisPlaneNormal );
+
+    snlPoint startPoint ( points [ 0 ] );
+
+    latTransf.rotate ( latMin - ( M_PI / 2.0 ), *sphereCentre, lonBasisNormal );
+
+    latTransf.transform ( startPoint );
+    lonTransf.transform ( startPoint );
+
+    latTransf.ident();
+    latTransf.rotate ( latMax - ( M_PI / 2.0 ), *sphereCentre, lonBasisNormal );
+    
+    snlPoint endPoint ( points [ 0 ] );
+
+    latTransf.transform ( endPoint );
+    lonTransf.transform ( endPoint );
+
+    snlCurve* generator = new snlCurve ( startPoint, endPoint, *sphereCentre );
+
+    // Create surface of revolution using arc.
+
+    snlPoint axisEnd = *sphereCentre + basisPlaneNormal;
+
+    double revAngleDeg = ( ( lonMax - lonMin ) / M_PI ) * 180.0;
+
+    genSurfRevolution ( *generator, *sphereCentre, axisEnd, revAngleDeg );
+
+    // Clean up.
+
+    delete generator;
+}
+
+
diff --git a/src/snlSurface_pointLoop.h b/src/snlSurface_pointLoop.h
new file mode 100644
index 0000000..292d784
--- /dev/null
+++ b/src/snlSurface_pointLoop.h
@@ -0,0 +1,22 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2003 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+
+// *** This file is only present for make to be happy ***
+
+// Point loop related definitions for snlSurface. ( snlSurface.cpp has been segmented to reduce size ).
+
+#include "snlSurface.h"
+#include "snlUtil.h"
+
+#include "snlNurbsCommon.h"
+
diff --git a/src/snlSurface_projection.cpp b/src/snlSurface_projection.cpp
new file mode 100644
index 0000000..319da33
--- /dev/null
+++ b/src/snlSurface_projection.cpp
@@ -0,0 +1,2243 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2006 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// snlSurface projection related functions.
+
+#include "snlSurface_projection.h"
+
+snlVertex* snlSurface::project_depr ( snlPoint* toProject, int numPoints, double convergTol,
+                                      double normTol, int maxPass )
+{
+    //! Project an array of points to surface.
+    //  --------------------------------------
+    //! @param toProject Array of snlPoints to project to surface.
+    //! @param numPoints Number of points being projected. ie size of points array.
+    //! @param convergTol If the difference between successive newton iterations converges and is
+    //!                   below this value then the point is taken to be projected.
+    //! @param normTol If the cosine of the angle between the projection vector and the
+    //!                projected points normal is under this value then the projection is used.
+    //! @param maxPass Maximum number of mesh refinement passes allowed. Stops infinite loops
+    //!                if projections don't converge.
+    //!
+    //! Notes: This function is now deprecated but stays around as a cross reference for
+    //!        new projection work.
+
+    sLocn* projections = new sLocn [ numPoints ];
+
+    ptrList < sLocn >* ambig = projPtSurf ( *this, toProject, numPoints, projections,
+                                            convergTol, normTol, maxPass );
+                                            
+    // Create vertexes to return and populate with converted sLocn's.
+    // -------------------------------------------------------------
+    
+    snlVertex* retVertexes = new snlVertex [ numPoints ];
+    
+    for ( int index = 0; index < numPoints; index ++ )
+    {
+        retVertexes [ index ] = projections [ index ].pt;
+        
+        retVertexes [ index ].evalParamU ( projections [ index ].paramT );
+        retVertexes [ index ].evalParamV ( projections [ index ].paramU );
+        
+        retVertexes [ index ].flag = projections [ index ].flag;
+    }
+                                    
+    delete ambig;
+    delete[] projections;
+
+    return retVertexes;
+}
+
+snlSurfLocn* snlSurface::invert ( snlPoint* toInvert, int numPoints, int* retArraySize,
+                                  double convergTol, double normTol, int maxPass )
+{
+    //! Find parametric location of given points
+    //  ----------------------------------------
+    //! @param toInvert Array of snlPoints to find on surface.
+    //! @param numPoints Number of points being inverted. ie size of points array.
+    //! @param retArraySize Size of array of surface locations that is returned.
+    //! @param convergTol If the difference between successive newton iterations converges and is
+    //!                   below this value then the point is taken to be inverted.
+    //! @param normTol How close to perpendicular the projection of the given point to the point
+    //!                found gets to the surface tangents at the found point for convergence to be
+    //!                successful.
+    //! @param maxPass Maximum number of refinement passes allowed. Stops infinite loops if
+    //!                projections don't converge.
+
+    // Generate point mask. If false, entry is not processed during pass.
+
+    bool* pointMask = new bool [ numPoints ];
+
+    for ( int index = 0; index < numPoints; index ++ )
+        pointMask [ index ] = true;
+
+    // Calculate best guess for each point to be inverted.
+
+    ptrList <snlSurfLocnGuess>* guesses = guessInvLocation ( toInvert, numPoints, pointMask,
+                                                             degU, degV );
+
+    delete[] pointMask;
+    
+    return processGuesses ( toInvert, numPoints, retArraySize, guesses, convergTol,
+                            normTol, maxPass );
+}
+
+snlSurfLocn* snlSurface::project ( snlPoint* toProject, int numPoints, int* retArraySize,
+                                   double convergTol, double normTol, int maxPass )
+{
+    //! Find parametric location of given points
+    //  ----------------------------------------
+    //! @param toProject Array of snlPoints to find projections of on surface.
+    //! @param numPoints Number of points being inverted. ie size of points array.
+    //! @param retArraySize Size of array of surface locations that is returned.
+    //! @param convergTol If the difference between successive newton iterations converges and is
+    //!                   below this value then the point is taken to be inverted.
+    //! @param normTol How close to perpendicular the projection of the given point to the point
+    //!                found gets to the surface tangents at the found point for convergence to be
+    //!                successful.
+    //! @param maxPass Maximum number of refinement passes allowed. Stops infinite loops if
+    //!                projections don't converge.
+
+    // Generate point mask. If false, entry is not processed during pass.
+
+    bool* pointMask = new bool [ numPoints ];
+
+    for ( int index = 0; index < numPoints; index ++ )
+        pointMask [ index ] = true;
+
+    // Only work on copy of surface.
+
+    snlSurface* tmpSurf = new snlSurface ( *this );
+
+    tmpSurf -> createConvexBezierSegments( 0, 0, 0.00009 );  // Sensitivity of 0.05 degrees.
+
+    // Calculate best guess for each point to be inverted.
+
+    ptrList <snlSurfLocnGuess>* guesses = tmpSurf -> guessProjLocation ( toProject, numPoints,
+                                                                         pointMask );
+
+    snlSurfLocn* retArray = processGuesses ( toProject, numPoints, retArraySize, guesses,
+                                             convergTol, normTol, maxPass, true );
+
+    // Clean up and return.
+    
+    delete[] pointMask;
+
+    delete tmpSurf;
+
+    return retArray;
+}
+
+snlSurfLocn* snlSurface::fastProject ( snlPoint* toProject, int numPoints, int* retArraySize,
+                                       double convergTol, double normTol, int maxPass,
+                                       int sensitivity, int maxLocns )
+{
+    //! Find parametric location of given points
+    //  ----------------------------------------
+    //! @param toProject Array of snlPoints to find projections of on surface.
+    //! @param numPoints Number of points being inverted. ie size of points array.
+    //! @param retArraySize Size of array of surface locations that is returned.
+    //! @param convergTol If the difference between successive newton iterations converges and is
+    //!                   below this value then the point is taken to be inverted.
+    //! @param normTol How close to perpendicular the projection of the given point to the point
+    //!                found gets to the surface tangents at the found point for convergence to be
+    //!                successful.
+    //! @param maxPass Maximum number of refinement passes allowed. Stops infinite loops if
+    //!                projections don't converge.
+    //! @param sensitivity The higher this number the more accurate the initial guess phase is but
+    //!                    the function becomes slower. Increases the number of subdivisions per
+    //!                    knot span by this amount. Must be a positive integer
+    //! @param maxLocns Maximum number of surface locations to return. If a surface is known to
+    //!                 have ambiguous areas then this number should be greater than one. Value is
+    //!                 clamped to be greater than zero.
+    //!
+    //! Notes: Fast project is not nearly as accurate as other project or invert functions.
+
+    ptrList <snlSurfLocnGuess>* guesses = guessFastProjLocation ( toProject, numPoints, maxLocns,
+                                                                  degU + sensitivity,
+                                                                  degV + sensitivity );
+
+    snlSurfLocn* retArray = processGuesses ( toProject, numPoints, retArraySize, guesses,
+                                             convergTol, normTol, maxPass, true, true );
+
+    return retArray;
+}
+
+snlSurfLocn* snlSurface::processGuesses ( snlPoint* points, int numPoints, int* retArraySize,
+                                          ptrList <snlSurfLocnGuess>* guesses, double convergTol,
+                                          double normTol, int maxPass, bool retNonConverged,
+                                          bool noCull, int numVelocity, int numNewton )
+{
+    //! Refine parametric location of given points
+    //  ------------------------------------------
+    //! @param points Array of snlPoints to find on surface.
+    //! @param numPoints Number of points being inverted. ie size of points array.
+    //! @param retArraySize Size of array of surface locations that is returned.
+    //! @param guesses Parametric location guesses for given points.
+    //! @param convergTol If the difference between successive newton iterations converges and is
+    //!                   below this value then the point is taken to be inverted.
+    //! @param normTol How close to perpendicular the projection of the given point to the point
+    //!                found gets to the surface tangents at the found point for convergence to be
+    //!                successful.
+    //! @param maxPass Maximum number of refinement passes allowed. Stops infinite loops if
+    //!                projections don't converge.
+    //! @param retNonConverged Return non converged points. Used for projection.
+    //! @param noCull Do not cull guesses that converge to the same point.
+    //! @param numVelocity Number of velocity iterations to perform per pass.
+    //! @param numNewton Number of newton iterations to perform per pass.
+    
+    // Converge to points using multiple passes if necessary.
+
+    for ( int pass = 0; pass < maxPass; pass ++ )
+    {
+        bool converged = convergeVelocity ( points, guesses, numVelocity, convergTol, normTol );
+
+        if ( ! converged )
+            converged = convergeNewton ( points, guesses, numNewton, convergTol, normTol );
+
+        // If all guesses have been culled for a given point then reprocess one of them.
+
+        snlSurfLocnGuess* guess = guesses -> first();
+
+        for ( int index = 0; index < numPoints; index ++ )
+        {
+            bool allCulled = true;
+
+            snlSurfLocnGuess* bestGuess = guess;
+
+            if ( guess )
+            {
+                while ( guess )
+                {
+    
+                    if ( guess -> origPtIndex != index ) break;
+    
+                    if ( ! guess -> culled )
+                        allCulled = false;
+                    else if ( guess -> dist < bestGuess -> dist )
+                        bestGuess = guess;
+
+                    guess = guesses -> next();
+                }
+    
+                if ( allCulled )
+                {
+                    // Force another pass to be performed.
+    
+                    bestGuess -> ignoreParamBounds = true;
+                    bestGuess -> culled = false;
+                    converged = false;
+                }
+            }
+        }
+    }
+
+    // Cull duplicate guesses that have converged to the same point. Keep best one.
+
+    snlSurfLocnGuess* guess = guesses -> first();
+
+    int pointIndex = -1;
+
+    ptrList <snlSurfLocnGuess> pointGuesses;
+
+    while ( guess && ! noCull )
+    {
+        if ( ! guess -> culled && ( guess -> converged || retNonConverged ) )
+        {
+            if ( guess -> origPtIndex != pointIndex )
+            {
+                // Move onto next point indexes guesses.
+                
+                pointIndex = guess -> origPtIndex;
+
+                pointGuesses.clear();
+
+                pointGuesses.append ( guess, false );
+            }
+            else
+            {
+                // Compare point with others found. Take best one.
+
+                snlSurfLocnGuess* ptGuess = pointGuesses.first();
+
+                while ( ptGuess )
+                {
+                    if ( ! ptGuess -> culled )
+                    {
+                        // If other guesses param bounds overlap current guess
+                        // then assume they have converged to the same point.
+
+                        if ( guess -> paramU >= ptGuess -> minU
+                             && guess -> paramU <= ptGuess -> maxU )
+                        {
+                            if ( guess -> paramV >= ptGuess -> minV
+                                 && guess -> paramV <= ptGuess -> maxV )
+                            {
+                                // Both guesses are in same param zone. Cull guess with largest
+                                // distance.
+
+                                if ( guess -> dist > ptGuess -> dist )
+                                    guess -> culled = true;
+                                else
+                                    ptGuess -> culled = true;
+                            }
+                        }
+                    }
+                
+                    ptGuess = pointGuesses.next();
+                }
+
+                pointGuesses.append ( guess, false );
+            }
+        }
+    
+        guess = guesses -> next();
+    }
+
+    // Assemble best found points.
+
+    // Get total number of points found.
+
+    int totalCount = 0;
+
+    guess = guesses -> first();
+
+    while ( guess )
+    {
+        if ( ! guess -> culled && ( guess -> converged || retNonConverged ) )
+            totalCount ++;
+
+        guess = guesses -> next();
+    }
+
+    snlSurfLocn* retLocns = new snlSurfLocn [ totalCount ];
+
+    int index = 0;
+
+    guess = guesses -> first();
+
+    while ( guess )
+    {
+        if ( ( retNonConverged || guess -> converged ) && ! guess -> culled )
+        {
+            retLocns [ index ].paramU = guess -> paramU;
+            retLocns [ index ].paramV = guess -> paramV;
+            retLocns [ index ].pt = guess -> pt;
+            retLocns [ index ].dist = sqrt ( guess -> dist );
+            retLocns [ index ].origPtIndex = guess -> origPtIndex;
+            retLocns [ index ].cos = guess -> cos;
+
+            index ++;
+        }
+
+        guess = guesses -> next();
+    }
+
+    // Clean up and return.
+    
+    delete guesses;
+
+    if ( retArraySize )
+        *retArraySize = totalCount;
+
+    return retLocns;
+}
+
+bool snlSurface::convergeVelocity ( snlPoint* convergToPts, ptrList <snlSurfLocnGuess>* guesses,
+                                    int numIterations, double convergTol, double normTol )
+{
+    //! Converge guesses to given points using velocity technique.
+    //  ----------------------------------------------------------
+    //! @param convergToPts Array of points that are to be converged to. ie Points to be found.
+    //! @param guesses List of current guesses to be converged.
+    //! @param numIterations Maximum number of convergence iterations to perform.
+    //! @param convergTol Maximum distance between point and guess allowed for convergence to be
+    //!                   successful.
+    //! @param normTol How close to perpendicular the projection of the given point to the guessed
+    //!                point gets to the surface tangents at the guessed point for convergence to be
+    //!                successful.
+
+    cout.precision ( 16 );
+    
+    double convTolSqrd = convergTol * convergTol;
+    
+    knot minU = knotVectU -> min();
+    knot maxU = knotVectU -> max();
+
+    knot minV = knotVectV -> min();
+    knot maxV = knotVectV -> max();
+    
+    snlSurfLocnGuess* guess = guesses -> first();
+
+    snlVector velocityU, velocityV;
+    snlPoint evalPoint;
+
+    bool converged = true;
+
+    while ( guess )
+    {
+        if ( ! guess -> converged && ! guess -> culled )
+        {
+            if ( guess -> dist >= convTolSqrd )
+            {
+                // Generate new guess.
+
+                snlSurfLocnGuess newGuess = *guess;
+
+                for ( int iteration = 0; iteration < numIterations; iteration ++ )
+                {
+                    // Get Initial velocities at guessed point
+
+                    if ( ! iteration )
+                        velocities ( newGuess.paramU, newGuess.paramV, evalPoint, velocityU,
+                                     velocityV );
+    
+                    // Calculate new parameters based on velocities.
+    
+                    snlVector guessToPt ( newGuess.pt, convergToPts [ newGuess.origPtIndex ] );
+    
+                    double lengthU = velocityU.length();
+
+                    if ( lengthU == 0.0 ) break;
+
+                    double distU = guessToPt.dot ( velocityU ) / lengthU;
+                    knot deltaU = distU / lengthU;
+
+                    double lengthV = velocityV.length();
+
+                    if ( lengthV == 0.0 ) break;
+                    
+                    double distV = guessToPt.dot ( velocityV ) / lengthV;
+                    knot deltaV =  distV / lengthV;
+
+                    knot newU = newGuess.paramU + deltaU;
+                    knot newV = newGuess.paramV + deltaV;
+
+                    if ( newU < minU ) newU = minU;
+                    if ( newU > maxU ) newU = maxU;
+    
+                    if ( newV < minV ) newV = minV;
+                    if ( newV > maxV ) newV = maxV;
+
+                    // If the distance travelled exceeds the required distance by more than 10% then
+                    // recalculate the param deltas.
+
+                    evalPoint = eval ( newU, newV );
+
+                    double newDist = evalPoint.distSqrd ( convergToPts [ newGuess.origPtIndex ] );
+                    
+
+                    int loopCount = -1;
+
+                    while ( newDist > newGuess.dist * 1.1 )
+                    {
+                        // Calculate new parameters based on actual distance travelled.
+                        
+                        loopCount ++;
+
+                        snlVector newGuessToPt ( newGuess.pt, evalPoint );
+
+                        double newDistU = newGuessToPt.dot ( velocityU ) / lengthU;
+                        double newDistV = newGuessToPt.dot ( velocityV ) / lengthV;
+
+                        if ( newDistU == 0.0 && newDistV == 0.0 )
+                            break;
+
+                        double loopAdj = 1.0 / (double) ( loopCount + 1 );
+
+                        knot newAdjustU;
+                        knot newAdjustV;
+
+                        if ( newDistU != 0.0 )
+                            newAdjustU = newGuess.paramU + ( ( distU * loopAdj / newDistU )
+                                         * ( newU - newGuess.paramU ) );
+                        else
+                            newAdjustU = newU;
+                        
+                        if ( newDistV != 0.0 )
+                            newAdjustV = newGuess.paramV + ( ( distV * loopAdj / newDistV )
+                                         * ( newV - newGuess.paramV ) );
+                        else
+                            newAdjustV = newV;
+
+                        // Do out of bounds check.
+
+                        if ( newAdjustU < minU ) newAdjustU = minU;
+                        if ( newAdjustU > maxU ) newAdjustU = maxU;
+        
+                        if ( newAdjustV < minV ) newAdjustV = minV;
+                        if ( newAdjustV > maxV ) newAdjustV = maxV;
+
+                        // Test for infinite loop.
+
+                        if ( newU == newAdjustU && newV == newAdjustV )
+                            break;
+
+                        newU = newAdjustU;
+                        newV = newAdjustV;
+
+                        // Re-evaluate parameters.
+                        
+                        evalPoint = eval ( newU, newV );
+
+                        newDist = evalPoint.distSqrd ( convergToPts [ newGuess.origPtIndex ] );
+                    }
+    
+                    // Store point and velocities that match new params.
+    
+                    velocities ( newU, newV, evalPoint, velocityU, velocityV );
+
+                    newGuess.paramU = newU;
+                    newGuess.paramV = newV;
+
+                    newGuess.pt = evalPoint;
+    
+                    newGuess.dist = evalPoint.distSqrd ( convergToPts [ newGuess.origPtIndex ] );
+
+                    // Test for guess going out of parametric bounds.
+
+                    if ( ! newGuess.ignoreParamBounds )
+                    {
+                        if ( newGuess.paramU < newGuess.minU || newGuess.paramU > newGuess.maxU )
+                        {
+                            guess -> culled = true;
+                            break;
+                        }
+        
+                        if ( newGuess.paramV < newGuess.minV || newGuess.paramV > newGuess.maxV )
+                        {
+                            guess -> culled = true;
+                            break;
+                        }
+                    }
+    
+                    // Test for convergence.
+    
+                    if ( newGuess.dist < convTolSqrd )
+                    {
+                        newGuess.converged = true;
+                        break;
+                    }
+
+                    snlVector projToSurf ( convergToPts [ newGuess.origPtIndex ], evalPoint );
+                    
+                    basis cosU = projToSurf.calcAbsCos ( velocityU );
+                    
+                    basis cosV = projToSurf.calcAbsCos ( velocityV );
+
+                    newGuess.cos = cosU > cosV ? cosU : cosV;
+
+                    if ( cosU <= normTol && cosV <= normTol )
+                    {
+                        newGuess.converged = true;
+                        break;
+                    }
+                }
+    
+                // If new guess is better than old and hasn't been culled then replace old guess.
+                
+                if ( ! guess -> culled )
+                {
+                    // If parameters haven't changed over all iterations then set as converged.
+                    
+                    if ( guess -> paramU == newGuess.paramU && guess -> paramV == newGuess.paramV )
+                        newGuess.converged = true;
+                   
+                    *guess = newGuess;
+                }
+            }
+            else
+                guess -> converged = true;
+
+            // Test for guess going out of parametric bounds.
+            if ( ! guess -> ignoreParamBounds && guess -> culled )
+            {
+                if ( guess -> paramU < guess -> minU || guess -> paramU > guess -> maxU )
+                    guess -> culled = true;
+    
+                if ( guess -> paramV < guess -> minV || guess -> paramV > guess -> maxV )
+                    guess -> culled = true;
+            }
+        }
+
+        if ( ! guess -> converged && ! guess -> culled ) converged = false;
+
+        guess = guesses -> next();
+    }
+    
+    return converged;
+}
+
+bool snlSurface::convergeNewton ( snlPoint* convergToPts, ptrList <snlSurfLocnGuess>* guesses,
+                                  int numIterations, double convergTol, double normTol )
+{
+    //! Converge guesses to given points using Newton iteration
+    //  -------------------------------------------------------
+    //! @param convergToPts Array of points that are to be converged to. ie Points to be found.
+    //! @param guesses List of current guesses to be converged.
+    //! @param numIterations Maximum number of convergence iterations to perform.
+    //! @param convergTol Maximum distance between point and guess allowed for convergence to be
+    //!                   successful.
+    //! @param normTol Cosine of angle between normal to surface at guess and projection from
+    //!                guess to given point. If cosine is below this angle then iterations stop.
+
+    double convTolSqrd = convergTol * convergTol;
+    
+    knot minU = knotVectU -> min();
+    knot maxU = knotVectU -> max();
+
+    knot minV = knotVectV -> min();
+    knot maxV = knotVectV -> max();
+
+    snlSurfLocnGuess* guess = guesses -> first();
+
+    snlPoint* derivs;
+
+    bool converged = true;
+
+    while ( guess )
+    {
+        derivs = 0;
+
+        if ( ! guess -> converged && ! guess -> culled )
+        {
+            snlSurfLocnGuess newGuess = *guess;
+
+            if ( guess -> dist >= convTolSqrd )
+            {
+                for ( int iteration = 0; iteration < numIterations; iteration ++ )
+                {
+                    // Get 1st and 2nd derivatives.
+
+                    if ( ! derivs )
+                        derivs = evalDerivs ( newGuess.paramU, newGuess.paramV, 2, 2 );
+
+                    // Generate next Newton approximation.
+
+                    knot deltaU, deltaV;
+                    
+                    if ( degU > 1 && degV > 1 )
+                    {
+                        if ( ! newtonIterStepSurf ( derivs, convergToPts + newGuess.origPtIndex,
+                                                    &deltaU, &deltaV ) )
+                            break;  // Param deltas would have gone to infinity.
+                    }
+                    else
+                    {
+                        // At least one of the degrees is 1.
+
+                        snlPoint uDerivs [ 3 ];
+
+                        uDerivs [ 0 ] = derivs [ 0 ];
+                        uDerivs [ 1 ] = derivs [ 3 ];
+                        uDerivs [ 2 ] = derivs [ 6 ];
+
+                        if ( degU == 1 )
+                        {
+                            if ( ! lineIterStepCurve ( uDerivs, convergToPts + newGuess.origPtIndex,
+                                                       &deltaU ) )
+                                break;
+                        }
+                        else
+                        {
+                            if ( ! newtonIterStepCurve ( uDerivs,
+                                                         convergToPts + newGuess.origPtIndex,
+                                                         &deltaU ) )
+                                break;
+                        }
+
+                        if ( degV == 1 )
+                        {
+                            if ( ! lineIterStepCurve ( derivs, convergToPts + newGuess.origPtIndex,
+                                                       &deltaV ) )
+                                break;
+                        }
+                        else
+                        {
+                            if ( ! newtonIterStepCurve ( derivs, convergToPts +
+                                                         newGuess.origPtIndex, &deltaV ) )
+                                break;
+                        }
+                    }
+
+                    // Calcualte and clamp new parameters.
+
+                    knot newU = newGuess.paramU + deltaU;
+                    knot newV = newGuess.paramV + deltaV;
+
+                    if ( newU < minU ) newU = minU;
+                    if ( newU > maxU ) newU = maxU;
+    
+                    if ( newV < minV ) newV = minV;
+                    if ( newV > maxV ) newV = maxV;
+
+                    // If parameters haven't changed between iterations then guess has converged.
+
+                    if ( newU == newGuess.paramU && newV == newGuess.paramV )
+                    {
+                        newGuess.converged = true;
+                        break;
+                    }
+                    
+                    newGuess.paramU = newU;
+                    newGuess.paramV = newV;
+
+                    // Evaluate new parameters.
+
+                    delete[] derivs;
+                    derivs = evalDerivs ( newGuess.paramU, newGuess.paramV, 2, 2 );
+
+                    newGuess.pt = derivs [ 0 ];
+    
+                    newGuess.dist = derivs [ 0 ].distSqrd ( convergToPts [ newGuess.origPtIndex ] );
+
+                    // Check for distance and cosine tolerances.
+
+                    if ( newGuess.dist < convTolSqrd )
+                    {
+                        newGuess.converged = true;
+                        break;
+                    }
+
+                    snlVector velocityU ( derivs [ 3 ] );
+                    snlVector velocityV ( derivs [ 1 ] );
+                    
+                    snlVector projToSurf ( convergToPts [ newGuess.origPtIndex ], newGuess.pt );
+                    
+                    basis cosU = projToSurf.calcAbsCos ( velocityU );
+                    
+                    basis cosV = projToSurf.calcAbsCos ( velocityV );
+
+                    newGuess.cos = cosU > cosV ? cosU : cosV;
+
+                    if ( cosU <= normTol && cosV <= normTol )
+                    {
+                        newGuess.converged = true;
+                        break;
+                    }
+                }
+            }
+            else
+                guess -> converged = true;
+
+            if ( ! guess -> culled && newGuess.dist < guess -> dist )
+                *guess = newGuess;
+        }
+
+        // Test for guess going out of parametric bounds.
+        
+        if ( ! guess -> ignoreParamBounds && ! guess -> culled )
+        {
+            if ( guess -> paramU < guess -> minU || guess -> paramU > guess -> maxU )
+                guess -> culled = true;
+    
+            if ( guess -> paramV < guess -> minV || guess -> paramV > guess -> maxV )
+                guess -> culled = true;
+        }
+
+        if ( ! guess -> converged && ! guess -> culled ) converged = false;
+
+        guess = guesses -> next();
+
+        // Clean up.
+
+        if ( derivs ) delete[] derivs;
+    }
+
+    return converged;
+}
+
+ptrList <snlSurfLocnGuess>* snlSurface::guessInvLocation ( snlPoint* points, int numPoints,
+                                                           bool* pointMask, int granU,
+                                                           int granV )
+{
+    //! Guess parametric location of given points.
+    //  ------------------------------------------
+    //! @param points Array of points to find matches with.
+    //! @param numPoints Number of points in array.
+    //! @param pointMask Array specifying which points to process. Corresponding index to points
+    //!                  array. Must be true to process.
+    //! @param granU Granularity of each span in U direction.
+    //! @param granV Granularity of each span in V direction.
+    //!
+    //! @return Array of surface location guess structs. Caller owns this array.
+    //!
+    //! Notes:       This function will return one guess per parametric span.
+
+    int     index;
+
+    int numSpansU = knotVectU -> getNumSpans();
+    int numSpansV = knotVectV -> getNumSpans();
+
+    int numEvalU = granU * numSpansU + 1;
+    int numEvalV = granV * numSpansV + 1;
+
+    // Pre-calculate parametric positions.
+
+    knot* paramU = new knot [ numEvalU ];
+    int* spanU = new int [ numEvalU ];
+
+    knot* paramV = new knot [ numEvalV ];
+    int* spanV = new int [ numEvalV ];
+
+    // U direction.
+
+    int cSpan = knotVectU -> getFirstSpan();
+
+    knot paramStart = knotVectU -> val ( cSpan );
+
+    cSpan = knotVectU -> getNextSpan ( cSpan );
+
+    knot paramEnd;
+
+    if ( cSpan )
+        paramEnd = knotVectU -> val ( cSpan );
+    else
+        paramEnd = knotVectU -> max();
+     
+
+    int numSteps;
+
+    int paramIndex = 0;
+
+    basis param;
+
+    for ( int span = 0; span < numSpansU; span ++ )
+    {
+        basis paramStep = ( paramEnd - paramStart ) / (double) granU;
+
+        param = paramStart;
+
+        if ( span )
+        {
+            numSteps = granU;
+            param += paramStep;
+        }
+        else
+        {
+            numSteps = granU + 1;
+        }
+    
+        // Generate params for span.
+        
+        for ( index = 0; index < numSteps; index ++ )
+        {
+            paramU [ paramIndex ] = param;
+
+            spanU [ paramIndex ++ ] = span;
+    
+            param += paramStep;
+    
+            if ( param > paramEnd ) param = paramEnd;  // Round off error trap.
+        }
+
+        paramStart = paramEnd;
+
+        cSpan = knotVectU -> getNextSpan ( cSpan );
+
+        if ( cSpan )
+            paramEnd = knotVectU -> val ( cSpan );
+        else
+            paramEnd = knotVectU -> max();
+    }
+
+    // V direction.
+    
+    cSpan = knotVectV -> getFirstSpan();
+
+    paramStart = knotVectV -> val ( cSpan );
+
+    cSpan = knotVectV -> getNextSpan ( cSpan );
+
+    if ( cSpan )
+        paramEnd = knotVectV -> val ( cSpan );
+    else
+        paramEnd = knotVectV -> max();
+
+    paramIndex = 0;
+
+    for ( int span = 0; span < numSpansV; span ++ )
+    {
+        basis paramStep = ( paramEnd - paramStart ) / (double) granV;
+
+        param = paramStart;
+
+        if ( span )
+        {
+            numSteps = granV;
+            param += paramStep;
+        }
+        else
+        {
+            numSteps = granV + 1;
+        }
+    
+        for ( index = 0; index < numSteps; index ++ )
+        {
+            paramV [ paramIndex ] = param;
+
+            spanV [ paramIndex ++ ] = span;
+    
+            param += paramStep;
+    
+            if ( param > paramEnd ) param = paramEnd;  // Round off error trap.
+        }
+
+        paramStart = paramEnd;
+
+        cSpan = knotVectV -> getNextSpan ( cSpan );
+
+        if ( cSpan )
+            paramEnd = knotVectV -> val ( cSpan );
+        else
+            paramEnd = knotVectV -> max();
+    }
+
+    // Pre-evaluate basis functions.
+
+    basis** basisU = new basis* [ numEvalU ];
+    basis** basisV = new basis* [ numEvalV ];
+
+    for ( index = 0; index < numEvalU; index ++ )
+        basisU [ index ] = knotVectU -> evalBasis ( paramU [ index ] );
+
+    for ( index = 0; index < numEvalV; index ++ )
+        basisV [ index ] = knotVectV -> evalBasis ( paramV [ index ] );
+
+    // Evaluate surface points and vectors.
+
+    int numEvalPts = numEvalU * numEvalV;
+
+    snlPoint* evalPts = new snlPoint [ numEvalPts ];
+
+    index = 0;
+
+    for ( int indexU = 0; indexU < numEvalU; indexU ++ )
+    {
+        for ( int indexV = 0; indexV < numEvalV; indexV ++ )
+        {
+            evalPts [ index ] = eval ( paramU [ indexU ], paramV [ indexV ], basisU [ indexU ],
+                                       basisV [ indexV ] );
+                          
+            index ++;
+        }
+    }
+
+    // Calculate bounding distances. They are used for culling improbable guesses.
+
+    double* boundDist = new double [ numEvalPts ];
+
+    index = 0;
+
+    for ( int indexU = 0; indexU < numEvalU; indexU ++ )
+    {
+        for ( int indexV = 0; indexV < numEvalV; indexV ++ )
+        {
+            double maxDist = 0;
+            double dist;
+            
+            if ( indexU > 0 )
+            {
+                // U before current index.
+
+                if ( indexV > 0 )
+                {
+                    dist = evalPts [ index - numEvalV - 1 ].distSqrd ( evalPts [ index ] );
+                    if ( dist > maxDist ) maxDist = dist;
+                }
+
+                if ( indexV < numEvalV - 1 )
+                {
+                    dist = evalPts [ index - numEvalV + 1 ].distSqrd ( evalPts [ index ] );
+                    if ( dist > maxDist ) maxDist = dist;
+                }
+            }
+
+            if ( indexU < numEvalU - 1 )
+            {
+                // U after current index.
+
+                if ( indexV > 0 )
+                {
+                    dist = evalPts [ index + numEvalV - 1 ].distSqrd ( evalPts [ index ] );
+                    if ( dist > maxDist ) maxDist = dist;
+                }
+
+                if ( indexV < numEvalV - 1 )
+                {
+                    dist = evalPts [ index + numEvalV + 1 ].distSqrd ( evalPts [ index ] );
+                    if ( dist > maxDist ) maxDist = dist;
+                }
+            }
+
+            boundDist [ index ] = maxDist;
+
+            index ++;
+        }
+    }
+
+    // Compare given points to evaluated points. One entry per span.
+
+    int numSpans = numSpansU * numSpansV;
+
+    int numSpanPoints = numPoints * numSpans;
+
+    // Two dimensional arrays [ given point index ] [ span index ].
+    
+    int* uIndexes = new int [ numSpanPoints ];  // NOT control point indexes.
+    int* vIndexes = new int [ numSpanPoints ];
+    double* distances = new double [ numSpanPoints ];
+    bool* populated = new bool [ numSpanPoints ];  // If false distances have not been populated.
+
+    for ( index = 0; index < numSpanPoints; index ++ )
+        populated [ index ] = false;
+
+    double distSqrd;
+
+    int numToReturn = 0;
+
+    index = 0;
+
+    for ( int indexU = 0; indexU < numEvalU; indexU ++ )
+    {
+        for ( int indexV = 0; indexV < numEvalV; indexV ++ )
+        {
+            int spanIndex = spanU [ indexU ] * numSpansV + spanV [ indexV ];
+
+            for ( int ptIndex = 0; ptIndex < numPoints; ptIndex ++ )
+            {
+                if ( pointMask [ ptIndex ] )
+                {
+                    int ptIndexOffset = ptIndex * numSpans;
+                    
+                    distSqrd = evalPts [ index ].distSqrd ( points [ ptIndex ] );
+
+                    // Only process span point if distance is within probable bounds.
+
+                    if ( distSqrd < boundDist [ index ] )
+                    {
+                        if ( distances [ ptIndexOffset + spanIndex ] > distSqrd
+                             || ! populated [ ptIndexOffset + spanIndex ])
+                        {
+                            if ( ! populated [ ptIndexOffset + spanIndex ] )
+                            {
+                                numToReturn ++;
+                                populated [ ptIndexOffset + spanIndex ] = true;
+                            }
+                            
+                            distances [ ptIndexOffset + spanIndex ] = distSqrd;
+                            uIndexes [ ptIndexOffset + spanIndex ] = indexU;
+                            vIndexes [ ptIndexOffset + spanIndex ] = indexV;
+                        }
+                    }
+                }
+            }
+
+            index ++;
+        }
+    }
+
+    // Build array of surface locations to return.
+
+    ptrList <snlSurfLocnGuess>* retList = new ptrList <snlSurfLocnGuess>;
+
+    int indexU, indexV;
+
+    index = 0;
+
+    for ( int ptIndex = 0; ptIndex < numPoints; ptIndex ++ )
+    {
+        for ( int spanIndex = 0; spanIndex < numSpans; spanIndex ++ )
+        {
+            if ( populated [ index ] )
+            {
+                snlSurfLocnGuess* guessLocn = new snlSurfLocnGuess;
+                
+                indexU = uIndexes [ index ];
+                indexV = vIndexes [ index ];
+    
+                guessLocn -> paramU = paramU [ indexU ];
+                guessLocn -> paramV = paramV [ indexV ];
+    
+                guessLocn -> pt = evalPts [ indexU * numEvalV + indexV ];
+
+                guessLocn -> dist = distances [ index ];
+
+                guessLocn -> origPtIndex = ptIndex;
+
+                guessLocn -> spanNumber = spanIndex;
+
+                if ( indexU > 0 )
+                    guessLocn -> minU = paramU [ indexU - 1 ];
+                else
+                    guessLocn -> minU = paramU [ indexU ];
+
+                if ( indexU < numEvalU - 1 )
+                    guessLocn -> maxU = paramU [ indexU + 1 ];
+                else
+                    guessLocn -> maxU = paramU [ indexU ];
+
+                if ( indexV > 0 )
+                    guessLocn -> minV = paramV [ indexV - 1 ];
+                else
+                    guessLocn -> minV = paramV [ indexV ];
+                    
+                if ( indexV < numEvalV -1 )
+                    guessLocn -> maxV = paramV [ indexV + 1 ];
+                else
+                    guessLocn -> maxV = paramV [ indexV ];
+
+                guessLocn -> culled = false;
+                guessLocn -> ignoreParamBounds = false;
+                guessLocn -> converged = false;
+
+                retList -> append ( guessLocn, true );
+            }
+
+            index ++;
+        }
+    }
+
+    // Clean up
+
+    delete[] uIndexes;
+    delete[] vIndexes;
+    delete[] distances;
+    delete[] populated;
+
+    delete[] evalPts;
+    delete[] boundDist;
+
+    delete[] paramU;
+    delete[] paramV;
+
+    delete[] spanU;
+    delete[] spanV;
+
+    for ( int index = 0; index < numEvalU; index ++ )
+        delete[] basisU [ index ];
+
+    for ( int index = 0; index < numEvalV; index ++ )
+        delete[] basisV [ index ];
+
+    delete[] basisU;
+    delete[] basisV;
+
+    return retList;
+}
+
+ptrList <snlSurfLocnGuess>* snlSurface::guessProjLocation ( snlPoint* points, int numPoints,
+                                                            bool* pointMask )
+{
+    //! Guess parametric location of given points.
+    //  ------------------------------------------
+    //! @param points Array of points to find matches with.
+    //! @param numPoints Number of points in array.
+    //! @param pointMask Array specifying which points to process. Corresponding index to points
+    //!                  array. Must be true to process.
+    //!
+    //! @return List of surface location guess structs. Caller owns this list.
+    //!
+    //! @par Notes: Function expects all spans to be convex Bezier segments. It will _not_ work
+    //!      if this is not so.
+
+    int     index;
+
+    int numSpansU = knotVectU -> getNumSpans();
+    int numSpansV = knotVectV -> getNumSpans();
+
+    int numEvalU = numSpansU + 1;
+    int numEvalV = numSpansV + 1;
+
+    // Pre-calculate parametric positions.
+
+    knot* paramU = new knot [ numEvalU ];
+
+    knot* paramV = new knot [ numEvalV ];
+
+    // U Direction.
+
+    int cSpan = knotVectU -> getFirstSpan();
+
+    for ( int evalIndex = 0; evalIndex < numEvalU - 1; evalIndex ++ )
+    {
+        paramU [ evalIndex ] = knotVectU -> val ( cSpan );
+
+        cSpan = knotVectU -> getNextSpan ( cSpan );
+    }
+
+    paramU [ numEvalU - 1 ] = knotVectU -> max();
+
+    // V Direction.
+
+    cSpan = knotVectV -> getFirstSpan();
+
+    for ( int evalIndex = 0; evalIndex < numEvalV - 1; evalIndex ++ )
+    {
+        paramV [ evalIndex ] = knotVectV -> val ( cSpan );
+
+        cSpan = knotVectV -> getNextSpan ( cSpan );
+    }
+
+    paramV [ numEvalV - 1 ] = knotVectV -> max();
+
+    // Evaluate surface points and velocities.
+    // Because the surface is segmented into Bezier segements the segment
+    // corners are the control points and the velocities are calculated
+    // directly from the control points without the need for basis functions.
+    
+    int numEvalPts = numEvalU * numEvalV;
+
+    snlPoint* evalPts = new snlPoint [ numEvalPts ];
+
+    snlVector* edgeNormalU = new snlVector [ 4 ];
+    snlVector* edgeNormalV = new snlVector [ 4 ];
+
+    bool* hasGuess = new bool [ numPoints ];
+    snlSurfLocnGuess** lastGuess = new snlSurfLocnGuess* [ numPoints ];
+
+    for ( int ptIndex = 0; ptIndex < numPoints; ptIndex ++ )
+        hasGuess [ ptIndex ] = false;
+
+    index = 0;
+    
+    int ctrlPtIndex;
+
+    const snlCtrlPoint* ctrlPts = ctrlPtNet -> getCtrlPts();
+
+    int vSize = sizeV();
+
+    for ( int indexU = 0; indexU < numEvalU; indexU ++ )
+    {
+        ctrlPtIndex = degU * indexU * vSize;
+        
+        for ( int indexV = 0; indexV < numEvalV; indexV ++ )
+        {
+            evalPts [ index ] = ctrlPts [ ctrlPtIndex ];
+            
+            index ++;
+
+            ctrlPtIndex += degV;
+        }
+    }
+
+    ptrList <snlSurfLocnGuess>* tmpList = new ptrList <snlSurfLocnGuess>;  // List of out of order
+                                                                           // guesses.
+
+    int spanEvalIndex = 0;
+
+    int spanNum = 0;
+
+    for ( int spanU = 0; spanU < numSpansU; spanU ++ )
+    {
+        for ( int spanV = 0; spanV < numSpansV; spanV ++ )
+        {
+            // Calculate eight edge normals per segment. 4 per parametric direction.
+            //
+            // Edge normal orientation per segment:
+            // 
+            // 1 ---- 2 --- V
+            // |      |
+            // 3 ---- 4
+            // |
+            // U
+
+            snlVector velocityU;
+            snlVector velocityV;
+            snlVector normal;
+            snlVector edge;
+
+            int baseIndex = spanU * degU * vSize + spanV * degV;
+
+            // Calculate and orient first set of edge normals
+
+            velocityU.calc ( ctrlPts [ baseIndex ], ctrlPts [ baseIndex + vSize ] );
+            velocityV.calc ( ctrlPts [ baseIndex ], ctrlPts [ baseIndex + 1 ] );
+            
+            normal.crossProduct ( velocityU, velocityV );
+
+            edge.calc ( evalPts [ spanEvalIndex ], evalPts [ spanEvalIndex + 1 ] );
+            edgeNormalU [ 0 ].crossProduct ( normal, edge );
+
+            snlVector orient ( evalPts [ spanEvalIndex ], evalPts [ spanEvalIndex + numEvalV ] );
+            if ( edgeNormalU [ 0 ].dot ( orient ) < 0.0 ) edgeNormalU [ 0 ] *= - 1.0;
+
+            if ( edgeNormalU [ 0 ].dot ( velocityV ) < 0.0 )
+            {
+                // If velocity vectors are outside of edge then use them for edge normal calculation
+                // instead.
+                
+                edgeNormalU [ 0 ].crossProduct ( normal, velocityV );
+                if ( edgeNormalU [ 0 ].dot ( orient ) < 0.0 ) edgeNormalU [ 0 ] *= - 1.0;
+            }
+
+            edge.calc ( evalPts [ spanEvalIndex ], evalPts [ spanEvalIndex + numEvalV ] );
+            edgeNormalV [ 0 ].crossProduct ( normal, edge );
+
+            orient.calc ( evalPts [ spanEvalIndex ], evalPts [ spanEvalIndex + 1 ] );
+            if ( edgeNormalV [ 0 ].dot ( orient ) < 0.0 ) edgeNormalV [ 0 ] *= - 1.0;
+
+            if ( edgeNormalV [ 0 ].dot ( velocityU ) < 0.0 )
+            {
+                edgeNormalV [ 0 ].crossProduct ( normal, velocityU );
+                if ( edgeNormalV [ 0 ].dot ( orient ) < 0.0 ) edgeNormalV [ 0 ] *= - 1.0;
+            }
+
+            // Calculate and orient second set of edge normals
+
+            velocityU.calc ( ctrlPts [ baseIndex + degV ], ctrlPts [ baseIndex + degV + vSize ] );
+            velocityV.calc ( ctrlPts [ baseIndex + degV ], ctrlPts [ baseIndex + degV - 1 ] );
+
+            normal.crossProduct ( velocityU, velocityV );
+
+            edge.calc ( evalPts [ spanEvalIndex + 1 ], evalPts [ spanEvalIndex ] );
+            edgeNormalU [ 1 ].crossProduct ( normal, edge );
+
+            orient.calc ( evalPts [ spanEvalIndex + 1 ], evalPts [ spanEvalIndex + numEvalV + 1 ] );
+            if ( edgeNormalU [ 1 ].dot ( orient ) < 0.0 ) edgeNormalU [ 1 ] *= - 1.0;
+
+            if ( edgeNormalU [ 1 ].dot ( velocityV ) < 0.0 )
+            {
+                edgeNormalU [ 1 ].crossProduct ( normal, velocityV );
+                if ( edgeNormalU [ 1 ].dot ( orient ) < 0.0 ) edgeNormalU [ 1 ] *= - 1.0;
+            }
+            
+            edge.calc ( evalPts [ spanEvalIndex + 1 ], evalPts [ spanEvalIndex + numEvalV + 1] );
+            edgeNormalV [ 1 ].crossProduct ( normal, edge );
+
+            orient.calc ( evalPts [ spanEvalIndex + 1 ], evalPts [ spanEvalIndex ] );
+            if ( edgeNormalV [ 1 ].dot ( orient ) < 0.0 ) edgeNormalV [ 1 ] *= - 1.0;
+
+            if ( edgeNormalV [ 1 ].dot ( velocityU ) < 0.0 )
+            {
+                edgeNormalV [ 1 ].crossProduct ( normal, velocityU );
+                if ( edgeNormalV [ 1 ].dot ( orient ) < 0.0 ) edgeNormalV [ 1 ] *= - 1.0;
+            }
+
+            baseIndex += degU * vSize;
+
+            // Calculate and orient third set of edge normals
+
+            velocityU.calc ( ctrlPts [ baseIndex ], ctrlPts [ baseIndex - vSize ] );
+            velocityV.calc ( ctrlPts [ baseIndex ], ctrlPts [ baseIndex + 1 ] );
+
+            normal.crossProduct ( velocityU, velocityV );
+
+            edge.calc ( evalPts [ spanEvalIndex + numEvalV ],
+                        evalPts [ spanEvalIndex + numEvalV + 1 ] );
+
+            edgeNormalU [ 2 ].crossProduct ( normal, edge );
+
+            orient.calc ( evalPts [ spanEvalIndex + numEvalV ], evalPts [ spanEvalIndex ] );
+
+            if ( edgeNormalU [ 2 ].dot ( orient ) < 0.0 ) edgeNormalU [ 2 ] *= - 1.0;
+
+            if ( edgeNormalU [ 2 ].dot ( velocityV ) < 0.0 )
+            {
+                edgeNormalU [ 2 ].crossProduct ( normal, velocityV );
+                if ( edgeNormalU [ 2 ].dot ( orient ) < 0.0 ) edgeNormalU [ 2 ] *= - 1.0;
+            }
+
+            edge.calc ( evalPts [ spanEvalIndex + numEvalV ], evalPts [ spanEvalIndex ] );
+            edgeNormalV [ 2 ].crossProduct ( normal, edge );
+
+            orient.calc ( evalPts [ spanEvalIndex + numEvalV ],
+                          evalPts [ spanEvalIndex + numEvalV + 1 ] );
+
+            if ( edgeNormalV [ 2 ].dot ( orient ) < 0.0 ) edgeNormalV [ 2 ] *= - 1.0;
+
+            if ( edgeNormalV [ 2 ].dot ( velocityU ) < 0.0 )
+            {
+                edgeNormalV [ 2 ].crossProduct ( normal, velocityU );
+                if ( edgeNormalV [ 2 ].dot ( orient ) < 0.0 ) edgeNormalV [ 2 ] *= - 1.0;
+            }
+
+            // Calculate and orient fourth set of edge normals
+
+            velocityU.calc ( ctrlPts [ baseIndex + degV ], ctrlPts [ baseIndex + degV - vSize ] );
+            velocityV.calc ( ctrlPts [ baseIndex + degV ], ctrlPts [ baseIndex + degV - 1 ] );
+
+            normal.crossProduct ( velocityU, velocityV );
+
+            edge.calc ( evalPts [ spanEvalIndex + numEvalV + 1 ],
+                        evalPts [ spanEvalIndex + numEvalV ] );
+
+            edgeNormalU [ 3 ].crossProduct ( normal, edge );
+
+            orient.calc ( evalPts [ spanEvalIndex + numEvalV + 1 ], evalPts [ spanEvalIndex + 1 ] );
+
+            if ( edgeNormalU [ 3 ].dot ( orient ) < 0.0 ) edgeNormalU [ 3 ] *= - 1.0;
+
+            if ( edgeNormalU [ 3 ].dot ( velocityV ) < 0.0 )
+            {
+                edgeNormalU [ 3 ].crossProduct ( normal, velocityV );
+                if ( edgeNormalU [ 3 ].dot ( orient ) < 0.0 ) edgeNormalU [ 3 ] *= - 1.0;
+            }
+
+            edge.calc ( evalPts [ spanEvalIndex + numEvalV + 1 ], evalPts [ spanEvalIndex + 1 ] );
+            edgeNormalV [ 3 ].crossProduct ( normal, edge );
+
+            orient.calc ( evalPts [ spanEvalIndex + numEvalV + 1 ],
+                          evalPts [ spanEvalIndex + numEvalV ] );
+
+            if ( edgeNormalV [ 3 ].dot ( orient ) < 0.0 ) edgeNormalV [ 3 ] *= - 1.0;
+
+            if ( edgeNormalV [ 3 ].dot ( velocityU ) < 0.0 )
+            {
+                edgeNormalV [ 3 ].crossProduct ( normal, velocityU );
+                if ( edgeNormalV [ 3 ].dot ( orient ) < 0.0 ) edgeNormalV [ 3 ] *= - 1.0;
+            }
+
+            // Step through each given point and check to see if it is probable that it belongs
+            // to this patch. If it is, create a guess for it.
+
+            knot minU = paramU [ spanU ];
+            knot maxU = paramU [ spanU + 1 ];
+            knot minV = paramV [ spanV ];
+            knot maxV = paramV [ spanV + 1 ];
+
+            for ( int ptIndex = 0; ptIndex < numPoints; ptIndex ++ )
+            {
+                bool withinEdge1_2 = false;
+                bool withinEdge2_4 = false;
+                bool withinEdge4_3 = false;
+                bool withinEdge3_1 = false;
+            
+                // Corner 1.
+                
+                orient.calc ( evalPts [ spanEvalIndex ], points [ ptIndex ] );
+                
+                if ( orient.dot ( edgeNormalU [ 0 ] ) >= 0.0 )
+                    withinEdge1_2 = true;
+
+                if ( orient.dot ( edgeNormalV [ 0 ] ) >= 0.0 )
+                    withinEdge3_1 = true;
+
+                // Corner 2.
+
+                orient.calc ( evalPts [ spanEvalIndex + 1 ], points [ ptIndex ] );
+
+                if ( orient.dot ( edgeNormalU [ 1 ] ) >= 0.0 )
+                    withinEdge1_2 = true;
+
+                if ( orient.dot ( edgeNormalV [ 1 ] ) >= 0.0 )
+                    withinEdge2_4 = true;
+
+                // Corner 3.
+
+                orient.calc ( evalPts [ spanEvalIndex + numEvalV ], points [ ptIndex ] );
+
+                if ( orient.dot ( edgeNormalU [ 2 ] ) >= 0.0 )
+                    withinEdge4_3 = true;
+                
+                if ( orient.dot ( edgeNormalV [ 2 ] ) >= 0.0 )
+                    withinEdge3_1 = true;
+
+                // Corner 4.
+                    
+                orient.calc ( evalPts [ spanEvalIndex + numEvalV + 1 ], points [ ptIndex ] );
+
+                if ( orient.dot ( edgeNormalU [ 3 ] ) >= 0.0 )
+                    withinEdge4_3 = true;
+                
+                if ( orient.dot ( edgeNormalV [ 3 ] ) >= 0.0 )
+                    withinEdge2_4 = true;
+
+                // If point is within all edges then a guess needs to be created.
+
+                if ( withinEdge1_2 && withinEdge2_4 && withinEdge4_3 && withinEdge3_1 )
+                {
+                    snlSurfLocnGuess* newGuess = new snlSurfLocnGuess;
+
+                    newGuess -> paramU = ( ( maxU - minU ) / 2 ) + minU;
+                    newGuess -> paramV = ( ( maxV - minV ) / 2 ) + minV;
+                    newGuess -> pt = eval ( newGuess -> paramU, newGuess -> paramV );
+                    newGuess -> dist = ( newGuess -> pt ).distSqrd ( points [ ptIndex ] );
+                    newGuess -> origPtIndex = ptIndex;
+                    newGuess -> spanNumber = spanNum;
+                    newGuess -> minU = minU;
+                    newGuess -> maxU= maxU;
+                    newGuess -> minV = minV;
+                    newGuess -> maxV = maxV;
+                    newGuess -> culled = false;
+                    newGuess -> ignoreParamBounds = false;
+                    newGuess -> converged = false;
+    
+                    tmpList -> append ( newGuess, false );
+    
+                    hasGuess [ ptIndex ] = true;
+                    lastGuess [ ptIndex ] = newGuess;
+                }
+            }
+
+            spanNum ++;
+
+            spanEvalIndex ++;
+        }
+
+        spanEvalIndex ++;
+    }
+
+    // If a candidate span is not found for a given point then find closest evaluated point
+    // but don't impose parametric bounds to the guess.
+        
+    for ( int ptIndex = 0; ptIndex < numPoints; ptIndex ++ )
+    {
+        if ( ! hasGuess [ ptIndex ] )
+        {
+            snlSurfLocnGuess* newGuess = new snlSurfLocnGuess;
+
+            index = 0;
+        
+            for ( int indexU = 0; indexU < numEvalU; indexU ++ )
+            {
+                for ( int indexV = 0; indexV < numEvalV; indexV ++ )
+                {
+                    double distSqrd = points [ ptIndex ].distSqrd ( evalPts [ index ] );
+
+                    if ( ( ! indexU && ! indexV ) || ( newGuess -> dist > distSqrd ) )
+                    {
+                        newGuess -> dist = distSqrd;
+                        newGuess -> paramU = paramU [ indexU ];
+                        newGuess -> paramV = paramV [ indexV ];
+                        newGuess -> pt = evalPts [ index ];
+                    }
+                        
+                    index ++;  
+                }
+            }
+
+            // Fill in rest of guess data.
+
+            newGuess -> origPtIndex = ptIndex;
+            newGuess -> culled = false;
+            newGuess -> ignoreParamBounds = true;
+            newGuess -> converged = false;
+
+            tmpList -> append ( newGuess, false );
+
+            lastGuess [ ptIndex ] = newGuess;
+        }
+    }
+
+    // Build properly ordered return list.
+
+    ptrList <snlSurfLocnGuess>* retList = new ptrList <snlSurfLocnGuess>; // List of guesses to
+                                                                          // return.
+
+    for ( int ptIndex = 0; ptIndex < numPoints; ptIndex ++ )
+    {
+        snlSurfLocnGuess* guess = tmpList -> first();
+
+        while ( guess )
+        {
+            if ( guess -> origPtIndex == ptIndex )
+                retList -> append ( guess, true );
+            
+            guess = tmpList -> next();
+        }
+    }
+
+    // Clean up and return.
+
+    delete[] paramU;
+    delete[] paramV;
+
+    delete[] evalPts;
+
+    delete[] edgeNormalU;
+    delete[] edgeNormalV;
+
+    delete[] hasGuess;
+    delete[] lastGuess;
+
+    delete tmpList;
+    
+    return retList;
+}
+
+ptrList <snlSurfLocnGuess>* snlSurface::guessFastProjLocation ( snlPoint* points, int numPoints, int numGuessesPerPt,
+                                                                int granU, int granV )
+{
+    //! Guess parametric location of given points.
+    //  ------------------------------------------
+    //! @param points Array of points to find matches with.
+    //! @param numPoints Number of points in array.
+    //! @param numGuessesPerPt Number of guesses to return per point.
+    //! @param granU Number of guesses per span.
+    //! @param granV Number of guesses per span.
+    //!
+    //! @return List of surface location guess structs. Caller owns this lists.
+
+    int index;
+
+    int numSpansU = knotVectU -> getNumSpans();
+    int numSpansV = knotVectV -> getNumSpans();
+
+    int numEvalU = granU * numSpansU + 1;
+    int numEvalV = granV * numSpansV + 1;
+
+    // Pre-calculate parametric positions.
+
+    knot* paramU = new knot [ numEvalU ];
+    int* spanU = new int [ numEvalU ];
+
+    knot* paramV = new knot [ numEvalV ];
+    int* spanV = new int [ numEvalV ];
+
+    // U direction.
+
+    int cSpan = knotVectU -> getFirstSpan();
+
+    knot paramStart = knotVectU -> val ( cSpan );
+
+    cSpan = knotVectU -> getNextSpan ( cSpan );
+
+    knot paramEnd;
+
+    if ( cSpan )
+        paramEnd = knotVectU -> val ( cSpan );
+    else
+        paramEnd = knotVectU -> max();
+     
+
+    int numSteps;
+
+    int paramIndex = 0;
+
+    basis param;
+
+    for ( int span = 0; span < numSpansU; span ++ )
+    {
+        basis paramStep = ( paramEnd - paramStart ) / (double) granU;
+
+        param = paramStart;
+
+        if ( span )
+        {
+            numSteps = granU;
+            param += paramStep;
+        }
+        else
+        {
+            numSteps = granU + 1;
+        }
+    
+        // Generate params for span.
+        
+        for ( index = 0; index < numSteps; index ++ )
+        {
+            paramU [ paramIndex ] = param;
+
+            spanU [ paramIndex ++ ] = span;
+    
+            param += paramStep;
+    
+            if ( param > paramEnd ) param = paramEnd;  // Round off error trap.
+        }
+
+        paramStart = paramEnd;
+
+        cSpan = knotVectU -> getNextSpan ( cSpan );
+
+        if ( cSpan )
+            paramEnd = knotVectU -> val ( cSpan );
+        else
+            paramEnd = knotVectU -> max();
+    }
+
+    // V direction.
+    
+    cSpan = knotVectV -> getFirstSpan();
+
+    paramStart = knotVectV -> val ( cSpan );
+
+    cSpan = knotVectV -> getNextSpan ( cSpan );
+
+    if ( cSpan )
+        paramEnd = knotVectV -> val ( cSpan );
+    else
+        paramEnd = knotVectV -> max();
+
+    paramIndex = 0;
+
+    for ( int span = 0; span < numSpansV; span ++ )
+    {
+        basis paramStep = ( paramEnd - paramStart ) / (double) granV;
+
+        param = paramStart;
+
+        if ( span )
+        {
+            numSteps = granV;
+            param += paramStep;
+        }
+        else
+        {
+            numSteps = granV + 1;
+        }
+    
+        for ( index = 0; index < numSteps; index ++ )
+        {
+            paramV [ paramIndex ] = param;
+
+            spanV [ paramIndex ++ ] = span;
+    
+            param += paramStep;
+    
+            if ( param > paramEnd ) param = paramEnd;  // Round off error trap.
+        }
+
+        paramStart = paramEnd;
+
+        cSpan = knotVectV -> getNextSpan ( cSpan );
+
+        if ( cSpan )
+            paramEnd = knotVectV -> val ( cSpan );
+        else
+            paramEnd = knotVectV -> max();
+    }
+
+    // Pre-evaluate basis functions.
+
+    basis** basisU = new basis* [ numEvalU ];
+    basis** basisV = new basis* [ numEvalV ];
+
+    for ( index = 0; index < numEvalU; index ++ )
+        basisU [ index ] = knotVectU -> evalBasis ( paramU [ index ] );
+
+    for ( index = 0; index < numEvalV; index ++ )
+        basisV [ index ] = knotVectV -> evalBasis ( paramV [ index ] );
+
+    // Evaluate surface points and vectors.
+
+    int numEvalPts = numEvalU * numEvalV;
+
+    snlPoint* evalPts = new snlPoint [ numEvalPts ];
+
+    index = 0;
+
+    for ( int indexU = 0; indexU < numEvalU; indexU ++ )
+    {
+        for ( int indexV = 0; indexV < numEvalV; indexV ++ )
+        {
+            evalPts [ index ] = eval ( paramU [ indexU ], paramV [ indexV ], basisU [ indexU ],
+                                       basisV [ indexV ] );
+                          
+            index ++;
+        }
+    }
+
+    // Compare given points to evaluated points.
+
+    int totalNumGuesses = numGuessesPerPt * numPoints;
+
+    snlSurfLocnGuess** guesses = new snlSurfLocnGuess* [ totalNumGuesses ];
+
+    for ( int guessIndex = 0; guessIndex < totalNumGuesses; guessIndex ++ )
+        guesses [ guessIndex ] = 0;
+
+    index = 0;
+
+    for ( int indexU = 0; indexU < numEvalU; indexU ++ )
+    {
+        for ( int indexV = 0; indexV < numEvalV; indexV ++ )
+        {
+            for ( int ptIndex = 0; ptIndex < numPoints; ptIndex ++ )
+            {
+                double distSqrd = evalPts [ index ].distSqrd ( points [ ptIndex ] );
+
+                // If no guesses in available guess position then add a new one.
+
+                int guessOffset = ptIndex * numGuessesPerPt;
+
+                bool guessInserted = false;
+
+                for ( int guessIndex = 0; guessIndex < numGuessesPerPt; guessIndex ++ )
+                {
+                    if ( ! guesses [ guessIndex + guessOffset ] )
+                    {
+                        // Create new guess.
+                        
+                        snlSurfLocnGuess* newGuess = new snlSurfLocnGuess;
+    
+                        newGuess -> paramU = paramU [ indexU ];
+                        newGuess -> paramV = paramV [ indexV ];
+                        newGuess -> pt = evalPts [ index ];
+                        newGuess -> dist = distSqrd;
+                        newGuess -> origPtIndex = ptIndex;
+                        newGuess -> spanNumber = - 1;
+                        newGuess -> culled = false;
+                        newGuess -> ignoreParamBounds = true;
+                        newGuess -> converged = false;
+
+                        guesses [ guessIndex + guessOffset ] = newGuess;
+
+                        guessInserted = true;
+
+                        break;
+                    }
+                }
+
+                if ( ! guessInserted )
+                {
+                    // Find guess with largest distance.
+
+                    int replaceIndex = guessOffset;
+                    double dist = guesses [ guessOffset ] -> dist;
+                    
+                    for ( int guessIndex = 1; guessIndex < numGuessesPerPt; guessIndex ++ )
+                    {
+                        if ( guesses [ guessIndex + guessOffset ] -> dist > dist )
+                        {
+                            replaceIndex = guessIndex + guessOffset;
+                            dist = guesses [ guessIndex + guessOffset ] -> dist;
+                        }
+                    }
+
+                    if ( dist > distSqrd )
+                    {
+                        // Replace guess.
+    
+                        guesses [ replaceIndex ] -> paramU = paramU [ indexU ];
+                        guesses [ replaceIndex ] -> paramV = paramV [ indexV ];
+                        guesses [ replaceIndex ] -> pt = evalPts [ index ];
+                        guesses [ replaceIndex ] -> dist = distSqrd;
+                        guesses [ replaceIndex ] -> origPtIndex = ptIndex;
+                    }
+                }
+            }
+
+            index ++;
+        }
+    }
+
+    // Assemble return list.
+
+    ptrList <snlSurfLocnGuess>* retList = new ptrList <snlSurfLocnGuess>;  // List of guesses to return.
+
+    for ( int guessIndex = 0; guessIndex < totalNumGuesses; guessIndex ++ )
+    {
+        if ( guesses [ guessIndex ] )
+            retList -> append ( guesses [ guessIndex ], true );
+    }
+
+    // Clean up and return.
+
+    delete[] evalPts;
+
+    delete[] guesses;
+
+    delete[] paramU;
+    delete[] paramV;
+
+    delete[] spanU;
+    delete[] spanV;
+
+    for ( int index = 0; index < numEvalU; index ++ )
+        delete[] basisU [ index ];
+
+    for ( int index = 0; index < numEvalV; index ++ )
+        delete[] basisV [ index ];
+
+    delete[] basisU;
+    delete[] basisV;
+
+    return retList;    
+}
+
+ptrList <snlSurfLocnGuess>* snlSurface::guessProjLocation_triMethod ( snlPoint* points, int numPoints, bool* pointMask )
+{
+    //! Guess parametric location of given points using triangular decomposition.
+    //  -------------------------------------------------------------------------
+    //! @param points Array of points to find matches with.
+    //! @param numPoints Number of points in array.
+    //! @param pointMask Array specifying which points to process. Corresponding index to points
+    //!                  array. Must be true to process.
+    //!
+    //! @return List of surface location guess structs. Caller owns this list.
+
+    
+    
+}
+
+int snlSurface::hasAmbigEdges ( sEdge* results, double tolerance )
+{
+    //! See if the surface has ambiguous edges
+    //  --------------------------------------    
+    //! @param results Array of sEdge. Should be size 4.
+    //! @param tolerance Tolerance of edge detection.
+    //!
+    //! @return Number of ambiguous edges.
+
+    snlPoint    evalPt [ 4 ];  // Current evaled non-homogeneous point.
+
+    knot        min_u, max_u, min_v, max_v;
+
+    min_u = knotVectU -> min();
+    max_u = knotVectU -> max();
+    min_v = knotVectV -> min();
+    max_v = knotVectV -> max();
+
+    // Generate points to process.
+
+    knot mid_u = ( max_u - min_u ) / 2.0 + min_u;
+    knot mid_v = ( max_v - min_v ) / 2.0 + min_v;
+
+    // Min / Max U
+
+    evalPt [ 0 ] = eval ( min_u, mid_v );
+
+    evalPt [ 1 ] = eval ( max_u, mid_v );
+
+    // Min / Max V
+
+    evalPt  [ 2 ] = eval ( mid_u, min_v );
+
+    evalPt [ 3 ] = eval ( mid_u, max_v );
+
+    // Generate initial guesses. 3 Per edge.
+
+    ptrList <snlSurfLocnGuess>* guessList = new ptrList <snlSurfLocnGuess>;
+
+    snlSurfLocnGuess guessTemplate;
+    guessTemplate.culled = false;
+    guessTemplate.ignoreParamBounds = true;
+    guessTemplate.converged = false;
+
+    // Edge: Min U. Eval index 0.
+
+    snlSurfLocnGuess* guess;
+
+    // Max U.
+    guess = new snlSurfLocnGuess;
+    *guess = guessTemplate;
+    guess -> paramU = max_u;
+    guess -> paramV = mid_v;
+    guess -> pt = evalPt [ 1 ];
+    guess -> dist = evalPt [ 1 ].distSqrd ( evalPt [ 0 ] );
+    guess -> origPtIndex = 0;
+    guessList -> append ( guess, true );
+
+    // Min V.
+
+    guess = new snlSurfLocnGuess;
+    *guess = guessTemplate;
+    guess -> paramU = mid_u;
+    guess -> paramV = min_v;
+    guess -> pt = evalPt [ 2 ];
+    guess -> dist = evalPt [ 2 ].distSqrd ( evalPt [ 0 ] );
+    guess -> origPtIndex = 0;
+    guessList -> append ( guess, true );
+
+    // Max V.
+
+    guess = new snlSurfLocnGuess;
+    *guess = guessTemplate;
+    guess -> paramU = mid_u;
+    guess -> paramV = max_v;
+    guess -> pt = evalPt [ 3 ];
+    guess -> dist = evalPt [ 3 ].distSqrd ( evalPt [ 0 ] );
+    guess -> origPtIndex = 0;
+    guessList -> append ( guess, true );
+
+    // Edge: Max U. Eval index 1.
+
+    // Min U.
+
+    guess = new snlSurfLocnGuess;
+    *guess = guessTemplate;
+    guess -> paramU = min_u;
+    guess -> paramV = mid_v;
+    guess -> pt = evalPt [ 0 ];
+    guess -> dist = evalPt [ 0 ].distSqrd ( evalPt [ 1 ] );
+    guess -> origPtIndex = 1;
+    guessList -> append ( guess, true );
+
+    // Min V.
+
+    guess = new snlSurfLocnGuess;
+    *guess = guessTemplate;
+    guess -> paramU = mid_u;
+    guess -> paramV = min_v;
+    guess -> pt = evalPt [ 2 ];
+    guess -> dist = evalPt [ 2 ].distSqrd ( evalPt [ 1 ] );
+    guess -> origPtIndex = 1;
+    guessList -> append ( guess, true );
+
+    // Max V.
+
+    guess = new snlSurfLocnGuess;
+    *guess = guessTemplate;
+    guess -> paramU = mid_u;
+    guess -> paramV = max_v;
+    guess -> pt = evalPt [ 3 ];
+    guess -> dist = evalPt [ 3 ].distSqrd ( evalPt [ 1 ] );
+    guess -> origPtIndex = 1;
+    guessList -> append ( guess, true );
+
+    // Edge: Min V. Eval index 2.
+
+    // Max V.
+
+    guess = new snlSurfLocnGuess;
+    *guess = guessTemplate;
+    guess -> paramU = mid_u;
+    guess -> paramV = max_v;
+    guess -> pt = evalPt [ 3 ];
+    guess -> dist = evalPt [ 3 ].distSqrd ( evalPt [ 2 ] );
+    guess -> origPtIndex = 2;
+    guessList -> append ( guess, true );
+
+    // Min U.
+
+    guess = new snlSurfLocnGuess;
+    *guess = guessTemplate;
+    guess -> paramU = min_u;
+    guess -> paramV = mid_v;
+    guess -> pt = evalPt [ 0 ];
+    guess -> dist = evalPt [ 0 ].distSqrd ( evalPt [ 2 ] );
+    guess -> origPtIndex = 2;
+    guessList -> append ( guess, true );
+
+    // Max U.
+
+    guess = new snlSurfLocnGuess;
+    *guess = guessTemplate;
+    guess -> paramU = max_u;
+    guess -> paramV = mid_v;
+    guess -> pt = evalPt [ 1 ];
+    guess -> dist = evalPt [ 1 ].distSqrd ( evalPt [ 2 ] );
+    guess -> origPtIndex = 2;
+    guessList -> append ( guess, true );
+
+    // Edge: Max V. Eval index 3.
+
+    // Min V.
+
+    guess = new snlSurfLocnGuess;
+    *guess = guessTemplate;
+    guess -> paramU = mid_u;
+    guess -> paramV = min_v;
+    guess -> pt = evalPt [ 2 ];
+    guess -> dist = evalPt [ 2 ].distSqrd ( evalPt [ 3 ] );
+    guess -> origPtIndex = 3;
+    guessList -> append ( guess, true );
+
+    // Min U.
+
+    guess = new snlSurfLocnGuess;
+    *guess = guessTemplate;
+    guess -> paramU = min_u;
+    guess -> paramV = mid_v;
+    guess -> pt = evalPt [ 0 ];
+    guess -> dist = evalPt [ 0 ].distSqrd ( evalPt [ 3 ] );
+    guess -> origPtIndex = 3;
+    guessList -> append ( guess, true );
+
+    // Max U.
+
+    guess = new snlSurfLocnGuess;
+    *guess = guessTemplate;
+    guess -> paramU = max_u;
+    guess -> paramV = mid_v;
+    guess -> pt = evalPt [ 1 ];
+    guess -> dist = evalPt [ 1 ].distSqrd ( evalPt [ 3 ] );
+    guess -> origPtIndex = 3;
+    guessList -> append ( guess, true );
+
+    // Process guesses.
+
+    int arraySize;
+
+    snlSurfLocn* locns = processGuesses ( evalPt, 4, &arraySize, guessList, tolerance, tolerance,
+                                          10, true, true );
+
+    // Check for projected points remaining, within tolerance, on their original edge.
+
+    bool edgeIsAmbig [ 4 ] = { false, false, false, false };
+
+    for ( int index = 0; index < arraySize; index ++ )
+    {
+        if ( locns [ index ].dist > tolerance ) continue;
+
+        switch ( locns [ index ].origPtIndex )
+        {
+            case 0:
+
+                // Min U.
+                
+                if ( locns [ index ].paramU < min_u + tolerance )
+                    edgeIsAmbig [ 0 ] = true;
+                    
+                break;
+                
+            case 1:
+            
+                // Max U.
+                
+                if ( locns [ index ].paramU > max_u - tolerance )
+                    edgeIsAmbig [ 1 ] = true;
+                    
+                break;
+                
+            case 2:
+
+                // Min V.
+
+                if ( locns [ index ].paramV < min_v + tolerance )
+                    edgeIsAmbig [ 2 ] = true;
+                    
+                break;
+                
+            case 3:
+
+                // Max V.
+
+                if ( locns [ index ].paramV > max_v - tolerance )
+                    edgeIsAmbig [ 3 ] = true;
+                    
+                break;
+        }
+    }
+
+    int cIndex = 0;
+    int numAmbig = 0;
+
+    // Process ambiguous points to find edges.
+
+    if ( edgeIsAmbig [ 0 ] )
+    {
+        results [ cIndex ].direction = 0;
+        results [ cIndex ].pVal = min_u;
+
+        numAmbig++;
+        cIndex++;
+    }
+
+    if ( edgeIsAmbig [ 1 ] )
+    {
+        results [ cIndex ].direction = 0;
+        results [ cIndex ].pVal = max_u;
+
+        numAmbig++;
+        cIndex++;
+    }
+
+    if ( edgeIsAmbig [ 2 ] )
+    {
+        results [ cIndex ].direction = 1;
+        results [ cIndex ].pVal = min_v;
+
+        numAmbig++;
+        cIndex++;
+    }
+
+    if ( edgeIsAmbig [ 3 ] )
+    {
+        results [ cIndex ].direction = 1;
+        results [ cIndex ].pVal = max_v;
+
+        numAmbig++;
+    }
+
+    delete[] locns;
+
+    return numAmbig;
+}
+
+int snlSurface::hasAmbigEdges_depr ( sEdge* results )
+{
+    //! See if the surface has ambiguous edges
+    //! --------------------------------------    
+    //! @param results Array of sEdge. Should be size 4.
+    //!
+    //! @return Number of ambiguous edges.
+    //!
+    //! Notes: Function is now deprecated.
+
+    snlPoint    evalPt [ 4 ];  // Current evaled non-homogeneous point.
+
+    knot        minT, maxT, minU, maxU;
+
+    minT = knotVectU -> min();
+    maxT = knotVectU -> max();
+    minU = knotVectV -> min();
+    maxU = knotVectV -> max();
+
+    // Min / Max T
+
+    evalPt [ 0 ] = eval ( minT, ( ( maxU - minU ) / 2 ) + minU );    
+
+    evalPt [ 1 ] = eval ( maxT, ( ( maxU - minU ) / 2 ) + minU );    
+
+    // Min / Max U
+
+    evalPt  [ 2 ] = eval (( ( maxT - minT ) / 2 ) + minT, minU );    
+
+    evalPt [ 3 ] = eval (( ( maxT - minT ) / 2 ) + minT, maxU );    
+
+    // Get projection function to do the work.
+
+    ptrList < sLocn >* ambig;
+
+    sLocn projns [ 4 ];
+
+    ambig = projPtSurf ( *this, evalPt, 4, projns,
+                         0.000001, 0.00001, 3 );
+
+    int cIndex = 0;
+    int numAmbig = 0;
+
+    // Process ambiguous points to find edges.
+
+    if ( projns [ 0 ].flag > 1 )
+    {
+        results [ cIndex ].direction = 0;
+        results [ cIndex ].pVal = minT;
+
+        numAmbig++;
+        cIndex++;
+    }
+
+    if ( projns [ 1 ].flag > 1 )
+    {
+        results [ cIndex ].direction = 0;
+        results [ cIndex ].pVal = maxT;
+
+        numAmbig++;
+        cIndex++;
+    }
+
+    if ( projns [ 2 ].flag > 1 )
+    {
+        results [ cIndex ].direction = 1;
+        results [ cIndex ].pVal = minU;
+
+        numAmbig++;
+        cIndex++;
+    }
+
+    if ( projns [ 3 ].flag > 1 )
+    {
+        results [ cIndex ].direction = 1;
+        results [ cIndex ].pVal = maxU;
+
+        numAmbig++;
+    }
+
+    delete ambig;
+
+    return numAmbig;
+}
+
diff --git a/src/snlSurface_projection.h b/src/snlSurface_projection.h
new file mode 100644
index 0000000..0c0639d
--- /dev/null
+++ b/src/snlSurface_projection.h
@@ -0,0 +1,23 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2006 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+#ifndef SNLSURFACE_PROJECTION_H
+#define SNLSURFACE_PROJECTION_H
+
+#include "snlSurface.h"
+
+#include "snlUtil.h"
+
+#include "snlNurbsCommon.h"
+
+#endif
diff --git a/src/snlTest.cpp b/src/snlTest.cpp
new file mode 100644
index 0000000..cee06a9
--- /dev/null
+++ b/src/snlTest.cpp
@@ -0,0 +1,1689 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2004 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** Test program for libSNL ***
+
+#ifdef SGI_MIPS
+
+    #include <iostream.h>
+    #include <math.h>
+    
+#else
+
+    #include <iostream>
+    #include <cmath>
+    
+    using namespace std;
+    
+#endif
+
+#include "snlSquareLinear.h"
+#include "snlCurve.h"
+#include "snlSurface.h"
+#include "snlCtrlPoint.h"
+#include "snlVector.h"
+
+
+snlCurve* generateCurve()
+{
+   // Create some points to interpolate.
+    
+    snlPoint* points = new snlPoint [ 10 ];
+    
+    double x = 0.0;
+    double y = 0.0;
+    double z = 10.0;
+    double w = 1.0;
+    
+    double zIncr = 1.0;
+    
+    for ( int count = 0; count < 10; count ++ )
+    {
+        points [ count ].components ( x, y, z, w );
+        
+        x += 1.0;
+        y += 1.0;
+        z -= zIncr;
+        
+        zIncr -= 0.2;
+    }
+    
+    knot* params;
+
+    // Generate curve.
+    
+    snlCurve* curve = new snlCurve ( points, 10, snlCurve::SNL_GLOBAL_INTERPOLATION, 3, false, &params );
+
+    delete[] params;
+    delete[] points;
+    
+    return curve;
+}
+
+snlCurve* generateReverseCurve()
+{
+   // Create some points to interpolate.
+    
+    snlPoint* points = new snlPoint [ 10 ];
+    
+    double x = 0.0;
+    double y = 0.0;
+    double z = 10.0;
+    double w = 1.0;
+    
+    double zIncr = 1.0;
+    
+    for ( int count = 9; count >= 0; count -- )
+    {
+        points [ count ].components ( x, y, z, w );
+        
+        x += 1.0;
+        y += 1.0;
+        z -= zIncr;
+        
+        zIncr -= 0.2;
+    }
+    
+    knot* params;
+
+    // Generate curve.
+    
+    snlCurve* curve = new snlCurve ( points, 10, snlCurve::SNL_GLOBAL_INTERPOLATION, 3, false, &params );
+
+    delete[] points;
+    delete[] params;
+    
+    return curve;
+}
+
+snlSurface* generateSurface()
+{
+    // Generate surface for use elsewhere in the test program.
+    // -------------------------------------------------------
+    
+    snlCtrlPoint* points = new snlCtrlPoint [ 100 ];
+    
+    double x = - 5.0;
+    double y = 0.0;
+    double z = 10.0;
+    double w = 1.0;
+    
+    double xyGrad = 0.02;
+    double xyGradIncr = 0.01;
+    
+    double xIncr = 0.02;
+    double yIncr = 0.05;    
+    double zIncr = -0.2;
+    
+    double xStep = 1.0;
+    
+    for ( int tIndex = 0; tIndex < 10; tIndex ++ )
+    {
+        for ( int uIndex = 0; uIndex < 10; uIndex ++ )
+        {
+            double newX = x + ( xStep * uIndex );
+            double newY = y + ( newX - x ) * xyGrad;            
+            
+            points [ ( tIndex * 10 ) + uIndex ].components ( newX, newY, z, w );
+            
+            x += xIncr;
+            y += yIncr;
+            z += zIncr;
+            
+            xyGrad += xyGradIncr;
+        }
+    }
+    
+    snlSurface* retSurface = new snlSurface ( 5, 5, 10, 10, points, 0, 0 );
+
+    return retSurface;
+}
+
+snlSurface* generateSurface2()
+{
+    // Generate surface for use elsewhere in the test program.
+    // -------------------------------------------------------
+    
+    snlCtrlPoint* points = new snlCtrlPoint [ 110 ];
+
+    double z = 2.5;
+    double w = 1.0;
+
+    points [ 0 ].components ( - 2.5, 0.5, z, w );
+    points [ 1 ].components ( - 2.0, 1.0 , z, w );
+    points [ 2 ].components ( - 1.5, 0.5, z, w );
+    points [ 3 ].components ( - 1.0, 0.0, z, w );
+    points [ 4 ].components ( - 0.5, 0.5, z, w );
+    points [ 5 ].components ( 0.0, 1.0, z, w );
+    points [ 6 ].components ( 0.5, 0.5, z, w );
+    points [ 7 ].components ( 1.0, 0.0, z, w );
+    points [ 8 ].components ( 1.5, 0.5, z, w );
+    points [ 9 ].components ( 2.0, 1.0, z, w );
+    points [ 10 ].components ( 2.5, 1.5, z, w );
+
+    double zIncr = - 0.5;
+    
+    double angleStep = 0.314;
+
+    snlTransform rotation;
+    snlTransform translation;
+
+    snlPoint axisStart ( 0.0, 0.0, 0.0 );
+    snlPoint axisEnd ( 0.0, 0.0, 1.0 );
+
+    rotation.rotate ( angleStep, axisStart, axisEnd );
+    translation.translate ( 0.0, 0.0, zIncr );
+
+    int index = 11;
+    
+    for ( int uIndex = 1; uIndex < 10; uIndex ++ )
+    {
+        for ( int vIndex = 0; vIndex < 11; vIndex ++ )
+        {
+            points [ index ] = points [ vIndex ];
+
+            rotation.transform ( points [ index ] );
+            translation.transform ( points [ index ] );
+        
+            index ++;
+        }
+
+        rotation.rotate ( angleStep, axisStart, axisEnd );
+        translation.translate ( 0.0, 0.0, zIncr );
+    }
+    
+    snlSurface* retSurface = new snlSurface ( 5, 5, 10, 11, points, 0, 0 );
+    
+    return retSurface;
+}
+
+snlSurface* generateSawToothSurface()
+{
+    // Generate surface for use elsewhere in the test program.
+    // -------------------------------------------------------
+    // Notes:   Generates a surface suitable for concave polygon detection.
+
+    int size = 11;
+    int deg = 5;
+    
+    snlCtrlPoint* points = new snlCtrlPoint [ size * size ];
+    
+    double x = - 5.0;
+    double y = 0.0;
+    double z = 5.0;
+    double w = 1.0;
+    
+    double ySaw = 0.5;
+    
+    double xStep = 1.0;
+    double yIncr = 0.1;    
+    double zIncr = -1.0;
+    
+    for ( int tIndex = 0; tIndex < size; tIndex ++ )
+    {
+        for ( int uIndex = 0; uIndex < size; uIndex ++ )
+        {
+            points [ ( tIndex * size ) + uIndex ].components ( x + ( xStep * uIndex ), y + ( uIndex % 2 ) * ySaw, z, w );
+        }
+
+        y += yIncr;
+        z += zIncr;
+    }
+
+    knot* knotsV = new knot [ size + deg + 1 ];
+
+    for ( int count = 0; count < deg + 1; count ++ ) knotsV [ count ] = 0.0;
+    for ( int count = deg + 1; count < deg * 2 + 1; count ++ ) knotsV [ count ] = 0.5;
+    for ( int count = deg * 2 + 1; count < size + deg + 1; count ++ ) knotsV [ count ] = 1.0;
+    
+    snlSurface* retSurface = new snlSurface ( 5, 5, 11, 11, points, 0, knotsV );
+
+    return retSurface;
+}
+
+snlSurface* generateSurfaceOfRevolution()
+{
+    // Generate surface for use elsewhere in the test program.
+    // -------------------------------------------------------
+
+    // Code used from vTurbine.
+
+    double impeller_depth = 2.0;
+    double inlet_radius = 1.0;
+    double outlet_radius = 3.0;
+    double innerProfile_weight1 = 0.5;
+    double innerProfile_weight2 = 0.5;
+    
+    snlPoint* start = new snlPoint ( 0.0, inlet_radius, impeller_depth, 1.0 );
+    snlPoint* end = new snlPoint ( 0.0, outlet_radius, 0.0, 1.0 );  // Axis is rooted at origin.
+    
+    snlCurve innerProfileCurve ( 3, 6, *start, *end );
+    
+    snlCtrlPoint* cpts = innerProfileCurve.controlPointNet().getCtrlPtsPtr();
+    
+    cpts [ 1 ].components ( 0.0, inlet_radius, ( impeller_depth * 2.0 ) / 3.0, 1.0 );
+    cpts [ 2 ].components ( 0.0, inlet_radius, impeller_depth / 3.0, 1.0 );
+    cpts [ 3 ].components ( 0.0, ( outlet_radius - inlet_radius ) / 3.0 + inlet_radius, 0.0, 1.0 );
+    cpts [ 4 ].components ( 0.0, ( ( outlet_radius - inlet_radius ) * 2.0 ) / 3.0 + inlet_radius, 0.0, 1.0 );    
+    
+    
+    cpts [ 2 ].weight ( innerProfile_weight1 );
+    cpts [ 3 ].weight ( innerProfile_weight2 );
+    
+    start -> components ( 0.0, 0.0, impeller_depth, 1.0 );
+    end -> components ( 0.0, 0.0, 0.0, 1.0 );
+    
+    return new snlSurface ( innerProfileCurve, *start, *end, 270.0 );
+}
+
+snlSurface* generateAmbigSurface()
+{
+    // Generate surface for ambiguous edge detection.
+    
+    int degree = 3;
+    
+    snlCtrlPoint* line = new snlCtrlPoint [ degree + 1 ];
+
+    line [ 0 ].components ( 0.0, 0.0, 5.0 );
+    line [ 1 ].components ( 0.0, 1.0, 1.0 );
+    line [ 2 ].components ( 0.0, - 1.0, 1.0 );
+    line [ 3 ].components ( 0.0, 0.0, 5.0 );
+
+    snlCtrlPoint* ctrlPts = new snlCtrlPoint [ ( degree + 1 ) * ( degree + 1 ) ];
+
+    double xAdjust = 1.0;
+
+    int index = 0;
+
+    for ( int uIndex = 0; uIndex < degree + 1; uIndex ++ )
+    {
+        for ( int vIndex = 0; vIndex < degree + 1; vIndex ++ )
+        {
+            ctrlPts [ index ] = line [ vIndex ];
+            ctrlPts [ index ].x ( ctrlPts [ index ].x() + xAdjust * uIndex );
+
+            index ++;
+        }
+    }
+
+    delete[] line;
+
+    return new snlSurface ( degree, degree, degree + 1, degree + 1, ctrlPts, 0, 0 );
+}
+
+snlSurface* generateAmbigSurface2()
+{
+    // Generate surface for ambiguous edge detection.
+    
+    int degree = 3;
+    
+    snlCtrlPoint* line = new snlCtrlPoint [ degree + 1 ];
+
+    line [ 0 ].components ( 0.0, 0.0, 5.0 );
+    line [ 1 ].components ( 0.0, 1.0, 1.0 );
+    line [ 2 ].components ( 0.0, - 1.0, 1.0 );
+    line [ 3 ].components ( 0.0, 0.0, 5.0 );
+
+    snlCtrlPoint* ctrlPts = new snlCtrlPoint [ ( degree + 1 ) * ( degree + 1 ) ];
+
+    double xAdjust = 1.0;
+
+    int index = 0;
+
+    for ( int uIndex = 0; uIndex < degree + 1; uIndex ++ )
+    {
+        for ( int vIndex = 0; vIndex < degree + 1; vIndex ++ )
+        {
+            ctrlPts [ index ] = line [ uIndex ];
+            ctrlPts [ index ].x ( ctrlPts [ index ].x() + xAdjust * vIndex );
+
+            index ++;
+        }
+    }
+
+    delete[] line;
+
+    return new snlSurface ( degree, degree, degree + 1, degree + 1, ctrlPts, 0, 0 );
+}
+
+bool test_surfaceInterp()
+{
+    bool passed = true;
+
+    double tolerance = 0.0001;
+
+    // Generate surface from existing surfaces control points.
+    
+    snlSurface* testSurf = generateSurface();
+
+    const snlCtrlPoint* ctrlPts = testSurf -> controlPoints();
+
+    int numU = testSurf -> sizeU();
+    int numV = testSurf -> sizeV();
+
+    int numPts = numU * numV;
+
+    snlPoint* pointsInterp = new snlPoint [ numPts ];
+
+    for ( int index = 0; index < numPts; index ++ )
+        pointsInterp [ index ] = ctrlPts [ index ];
+
+    snlSurface* interpSurf = new snlSurface ( snlSurface::SNL_GLOBAL_INTERP_CENTRIFUGAL, pointsInterp,
+                                              numU, numV, 4, 4 );
+
+    // Project orginal data points to surface and calculate error.
+
+    double maxError = 0.0;
+    
+    snlPoint* toProject = new snlPoint [ numPts ];
+
+    for ( int index = 0; index < numPts; index ++ )
+        toProject [ index ] = pointsInterp [ index ];
+
+    int numProjLocns;
+
+    snlSurfLocn* projLocns = interpSurf -> fastProject ( toProject, numPts, &numProjLocns, tolerance, 0.00001, 2, 1, 1 );
+
+    for ( int index = 0; index < numPts; index ++ )
+    {
+        if ( maxError < projLocns [ index ].dist ) maxError = projLocns [ index ].dist;
+    }
+
+    delete[] projLocns;
+    delete[] toProject;
+
+    if ( maxError > tolerance ) passed = false;
+
+    cout << "Max Error: " << maxError << " - ";
+
+    delete testSurf;
+    delete interpSurf;
+    delete[] pointsInterp;
+    
+    if ( ! passed )
+    {    
+        cout << "Failed\n";
+        return false;
+    }
+    else
+        cout << "Passed\n";
+    
+    return passed;
+}
+
+bool test_surfaceRefine()
+{
+    bool passed = true;
+
+    double tolerance = 0.05;
+    
+    snlSurface* testSurf = generateSurface();
+    snlSurface* testSurf2 = generateSurface2();
+
+    testSurf -> refine ( tolerance );
+    //testSurf2 -> refineHullBezier ( tolerance );  // Broken with no time to fix.
+    testSurf2 -> refine ( tolerance );
+
+    // Project all control points to surface and calculate error.
+
+    int numSurfaces = 1;
+
+    snlSurface* surfs [ 2 ];
+
+    surfs [ 0 ] = testSurf;
+    surfs [ 1 ] = testSurf2;
+
+    double maxError = 0.0;
+
+    cout << "\n\n";
+
+    cout << "\tFinished refinement, starting projections. This may take a while.\n\n";
+
+    for ( int surfNum = 0; surfNum < numSurfaces; surfNum ++ )
+    {
+        snlSurface* cSurf = surfs [ surfNum ];
+        
+        int numPoints = ( cSurf -> controlPointNet() ).getNumPts();
+
+        cout << "\tTesting Surface Number: " << surfNum << "  Number of projections: " << numPoints << "\n";
+    
+        const snlCtrlPoint* ctrlPoints = cSurf -> controlPoints();
+    
+        snlPoint* toProject = new snlPoint [ numPoints ];
+    
+        for ( int index = 0; index < numPoints; index ++ )
+            toProject [ index ] = ctrlPoints [ index ];
+    
+        int numProjLocns;
+
+        snlSurfLocn* projLocns = cSurf -> fastProject ( toProject, numPoints, &numProjLocns, tolerance, 0.00001, 2, 1, 1 );
+
+        for ( int index = 0; index < numPoints; index ++ )
+        {
+            if ( maxError < projLocns [ index ].dist ) maxError = projLocns [ index ].dist;
+        }
+    
+        delete[] projLocns;
+        delete[] toProject;
+    }
+
+    if ( maxError > tolerance ) passed = false;
+
+    cout << "\tMax Error: " << maxError << " - ";
+
+    delete testSurf;
+    delete testSurf2;
+    
+    if ( ! passed )
+    {    
+        cout << "Failed\n";
+        return false;
+    }
+    else
+        cout << "Passed\n";
+    
+    return passed;
+}
+
+bool test_ambig()
+{
+    bool passed = true;
+    
+    snlSurface* testSurf = generateAmbigSurface();
+    snlSurface* testSurf2 = generateAmbigSurface2();
+
+    sEdge surfEdges [ 4 ];
+    sEdge surfEdges2 [ 4 ];
+
+    int numEdges = testSurf -> hasAmbigEdges ( surfEdges, 1.0e-6 );
+    int numEdges2 = testSurf2 -> hasAmbigEdges ( surfEdges2, 1.0e-6 );
+
+    if ( numEdges == 0 ) passed = false;
+    if ( numEdges2 == 0 ) passed = false;
+
+    for ( int index = 0; index < numEdges; index ++ )
+    {
+        if ( surfEdges [ index ].direction != 0 ) passed = false;
+        if ( surfEdges2 [ index ].direction != 1 ) passed = false;
+    }
+
+    delete testSurf;
+    delete testSurf2;
+    
+    if ( ! passed )
+    {    
+        cout << "Failed\n";
+        return false;
+    }
+    else
+        cout << "Passed\n";
+    
+    return passed;
+}
+
+bool testSurfaceOfRevolution()
+{
+    // Test accuracy of surface of rotation.
+    // -------------------------------------
+    
+    cout << "\n\n";
+    
+    bool passed = true;
+
+    snlSurface* origSurf = generateSurfaceOfRevolution();
+    snlSurface* testSurf = generateSurfaceOfRevolution();
+
+    // Test knot insertion at multiple locations.
+
+    int numSteps = 10;
+    
+    // Step through surface and evaluate.
+    
+    double minParamU = ( testSurf -> knotVectorU() ).min();
+    double minParamV = ( testSurf -> knotVectorV() ).min();
+    double maxParamU = ( testSurf -> knotVectorU() ).max();
+    double maxParamV = ( testSurf -> knotVectorV() ).max();
+    
+    double paramStepU = ( maxParamU - minParamU ) / ( numSteps - 1 );
+    double paramStepV = ( maxParamV - minParamV ) / ( numSteps - 1 );
+    
+    double paramU = minParamU + paramStepU;
+    double paramV = minParamV;
+    
+    double maxError = 0;
+
+    int index = 0;
+
+    testSurf -> refineHull_U ( 0.005 );
+    testSurf -> refineHull_V ( 0.005 );
+
+    for ( int indexU = 0; indexU < numSteps; indexU ++ )
+    {
+        paramV = minParamV;
+        
+        for ( int indexV = 0; indexV < numSteps; indexV ++ )
+        {
+            
+            paramV += paramStepV;
+            
+            if ( paramV > maxParamV ) paramV = maxParamV;
+
+            index ++;
+        }
+
+        paramU += paramStepU;
+        
+        if ( paramU > maxParamU ) paramU = maxParamU;
+    }
+
+    // Clean up, report and return.    
+    
+    cout << "Max Error = " << maxError << " - ";
+    
+    delete testSurf;
+    delete origSurf;
+    
+    if ( ! passed )
+    {    
+        cout << "Failed\n";
+        return false;
+    }
+    else
+        cout << "Passed\n";
+    
+    return passed;    
+}
+
+bool testSurfacePointInversion()
+{
+    // Test Surface Point Inversion Function
+    // -------------------------------------
+    
+    //cout << "\n\n";
+    
+    bool passed = true;
+    
+    snlSurface* testSurf = generateSurface();
+    
+    int numSteps = 10;
+    
+    int maxPass = 10;
+    
+    // Step through surface and evaluate.
+    
+    double minParamU = ( testSurf -> knotVectorU() ).min();
+    double minParamV = ( testSurf -> knotVectorV() ).min();
+    double maxParamU = ( testSurf -> knotVectorU() ).max();
+    double maxParamV = ( testSurf -> knotVectorV() ).max();
+    
+    double paramStepU = ( maxParamU - minParamU ) / ( numSteps - 1 );
+    double paramStepV = ( maxParamV - minParamV ) / ( numSteps - 1 );
+    
+    double paramU = 0.0;
+    double paramV = 0.0;    
+    
+    double maxError = 0;
+    
+    double iterTol = 1.0e-8;
+    double normTol = 1.0e-6;
+
+    int numEval = numSteps * numSteps;
+
+    // Fill array with evaluated points.
+
+    snlPoint* evalPts = new snlPoint [ numEval ];
+
+    int index = 0;
+    
+    for ( int indexU = 0; indexU < numSteps; indexU ++ )
+    {
+        paramV = minParamV;
+        
+        for ( int indexV = 0; indexV < numSteps; indexV ++ )
+        {
+            evalPts [ index ] = testSurf -> eval ( paramU, paramV );
+
+            //cout << "(" << indexU << ", " << indexV << ") ";
+            //cout << "EvalParamU: " << paramU << "  EvalParamV: " << paramV << "\n";
+            
+            paramV += paramStepV;
+            
+            if ( paramV > maxParamV ) paramV = maxParamV;
+
+            index ++;
+        }
+
+        paramU += paramStepU;
+        
+        if ( paramU > maxParamU ) paramU = maxParamU;
+    }
+    
+    int numReturned;
+
+    snlSurfLocn* inverted = testSurf -> invert ( evalPts, numEval, &numReturned, iterTol, normTol, maxPass );
+
+    //cout << "Num Sent: " << numEval << " Num Returned: " << numReturned << "\n";
+
+    for ( index = 0; index < numReturned; index ++ )
+    {
+        snlVector delta ( evalPts [ inverted [ index ].origPtIndex ], inverted [ index ].pt );
+
+        if ( maxError < delta.length() )
+            maxError = delta.length();
+    }
+
+    delete[] inverted;
+    delete[] evalPts;
+    
+    if ( maxError > iterTol ) passed = false;    
+    
+    // Clean up, report and return.    
+    
+    cout << "Tolerance = " << iterTol << ", Max Error = " << maxError << " - ";
+
+    if ( numEval > numReturned )
+    {
+        cout << "Number Sent: " << numEval << " Number Returned: " << numReturned << " - ";
+        passed = false;
+    }
+    
+    delete testSurf;
+    
+    if ( ! passed )
+    {    
+        cout << "Failed\n";
+        return false;
+    }
+    else
+        cout << "Passed\n";
+    
+    return passed;    
+}
+
+bool testSurfaceConvexBezierSegmentation()
+{
+    // Test decomposition of surface into Bezier segements.
+    // ----------------------------------------------------
+    
+    cout << "\n\n";
+    
+    bool passed = true;
+
+    snlSurface* origSurf = generateSawToothSurface();
+    snlSurface* testSurf = generateSawToothSurface();
+
+    // Test convex detection function.
+    
+    snlPoint testPoints[] = { snlPoint ( - 3.0, 0.0, 0.0 ),
+                              snlPoint ( - 1.5, 2.5, 0.0 ),
+                              snlPoint ( 1.5, - 2.0, 0.0 ),
+                              snlPoint ( 2.5, 1.5, 0.0 ),
+                              snlPoint ( 3.0, 2.5, 0.0 )
+                            };
+
+    snlPoint testPoints2[] = { snlPoint ( - 3.0, 0.0, 0.0 ),
+                               snlPoint ( - 1.5, 2.5, 0.0 ),
+                               snlPoint ( 1.5, 5.0, 0.0 ),
+                               snlPoint ( 2.5, 1.5, 0.0 ),
+                               snlPoint ( 3.0, 2.5, 0.0 )
+                             };
+
+
+    snlPoint* testPointPtrs[] = { testPoints, testPoints + 1, testPoints + 2, testPoints + 3, testPoints + 4 };
+    snlPoint* testPointPtrs2[] = { testPoints2, testPoints2 + 1, testPoints2 + 2, testPoints2 + 3, testPoints2 + 4 };
+
+    if ( ( testSurf -> controlPointNet() ).isConvex ( testPointPtrs, 5 )
+         &&  ! ( testSurf -> controlPointNet() ).isConvex ( testPointPtrs2, 5 ) )
+    {
+        passed = false;
+        cout << "\tConvex Points Test - Failed\n";
+    }
+    else
+        cout << "\tConvex Points Test - Passed\n";
+        
+    // Test knot insertion at multiple locations.
+
+    int numSteps = 10;
+    
+    // Step through surface and evaluate.
+    
+    double minParamU = ( testSurf -> knotVectorU() ).min();
+    double minParamV = ( testSurf -> knotVectorV() ).min();
+    double maxParamU = ( testSurf -> knotVectorU() ).max();
+    double maxParamV = ( testSurf -> knotVectorV() ).max();
+    
+    double paramStepU = ( maxParamU - minParamU ) / ( numSteps - 1 );
+    double paramStepV = ( maxParamV - minParamV ) / ( numSteps - 1 );
+    
+    double paramU = minParamU + paramStepU;
+    double paramV = minParamV;
+    
+    double maxError = 0;
+
+    int numV, numU;
+
+    testSurf -> createConvexBezierSegments ( &numU, &numV );
+
+    //cout << "Num U Segments: " << numU << "  Num V Segments: " << numV << "\n";
+
+    for ( int indexU = 1; indexU < numSteps - 1; indexU ++ )
+    {
+        paramV = minParamV + paramStepV;
+        
+        for ( int indexV = 1; indexV < numSteps - 1; indexV ++ )
+        {
+            snlPoint original = origSurf -> eval ( paramU, paramV );
+
+            snlPoint test = testSurf -> eval ( paramU, paramV );
+
+            double error = snlVector ( original, test ).length();
+
+            if ( error > maxError ) maxError = error;
+            
+            paramV += paramStepV;
+        }
+
+        paramU += paramStepU;
+    }
+
+    delete origSurf;
+    delete testSurf;
+
+    if ( maxError > 2.0e-13 ) passed = false;
+    
+    cout << "\tSurface Decomposition Into Convex Bezier Segments - Max Error = " << maxError << "\n";
+    
+    if ( ! passed )
+        cout << "\tFailed\n";
+    else
+        cout << "\tPassed\n";
+    
+    return passed;
+}
+
+bool testSurfaceBezierSegmentation()
+{
+    // Test decomposition of surface into Bezier segements.
+    // ----------------------------------------------------
+    
+    cout << "\n\n";
+    
+    bool passed = true;
+
+    snlSurface* origSurf = generateSurface();
+    snlSurface* testSurf = generateSurface();
+
+    // Test knot insertion at multiple locations.
+
+    int numSteps = 10;
+    
+    // Step through surface and evaluate.
+    
+    double minParamU = ( testSurf -> knotVectorU() ).min();
+    double minParamV = ( testSurf -> knotVectorV() ).min();
+    double maxParamU = ( testSurf -> knotVectorU() ).max();
+    double maxParamV = ( testSurf -> knotVectorV() ).max();
+    
+    double paramStepU = ( maxParamU - minParamU ) / ( numSteps - 1 );
+    double paramStepV = ( maxParamV - minParamV ) / ( numSteps - 1 );
+    
+    double paramU = minParamU + paramStepU;
+    double paramV = minParamV;
+    
+    double maxError = 0;
+
+    testSurf -> createBezierSegments();
+
+    for ( int indexU = 1; indexU < numSteps - 1; indexU ++ )
+    {
+        paramV = minParamV + paramStepV;
+        
+        for ( int indexV = 1; indexV < numSteps - 1; indexV ++ )
+        {
+            snlPoint original = origSurf -> eval ( paramU, paramV );
+
+            snlPoint test = testSurf -> eval ( paramU, paramV );
+
+            double error = snlVector ( original, test ).length();
+
+            if ( error > maxError ) maxError = error;
+            
+            paramV += paramStepV;
+        }
+
+        paramU += paramStepU;
+    }
+
+    delete origSurf;
+    delete testSurf;
+
+    if ( maxError > 2.0e-13 ) passed = false;
+    
+    cout << "\tSurface Decomposition Into Bezier Segments - Max Error = " << maxError << "\n";
+    
+    if ( ! passed )
+        cout << "\tFailed\n";
+    else
+        cout << "\tPassed\n";
+    
+    return passed;
+}
+
+bool testSurfaceKnotInsert()
+{
+    // Test knot insertion functions.
+    // ------------------------------
+    
+    cout << "\n\n";
+    
+    bool passed = true;
+
+    snlSurface* origSurf = generateSurface();
+    snlSurface* testSurf = generateSurface();
+    snlSurface* testSurf2 = generateSurface();
+
+    // Test knot insertion at multiple locations.
+
+    int numSteps = 10;
+    
+    // Step through surface and evaluate.
+    
+    double minParamU = ( testSurf -> knotVectorU() ).min();
+    double minParamV = ( testSurf -> knotVectorV() ).min();
+    double maxParamU = ( testSurf -> knotVectorU() ).max();
+    double maxParamV = ( testSurf -> knotVectorV() ).max();
+    
+    double paramStepU = ( maxParamU - minParamU ) / ( numSteps - 1 );
+    double paramStepV = ( maxParamV - minParamV ) / ( numSteps - 1 );
+    
+    double paramU = minParamU + paramStepU;
+    double paramV = minParamV;
+    
+    double maxError = 0;
+    double maxError2 = 0;
+
+    // Warning!! Don't insert knots at end clamps.
+    
+    for ( int indexU = 1; indexU < numSteps - 1; indexU ++ )
+    {
+        paramV = minParamV + paramStepV;
+
+        int multi = ( origSurf -> knotVectorU() ).findMultiplicity ( paramU );
+        
+        testSurf -> insertKnot ( paramU, snlSurface::SNL_U_DIR );
+        
+        testSurf2 -> insertKnot ( paramU, snlSurface::SNL_U_DIR, testSurf2 -> degreeU() - multi );
+        //testSurf2 -> insertKnot ( paramU, snlSurface::SNL_U_DIR, 4 );
+
+        //( testSurf2 -> controlPointNet() ).printCompare ( testSurf -> controlPointNet() );
+        
+        for ( int indexV = 1; indexV < numSteps - 1; indexV ++ )
+        {
+            // Evaluate before knot insertion.
+
+            snlPoint original = origSurf -> eval ( paramU, paramV );
+
+            // Insert knot and evaluate.
+            
+            if ( indexU < 2 )
+            {
+                multi = ( origSurf -> knotVectorV() ).findMultiplicity ( paramV );
+        
+                testSurf -> insertKnot ( paramV, snlSurface::SNL_V_DIR );
+                
+                testSurf2 -> insertKnot ( paramV, snlSurface::SNL_V_DIR, testSurf2 -> degreeV() - multi );
+                //testSurf2 -> insertKnot ( paramV, snlSurface::SNL_V_DIR, 4 );
+            }
+            
+            snlPoint inserted = testSurf -> eval ( paramU, paramV );
+            snlPoint inserted2 = testSurf2 -> eval ( paramU, paramV );
+
+            double error = snlVector ( original, inserted ).length();
+            double error2 = snlVector ( original, inserted2 ).length();
+
+            //cout << "Param U, V: " << paramU << ", " << paramV << "Error: " << error << " Error2: " << error2 << "\n";
+
+            if ( error > maxError ) maxError = error;
+            if ( error2 > maxError2 ) maxError2 = error2;
+
+            paramV += paramStepV;
+        }
+
+        paramU += paramStepU;
+    }
+
+    delete origSurf;
+    delete testSurf;
+    delete testSurf2;
+
+    if ( maxError > 1.5e-13 ) passed = false;
+    if ( maxError2 > 1.5e-13 ) passed = false;
+
+    cout << "\tSurface Single Knot Insertion - Max Error = " << maxError << "\n";
+    cout << "\tSurface Multiple Knot Insertion - Max Error = " << maxError2 << "\n";
+    
+    if ( ! passed )
+        cout << "\tFailed\n";
+    else
+        cout << "\tPassed\n";
+    
+    return passed;
+}
+
+bool testDerivEval()
+{
+    // Test derivative evaluation.
+    // ---------------------------
+    
+    cout << "\n\n";
+    
+    bool passed = true;
+    
+    snlSurface* testSurf = generateSurface();
+    
+    int numSteps = 10;
+    
+    // Step through surface and evaluate.
+    
+    double minParamU = ( testSurf -> knotVectorU() ).min();
+    double minParamV = ( testSurf -> knotVectorV() ).min();
+    double maxParamU = ( testSurf -> knotVectorU() ).max();
+    double maxParamV = ( testSurf -> knotVectorV() ).max();
+    
+    double paramStepU = ( maxParamU - minParamU ) / ( numSteps - 1 );
+    double paramStepV = ( maxParamV - minParamV ) / ( numSteps - 1 );
+    
+    double paramU = minParamU;
+    double paramV = minParamV;
+    
+    double deltaU_val = ( maxParamU - minParamU ) / 1000000.0;
+    double deltaV_val = ( maxParamV - minParamV ) / 1000000.0;
+    
+    double maxError = 0;
+    double maxMixedPartialError = 0;
+    
+    for ( int indexU = 0; indexU < numSteps; indexU ++ )
+    {
+        paramV = minParamV;
+        
+        for ( int indexV = 0; indexV < numSteps; indexV ++ )
+        {
+            // Get derivatives            
+            snlPoint* derivs = testSurf -> evalDerivs ( paramU, paramV, 2, 2 );  // Calculate 2nd derivs as well.
+            
+            double deltaU, deltaV;
+            
+            if ( paramU < maxParamU )
+                deltaU = deltaU_val;
+            else
+                deltaU = - deltaU_val;
+                
+            if ( paramV < maxParamV )
+                deltaV = deltaV_val;
+            else
+                deltaV = - deltaV_val;
+                
+            snlVector velocityU ( derivs [ 3 ] );
+            snlVector velocityV ( derivs [ 1 ] );
+            
+            // Get points a little tiny delta from evaluated point.
+            
+            // Vo.t + 0.5 a.(t*t).
+            
+            snlPoint deltaPointU = derivs [ 0 ];
+            
+            deltaPointU += velocityU * deltaU;
+            
+            deltaPointU += derivs [ 6 ] * ( deltaU * deltaU * 0.5 );
+            
+            snlPoint deltaPointV = derivs [ 0 ];
+            
+            deltaPointV += velocityV * deltaV;
+            
+            deltaPointV += derivs [ 2 ] * ( deltaV * deltaV * 0.5 );
+            
+            // Calculate distance between actual and approximated points.
+            
+            snlVector deltaVectorU ( testSurf -> eval ( paramU + deltaU, paramV ), deltaPointU );
+            snlVector deltaVectorV ( testSurf -> eval ( paramU, paramV + deltaV ), deltaPointV );            
+            
+            double distU = deltaVectorU.length();
+            double distV = deltaVectorV.length();
+            
+            //cout << "ParamU: " << paramU << "  ParamV: " << paramV << "  distU: " << distU << " distV: " << distV << "\n";
+            
+            // Asses first mixed partial.                        
+            
+            snlVector deltaVelocityU ( derivs [ 4 ] );
+            deltaVelocityU *= deltaV;
+            deltaVelocityU += velocityU;
+            
+            snlVector deltaVelocityV ( derivs [ 4 ] );
+            deltaVelocityV *= deltaU;
+            deltaVelocityV += velocityV;
+            
+            
+            snlPoint* derivsDeltaU = testSurf -> evalDerivs ( paramU + deltaU, paramV, 2, 2 );
+            snlPoint* derivsDeltaV = testSurf -> evalDerivs ( paramU, paramV + deltaV, 2, 2 );
+            
+            double discrMixedU = deltaVelocityU.dot ( derivsDeltaV [ 3 ] ) - snlVector ( derivsDeltaV [ 3 ] ).lengthSqrd();
+            discrMixedU /= snlVector ( derivsDeltaV [ 3 ] ).lengthSqrd();
+            
+            double discrMixedV = deltaVelocityV.dot ( derivsDeltaU [ 1 ] ) - snlVector ( derivsDeltaU [ 1 ] ).lengthSqrd();
+            discrMixedV /= snlVector ( derivsDeltaU [ 1 ] ).lengthSqrd();
+            
+            delete[] derivsDeltaU;
+            delete[] derivsDeltaV;
+            
+            //cout << "ParamU: " << paramU << "  ParamV: " << paramV << "  discrU: " << discrMixedU << " discrV: " << discrMixedV << "\n";
+                                    
+            // Final processing
+            
+            paramV += paramStepV;
+            
+            if ( paramV > maxParamV ) paramV = maxParamV;
+            
+            if (  distU > maxError ) maxError = distU;
+            
+            if (  distV > maxError ) maxError = distV;
+            
+            if ( discrMixedV > maxMixedPartialError ) maxMixedPartialError = discrMixedV;
+            if ( discrMixedU > maxMixedPartialError ) maxMixedPartialError = discrMixedU;
+            
+            delete[] derivs;
+        }
+        
+        paramU += paramStepU;
+        
+        if ( paramU > maxParamU ) paramU = maxParamU;        
+    }
+    
+    if ( maxError > 1.0e-12 || maxMixedPartialError > 1.0e-9 ) passed = false;    
+    
+    // Clean up, report and return.
+    
+    cout << "\tSurface Derivatives, velocity / acceleration approximation - Max Error = " << maxError << "\n";
+    cout << "\tSurface Derivatives, mixed partial velocity approximation - Max Error = " << maxMixedPartialError << "\n";
+    
+    delete testSurf;
+    
+    if ( ! passed )
+    {    
+        cout << "\t\tFailed\n";
+        return false;
+    }
+    else
+        cout << "\tPassed\n";
+    
+    return passed;    
+}
+
+bool testSurfaceProjection()
+{
+    // Test Surface Projection
+    // -----------------------
+    
+    cout << "\n\n";
+    
+    bool passed = true;
+    
+    //snlSurface* testSurf = generateSurface();
+    snlSurface* testSurf = generateSurface2();
+    
+    int numSteps = 10;
+    
+    int maxPass = 10;
+    
+    // Step through surface and evaluate.
+    
+    double minParamU = ( testSurf -> knotVectorU() ).min();
+    double minParamV = ( testSurf -> knotVectorV() ).min();
+    double maxParamU = ( testSurf -> knotVectorU() ).max();
+    double maxParamV = ( testSurf -> knotVectorV() ).max();
+    
+    double paramStepU = ( maxParamU - minParamU ) / ( numSteps - 1 );
+    double paramStepV = ( maxParamV - minParamV ) / ( numSteps - 1 );
+    
+    double paramU = 0.0;
+    double paramV = 0.0;    
+    
+    double maxError = 0;
+    double maxErrorFast = 0;
+    
+    double normTol = 1.0e-6;
+    double iterTol = 1.0e-8;
+
+    double normLength = 0.01;
+
+    int numEval = numSteps * numSteps;
+
+    // Fill array with evaluated points.
+
+    snlPoint* evalPts = new snlPoint [ numEval ];
+
+    int index = 0;
+    
+    for ( int indexU = 0; indexU < numSteps; indexU ++ )
+    {
+        paramV = minParamV;
+        
+        for ( int indexV = 0; indexV < numSteps; indexV ++ )
+        {
+            snlPoint point;
+            snlVector velU;
+            snlVector velV;
+
+            testSurf -> velocities ( paramU, paramV, point, velU, velV );
+
+            snlVector normal;
+
+            normal.crossProduct ( velU, velV );
+
+            normal.length ( normLength );
+
+            point += normal;
+                
+            evalPts [ index ] = point;
+            
+/*
+if ( index == 1 )
+{
+            //cout << "(" << indexU << ", " << indexV << ") ";
+            cout << index << "- EvalParamU: " << paramU << "  EvalParamV: " << paramV << "\n";
+
+    int numReturned;
+    snlSurfLocn* projected = testSurf -> project ( evalPts + index, 1, &numReturned, iterTol, normTol, maxPass );
+    for ( int retIndex = 0; retIndex < numReturned; retIndex ++ )
+    {
+        cout << "Num Returned: " << numReturned << "\n";
+        cout << "ParamU: " << projected [ retIndex ].paramU << " ParamV: " << projected [ retIndex ].paramV
+             << "  Dist: " << projected [ retIndex ].dist << "\n";
+    }
+    delete[] projected;
+}
+*/          
+            paramV += paramStepV;
+            
+            if ( paramV > maxParamV ) paramV = maxParamV;
+
+            index ++;
+        }
+
+        paramU += paramStepU;
+        
+        if ( paramU > maxParamU ) paramU = maxParamU;
+    }
+    
+    int numReturned;
+    int numReturnedFast;
+
+    snlSurfLocn* projected = testSurf -> project ( evalPts, numEval, &numReturned, iterTol, normTol, maxPass );
+    snlSurfLocn* fastProjected = testSurf -> fastProject ( evalPts, numEval, &numReturnedFast, iterTol, normTol, maxPass,
+                                                           1, 2 );
+
+    //cout << "Fast -> Num Sent: " << numEval << " Num Returned: " << numReturnedFast << "\n";
+
+    for ( index = 0; index < numReturned; index ++ )
+    {
+        if ( maxError < projected [ index ].cos )
+            maxError = projected [ index ].cos;
+
+        if ( maxErrorFast < fastProjected [ index ].cos )
+            maxErrorFast = fastProjected [ index ].cos;
+
+        if ( fastProjected [ index ].cos > normTol )
+        {
+            cout << "Fast - not under tolerance - ptIndex: " << fastProjected [ index ].origPtIndex << "\n";
+        }
+
+        if ( index )
+        {
+            if ( projected [ index ].origPtIndex - projected [ index - 1 ].origPtIndex > 1 )
+                cout << "Hole Found - start: " <<  projected [ index - 1 ].origPtIndex << "  end: "
+                     << projected [ index ].origPtIndex << "\n";
+
+            if ( fastProjected [ index ].origPtIndex - fastProjected [ index - 1 ].origPtIndex > 1 )
+                cout << "Hole Found - start: " <<  fastProjected [ index - 1 ].origPtIndex << "  end: "
+                     << fastProjected [ index ].origPtIndex << "\n";
+        }
+        else
+        {
+            if ( projected [ index ].origPtIndex != 0 )
+                cout << "Starts at: " << projected [ index ].origPtIndex << "\n";
+
+            if ( fastProjected [ index ].origPtIndex != 0 )
+                cout << "Starts at: " << fastProjected [ index ].origPtIndex << "\n";
+        }
+    }
+
+    delete[] projected;
+    delete[] fastProjected;
+    delete[] evalPts;
+    
+    if ( maxError > normTol ) passed = false;
+    if ( maxErrorFast > normTol ) passed = false;
+    
+    // Clean up, report and return.    
+    
+    cout << "\tTolerance = " << normTol << ", Max Error = " << maxError << " - "
+         << "Fast Max Error = " << maxErrorFast << " - ";
+
+    if ( numEval > numReturned )
+    {
+        cout << "\nNumber Sent: " << numEval << " Number Returned: " << numReturned << " - ";
+        passed = false;
+    }
+
+    if ( numEval > numReturnedFast )
+    {
+        cout << "\nFast Proj -> Number Sent: " << numEval << " Number Returned: " << numReturned << " - ";
+        passed = false;
+    }
+    
+    delete testSurf;
+    
+    if ( ! passed )
+    {    
+        cout << "Failed\n";
+        return false;
+    }
+    else
+        cout << "Passed\n";
+    
+    return passed;    
+}
+
+bool testSquareLinear()
+{
+   // Test square linear class.
+    
+    double* coeffs = new double [ 16 ];
+    
+    double coeffVals [ 16 ] = { -2,  2, -4,  -6,
+                                -3,  6,  3, -15,
+                                 5, -8, -1,  17,
+                                 1,  1, 11,   7 };
+    
+    for ( int index = 0; index < 16; index ++ )
+        coeffs [ index ] = coeffVals [ index ];
+        
+    double * rhs = new double [ 12 ];
+    
+    double rhsVals [ 12 ] = { -4,  2, -7,
+                              -3,  8,  4,
+                               9, -2, -5,
+                               7, 10,  6 };
+    
+    for ( int index = 0; index < 12; index ++ )
+        rhs [ index ] = rhsVals [ index ];
+    
+    snlSquareLinear solver ( 4, 3, coeffs, rhs );
+    
+    //solver.print();
+    
+    // Compare the solutions to known values.
+    
+    float knownVals [ 12 ] = { 35, 655.0 / 6.0, - 1175.0 / 12.0,
+                               28, 554.0 / 6.0, - 1006.0 / 12.0,
+                               -7, -142.0 / 6.0, 278.0 / 12.0,
+                               3, 59.0 / 6.0, -115.0 / 12.0 };
+                               
+    bool success = true;
+    
+    for ( int index = 0; index < 12; index ++ )
+    {
+        if ( ( (float) rhs [ index ] ) != knownVals [ index ] ) success = false;
+    }
+    
+    return success;
+}
+
+bool testCurveInterpolation()
+{
+    // Create some points to interpolate.
+    
+    snlPoint* points = new snlPoint [ 10 ];
+    
+    double x = 0.0;
+    double y = 0.0;
+    double z = 10.0;
+    double w = 1.0;
+    
+    double zIncr = 1.0;
+    
+    for ( int count = 0; count < 10; count ++ )
+    {
+        points [ count ].components ( x, y, z, w );
+        
+        x += 1.0;
+        y += 1.0;
+        z -= zIncr;
+        
+        zIncr -= 0.2;
+    }
+    
+    knot* params;
+
+    // Generate curve.
+    
+    cout << "\n\tGlobal Interpolation ... ";
+    snlCurve curve ( points, 10, snlCurve::SNL_GLOBAL_INTERPOLATION, 3, false, &params );
+    
+    double maxError = 0.0;
+    
+    // Evaluate curve at params and check against control points.    
+    
+    for ( int count = 0; count < 10; count ++ )
+    {
+        snlPoint pt = curve.evalHmg ( params [ count ] );
+        
+        snlVector vect ( points [ count ], pt );
+        
+        //cout << " Point " << count << " error: " << vect.length() << "\n";
+        
+        if ( maxError < vect.length() ) maxError = vect.length();
+    }
+    
+    cout << "Max Error = " << maxError << " - ";
+    
+    if ( maxError > 1.0e-14 )
+        cout << "Failed\n";
+    else
+        cout << "Passed\n";
+    
+    delete[] points;
+    delete[] params;
+    
+    return true;
+    
+}
+
+bool testKnotRemoval()
+{
+    snlCurve* curve = generateCurve();
+    
+    double maxParam = curve -> maxParam();
+    double minParam = curve -> minParam();
+    double paramStep = ( maxParam - minParam ) / 10.0;
+
+    double maxError = 0;
+
+    for ( double param = minParam + paramStep; param < maxParam; param += paramStep )
+    {
+        // Insert and remove knot at param up to degree times.
+        
+        int multi = curve -> knotVector().findMultiplicity ( param );
+
+        curve -> insertKnots ( param, curve -> degree() - multi, true );        
+        
+        for ( int removalNum = 0; removalNum < curve -> degree() - multi; removalNum ++ )        
+        {
+            unsigned rSpan = curve -> knotVector().findSpan ( param );            
+            
+            double error = curve -> removeKnot ( rSpan, 0.0 );            
+            
+            if ( error > maxError ) maxError = error;
+        }        
+
+    }
+    
+    cout << "Max Error = " << maxError << " - ";
+    
+    if ( maxError > 1.0e-12 )
+    {
+        cout << "Failed\n";
+        return false;
+    }
+    else
+        cout << "Passed\n";
+    
+    delete curve;
+    
+    return true;
+}
+
+bool testDegreeElevation()
+{
+    cout << "\n\n";
+
+    snlCurve* curve = generateCurve();
+    
+    bool passed = true;
+            
+    // Generate points on original curve.
+    
+    snlPoint* testPoints = new snlPoint [ 100 ];
+    
+    double maxParam = curve -> maxParam();
+    double minParam = curve -> minParam();
+    
+    double paramStep = ( maxParam - minParam ) / 99.0;
+    
+    double param = minParam;
+    
+    for ( int index = 0; index < 100; index ++ )
+    {
+        testPoints [ index ] = curve -> eval ( param );
+        
+        param += paramStep;
+    }
+    
+    double maxError = 0.0;
+        
+    // Test elevation by n degrees.
+    
+    for ( int degElev = 1; degElev <= 5; degElev ++ )
+    {
+    
+        snlCurve* compareCurve = new snlCurve ( *curve );
+            
+        compareCurve -> elevateDegree ( degElev );
+        
+        param = minParam;
+        
+        for ( int index = 0; index < 100; index ++ )
+        {
+            snlPoint testPoint = compareCurve -> eval ( param );
+            
+            param += paramStep;
+            
+            double error = snlVector ( testPoint, testPoints [ index ] ).length();
+            
+            if ( error > maxError ) maxError = error;
+        }
+        
+        cout << "\tCurve Elevation By " << degElev << " - Max Error = " << maxError << "\n";
+        
+        delete compareCurve;
+        
+        if ( maxError > 1.5e-14 ) passed = false;
+    }    
+            
+    delete curve;
+    delete[] testPoints;
+    
+    if ( ! passed )
+    {    
+        cout << "\tFailed\n";
+        return false;
+    }
+    else
+        cout << "\tPassed\n";
+    
+    return passed;
+}
+
+bool testCurveAppend()
+{
+    bool passed = true;
+    
+    // Generate two curves that can be joined.
+    
+    snlCurve* curve1 = generateCurve();
+    
+    snlCurve* curve2 = generateReverseCurve();
+    
+    // Sample points along both curves to test later.
+    
+    snlPoint* testPoints1 = new snlPoint [ 10 ];    
+    
+    double maxParam1 = curve1 -> maxParam();
+    double minParam1 = curve1 -> minParam();
+    
+    double paramStep = ( maxParam1 - minParam1 ) / 9.0;
+    
+    double param = minParam1;
+    
+    for ( int index = 0; index < 10; index ++ )
+    {
+        testPoints1 [ index ] = curve1 -> eval ( param );
+        
+        param += paramStep;
+    }
+    
+    snlPoint* testPoints2 = new snlPoint [ 10 ];
+    
+    double maxParam2 = curve2 -> maxParam();
+    double minParam2 = curve2 -> minParam();
+    
+    paramStep = ( maxParam2 - minParam2 ) / 9.0;
+    
+    param = minParam2;
+    
+    for ( int index = 0; index < 10; index ++ )
+    {
+        testPoints2 [ index ] = curve2 -> eval ( param );
+        
+        param += paramStep;
+    }
+    
+    // Append curve2 to curve1.
+    
+    int origSize = curve1 -> size();
+    
+    curve1 -> appendCurve ( curve2, false );
+    
+    // Step through
+    
+    double joinParam = curve1 -> param ( origSize );    
+    
+    double paramStep1 = joinParam / 9.0;
+    double paramStep2 = ( curve1 -> maxParam() - joinParam ) / 9.0;
+    
+    double param1 = curve1 -> minParam();
+    double param2 = joinParam;
+    
+    double maxError = 0.0;
+        
+    for ( int index = 0; index < 10; index ++ )
+    {
+            snlPoint testPoint1 = curve1 -> eval ( param1 );
+            snlPoint testPoint2 = curve1 -> eval ( param2 );
+            
+            param1 += paramStep1;
+            param2 += paramStep2;
+            
+            double error1 = snlVector ( testPoint1, testPoints1 [ index ] ).length();
+            double error2 = snlVector ( testPoint2, testPoints2 [ index ] ).length();
+            
+            if ( error1 > maxError ) maxError = error1;
+            if ( error2 > maxError ) maxError = error2;
+    }
+    
+    cout << "Max Error = " << maxError << " - ";
+    
+    if ( maxError > 2.0e-14 )
+    {
+        cout << "Failed\n";
+        passed = false;
+    }
+    else
+        cout << "Passed\n";
+    
+    delete curve1;
+    delete curve2;
+    
+    delete[] testPoints1;
+    delete[] testPoints2;
+    
+    return passed;
+}
+
+int main ( int argc, char* argv[] )
+{
+    bool allTestsPassed = true;
+
+    cout << "\nTesting surface point interpolation ... " << flush;
+    if ( ! test_surfaceInterp() ) allTestsPassed = false;
+
+    cout << "\nTesting surface refinement ... " << flush;
+    if ( ! test_surfaceRefine() ) allTestsPassed = false;
+
+    cout << "\nTesting ambiguous edge detection ... " << flush;
+    if ( ! test_ambig() ) allTestsPassed = false;
+
+    //cout << "\nTesting Surface Of Rotation ... " << flush;
+    //if ( ! testSurfaceOfRevolution() ) allTestsPassed = false;
+
+    cout << "\nTesting Surface Point Inversion ... " << flush;
+    if ( ! testSurfacePointInversion() ) allTestsPassed = false;
+
+    cout << "\nTesting Surface Convex Bezier Decomposition ... " << flush;
+    if ( ! testSurfaceConvexBezierSegmentation() ) allTestsPassed = false;
+
+    cout << "\nTesting Surface Bezier Decomposition ... " << flush;
+    if ( ! testSurfaceBezierSegmentation() ) allTestsPassed = false;
+
+    cout << "\nTesting Surface Knot Insertion ... " << flush;
+    if ( ! testSurfaceKnotInsert() ) allTestsPassed = false;
+    
+    cout << "\nTesting Derivative Evaluation ... " << flush;
+    if ( ! testDerivEval() ) allTestsPassed = false;
+    
+    cout << "\nTesting Surface Projection ... " << flush;
+    if ( ! testSurfaceProjection() ) allTestsPassed = false;
+    
+    cout << "\nTesting snlSquareLinear ... " << flush;
+    if ( testSquareLinear() )    
+        cout << "Passed\n";
+    else
+    {
+        cout << "Failed\n";
+        allTestsPassed = false;
+    }
+
+    cout << "\nTesting Curve Interpolation\n";
+    if ( ! testCurveInterpolation() ) allTestsPassed = false;
+    
+    cout << "\nTesting Curve Knot Removal ... " << flush;
+    if ( ! testKnotRemoval() ) allTestsPassed = false;    
+    
+    cout << "\nTesting Degree Elevation ... " << flush;
+    if ( ! testDegreeElevation() ) allTestsPassed = false;
+    
+    cout << "\nTesting Curve Append ... " << flush;
+    if ( ! testCurveAppend() ) allTestsPassed = false;
+    
+    cout << "\n";
+    
+    if ( allTestsPassed )
+        cout << "All tests have passed :-)\n\n";
+    else
+        cout << "*** Some tests have not passed ***\n\n";
+}
+
+
diff --git a/src/snlTransform.cpp b/src/snlTransform.cpp
new file mode 100644
index 0000000..fd18d01
--- /dev/null
+++ b/src/snlTransform.cpp
@@ -0,0 +1,343 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** Geometric Transformations ***
+
+#include "snlTransform.h"
+
+#ifdef SGI_MIPS
+
+    #include <math.h>
+    
+#else
+
+    #include <cmath>
+    using namespace std;
+    
+#endif
+
+snlTransform::snlTransform()
+    : snlMatrix_4X4()
+{
+    ident();
+}
+
+snlTransform::snlTransform ( double* matrix )
+    : snlMatrix_4X4()
+{
+    // Copy matrix into transform. Expects OpenGL style array NOT c/c++ style.
+    // -----------------------------------------------------------------------
+
+    for ( int index = 0; index < 16; index ++ )
+        element [ index ] = matrix [ index ];
+}
+
+void snlTransform::transform ( snlPoint* pt )
+{
+    // Transform given point with matrix
+    // ---------------------------------
+    // pt:      Homogeneous point to process.
+
+    transform ( pt -> elements );
+}
+
+void snlTransform::transform ( snlPoint& pt )
+{
+    // Transform given point with matrix
+    // ---------------------------------
+    // pt:      Homogeneous point to process.
+
+    transform ( pt.elements );
+}
+
+void snlTransform::transform ( snlVector& vect )
+{
+    // Transform given vector with matrix
+    // ----------------------------------
+    // Vect:    Vector to process.
+
+    transform ( vect.elements );
+}
+
+void snlTransform::transform ( double* elements )
+{
+    // Transform given point elements.
+    // -------------------------------
+    
+    double* ptArray = elements;
+
+    double  tmpPt [ 4 ] = { 0.0, 0.0, 0.0, 0.0 };
+    double  cVal;
+
+    int     mRow;
+    int     mIndex = 0;
+
+    for ( int ptRow = 0; ptRow < 4; ptRow ++ )
+    {
+        cVal =  ptArray [ ptRow ];
+
+        for ( mRow = 0; mRow < 4; mRow ++ )
+        {
+            tmpPt [ mRow ] += cVal * element [ mIndex ++ ];
+        }
+    }
+    
+    for ( int index = 0; index < 4; index ++ )
+        elements [ index ] = tmpPt [ index ];
+}
+
+void snlTransform::translate ( double x, double y, double z, bool pre )
+{
+    // Apply translation
+    // -----------------
+    // x, y, z:     Translation coordinates.
+    // pre:         Pre multiply instead of post multiply.
+
+    if ( x == 0.0 && y == 0.0 && z == 0.0 ) return;
+
+    // Construct translation matrix.
+
+    snlMatrix_4X4   matrix;
+
+    matrix.translateIdent ( x, y, z );
+
+    // Multiply it to current transform matrix.
+
+    multiply ( matrix, pre );
+}
+
+void snlTransform::rotateX ( double angle, bool pre )
+{
+    // Rotate about +ve x axis
+    // -----------------------
+    // angle:       Rotation angle in radians.
+
+    snlMatrix_4X4   matrix;
+
+    matrix.rotateXIdent ( cos ( angle ), - sin ( angle), sin ( angle ), cos ( angle ) );
+
+    // Multiply it to current transform matrix.
+
+    multiply ( matrix, pre );
+
+}
+
+void snlTransform::rotateY ( double angle, bool pre )
+{
+    // Rotate about +ve y axis
+    // -----------------------
+    // angle:       Rotation angle in radians.
+
+    snlMatrix_4X4   matrix;
+
+    matrix.rotateYIdent ( cos ( angle ), sin ( angle), - sin ( angle ), cos ( angle ) );
+
+    // Multiply it to current transform matrix.
+
+    multiply ( matrix, pre );
+
+}
+
+void snlTransform::rotateZ ( double angle, bool pre )
+{
+    // Rotate about +ve z axis
+    // -----------------------
+    // angle:       Rotation angle in radians.
+
+    snlMatrix_4X4   matrix;
+
+    matrix.rotateZIdent ( cos ( angle ), - sin ( angle), sin ( angle ), cos ( angle ) );
+
+    // Multiply it to current transform matrix.
+
+    multiply ( matrix, pre );
+
+}
+
+void snlTransform::rotate ( double angle, snlPoint& axisStart, snlVector& axisDirection, bool pre )
+{
+    // Rotation wrapper function.
+    // --------------------------
+    
+    snlPoint axisEnd = axisStart + axisDirection;
+    
+    snlTransform tmpTransform;
+    
+    tmpTransform.rotate ( angle, axisStart, axisEnd );
+    
+    multiply ( tmpTransform, pre );
+}
+
+void snlTransform::rotate ( double angle, snlPoint& axisStart, snlPoint& axisEnd )
+{
+    // Rotate about an arbitrary axis
+    // ------------------------------
+    // angle:       Angle to rotate about axis in radians.
+    // axisStart:   Start of axis.
+    // axisEnd:     End of axis.
+    //
+    // Notes:       Assumes axisStart and axisEnd have been normalised.
+    //              Only works if transform initialised to ident.
+
+    snlMatrix_4X4   matrix;
+
+    snlVector vect ( axisStart, axisEnd );
+    vect.unitise();
+
+    // Move axis to origin
+    translate ( - axisStart.x(), - axisStart.y(), - axisStart.z(), true );
+
+    if ( vect.y() == 0.0 && vect.z() == 0.0 )
+    {
+        // Just a rotation about the x axis.
+        
+        if ( vect.x() > 0.0 )
+            rotateX ( angle, true );
+        else
+            rotateX ( - angle, true );
+    }
+    else if ( vect.x() == 0.0 && vect.z() == 0.0 )
+    {
+        // Just a rotation about the Y axis.
+        
+        if ( vect.y() > 0.0 )
+            rotateY ( angle, true );
+        else
+            rotateY ( - angle, true );
+    }
+    else if ( vect.x() == 0.0 && vect.y() == 0.0 )
+    {
+        // Just a rotation about the x axis.
+        
+        if ( vect.z() > 0.0 )
+            rotateZ ( angle, true );
+        else
+            rotateZ ( - angle, true );
+    }
+    else
+    {
+        // Rotate about x axis to yz plane.
+        double projHyp = sqrt ( vect.y() * vect.y() + vect.z() * vect.z() );
+
+        double sinRotX = vect.y() / projHyp;
+        double cosRotX = vect.z() / projHyp;
+
+        matrix.rotateXIdent ( cosRotX, - sinRotX, sinRotX, cosRotX );
+        multiply ( matrix, true );
+
+        // Rotate about y axis to z axis.
+        double sinRotY = - ( vect.x() );
+        double cosRotY = projHyp;
+
+        matrix.rotateYIdent ( cosRotY, sinRotY, - sinRotY, cosRotY );
+        multiply ( matrix, true );
+
+        // Rotate about z axis.
+        rotateZ ( angle, true );
+
+        // Inverse rotation about y axis. ie Rotate back into yz plane. Negate angle of rotation.
+        matrix.rotateYIdent ( cosRotY, - sinRotY, sinRotY, cosRotY );
+        multiply ( matrix, true );
+
+        // Inverse rotation about x axis.
+        matrix.rotateXIdent ( cosRotX, sinRotX, - sinRotX, cosRotX );
+        multiply ( matrix, true );
+    }
+
+    // Inverse translation.
+    translate ( axisStart.x(), axisStart.y(), axisStart.z(), true );
+}
+
+void snlTransform::scale ( double x, double y, double z, snlPoint& relTo, bool pre )
+{
+
+    // Add scaling to transform matrix.
+    // --------------------------------
+
+    snlMatrix_4X4   matrix;
+    snlMatrix_4X4   step;
+
+    bool isRelTo = ( relTo.x() != 0.0 || relTo.y() != 0.0 || relTo.z() != 0.0 );
+
+    if ( isRelTo ) relTo.normalise();
+
+    matrix.ident();
+
+    if ( isRelTo )
+    {
+        step.translateIdent ( - ( relTo.x() ), - ( relTo.y() ), - ( relTo.z() ) );
+
+        matrix.multiply ( step, true );
+    }
+
+    step.scaleIdent ( x, y, z );
+
+    matrix.multiply ( step, true );
+
+    // Translate back again.
+    if ( isRelTo )
+    {
+        step.translateIdent ( relTo.x(), relTo.y(), relTo.z() );
+
+        matrix.multiply ( step, true );
+    }
+
+    multiply ( matrix, pre );
+}
+
+void snlTransform::scale ( double x, double y, double z, bool pre )
+{
+    // Add scaling to transform matrix.
+    // --------------------------------
+
+    snlPoint tmpPt ( 0.0, 0.0, 0.0, 1.0 );
+
+    scale ( x, y, z, tmpPt, pre );
+
+}
+
+void snlTransform::align ( snlVector& vector1, snlVector& vector2, bool pre )
+{
+    //! @param vector1 Vector to align.
+    //! @param vector2 Vector to align vector 1 to.
+    //! @param pre Pre multiply result to existing matrix.
+    //!
+    //! @par Notes   Can be used for transforming coordinate systems.
+    //!              Rotates vector1 about normal to plane described by vector1 and vector2.
+    
+    // Get angle to rotate through.
+    double rotAngle = vector1.angle ( vector2 );
+
+    if ( rotAngle == 0.0 ) return;
+    
+    // Generate normal to both vectors.
+    snlVector normal;    
+    normal.crossProduct ( vector1, vector2 );
+    
+    snlTransform tmpTransform;
+    
+    snlPoint origin;
+        
+    // Rotate about normal.
+    if ( ( rotAngle != 0.0 ) && ( ! normal.isNull() ) )
+        tmpTransform.rotate ( rotAngle, origin, normal );    
+    
+    multiply ( tmpTransform, pre );
+}
+
+
+
diff --git a/src/snlTransform.h b/src/snlTransform.h
new file mode 100644
index 0000000..0622511
--- /dev/null
+++ b/src/snlTransform.h
@@ -0,0 +1,56 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** Geometric Transformations ***
+
+#ifndef SNLTRANSFORM_H
+#define SNLTRANSFORM_H
+
+#include "snlPoint.h"
+#include "snlVector.h"
+#include "snlMatrix_4x4.h"
+
+class snlTransform : public snlMatrix_4X4
+{
+    public:
+
+        snlTransform();
+
+        snlTransform ( double* matrix );
+
+        void translate ( double x, double y, double z, bool pre = false );
+
+        void rotateX ( double angle, bool pre = false );
+        void rotateY ( double angle, bool pre = false );
+        void rotateZ ( double angle, bool pre = false );
+        
+        void rotate ( double angle, snlPoint& axisStart, snlVector& axisDirection, bool pre = false);
+        void rotate ( double angle, snlPoint& axisStart, snlPoint& axisEnd );
+
+        void scale ( double x, double y, double z, snlPoint& relTo, bool pre = false );
+        void scale ( double x, double y, double z, bool pre = false );
+        
+        //! Generate transform that will align vector1 to vector2
+        void align ( snlVector& vector1, snlVector& vector2, bool pre = false );
+
+        void transform ( snlPoint* pt );
+        void transform ( snlPoint& pt );
+        void transform ( snlVector& vect );
+        void transform ( double* elements );
+};
+
+#endif
diff --git a/src/snlTriangleMesh.cpp b/src/snlTriangleMesh.cpp
new file mode 100644
index 0000000..1b9fdb9
--- /dev/null
+++ b/src/snlTriangleMesh.cpp
@@ -0,0 +1,143 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2005 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+
+// *** Mesh of Triangle Elements ***
+
+#include "snlTriangleMesh.h"
+
+snlTriangleMesh::snlTriangleMesh()
+{
+    array_page_size = 0;
+
+    init();
+}
+
+snlTriangleMesh::snlTriangleMesh ( int arrayPageSize )
+{
+    array_page_size = arrayPageSize;
+
+    init();
+}
+
+void snlTriangleMesh::init()
+{
+    // Initialisation function.
+    // ------------------------
+    
+    if ( array_page_size )
+    {
+        vertexes = new dynamicArray< snlVertex > ( array_page_size );
+        edges = new dynamicArray< snlTriangleEdge > ( array_page_size );
+        triangles = new dynamicArray< snlTriangle > ( array_page_size );
+    }
+    else
+    {
+        vertexes = new dynamicArray< snlVertex >;
+        edges = new dynamicArray< snlTriangleEdge >;
+        triangles = new dynamicArray< snlTriangle >;
+    }
+    
+    num_vertexes = 0;
+    num_edges = 0;
+    num_triangles = 0;
+}
+
+int snlTriangleMesh::addVertex ( snlVertex& vertex )
+{
+    // Add vertex to mesh.
+    // -------------------
+    // vertex:      Vertex to add.
+    //
+    // Returns:     Index of added vertex.
+
+    (*vertexes) [ num_vertexes ++ ] = vertex;
+
+    return num_vertexes - 1;
+}
+
+int snlTriangleMesh::addVertexes ( const snlVertex* vertexesToCopy, int numberToAdd )
+{
+    // Add Vertexes to mesh.
+    // ---------------------
+    // vertexesToCopy:  Pointer to vertexes to copy.
+    // numberToAdd:     Number of vertexes to being pointed to.
+    //
+    // Returns:      Index of last vertex added.
+
+    for ( int index = 0; index < numberToAdd; index ++ )
+        (*vertexes) [ num_vertexes ++ ] = vertexesToCopy [ index ];
+
+    return num_vertexes - 1;
+}
+
+int snlTriangleMesh::addEdge ( int vertexIndex1, int vertexIndex2 )
+{
+    // Add edge to mesh.
+    // -----------------
+    // vertexIndex1:    Index of edges first vertex.
+    // vertexIndex2:    Index of edges second vertex.
+    //
+    // returns:         Index of added edge.
+
+    snlTriangleEdge& edge = (*edges) [ num_edges ++ ];
+
+    edge.vertexIndex1 = vertexIndex1;
+    edge.vertexIndex2 = vertexIndex2;
+    edge.triangleIndex1 = -1;
+    edge.triangleIndex2 = -1;
+    
+    return num_edges - 1;
+}
+
+int snlTriangleMesh::addTriangle ( int edgeIndex1, int edgeIndex2, int edgeIndex3 )
+{
+    // Add triangle to mesh.
+    // ---------------------
+    // edgeIndex1:      Index of triangles first edge.
+    // edgeIndex2:      Index of triangles second edge.
+    // edgeIndex3:      Index of triangles third edge.
+    //
+    // Returns:         Index of added triangle.
+
+    int triIndex = num_triangles;  // New triangle's index.
+
+    num_triangles ++;
+
+    snlTriangle& triangle = (*triangles) [ triIndex ];
+
+    triangle.edgeIndex1 = edgeIndex1;
+    triangle.edgeIndex2 = edgeIndex2;
+    triangle.edgeIndex3 = edgeIndex3;
+
+    snlTriangleEdge& edge1 = (*edges) [ edgeIndex1 ];
+    snlTriangleEdge& edge2 = (*edges) [ edgeIndex2 ];
+    snlTriangleEdge& edge3 = (*edges) [ edgeIndex3 ];
+
+    if ( edge1.triangleIndex1 == -1 )
+        edge1.triangleIndex1 = triIndex;
+    else
+        edge1.triangleIndex2 = triIndex;
+
+    if ( edge2.triangleIndex1 == -1 )
+        edge2.triangleIndex1 = triIndex;
+    else
+        edge2.triangleIndex2 = triIndex;
+    
+    if ( edge3.triangleIndex1 == -1 )
+        edge3.triangleIndex1 = triIndex;
+    else
+        edge3.triangleIndex2 = triIndex;
+
+    return triIndex;
+}
+
diff --git a/src/snlTriangleMesh.h b/src/snlTriangleMesh.h
new file mode 100644
index 0000000..ee5de58
--- /dev/null
+++ b/src/snlTriangleMesh.h
@@ -0,0 +1,87 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2005 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+
+// *** Mesh of Triangle Elements ***
+
+#ifndef SNL_TRIANGLEMESH_H
+#define SNL_TRIANGLEMESH_H
+
+#include "snlVertex.h"
+
+#include "dynamicArray.h"
+
+#ifdef SGI_MIPS
+
+    #include <iostream.h>
+    #include <math.h>
+    
+#else
+
+    #include <iostream>
+    #include <cmath>
+    
+    using namespace std;
+    
+#endif
+
+typedef struct
+{
+    int     vertexIndex1;
+    int     vertexIndex2;
+    int     triangleIndex1;  // Used for efficient ordering of triangles.
+    int     triangleIndex2;
+
+} snlTriangleEdge;
+
+typedef struct
+{
+    int     edgeIndex1;
+    int     edgeIndex2;
+    int     edgeIndex3;
+
+} snlTriangle;
+
+class snlTriangleMesh
+{
+    public:
+
+        snlTriangleMesh();
+
+        snlTriangleMesh ( int arrayPageSize );
+
+        void init();
+
+        int addVertex ( snlVertex& vertex );
+        int addVertexes ( const snlVertex* vertexesToCopy, int numberToAdd );
+        int addEdge ( int vertexIndex1, int vertexIndex2 );
+        int addTriangle ( int edgeIndex1, int edgeIndex2, int edgeIndex3 );
+
+    protected:
+
+    private:
+
+        int         array_page_size;  // Number of array elements per page for "growable" arrays.
+        
+
+        dynamicArray< snlVertex >*          vertexes;
+        int                                 num_vertexes;
+
+        dynamicArray< snlTriangleEdge >*    edges;
+        int                                 num_edges;
+        
+        dynamicArray< snlTriangle >*        triangles;
+        int                                 num_triangles;
+};
+
+#endif
+
diff --git a/src/snlUtil.cpp b/src/snlUtil.cpp
new file mode 100644
index 0000000..e54170d
--- /dev/null
+++ b/src/snlUtil.cpp
@@ -0,0 +1,155 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2003 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+
+// *** Small Utility Classes and Functions ***
+
+#include "snlUtil.h"
+
+// Static member declarations
+// --------------------------
+
+binCoefs    bCoefs;  // Force constructor to build array.
+int         binCoefs::binCoefArray [ MAX_BINOMIAL ] [ MAX_BINOMIAL ];
+
+binCoefs::binCoefs()
+{
+    for ( int k = 0; k < MAX_BINOMIAL; k ++ )
+
+        for ( int i = 0; i < MAX_BINOMIAL; i ++ )
+        {
+            if ( k == i )
+                binCoefArray [ k ] [ i ] = 1;
+            else if ( i == 0 )
+                binCoefArray [ k ] [ i ] = 1;
+            else if ( i > k )
+                binCoefArray [ k ] [ i ] = 0;
+            else
+                binCoefArray [ k ] [ i ] = binCoefArray [ k - 1 ] [ i ] + binCoefArray [ k - 1 ] [ i - 1 ];
+        }
+}
+
+double distToLine ( snlPoint lineStart, snlPoint lineEnd, snlPoint compare )
+{
+    // Calculate distance to line from point.
+    // --------------------------------------
+    // lineStart:    Start of line.
+    // lineEnd:      End of line.
+    // compare:      Point to compare to line.
+    
+    lineStart.normalise();
+    lineEnd.normalise();
+    compare.normalise();
+    
+    snlVector lineV ( lineStart, lineEnd );    
+    
+    snlVector testV ( lineStart, compare );
+
+    // Calculate dot product.
+    double dotP = lineV.dot ( testV );
+
+    // Project test vector onto baseline vector.
+    double proj = dotP / lineV.length();
+
+    // Length of test vector.
+    double testLength = testV.length();
+
+    // Length of normal from baseline to test point.
+    double normDist = sqrt ( testLength * testLength - proj * proj );
+
+    return normDist;
+}
+
+snlVector projectToLine ( snlPoint lineStart, snlPoint lineEnd, snlPoint compare )
+{
+    // Calculate vector perpendicular to line from point.
+    // --------------------------------------------------
+    // lineStart:    Start of line.
+    // lineEnd:      End of line.
+    // compare:      Point to project to line.
+    //
+    // Notes:        Returned vector points _TO_ line if based from given point "compare".
+
+    lineStart.normalise();
+    lineEnd.normalise();
+    compare.normalise();
+    
+    snlVector lineV ( lineStart, lineEnd );    
+    
+    snlVector testV ( lineStart, compare );
+
+    // Calculate dot product.
+    double dotP = lineV.dot ( testV );
+
+    // Calculate length of test vector after it is projected onto baseline vector.
+    double proj = dotP / lineV.length();
+
+    // Generate projected vector.
+    
+    lineV.length ( proj );
+    lineV -= testV;
+
+    return lineV;
+}
+
+bool isInteriorToTriangle ( snlPoint& testPt, snlPoint& verticeA, snlVector& boundA1, snlVector& boundA2,
+                            snlPoint& verticeB, snlVector& boundB1, snlVector& boundB2 )
+{
+    // Check to see if a point is interior to a triangle.
+    // --------------------------------------------------
+    // testPt:      Point to test.
+    // verticeA:    First vertice to test against.
+    // boundA1:     Unit vector from vertice A that represents triangle boundary or edge.
+    // boundA2:     Unit vector from vertice A that represents triangle boundary or edge.
+    // verticeB:    Second vertice to test against.
+    // boundB1:     Unit vector from vertice B that represents triangle boundary or edge.
+    // boundB2:     Unit vector from vertice B that represents triangle boundary or edge.
+    //
+    // Notes:       Assumes testPt is coplanar to triangle.
+    //              Make sure bounds are unit vectors!
+
+    bool isInterior = true;
+
+    double triCos = boundA1.dot ( boundA2 );  // Cosine between two edges of the triangle.
+
+    snlVector testPointVector ( verticeA, testPt );  // Vector from triangle vertice to projected point.
+
+    testPointVector.unitise();
+
+    // Remember that the cosine that results from a dot product is clamped between 1 and -1.
+    // 1 represent and angle of 0 degrees and -1 represents and angle of 180 degrees.
+
+    if ( testPointVector.dot ( boundA1 ) < triCos || testPointVector.dot ( boundA2 ) < triCos )
+        isInterior = false;
+
+    // Must do a second check rooted at a different vertice.
+
+    triCos = boundB1.dot ( boundB2 );
+
+    testPointVector.calc ( verticeB, testPt );
+
+    testPointVector.unitise();
+
+    if ( testPointVector.dot ( boundB1 ) < triCos || testPointVector.dot ( boundB2 ) < triCos )
+        isInterior = false;
+
+    return isInterior;
+}
+
+void snlVersion ( int* major, int* minor, int* release )
+{
+    *major = SNL_VERSION_MAJOR;
+    *minor = SNL_VERSION_MINOR;
+    *release = SNL_VERSION_RELEASE;
+}
+
diff --git a/src/snlUtil.h b/src/snlUtil.h
new file mode 100644
index 0000000..06cb85b
--- /dev/null
+++ b/src/snlUtil.h
@@ -0,0 +1,73 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2003 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+
+// *** Small Utility Classes and Functions ***
+
+#ifndef SNLUTIL_H
+#define SNLUTIL_H
+
+#include "snlPoint.h"
+#include "snlVector.h"
+#include "snlVersion.h"
+
+#ifdef SGI_MIPS
+
+    #include <iostream.h>
+    #include <math.h>
+    
+#else
+
+    #include <iostream>
+    #include <cmath>
+    
+    using namespace std;
+    
+#endif
+
+#ifndef M_PI
+
+    #define M_PI 3.1415926535897932384626433832795
+        
+#endif
+
+#ifdef WIN32
+
+    #define isnan _isnan
+        
+#endif
+
+const int MAX_BINOMIAL = 64;  // Maximum binomial array dimension.
+
+class binCoefs
+{
+    // Generate a static array of Binomial Coefficients
+    // Array structure is [k][i] where k! / i! ( k - i )!.
+
+    public:
+
+        static int  binCoefArray [ MAX_BINOMIAL ] [ MAX_BINOMIAL ];
+
+        binCoefs();
+};
+
+double distToLine ( snlPoint lineStart, snlPoint lineEnd, snlPoint compare );
+snlVector projectToLine ( snlPoint lineStart, snlPoint lineEnd, snlPoint compare );
+
+bool isInteriorToTriangle ( snlPoint& testPt, snlPoint& verticeA, snlVector& boundA1, snlVector& boundA2,
+                            snlPoint& verticeB, snlVector& boundB1, snlVector& boundB2 );
+
+void snlVersion ( int* major, int* minor, int* release );
+
+#endif
+
+
diff --git a/src/snlVector.cpp b/src/snlVector.cpp
new file mode 100644
index 0000000..36e2005
--- /dev/null
+++ b/src/snlVector.cpp
@@ -0,0 +1,493 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "snlVector.h"
+
+snlVector::snlVector()
+{
+    homogeneous = false;
+}
+
+snlVector::snlVector ( const snlPoint& pt1, const snlPoint& pt2, bool hmg )
+{
+    // Create vector from two points pt1 -> pt2
+    // ----------------------------------------
+
+    homogeneous = hmg;    
+    
+    calc ( pt1, pt2 );    
+}
+
+snlVector::snlVector ( snlPoint& pt, bool hmg )
+{
+    homogeneous = hmg;
+    
+    snlPoint tmpPt ( pt );
+    
+    if ( ! hmg )
+    {
+        tmpPt.normalise();
+        elements [ 3 ] = 0.0;
+    }
+    else    
+        elements [ 3 ] = tmpPt.w();
+        
+    elements [ 0 ] = tmpPt.x();
+    elements [ 1 ] = tmpPt.y();
+    elements [ 2 ] = tmpPt.z();    
+}
+
+snlVector::snlVector ( double x, double y, double z, double w, bool hmg )
+{
+    homogeneous = hmg;
+    
+    elements [ 0 ] = x;
+    elements [ 1 ] = y;
+    elements [ 2 ] = z;
+    
+    if ( hmg )
+        elements [ 3 ] = w;
+    else
+        elements [ 3 ] = 0;
+}
+
+snlVector::snlVector ( snlVector& v1, snlVector& v2 )
+{
+    // Construct a vector that is normal to v1 and v2.
+    // -----------------------------------------------
+
+    homogeneous = false;
+
+    crossProduct ( v1, v2 );
+}
+
+void snlVector::calc ( const snlPoint& pt1, const snlPoint& pt2 )
+{
+    // Calculate vector given start and end points.
+    // --------------------------------------------
+
+    if ( homogeneous )
+    {
+        for ( int index = 0; index < 4; index ++ )
+            elements [ index ] = pt2.elements [ index ] - pt1.elements [ index ];
+    }
+    else
+    {        
+        for ( int index = 0; index < 3; index ++ )
+            elements [ index ] = ( pt2.elements [ index ] / pt2.elements [ 3 ] ) -
+                                 ( pt1.elements [ index ] / pt1.elements [ 3 ] );
+        elements [ 3 ] = 0.0;
+    }
+}
+
+void snlVector::crossProduct ( snlVector& v1, snlVector& v2 )
+{
+    // Calculate cross product of v1 X v2.
+    // -----------------------------------
+    // 
+    // Notes:   Stores result in this vector.
+
+    elements [ 0 ] = ( v1.elements [ 1 ] * v2.elements [ 2 ] ) -
+                     ( v1.elements [ 2 ] * v2.elements [ 1 ] );
+
+    elements [ 1 ] = ( v1.elements [ 2 ] * v2.elements [ 0 ] ) -
+                     ( v1.elements [ 0 ] * v2.elements [ 2 ] );
+
+    elements [ 2 ] = ( v1.elements [ 0 ] * v2.elements [ 1 ] ) -
+                     ( v1.elements [ 1 ] * v2.elements [ 0 ] );
+
+    elements [ 3 ] = 0.0;
+}
+
+double snlVector::dot ( snlVector& vect )
+{
+    // Calculate dot product between this vector and vect.
+    // ---------------------------------------------------
+    
+    if ( homogeneous )
+    {
+        return ( elements [ 0 ] * vect.elements [ 0 ] + 
+                 elements [ 1 ] * vect.elements [ 1 ] +
+                 elements [ 2 ] * vect.elements [ 2 ] +
+                 elements [ 3 ] * vect.elements [ 3 ] );
+    }
+    else
+    {
+        return ( elements [ 0 ] * vect.elements [ 0 ] + 
+                 elements [ 1 ] * vect.elements [ 1 ] +
+                 elements [ 2 ] * vect.elements [ 2 ] );
+    }
+}
+
+double snlVector::dot ( snlPoint& pt )
+{
+    // Calculate dot product between this vector and pt.
+    // -------------------------------------------------
+    // Notes:   Treats pt as a vector.
+    
+    double dotProd = elements [ 0 ] * pt.elements [ 0 ] + 
+                     elements [ 1 ] * pt.elements [ 1 ] +
+                     elements [ 2 ] * pt.elements [ 2 ];
+    
+    if ( homogeneous )       
+        dotProd += elements [ 3 ] * pt.elements [ 3 ];
+        
+    return dotProd;
+}
+
+
+snlVector snlVector::operator * ( double scalar )
+{
+    // Return vector multiplied by a scalar
+    // ------------------------------------
+
+    snlVector    retVect;
+
+    for ( int index = 0; index < 4; index ++ )
+        retVect.elements [ index ] = elements [ index ] * scalar;
+
+    return retVect;
+}
+
+snlVector snlVector::operator + ( snlVector& vect )
+{
+    // Add vect to this one.
+    // ---------------------
+
+    snlVector    retVect;
+
+    for ( int index = 0; index < 4; index ++ )
+        retVect.elements [ index ] = elements [ index ] + vect.elements [ index ];
+
+    return retVect;
+}
+
+snlVector snlVector::operator - ( snlVector& vect )
+{
+    // Subtract vect from this one.
+    // ----------------------------
+
+    snlVector    retVect;
+
+    for ( int index = 0; index < 4; index ++ )
+        retVect.elements [ index ] = elements [ index ] - vect.elements [ index ];
+
+    return retVect;
+}
+
+void snlVector::operator += ( snlVector& vect )
+{
+    elements [ 0 ] += vect.elements [ 0 ];
+    elements [ 1 ] += vect.elements [ 1 ];
+    elements [ 2 ] += vect.elements [ 2 ];
+    
+    if ( homogeneous )
+        elements [ 3 ] += vect.elements [ 3 ];
+}
+
+void snlVector::operator -= ( snlVector& vect )
+{
+    elements [ 0 ] -= vect.elements [ 0 ];
+    elements [ 1 ] -= vect.elements [ 1 ];
+    elements [ 2 ] -= vect.elements [ 2 ];
+    
+    if ( homogeneous )
+        elements [ 3 ] -= vect.elements [ 3 ];
+}
+
+void snlVector::operator *= ( double scalar )
+{
+    // Multiply this vector by scalar.
+    // -------------------------------
+
+    for ( int index = 0; index < 4; index ++ )
+        elements [ index ] *= scalar;
+}
+
+bool snlVector::operator == ( snlVector& compare )
+{
+    // Return true if compare is eqivalent to this vector.
+    // ---------------------------------------------------
+    
+    bool retVal = true;
+
+    if ( homogeneous )
+    {
+        for ( int index = 0; index < 4; index ++ )
+            if ( elements [ index ] != compare.elements [ index ] )
+                retVal = false;
+    }
+    else
+    {
+        for ( int index = 0; index < 3; index ++ )
+            if ( elements [ index ] != compare.elements [ index ] )
+                retVal = false;
+    }
+            
+    return retVal;
+}
+
+double snlVector::lengthSqrd()
+{
+    // Return length of this vector squared
+    // ------------------------------------
+
+    double sum = 0;
+
+    for ( int index = 0; index < 3; index ++ )
+        sum += elements [ index ] * elements [ index ];
+        
+    if ( homogeneous )
+        sum += elements [ 3 ] * elements [ 3 ];
+        
+    return sum;
+}
+
+double snlVector::length()
+{
+    // Return length of this vector
+    // ----------------------------
+
+    return sqrt ( lengthSqrd() );
+}
+
+void snlVector::length ( double val )
+{
+    // Set length of vector.
+    // ---------------------
+    // val:    Value to set length to.
+    
+    double multiplier = val / length();
+    
+    operator *= ( multiplier );
+}
+
+double snlVector::calcAbsCos ( snlVector& vect )
+{
+    // Calculate absolute cosine between this vector and vect.
+    // -------------------------------------------------------
+    
+    return fabs ( dot ( vect ) / ( length() * vect.length() ) );
+}
+
+double snlVector::angle ( snlVector& vect )
+{
+    // Calculate angle between this vector and vect
+    // --------------------------------------------
+    //
+    // Returns:     Value between 0 and PI in radians.
+
+    double cos_angle = dot ( vect ) / ( length() * vect.length() );
+
+    if ( cos_angle > 1.0 )
+        cos_angle = 1.0;
+    else if ( cos_angle < -1.0 )
+        cos_angle = -1.0;
+    
+    return acos ( cos_angle );
+}
+
+void snlVector::unitise()
+{
+    // Turn vector into unit vector
+    // ----------------------------
+
+    double len = length();
+
+    for ( int index = 0; index < 4; index ++ )
+        elements [ index ] /= len;
+}
+
+double snlVector::projectDist ( snlVector& fromVector )
+{
+    // Caclulate projection from tip of vector to this vector.
+    // -------------------------------------------------------
+    // fromVector:      Vector to project tip of.
+    //
+    // Notes:           Vectors are assumed to originate from the same point.
+
+    double dotP = dot ( fromVector );
+
+    return sqrt ( fromVector.lengthSqrd() - ( dotP * dotP / lengthSqrd() ) );
+}
+
+snlVector snlVector::project ( snlVector& ontoVector )
+{
+    // Project this vector onto another vector.
+    // ----------------------------------------
+    // ontoVector:    Vector to project onto.
+    //
+    // Returns:       Vector that is the result of the projection.
+    
+    double newLength = dot ( ontoVector ) / ontoVector.length();
+    
+    snlVector retVector = ontoVector;
+    
+    retVector.length ( newLength );
+    
+    return retVector;
+}
+
+void snlVector::projectXZ()
+{
+    // Project onto the X-Z plane.
+    // ---------------------------
+    
+    elements [ 1 ] = 0.0;  // y = 0.
+}
+
+void snlVector::projectXY()
+{
+    // Project onto the X-Y plane.
+    // ---------------------------
+    
+    elements [ 2 ] = 0.0;  // z = 0.    
+}
+
+void snlVector::projectYZ()
+{
+    // Project onto the Y-Z plane.
+    // ---------------------------
+    
+    elements [ 0 ] = 0.0;    
+}
+
+double snlVector::x()
+{
+    return elements [ 0 ];
+}
+
+double snlVector::y()
+{
+    return elements [ 1 ];
+}
+
+double snlVector::z()
+{
+    return elements [ 2 ];
+}
+
+double snlVector::w()
+{
+    return elements [ 3 ];
+}
+
+void snlVector::x ( double val )
+{
+    elements [ 0 ] = val;
+}
+
+void snlVector::y ( double val )
+{
+    elements [ 1 ] = val;
+}
+
+void snlVector::z ( double val )
+{
+    elements [ 2 ] = val;
+}
+
+void snlVector::w ( double val )
+{
+    elements [ 3 ] = val;
+}
+
+void snlVector::calcNormal ( snlPoint& pt1, snlPoint& pt2, snlPoint& pt3, snlPoint& pt4 )
+{
+    // Calculate normal and set this vector to it.
+    // -------------------------------------------
+    // NOTE:        Only does 3D vector product.
+
+    snlVector v1 ( pt1, pt2 );
+    snlVector v2 ( pt3, pt4 );
+
+    crossProduct ( v1, v2 );
+
+    unitise();
+}
+
+void snlVector::components ( double x, double y, double z, double w )
+{
+    elements [ 0 ] = x;
+    elements [ 1 ] = y;
+    elements [ 2 ] = z;
+    elements [ 3 ] = w;
+}
+
+void snlVector::components ( double x, double y, double z )
+{
+    elements [ 0 ] = x;
+    elements [ 1 ] = y;
+    elements [ 2 ] = z;
+    elements [ 3 ] = 0.0;
+}
+
+void snlVector::components ( double* x, double* y, double* z, double* w )
+{
+    *x = elements [ 0 ];
+    *y = elements [ 1 ];
+    *z = elements [ 2 ];
+    *w = elements [ 3 ];
+}
+
+void snlVector::components ( double* x, double* y, double* z )
+{
+    *x = elements [ 0 ];
+    *y = elements [ 1 ];
+    *z = elements [ 2 ];
+}
+
+void snlVector::zero()
+{
+    // Zero the vector.
+    // ----------------
+    
+    elements [ 0 ] = 0.0;
+    elements [ 1 ] = 0.0;
+    elements [ 2 ] = 0.0;
+    elements [ 3 ] = 0.0;
+}
+
+bool snlVector::isNull()
+{
+    if ( homogeneous )
+    {
+        if ( ! elements [ 0 ] &&
+             ! elements [ 1 ] &&
+             ! elements [ 2 ] &&
+             ! elements [ 3 ] )
+            return true;
+    }
+    else    
+    {
+        if ( ! elements [ 0 ] &&
+             ! elements [ 1 ] &&
+             ! elements [ 2 ] )
+        return true;
+    }
+
+    return false;        
+}
+
+void snlVector::print()
+{
+    // Print vector contents to cout.
+    // ------------------------------
+
+    cout << "X: " << elements [ 0 ] << " Y: " << elements [ 1 ]
+         << " Z: " << elements [ 2 ] << " W: " << elements [ 3 ] << "\n";
+}
diff --git a/src/snlVector.h b/src/snlVector.h
new file mode 100644
index 0000000..4101335
--- /dev/null
+++ b/src/snlVector.h
@@ -0,0 +1,111 @@
+// libSNL - Simple Nurbs Library
+// Copyright Scott A.E. Lanham, Australia.
+// ---------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef SNLVECTOR_H
+#define SNLVECTOR_H
+
+class snlPoint;
+
+#include "snlPoint.h"
+
+#ifdef SGI_MIPS
+
+    #include <iostream.h>
+    #include <math.h>
+    
+#else
+
+    #include <iostream>
+    #include <cmath>
+    
+    using namespace std;
+    
+#endif
+
+class snlVector
+{
+    public:
+
+        snlVector();
+        snlVector ( const snlPoint& pt1, const snlPoint& pt2, bool hmg = false );  // pt1 -> pt2. ( ie pt2 - pt1 ).
+        snlVector ( snlPoint& pt, bool hmg = false );        
+        snlVector ( double x, double y, double z, double w = 0.0, bool hmg = false );
+        snlVector ( snlVector& v1, snlVector& v2 );
+
+        void calc ( const snlPoint& pt1, const snlPoint& pt2 );
+        
+        void calcNormal ( snlPoint& pt1, snlPoint& pt2, snlPoint& pt3, snlPoint& pt4 );
+
+        void crossProduct ( snlVector& v1, snlVector& v2 );
+        
+        double dot ( snlVector& vect );  // Dot product of two vectors.
+        double dot ( snlPoint& pt );  // Treats point as vector.        
+
+        double lengthSqrd();  // Length of vector squared.
+        double length();  // Length of vector.
+        void length ( double val );  // Set length of vector.
+        
+        double calcAbsCos ( snlVector& vect );  // Calculate absolute cosine of the angle between vectors.        
+        double angle ( snlVector& vect );
+        
+        void unitise();  // Don't know if this is a real word. Turn vector into unit vector.
+
+        double projectDist ( snlVector& fromVector );
+        snlVector project ( snlVector& ontoVector );
+        
+        void projectXZ();  // Project onto the X-Z plane.        
+        void projectXY();  // Project onto the X-Y plane.
+        void projectYZ();  // Project onto the Y-Z plane.
+
+        snlVector operator * ( double );  // Return vector multiplied by a scalar.
+        snlVector operator + ( snlVector& vect );
+        snlVector operator - ( snlVector& vect );
+        
+        void operator += ( snlVector& vect );
+        void operator -= ( snlVector& vect );
+        void operator *= ( double );  // Multiply this vector by a scalar.
+
+        bool operator == ( snlVector& compare );
+
+        double x();
+        double y();
+        double z();
+        double w();
+        
+        void x ( double val );
+        void y ( double val );
+        void z ( double val );
+        void w ( double val );
+        
+        void components ( double x, double y, double z, double w );
+        void components ( double x, double y, double z );
+        
+        void components ( double* x, double* y, double* z, double* w );
+        void components ( double* x, double* y, double* z );
+        
+        void zero();  // Zero the vector.
+        
+        bool isNull();
+
+        void print();
+
+        double  elements [ 4 ];
+        
+        bool    homogeneous;  // If true then is a 4-D vector.
+};
+
+#endif
diff --git a/src/snlVersion.h b/src/snlVersion.h
new file mode 100644
index 0000000..cb2ddd2
--- /dev/null
+++ b/src/snlVersion.h
@@ -0,0 +1,21 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2003 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+
+#ifndef SNL_VERSION_MAJOR
+
+#define SNL_VERSION_MAJOR 0
+#define SNL_VERSION_MINOR 2
+#define SNL_VERSION_RELEASE 1
+
+#endif
+
diff --git a/src/snlVertex.cpp b/src/snlVertex.cpp
new file mode 100644
index 0000000..4ebbc52
--- /dev/null
+++ b/src/snlVertex.cpp
@@ -0,0 +1,88 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2003 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#include "snlVertex.h"
+
+snlVertex::snlVertex()
+{
+}
+
+snlVertex::snlVertex ( double x, double y, double z, double w )
+    : snlPoint ( x, y, z, w )
+{    
+}
+
+snlVertex::snlVertex ( const snlPoint&  copyFrom )
+    : snlPoint ( copyFrom )
+{    
+}
+
+void snlVertex::operator = ( const snlPoint& copyFrom )
+{
+    // Copy data from a point.
+    // -----------------------
+    
+   snlPoint::operator = ( copyFrom );   
+}
+
+void snlVertex::normal ( snlVector& setTo )
+{
+    // Set vertex's normal.
+    // --------------------
+    
+    norm = setTo;
+}
+
+snlVector& snlVertex::normal()
+{
+    return norm;
+}
+
+void snlVertex::evalParamU ( knot value )
+{
+    // Set U parameter vertex was evaluated at.
+    // ----------------------------------------
+    
+    paramU = value;
+}
+
+knot snlVertex::evalParamU()
+{
+    // Get U parameter vertex was evaluated at.
+    // ----------------------------------------
+    
+    return paramU;
+}
+        
+void snlVertex::evalParamV ( knot value )
+{
+    // Set V parameter vertex was evaluated at.
+    // ----------------------------------------
+    
+    paramV = value;
+}
+
+knot snlVertex::evalParamV()
+{
+    // Set V parameter vertex was evaluated at.
+    // ----------------------------------------
+    
+    return paramV;
+}
+
+
+
diff --git a/src/snlVertex.h b/src/snlVertex.h
new file mode 100644
index 0000000..0c395e6
--- /dev/null
+++ b/src/snlVertex.h
@@ -0,0 +1,59 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2003 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef SNLVERTEX_H
+#define SNLVERTEX_H
+
+#include "snlPoint.h"
+#include "snlKnotVector.h"
+
+class snlVertex : public snlPoint
+{
+    public:
+    
+        snlVertex();
+        
+        snlVertex ( double x, double y, double z, double w );
+        
+        snlVertex ( const snlPoint& copyFrom );
+        
+        snlVector& normal();
+        void normal ( snlVector& setTo );
+        
+        void evalParamU ( knot value );
+        knot evalParamU();
+        
+        void evalParamV ( knot value );
+        knot evalParamV();
+        
+        void operator = ( const snlPoint& copyFrom );        
+
+        int         flag;  // Meaning depends on last operation performed on vertex.
+    
+    protected:
+    
+        snlVector   norm;  // Normal vector associated with vertex.
+        
+        knot        paramU;  // Parameter values that vertex was evaluated at.
+        knot        paramV;  // Only one is used if a curve.
+    
+    private:
+    
+};
+
+#endif
+
diff --git a/src/snlVertexNet.cpp b/src/snlVertexNet.cpp
new file mode 100644
index 0000000..285d3df
--- /dev/null
+++ b/src/snlVertexNet.cpp
@@ -0,0 +1,393 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2003 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** 2D Vertex Net ***
+
+// Natural orientation of the network is:
+//
+//   Maximum V
+//      |
+//      |
+//      |
+//      |
+//      0 ------- Maximum U
+//
+// You are looking at the front face. Unless you are inside the monitor ;-)
+
+#include "snlVertexNet.h"
+
+snlVertexNet::snlVertexNet()
+{
+    vertex_net = 0;
+    
+    size_u = 0;
+    size_v = 0;
+}
+
+snlVertexNet::~snlVertexNet()
+{
+    if ( vertex_net ) delete[] vertex_net;
+}
+
+snlVertexNet::snlVertexNet ( const snlVertexNet& copyFrom )
+{
+    // Copy constructor.
+    // -----------------
+    
+    int arraySize = copyFrom.size_u * copyFrom.size_v;
+    
+    vertex_net = new snlVertex [ arraySize ];
+    
+    for ( int index = 0; index < arraySize; index ++ )
+        vertex_net [ index ] = copyFrom.vertex_net [ index ];    
+}
+
+void snlVertexNet::vertexNet ( const snlCtrlPoint* ctrlPts, int sizeU, int sizeV )
+{    
+    // Generate vertex net based on control point net.
+    // -----------------------------------------------
+    // ctrlPts:    Array of control points to copy.
+    // sizeU:      Size of array in first dimension.
+    // sizeV:      Size of array in second dimension.
+    
+    if ( vertex_net ) delete[] vertex_net;
+    
+    int numPts = sizeU * sizeV;
+    
+    vertex_net = new snlVertex [ numPts ];    
+    
+    size_u = sizeU;
+    size_v = sizeV;
+    
+    // Translate control points into vertexs.
+    
+    for ( int index = 0; index < numPts; index ++ )
+    {
+        vertex_net [ index ] = ctrlPts [ index ];
+        vertex_net [ index ].normalise();
+    }
+            
+    calcNormals();
+}
+
+void snlVertexNet::vertexNet ( const snlCtrlPoint* ctrlPts, int numPts )
+{    
+    // Generate vertex net based on 1-D control point array.
+    // -----------------------------------------------------
+    // Notes:    Normals aren't calculated for a curve.
+    
+    if ( vertex_net ) delete[] vertex_net;    
+    
+    vertex_net = new snlVertex [ numPts ];
+    
+    size_u = 1;
+    size_v = numPts;
+    
+    // Translate control points into vertexs.
+    
+    for ( int index = 0; index < numPts; index ++ )
+    {
+        vertex_net [ index ] = ctrlPts [ index ];
+        vertex_net [ index ].normalise();
+        ( vertex_net [ index ].normal() ).zero();
+    }    
+}
+
+void snlVertexNet::vertexNet ( const snlPoint* pts, int sizeU, int sizeV )
+{    
+    // Generate vertex net based on point net.
+    // ---------------------------------------
+    // ctrlPts:    Array of control points to copy.
+    // sizeU:      Size of array in first dimension.
+    // sizeV:      Size of array in second dimension.
+    
+    if ( vertex_net ) delete[] vertex_net;
+    
+    int numPts = sizeU * sizeV;
+    
+    vertex_net = new snlVertex [ numPts ];    
+    
+    size_u = sizeU;
+    size_v = sizeV;
+    
+    // Translate control points into vertexs.
+    
+    for ( int index = 0; index < numPts; index ++ )
+    {
+        vertex_net [ index ] = pts [ index ];
+        vertex_net [ index ].normalise();
+    }
+            
+    calcNormals();
+}
+
+void snlVertexNet::vertexNet ( const snlPoint* pts, int numPts )
+{    
+    // Generate vertex net based on 1-D point array.
+    // ---------------------------------------------
+    // Notes:    Normals aren't calculated for a curve.
+    
+    if ( vertex_net ) delete[] vertex_net;    
+    
+    vertex_net = new snlVertex [ numPts ];
+    
+    size_u = 1;
+    size_v = numPts;
+    
+    // Translate control points into vertexs.
+    
+    for ( int index = 0; index < numPts; index ++ )
+    {
+        vertex_net [ index ] = pts [ index ];
+        vertex_net [ index ].normalise();
+        ( vertex_net [ index ].normal() ).zero();
+    }    
+}
+
+void snlVertexNet::appendRow ( const snlCtrlPoint* ctrlPts )
+{
+    // Append row of points to end of net.
+    // -----------------------------------
+    
+    if ( size_u < 1 ) return;  // Can't append to empty array.    
+    
+    int numPts = size_u * size_v;
+    
+    snlVertex* newVNet = new snlVertex [ numPts + size_v ];
+    
+    int index;
+    
+    for ( index = 0; index < numPts; index ++ )
+        newVNet [ index ] = vertex_net [ index ];
+        
+    for ( index = 0; index < size_v; index ++ )
+    {
+        newVNet [ index + numPts ] = ctrlPts [ index ];
+        newVNet [ index + numPts ].normalise();        
+    }
+    
+    // Install new array.
+    
+    size_u ++;
+    
+    if ( vertex_net ) delete[] vertex_net;
+    
+    vertex_net = newVNet;
+    
+    calcNormals();
+}
+
+snlVertex* snlVertexNet::vertex ( int index )
+{
+    // Return reference to vertex at index.
+    // ------------------------------------
+    
+    return vertex_net + index;
+}
+
+snlVertex* snlVertexNet::vertex ( int U, int V )
+{
+    // Return vertex at U,V coordinates.
+    // ---------------------------------
+    
+    return vertex_net + ( U * size_v + V );
+}
+
+snlVertex* snlVertexNet::vertexes()
+{
+    // Return pointer to vertex array.
+    // -------------------------------
+
+    return vertex_net;
+}
+
+int snlVertexNet::size() const
+{
+    // Return total number of vertexes.
+    // --------------------------------
+    
+    return size_u * size_v;
+}
+
+int snlVertexNet::sizeU() const
+{
+    return size_u;
+}
+
+int snlVertexNet::sizeV() const
+{
+    return size_v;
+}
+
+void snlVertexNet::calcNormals()
+{
+    // Calculate normals based on the vertex network.
+    // ----------------------------------------------
+    //
+    // Notes:      maxV
+    //              |
+    //       minU --0-- maxU
+    //              |
+    //             minV
+    
+    
+    snlVector   maxU;
+    bool        useMaxU;
+    
+    snlVector   minU;
+    bool        useMinU;
+    
+    snlVector   maxV;
+    bool        useMaxV;
+    
+    snlVector   minV;
+    bool        useMinV;
+
+    for ( int indexU = 0; indexU < size_u; indexU ++ )
+    {
+        for ( int indexV = 0; indexV < size_v; indexV ++ )
+        {
+            int baseIndex = indexU * size_v + indexV;
+            
+            // U direction vectors.
+            
+            if ( indexU < size_u - 1 )
+            {
+                // MaxU can be calculated.
+                
+                maxU.calc ( vertex_net [ baseIndex ], vertex_net [ baseIndex + size_v ] );
+                useMaxU = true;                
+            }
+            else
+                useMaxU = false;
+            
+            if ( indexU > 0 )
+            {
+                // MinU can be calculated.
+                
+                minU.calc ( vertex_net [ baseIndex ], vertex_net [ baseIndex - size_v ] );
+                useMinU = true;                
+            }
+            else
+                useMinU = false;
+            
+            // V direction vectors.
+            
+            if ( indexV < size_v - 1 )
+            {
+                // Calculate maxV.
+                maxV.calc ( vertex_net [ baseIndex ], vertex_net [ baseIndex + 1 ] );
+                useMaxV = true;                
+            }
+            else
+                useMaxV = false;
+                
+            if ( indexV > 0 )
+            {
+                minV.calc ( vertex_net [ baseIndex ], vertex_net [ baseIndex - 1 ] );
+                useMinV = true;
+            }
+            else
+                useMinV = false;
+                
+            // Calculate normals by combining normals from all combinations of
+            // available vectors.
+            
+            snlVector   tmpNormal;
+            snlVector   finalNormal;
+            
+            finalNormal.zero();
+            
+            if ( useMaxV && useMinU )
+            {
+                tmpNormal.crossProduct ( maxV, minU );
+                finalNormal += tmpNormal;
+
+                // Process diagonals.
+                
+                snlVector diag;
+                diag.calc ( vertex_net [ baseIndex ], vertex_net [ baseIndex + 1 - size_v ] );
+
+                tmpNormal.crossProduct ( maxV, diag );
+                finalNormal += tmpNormal;
+
+                tmpNormal.crossProduct ( diag, minU );
+                finalNormal += tmpNormal;
+            }
+            
+            if ( useMinU && useMinV )
+            {
+                tmpNormal.crossProduct ( minU, minV );
+                finalNormal += tmpNormal;
+
+                // Process diagonals.
+                
+                snlVector diag;
+                diag.calc ( vertex_net [ baseIndex ], vertex_net [ baseIndex - 1 - size_v ] );
+
+                tmpNormal.crossProduct ( minU, diag );
+                finalNormal += tmpNormal;
+
+                tmpNormal.crossProduct ( diag, minV );
+                finalNormal += tmpNormal;
+            }
+            
+            if ( useMinV && useMaxU )
+            {
+                tmpNormal.crossProduct ( minV, maxU );
+                finalNormal += tmpNormal;
+
+                // Process diagonals.
+                
+                snlVector diag;
+                diag.calc ( vertex_net [ baseIndex ], vertex_net [ baseIndex - 1 + size_v ] );
+
+                tmpNormal.crossProduct ( minV, diag );
+                finalNormal += tmpNormal;
+
+                tmpNormal.crossProduct ( diag, maxU );
+                finalNormal += tmpNormal;
+            }
+            
+            if ( useMaxU && useMaxV )
+            {
+                tmpNormal.crossProduct ( maxU, maxV );
+                finalNormal += tmpNormal;
+
+                // Process diagonals.
+                
+                snlVector diag;
+                diag.calc ( vertex_net [ baseIndex ], vertex_net [ baseIndex + 1 + size_v ] );
+
+                tmpNormal.crossProduct ( maxU, diag );
+                finalNormal += tmpNormal;
+
+                tmpNormal.crossProduct ( diag, maxV );
+                finalNormal += tmpNormal;
+            }
+            
+            // Set vertex's normal.
+            
+            finalNormal.unitise();
+            
+            vertex_net [ baseIndex ].normal ( finalNormal );
+        }
+    }    
+}
+
+
+
diff --git a/src/snlVertexNet.h b/src/snlVertexNet.h
new file mode 100644
index 0000000..dd4a932
--- /dev/null
+++ b/src/snlVertexNet.h
@@ -0,0 +1,76 @@
+// libSNL - Simple Nurbs Library
+// Copyright 2003 Scott A.E. Lanham, Australia.
+// --------------------------------------------
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU Library General Public License for more details.
+//
+//  You should have received a copy of the GNU Library General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+// *** 2D Vertex Net ***
+
+#ifndef SNLVERTEXNET_H
+#define SNLVERTEXNET_H
+
+#include "snlVertex.h"
+#include "snlCtrlPoint.h"
+
+#ifdef SGI_MIPS
+
+    #include <iostream.h>
+    #include <math.h>
+    
+#else
+
+    #include <iostream>
+    #include <cmath>
+    
+    using namespace std;
+    
+#endif
+
+class snlVertexNet
+{
+    public:        
+
+        snlVertexNet();
+        virtual ~snlVertexNet();
+        
+        snlVertexNet ( const snlVertexNet& copyFrom );
+        
+        void vertexNet ( const snlCtrlPoint* ctrlPts, int sizeU, int sizeV );
+        void vertexNet ( const snlCtrlPoint* ctrlPts, int numPts );
+        
+        void vertexNet ( const snlPoint* pts, int sizeU, int sizeV );
+        void vertexNet ( const snlPoint* pts, int numPts );
+        
+        void appendRow ( const snlCtrlPoint* ctrlPts );
+        
+        snlVertex* vertex ( int index );
+        snlVertex* vertex ( int U, int V );
+
+        snlVertex* vertexes();
+        
+        int size() const;
+        int sizeU() const;
+        int sizeV() const;
+        
+        void calcNormals();
+        
+    private:
+    
+        snlVertex*      vertex_net;  // 2D Vertex network.
+        
+        int     size_u;  // Size of network in first dimension.
+        int     size_v;  // Size of network in second dimension.
+};
+
+#endif

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/libsnl.git



More information about the debian-science-commits mailing list