[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, ¶ms );
+
+ 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, ¶ms );
+
+ 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, ¶ms );
+
+ 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