diff --git a/.cproject b/.cproject deleted file mode 100644 index 73bc743d..00000000 --- a/.cproject +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.gitignore b/.gitignore index 881e3650..33aec96e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ -bin/* -gen/* -obj/* +*/bin/* +*/gen/* +*/obj/* +*.o +*.class diff --git a/src/org/oscim/backend/BitmapUtils.java b/src/org/oscim/backend/BitmapUtils.java deleted file mode 100644 index 6a952cd6..00000000 --- a/src/org/oscim/backend/BitmapUtils.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2010, 2011, 2012 mapsforge.org - * - * This program is free software: you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free Software - * Foundation, either version 3 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with - * this program. If not, see . - */ -package org.oscim.backend; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; - -import org.oscim.backend.canvas.Bitmap; - - -public final class BitmapUtils { - private static final String PREFIX_FILE = "file:"; - private static final String PREFIX_JAR = "jar:"; - - private static InputStream createInputStream(String src) throws FileNotFoundException { - if (src.startsWith(PREFIX_JAR)) { - String name = "/org/oscim/theme/styles/" + src.substring(PREFIX_JAR.length()); - - InputStream inputStream = BitmapUtils.class.getResourceAsStream(name); - if (inputStream == null) { - throw new FileNotFoundException("resource not found: " + src + " " + name); - } - return inputStream; - } else if (src.startsWith(PREFIX_FILE)) { - File file = new File(src.substring(PREFIX_FILE.length())); - if (!file.exists()) { - throw new IllegalArgumentException("file does not exist: " + src); - } else if (!file.isFile()) { - throw new IllegalArgumentException("not a file: " + src); - } else if (!file.canRead()) { - throw new IllegalArgumentException("cannot read file: " + src); - } - return new FileInputStream(file); - } - throw new IllegalArgumentException("invalid bitmap source: " + src); - } - - public static Bitmap createBitmap(String src) throws IOException { - if (src == null || src.length() == 0) { - // no image source defined - return null; - } - - InputStream inputStream = createInputStream(src); - Bitmap bitmap = CanvasAdapter.g.decodeBitmap(inputStream); - inputStream.close(); - return bitmap; - } - - - - private BitmapUtils() { - throw new IllegalStateException(); - } -} diff --git a/vtm-android/.classpath b/vtm-android/.classpath new file mode 100755 index 00000000..ab548081 --- /dev/null +++ b/vtm-android/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/.externalToolBuilders/org.eclipse.cdt.managedbuilder.core.genmakebuilder.launch b/vtm-android/.externalToolBuilders/org.eclipse.cdt.managedbuilder.core.genmakebuilder.launch similarity index 100% rename from .externalToolBuilders/org.eclipse.cdt.managedbuilder.core.genmakebuilder.launch rename to vtm-android/.externalToolBuilders/org.eclipse.cdt.managedbuilder.core.genmakebuilder.launch diff --git a/vtm-android/.project b/vtm-android/.project new file mode 100755 index 00000000..205b4f13 --- /dev/null +++ b/vtm-android/.project @@ -0,0 +1,34 @@ + + + vtm-android + + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/.settings/de.loskutov.anyedit.AnyEditTools.prefs b/vtm-android/.settings/de.loskutov.anyedit.AnyEditTools.prefs similarity index 100% rename from .settings/de.loskutov.anyedit.AnyEditTools.prefs rename to vtm-android/.settings/de.loskutov.anyedit.AnyEditTools.prefs diff --git a/vtm-android/.settings/edu.umd.cs.findbugs.core.prefs b/vtm-android/.settings/edu.umd.cs.findbugs.core.prefs new file mode 100644 index 00000000..a374267c --- /dev/null +++ b/vtm-android/.settings/edu.umd.cs.findbugs.core.prefs @@ -0,0 +1,132 @@ +#FindBugs User Preferences +#Fri Oct 12 01:06:57 CEST 2012 +cloud_id=edu.umd.cs.findbugs.cloud.doNothingCloud +detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true +detectorAtomicityProblem=AtomicityProblem|true +detectorBadAppletConstructor=BadAppletConstructor|false +detectorBadResultSetAccess=BadResultSetAccess|true +detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true +detectorBadUseOfReturnValue=BadUseOfReturnValue|true +detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true +detectorBooleanReturnNull=BooleanReturnNull|true +detectorCallToUnsupportedMethod=CallToUnsupportedMethod|false +detectorCheckExpectedWarnings=CheckExpectedWarnings|false +detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true +detectorCheckTypeQualifiers=CheckTypeQualifiers|true +detectorCloneIdiom=CloneIdiom|true +detectorComparatorIdiom=ComparatorIdiom|true +detectorConfusedInheritance=ConfusedInheritance|true +detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true +detectorCrossSiteScripting=CrossSiteScripting|true +detectorDefaultEncodingDetector=DefaultEncodingDetector|true +detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true +detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true +detectorDontIgnoreResultOfPutIfAbsent=DontIgnoreResultOfPutIfAbsent|true +detectorDontUseEnum=DontUseEnum|true +detectorDroppedException=DroppedException|true +detectorDumbMethodInvocations=DumbMethodInvocations|true +detectorDumbMethods=DumbMethods|true +detectorDuplicateBranches=DuplicateBranches|true +detectorEmptyZipFileEntry=EmptyZipFileEntry|true +detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true +detectorExplicitSerialization=ExplicitSerialization|true +detectorFinalizerNullsFields=FinalizerNullsFields|true +detectorFindBadCast2=FindBadCast2|true +detectorFindBadForLoop=FindBadForLoop|true +detectorFindCircularDependencies=FindCircularDependencies|false +detectorFindDeadLocalStores=FindDeadLocalStores|true +detectorFindDoubleCheck=FindDoubleCheck|true +detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true +detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true +detectorFindFinalizeInvocations=FindFinalizeInvocations|true +detectorFindFloatEquality=FindFloatEquality|true +detectorFindHEmismatch=FindHEmismatch|true +detectorFindInconsistentSync2=FindInconsistentSync2|true +detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true +detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true +detectorFindMaskedFields=FindMaskedFields|true +detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true +detectorFindNakedNotify=FindNakedNotify|true +detectorFindNonShortCircuit=FindNonShortCircuit|true +detectorFindNullDeref=FindNullDeref|true +detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true +detectorFindOpenStream=FindOpenStream|true +detectorFindPuzzlers=FindPuzzlers|true +detectorFindRefComparison=FindRefComparison|true +detectorFindReturnRef=FindReturnRef|true +detectorFindRunInvocations=FindRunInvocations|true +detectorFindSelfComparison=FindSelfComparison|true +detectorFindSelfComparison2=FindSelfComparison2|true +detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true +detectorFindSpinLoop=FindSpinLoop|true +detectorFindSqlInjection=FindSqlInjection|true +detectorFindTwoLockWait=FindTwoLockWait|true +detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true +detectorFindUnconditionalWait=FindUnconditionalWait|true +detectorFindUninitializedGet=FindUninitializedGet|true +detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true +detectorFindUnreleasedLock=FindUnreleasedLock|true +detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true +detectorFindUnsyncGet=FindUnsyncGet|true +detectorFindUseOfNonSerializableValue=FindUseOfNonSerializableValue|true +detectorFindUselessControlFlow=FindUselessControlFlow|true +detectorFormatStringChecker=FormatStringChecker|true +detectorHugeSharedStringConstants=HugeSharedStringConstants|true +detectorIDivResultCastToDouble=IDivResultCastToDouble|true +detectorIncompatMask=IncompatMask|true +detectorInconsistentAnnotations=InconsistentAnnotations|true +detectorInefficientMemberAccess=InefficientMemberAccess|false +detectorInefficientToArray=InefficientToArray|true +detectorInfiniteLoop=InfiniteLoop|true +detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true +detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true +detectorInitializationChain=InitializationChain|true +detectorInitializeNonnullFieldsInConstructor=InitializeNonnullFieldsInConstructor|true +detectorInstantiateStaticClass=InstantiateStaticClass|true +detectorIntCast2LongAsInstant=IntCast2LongAsInstant|true +detectorInvalidJUnitTest=InvalidJUnitTest|true +detectorIteratorIdioms=IteratorIdioms|true +detectorLazyInit=LazyInit|true +detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true +detectorLostLoggerDueToWeakReference=LostLoggerDueToWeakReference|true +detectorMethodReturnCheck=MethodReturnCheck|true +detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true +detectorMutableLock=MutableLock|true +detectorMutableStaticFields=MutableStaticFields|true +detectorNaming=Naming|true +detectorNoteUnconditionalParamDerefs=NoteUnconditionalParamDerefs|true +detectorNumberConstructor=NumberConstructor|true +detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true +detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true +detectorPublicSemaphores=PublicSemaphores|false +detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true +detectorReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass=ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass|true +detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true +detectorRedundantInterfaces=RedundantInterfaces|true +detectorRepeatedConditionals=RepeatedConditionals|true +detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true +detectorSerializableIdiom=SerializableIdiom|true +detectorStartInConstructor=StartInConstructor|true +detectorStaticCalendarDetector=StaticCalendarDetector|true +detectorStringConcatenation=StringConcatenation|true +detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true +detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true +detectorSwitchFallthrough=SwitchFallthrough|true +detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true +detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true +detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true +detectorURLProblems=URLProblems|true +detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true +detectorUnnecessaryMath=UnnecessaryMath|true +detectorUnreadFields=UnreadFields|true +detectorUselessSubclassMethod=UselessSubclassMethod|false +detectorVarArgsProblems=VarArgsProblems|true +detectorVolatileUsage=VolatileUsage|true +detectorWaitInLoop=WaitInLoop|true +detectorWrongMapIterator=WrongMapIterator|true +detectorXMLFactoryBypass=XMLFactoryBypass|true +detector_threshold=2 +effort=max +filter_settings=Medium|BAD_PRACTICE,CORRECTNESS,MT_CORRECTNESS,PERFORMANCE,STYLE|false|15 +filter_settings_neg=MALICIOUS_CODE,NOISE,I18N,SECURITY,EXPERIMENTAL| +run_at_full_build=false diff --git a/vtm-android/.settings/org.eclipse.cdt.codan.core.prefs b/vtm-android/.settings/org.eclipse.cdt.codan.core.prefs new file mode 100644 index 00000000..0710d384 --- /dev/null +++ b/vtm-android/.settings/org.eclipse.cdt.codan.core.prefs @@ -0,0 +1,65 @@ +eclipse.preferences.version=1 +org.eclipse.cdt.codan.checkers.errnoreturn=Warning +org.eclipse.cdt.codan.checkers.errnoreturn.params={implicit\=>false} +org.eclipse.cdt.codan.checkers.errreturnvalue=Error +org.eclipse.cdt.codan.checkers.errreturnvalue.params={} +org.eclipse.cdt.codan.checkers.noreturn=Error +org.eclipse.cdt.codan.checkers.noreturn.params={implicit\=>false} +org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation=Error +org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem=Error +org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem=Warning +org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem.params={} +org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem=Error +org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem.params={} +org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem=Warning +org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem.params={no_break_comment\=>"no break",last_case_param\=>true,empty_case_param\=>false} +org.eclipse.cdt.codan.internal.checkers.CatchByReference=Warning +org.eclipse.cdt.codan.internal.checkers.CatchByReference.params={unknown\=>false,exceptions\=>()} +org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem=Error +org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem=Error +org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem=Error +org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.InvalidArguments=Error +org.eclipse.cdt.codan.internal.checkers.InvalidArguments.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem=Error +org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem=Error +org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem=Error +org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem=Error +org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker=-Info +org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker.params={pattern\=>"^[a-z]",macro\=>true,exceptions\=>()} +org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=Warning +org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem.params={} +org.eclipse.cdt.codan.internal.checkers.OverloadProblem=Error +org.eclipse.cdt.codan.internal.checkers.OverloadProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem=Error +org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem=Error +org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem=-Warning +org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem.params={} +org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem=-Warning +org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem.params={} +org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem=Warning +org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem.params={macro\=>true,exceptions\=>()} +org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem=Warning +org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem.params={paramNot\=>false} +org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem=Warning +org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem.params={else\=>false,afterelse\=>false} +org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem=Error +org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem=Warning +org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem.params={macro\=>true} +org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem=Warning +org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem.params={macro\=>true} +org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem=Warning +org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem.params={macro\=>true,exceptions\=>("@(\#)","$Id")} +org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem=Error +org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} diff --git a/vtm-android/.settings/org.eclipse.core.resources.prefs b/vtm-android/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000..e616f500 --- /dev/null +++ b/vtm-android/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +#Sat Dec 17 10:00:00 CET 2011 +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/vtm-android/.settings/org.eclipse.core.runtime.prefs b/vtm-android/.settings/org.eclipse.core.runtime.prefs new file mode 100644 index 00000000..db872193 --- /dev/null +++ b/vtm-android/.settings/org.eclipse.core.runtime.prefs @@ -0,0 +1,3 @@ +#Sat Dec 17 10:00:00 CET 2011 +eclipse.preferences.version=1 +line.separator=\n diff --git a/vtm-android/.settings/org.eclipse.jdt.core.prefs b/vtm-android/.settings/org.eclipse.jdt.core.prefs new file mode 100755 index 00000000..f8801d84 --- /dev/null +++ b/vtm-android/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,376 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.doc.comment.support=enabled +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=warning +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=ignore +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=enabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=warning +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected +org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nullReference=warning +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=warning +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=disabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=disabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=warning +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.6 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=true +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=true +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=false +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert +org.eclipse.jdt.core.formatter.comment.line_length=80 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=true +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=false +org.eclipse.jdt.core.formatter.join_wrapped_lines=false +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=100 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false +org.eclipse.jdt.core.formatter.tabulation.char=tab +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=false +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true diff --git a/vtm-android/.settings/org.eclipse.jdt.ui.prefs b/vtm-android/.settings/org.eclipse.jdt.ui.prefs new file mode 100755 index 00000000..5bdeaf3d --- /dev/null +++ b/vtm-android/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,115 @@ +cleanup.add_default_serial_version_id=true +cleanup.add_generated_serial_version_id=false +cleanup.add_missing_annotations=true +cleanup.add_missing_deprecated_annotations=true +cleanup.add_missing_methods=false +cleanup.add_missing_nls_tags=false +cleanup.add_missing_override_annotations=true +cleanup.add_missing_override_annotations_interface_methods=true +cleanup.add_serial_version_id=false +cleanup.always_use_blocks=true +cleanup.always_use_parentheses_in_expressions=false +cleanup.always_use_this_for_non_static_field_access=false +cleanup.always_use_this_for_non_static_method_access=false +cleanup.convert_to_enhanced_for_loop=false +cleanup.correct_indentation=false +cleanup.format_source_code=true +cleanup.format_source_code_changes_only=false +cleanup.make_local_variable_final=true +cleanup.make_parameters_final=false +cleanup.make_private_fields_final=true +cleanup.make_type_abstract_if_missing_method=false +cleanup.make_variable_declarations_final=false +cleanup.never_use_blocks=false +cleanup.never_use_parentheses_in_expressions=true +cleanup.organize_imports=true +cleanup.qualify_static_field_accesses_with_declaring_class=false +cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +cleanup.qualify_static_member_accesses_with_declaring_class=true +cleanup.qualify_static_method_accesses_with_declaring_class=false +cleanup.remove_private_constructors=true +cleanup.remove_trailing_whitespaces=true +cleanup.remove_trailing_whitespaces_all=true +cleanup.remove_trailing_whitespaces_ignore_empty=false +cleanup.remove_unnecessary_casts=true +cleanup.remove_unnecessary_nls_tags=true +cleanup.remove_unused_imports=true +cleanup.remove_unused_local_variables=false +cleanup.remove_unused_private_fields=true +cleanup.remove_unused_private_members=false +cleanup.remove_unused_private_methods=true +cleanup.remove_unused_private_types=true +cleanup.sort_members=false +cleanup.sort_members_all=false +cleanup.use_blocks=false +cleanup.use_blocks_only_for_return_and_throw=false +cleanup.use_parentheses_in_expressions=false +cleanup.use_this_for_non_static_field_access=false +cleanup.use_this_for_non_static_field_access_only_if_necessary=true +cleanup.use_this_for_non_static_method_access=false +cleanup.use_this_for_non_static_method_access_only_if_necessary=true +cleanup_profile=_eclipse-cs VectorTileMap +cleanup_settings_version=2 +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +formatter_profile=_vtm +formatter_settings_version=12 +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=java;javax;org;com; +org.eclipse.jdt.ui.javadoc=false +org.eclipse.jdt.ui.ondemandthreshold=99 +org.eclipse.jdt.ui.staticondemandthreshold=99 +org.eclipse.jdt.ui.text.custom_code_templates= +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=true +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=false +sp_cleanup.always_use_this_for_non_static_field_access=false +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_to_enhanced_for_loop=false +sp_cleanup.correct_indentation=false +sp_cleanup.format_source_code=false +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.make_local_variable_final=false +sp_cleanup.make_parameters_final=false +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=true +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.on_save_use_additional_actions=true +sp_cleanup.organize_imports=true +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=false +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_trailing_whitespaces=true +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=false +sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.use_blocks=false +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_parentheses_in_expressions=false +sp_cleanup.use_this_for_non_static_field_access=false +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=false +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true diff --git a/AndroidManifest.xml b/vtm-android/AndroidManifest.xml similarity index 100% rename from AndroidManifest.xml rename to vtm-android/AndroidManifest.xml diff --git a/COPYING b/vtm-android/COPYING similarity index 100% rename from COPYING rename to vtm-android/COPYING diff --git a/COPYING.LESSER b/vtm-android/COPYING.LESSER similarity index 100% rename from COPYING.LESSER rename to vtm-android/COPYING.LESSER diff --git a/README b/vtm-android/README similarity index 100% rename from README rename to vtm-android/README diff --git a/ant.properties b/vtm-android/ant.properties similarity index 100% rename from ant.properties rename to vtm-android/ant.properties diff --git a/build.xml b/vtm-android/build.xml similarity index 100% rename from build.xml rename to vtm-android/build.xml diff --git a/jni/Android.mk b/vtm-android/jni/Android.mk similarity index 100% rename from jni/Android.mk rename to vtm-android/jni/Android.mk diff --git a/jni/Application.mk b/vtm-android/jni/Application.mk similarity index 100% rename from jni/Application.mk rename to vtm-android/jni/Application.mk diff --git a/jni/VectorTileMap.cpp b/vtm-android/jni/VectorTileMap.cpp similarity index 100% rename from jni/VectorTileMap.cpp rename to vtm-android/jni/VectorTileMap.cpp diff --git a/jni/gl/utils.c b/vtm-android/jni/gl/utils.c similarity index 100% rename from jni/gl/utils.c rename to vtm-android/jni/gl/utils.c diff --git a/jni/tessellate/LICENSE b/vtm-android/jni/tessellate/LICENSE similarity index 100% rename from jni/tessellate/LICENSE rename to vtm-android/jni/tessellate/LICENSE diff --git a/jni/tessellate/Makefile b/vtm-android/jni/tessellate/Makefile similarity index 100% rename from jni/tessellate/Makefile rename to vtm-android/jni/tessellate/Makefile diff --git a/jni/tessellate/README.md b/vtm-android/jni/tessellate/README.md similarity index 100% rename from jni/tessellate/README.md rename to vtm-android/jni/tessellate/README.md diff --git a/jni/tessellate/dict-list.h b/vtm-android/jni/tessellate/dict-list.h similarity index 100% rename from jni/tessellate/dict-list.h rename to vtm-android/jni/tessellate/dict-list.h diff --git a/jni/tessellate/dict.c b/vtm-android/jni/tessellate/dict.c similarity index 100% rename from jni/tessellate/dict.c rename to vtm-android/jni/tessellate/dict.c diff --git a/jni/tessellate/dict.h b/vtm-android/jni/tessellate/dict.h similarity index 100% rename from jni/tessellate/dict.h rename to vtm-android/jni/tessellate/dict.h diff --git a/jni/tessellate/geom.c b/vtm-android/jni/tessellate/geom.c similarity index 100% rename from jni/tessellate/geom.c rename to vtm-android/jni/tessellate/geom.c diff --git a/jni/tessellate/geom.h b/vtm-android/jni/tessellate/geom.h similarity index 100% rename from jni/tessellate/geom.h rename to vtm-android/jni/tessellate/geom.h diff --git a/jni/tessellate/glu.h b/vtm-android/jni/tessellate/glu.h similarity index 100% rename from jni/tessellate/glu.h rename to vtm-android/jni/tessellate/glu.h diff --git a/jni/tessellate/gluos.h b/vtm-android/jni/tessellate/gluos.h similarity index 100% rename from jni/tessellate/gluos.h rename to vtm-android/jni/tessellate/gluos.h diff --git a/jni/tessellate/main.c b/vtm-android/jni/tessellate/main.c similarity index 100% rename from jni/tessellate/main.c rename to vtm-android/jni/tessellate/main.c diff --git a/jni/tessellate/memalloc.c b/vtm-android/jni/tessellate/memalloc.c similarity index 100% rename from jni/tessellate/memalloc.c rename to vtm-android/jni/tessellate/memalloc.c diff --git a/jni/tessellate/memalloc.h b/vtm-android/jni/tessellate/memalloc.h similarity index 100% rename from jni/tessellate/memalloc.h rename to vtm-android/jni/tessellate/memalloc.h diff --git a/jni/tessellate/mesh.c b/vtm-android/jni/tessellate/mesh.c similarity index 100% rename from jni/tessellate/mesh.c rename to vtm-android/jni/tessellate/mesh.c diff --git a/jni/tessellate/mesh.h b/vtm-android/jni/tessellate/mesh.h similarity index 100% rename from jni/tessellate/mesh.h rename to vtm-android/jni/tessellate/mesh.h diff --git a/jni/tessellate/normal.c b/vtm-android/jni/tessellate/normal.c similarity index 100% rename from jni/tessellate/normal.c rename to vtm-android/jni/tessellate/normal.c diff --git a/jni/tessellate/normal.h b/vtm-android/jni/tessellate/normal.h similarity index 100% rename from jni/tessellate/normal.h rename to vtm-android/jni/tessellate/normal.h diff --git a/jni/tessellate/priorityq-heap.c b/vtm-android/jni/tessellate/priorityq-heap.c similarity index 100% rename from jni/tessellate/priorityq-heap.c rename to vtm-android/jni/tessellate/priorityq-heap.c diff --git a/jni/tessellate/priorityq-heap.h b/vtm-android/jni/tessellate/priorityq-heap.h similarity index 100% rename from jni/tessellate/priorityq-heap.h rename to vtm-android/jni/tessellate/priorityq-heap.h diff --git a/jni/tessellate/priorityq-sort.h b/vtm-android/jni/tessellate/priorityq-sort.h similarity index 100% rename from jni/tessellate/priorityq-sort.h rename to vtm-android/jni/tessellate/priorityq-sort.h diff --git a/jni/tessellate/priorityq.c b/vtm-android/jni/tessellate/priorityq.c similarity index 100% rename from jni/tessellate/priorityq.c rename to vtm-android/jni/tessellate/priorityq.c diff --git a/jni/tessellate/priorityq.h b/vtm-android/jni/tessellate/priorityq.h similarity index 100% rename from jni/tessellate/priorityq.h rename to vtm-android/jni/tessellate/priorityq.h diff --git a/jni/tessellate/render.c b/vtm-android/jni/tessellate/render.c similarity index 100% rename from jni/tessellate/render.c rename to vtm-android/jni/tessellate/render.c diff --git a/jni/tessellate/render.h b/vtm-android/jni/tessellate/render.h similarity index 100% rename from jni/tessellate/render.h rename to vtm-android/jni/tessellate/render.h diff --git a/jni/tessellate/sweep.c b/vtm-android/jni/tessellate/sweep.c similarity index 100% rename from jni/tessellate/sweep.c rename to vtm-android/jni/tessellate/sweep.c diff --git a/jni/tessellate/sweep.h b/vtm-android/jni/tessellate/sweep.h similarity index 100% rename from jni/tessellate/sweep.h rename to vtm-android/jni/tessellate/sweep.h diff --git a/jni/tessellate/tess.c b/vtm-android/jni/tessellate/tess.c similarity index 100% rename from jni/tessellate/tess.c rename to vtm-android/jni/tessellate/tess.c diff --git a/jni/tessellate/tess.h b/vtm-android/jni/tessellate/tess.h similarity index 100% rename from jni/tessellate/tess.h rename to vtm-android/jni/tessellate/tess.h diff --git a/jni/tessellate/tessellate.c b/vtm-android/jni/tessellate/tessellate.c similarity index 100% rename from jni/tessellate/tessellate.c rename to vtm-android/jni/tessellate/tessellate.c diff --git a/jni/tessellate/tessellate.h b/vtm-android/jni/tessellate/tessellate.h similarity index 100% rename from jni/tessellate/tessellate.h rename to vtm-android/jni/tessellate/tessellate.h diff --git a/jni/tessellate/tessmono.c b/vtm-android/jni/tessellate/tessmono.c similarity index 100% rename from jni/tessellate/tessmono.c rename to vtm-android/jni/tessellate/tessmono.c diff --git a/jni/tessellate/tessmono.h b/vtm-android/jni/tessellate/tessmono.h similarity index 100% rename from jni/tessellate/tessmono.h rename to vtm-android/jni/tessellate/tessmono.h diff --git a/jni/triangle/README b/vtm-android/jni/triangle/README similarity index 100% rename from jni/triangle/README rename to vtm-android/jni/triangle/README diff --git a/jni/triangle/TriangleJni.c b/vtm-android/jni/triangle/TriangleJni.c similarity index 100% rename from jni/triangle/TriangleJni.c rename to vtm-android/jni/triangle/TriangleJni.c diff --git a/jni/triangle/triangle.c b/vtm-android/jni/triangle/triangle.c similarity index 100% rename from jni/triangle/triangle.c rename to vtm-android/jni/triangle/triangle.c diff --git a/jni/triangle/triangle.h b/vtm-android/jni/triangle/triangle.h similarity index 100% rename from jni/triangle/triangle.h rename to vtm-android/jni/triangle/triangle.h diff --git a/jni/triangle/triangle_dbg.c b/vtm-android/jni/triangle/triangle_dbg.c similarity index 100% rename from jni/triangle/triangle_dbg.c rename to vtm-android/jni/triangle/triangle_dbg.c diff --git a/jni/triangle/triangle_private.h b/vtm-android/jni/triangle/triangle_private.h similarity index 100% rename from jni/triangle/triangle_private.h rename to vtm-android/jni/triangle/triangle_private.h diff --git a/vtm-android/lint.xml b/vtm-android/lint.xml new file mode 100644 index 00000000..8a7b1fac --- /dev/null +++ b/vtm-android/lint.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/vtm-android/local.properties b/vtm-android/local.properties new file mode 100644 index 00000000..116edcd2 --- /dev/null +++ b/vtm-android/local.properties @@ -0,0 +1,10 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must *NOT* be checked in Version Control Systems, +# as it contains information specific to your local configuration. + +# location of the SDK. This is only used by Ant +# For customization when using a Version Control System, please read the +# header note. +sdk.dir=/home/jeff/src/android diff --git a/myproguard.cfg b/vtm-android/proguard.cfg similarity index 58% rename from myproguard.cfg rename to vtm-android/proguard.cfg index 91a2a7de..a10bf7ac 100644 --- a/myproguard.cfg +++ b/vtm-android/proguard.cfg @@ -1,55 +1,44 @@ --injars bin/classes - --libraryjars /home/jeff/src/android/platforms/android-16/android.jar --libraryjars lib/postgresql-9.0-801.jdbc4.jar - --optimizations !code/simplification/arithmetic,!field/*,!class/merging/* -optimizationpasses 5 -dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-dontskipnonpubliclibraryclassmembers -dontpreverify -verbose +-libraryjars lib/postgresql-9.0-801.jdbc4.jar +-ignorewarnings +-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* -keep public class * extends android.app.Activity - -keep public class * extends android.app.Application - -keep public class * extends android.app.Service - -keep public class * extends android.content.BroadcastReceiver - -keep public class * extends android.content.ContentProvider - -keep public class * extends android.app.backup.BackupAgentHelper - -keep public class * extends android.preference.Preference - -keep public class com.android.vending.licensing.ILicensingService --keepclasseswithmembers class * { - public (android.content.Context,android.util.AttributeSet); +-keepclasseswithmembernames class * { + native ; } -keepclasseswithmembers class * { - public (android.content.Context,android.util.AttributeSet,int); + public (android.content.Context, android.util.AttributeSet); +} + +-keepclasseswithmembers class * { + public (android.content.Context, android.util.AttributeSet, int); } -keepclassmembers class * extends android.app.Activity { - public void *(android.view.View); + public void *(android.view.View); } --keep class * extends android.os.Parcelable { - public static final android.os.Parcelable$Creator *; -} - -# Also keep - Enumerations. Keep the special static methods that are required in -# enumeration classes. --keepclassmembers enum * { +-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } -# Keep names - Native method names. Keep all native class/method names. --keepclasseswithmembers,allowshrinking class * { - native ; +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; } diff --git a/vtm-android/project.properties b/vtm-android/project.properties new file mode 100644 index 00000000..bef2caff --- /dev/null +++ b/vtm-android/project.properties @@ -0,0 +1,16 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "ant.properties", and override values to adapt the script to your +# project structure. + +# Indicates whether an apk should be generated for each density. +split.density=false +# Project target. +target=android-17 + +#proguard.config=proguard.cfg +android.library=true diff --git a/res/drawable-nodpi/marker_default.png b/vtm-android/res/drawable-nodpi/marker_default.png similarity index 100% rename from res/drawable-nodpi/marker_default.png rename to vtm-android/res/drawable-nodpi/marker_default.png diff --git a/res/drawable-nodpi/marker_default_focused_base.png b/vtm-android/res/drawable-nodpi/marker_default_focused_base.png similarity index 100% rename from res/drawable-nodpi/marker_default_focused_base.png rename to vtm-android/res/drawable-nodpi/marker_default_focused_base.png diff --git a/res/drawable-nodpi/pin.png b/vtm-android/res/drawable-nodpi/pin.png similarity index 100% rename from res/drawable-nodpi/pin.png rename to vtm-android/res/drawable-nodpi/pin.png diff --git a/src/com/badlogic/gdx/backends/android/AndroidGL20.java b/vtm-android/src/com/badlogic/gdx/backends/android/AndroidGL20.java similarity index 100% rename from src/com/badlogic/gdx/backends/android/AndroidGL20.java rename to vtm-android/src/com/badlogic/gdx/backends/android/AndroidGL20.java diff --git a/vtm-android/src/org/oscim/android/AndroidAssetAdapter.java b/vtm-android/src/org/oscim/android/AndroidAssetAdapter.java new file mode 100644 index 00000000..5b88fcc7 --- /dev/null +++ b/vtm-android/src/org/oscim/android/AndroidAssetAdapter.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013 + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program. If not, see . + */ +package org.oscim.android; + +import java.io.IOException; +import java.io.InputStream; + +import org.oscim.backend.AssetAdapter; + +import android.content.Context; + +public class AndroidAssetAdapter extends AssetAdapter { + Context mContext; + public AndroidAssetAdapter(Context ctx) { + mContext = ctx; + } + @Override + public InputStream openFileAsStream(String fileName) { + try { + return mContext.getAssets().open(fileName); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/src/org/oscim/android/AndroidGLRenderer.java b/vtm-android/src/org/oscim/android/AndroidGLRenderer.java similarity index 100% rename from src/org/oscim/android/AndroidGLRenderer.java rename to vtm-android/src/org/oscim/android/AndroidGLRenderer.java diff --git a/src/org/oscim/android/AndroidGraphics.java b/vtm-android/src/org/oscim/android/AndroidGraphics.java similarity index 100% rename from src/org/oscim/android/AndroidGraphics.java rename to vtm-android/src/org/oscim/android/AndroidGraphics.java diff --git a/src/org/oscim/android/AndroidLog.java b/vtm-android/src/org/oscim/android/AndroidLog.java similarity index 100% rename from src/org/oscim/android/AndroidLog.java rename to vtm-android/src/org/oscim/android/AndroidLog.java diff --git a/src/org/oscim/android/AndroidMapView.java b/vtm-android/src/org/oscim/android/AndroidMapView.java similarity index 93% rename from src/org/oscim/android/AndroidMapView.java rename to vtm-android/src/org/oscim/android/AndroidMapView.java index 0c67d5ac..bc5f9b49 100644 --- a/src/org/oscim/android/AndroidMapView.java +++ b/vtm-android/src/org/oscim/android/AndroidMapView.java @@ -17,7 +17,9 @@ package org.oscim.android; import org.oscim.android.canvas.AndroidGraphics; import org.oscim.android.input.AndroidMotionEvent; +import org.oscim.backend.AssetAdapter; import org.oscim.backend.CanvasAdapter; +import org.oscim.backend.GLAdapter; import org.oscim.backend.Log; import org.oscim.core.Tile; import org.oscim.view.MapRenderCallback; @@ -28,6 +30,8 @@ import android.util.AttributeSet; import android.util.DisplayMetrics; import android.widget.RelativeLayout; +import com.badlogic.gdx.backends.android.AndroidGL20; + /** * A MapView shows a map on the display of the device. It handles all user input * and touch gestures to move and zoom the map. @@ -54,7 +58,12 @@ public class AndroidMapView extends RelativeLayout implements MapRenderCallback private final MapView mMapView; static { + System.loadLibrary("glutils"); + System.loadLibrary("triangle"); + //System.loadLibrary("tessellate"); + CanvasAdapter.g = AndroidGraphics.INSTANCE; + GLAdapter.INSTANCE = new AndroidGL20(); Log.logger = new AndroidLog(); } @@ -86,7 +95,9 @@ public class AndroidMapView extends RelativeLayout implements MapRenderCallback throw new IllegalArgumentException( "context is not an instance of MapActivity"); } - //CanvasAdapter.g = AndroidCanvas; + + AssetAdapter.g = new AndroidAssetAdapter(context); + this.setWillNotDraw(true); @@ -156,9 +167,9 @@ public class AndroidMapView extends RelativeLayout implements MapRenderCallback return mMapView.getLayerManager().handleMotionEvent(mMotionEvent); } - + // synchronized ??? @Override - protected synchronized void onSizeChanged(int width, int height, + protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) { Log.d(TAG, "onSizeChanged: " + width + "x" + height); diff --git a/src/org/oscim/android/Compass.java b/vtm-android/src/org/oscim/android/Compass.java similarity index 100% rename from src/org/oscim/android/Compass.java rename to vtm-android/src/org/oscim/android/Compass.java diff --git a/src/org/oscim/android/GLView.java b/vtm-android/src/org/oscim/android/GLView.java similarity index 100% rename from src/org/oscim/android/GLView.java rename to vtm-android/src/org/oscim/android/GLView.java diff --git a/src/org/oscim/android/GlConfigChooser.java b/vtm-android/src/org/oscim/android/GlConfigChooser.java similarity index 100% rename from src/org/oscim/android/GlConfigChooser.java rename to vtm-android/src/org/oscim/android/GlConfigChooser.java diff --git a/src/org/oscim/android/MapActivity.java b/vtm-android/src/org/oscim/android/MapActivity.java similarity index 100% rename from src/org/oscim/android/MapActivity.java rename to vtm-android/src/org/oscim/android/MapActivity.java diff --git a/src/org/oscim/android/MapZoomControls.java b/vtm-android/src/org/oscim/android/MapZoomControls.java similarity index 100% rename from src/org/oscim/android/MapZoomControls.java rename to vtm-android/src/org/oscim/android/MapZoomControls.java diff --git a/src/org/oscim/android/canvas/AndroidBitmap.java b/vtm-android/src/org/oscim/android/canvas/AndroidBitmap.java similarity index 100% rename from src/org/oscim/android/canvas/AndroidBitmap.java rename to vtm-android/src/org/oscim/android/canvas/AndroidBitmap.java diff --git a/src/org/oscim/android/canvas/AndroidCanvas.java b/vtm-android/src/org/oscim/android/canvas/AndroidCanvas.java similarity index 100% rename from src/org/oscim/android/canvas/AndroidCanvas.java rename to vtm-android/src/org/oscim/android/canvas/AndroidCanvas.java diff --git a/src/org/oscim/android/canvas/AndroidGraphics.java b/vtm-android/src/org/oscim/android/canvas/AndroidGraphics.java similarity index 100% rename from src/org/oscim/android/canvas/AndroidGraphics.java rename to vtm-android/src/org/oscim/android/canvas/AndroidGraphics.java diff --git a/src/org/oscim/android/canvas/AndroidPaint.java b/vtm-android/src/org/oscim/android/canvas/AndroidPaint.java similarity index 100% rename from src/org/oscim/android/canvas/AndroidPaint.java rename to vtm-android/src/org/oscim/android/canvas/AndroidPaint.java diff --git a/src/org/oscim/android/gl/AndroidGL.java b/vtm-android/src/org/oscim/android/gl/AndroidGL.java similarity index 100% rename from src/org/oscim/android/gl/AndroidGL.java rename to vtm-android/src/org/oscim/android/gl/AndroidGL.java diff --git a/src/org/oscim/android/input/AndroidMotionEvent.java b/vtm-android/src/org/oscim/android/input/AndroidMotionEvent.java similarity index 100% rename from src/org/oscim/android/input/AndroidMotionEvent.java rename to vtm-android/src/org/oscim/android/input/AndroidMotionEvent.java diff --git a/vtm-extras/.classpath b/vtm-extras/.classpath new file mode 100644 index 00000000..1a530a4d --- /dev/null +++ b/vtm-extras/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/vtm-extras/.project b/vtm-extras/.project new file mode 100644 index 00000000..60853f9b --- /dev/null +++ b/vtm-extras/.project @@ -0,0 +1,17 @@ + + + vtm-extras + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/vtm-extras/.settings/org.eclipse.jdt.core.prefs b/vtm-extras/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..8000cd6c --- /dev/null +++ b/vtm-extras/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/src/org/oscim/utils/Triangulator.java b/vtm-extras/src/org/oscim/utils/Triangulator.java similarity index 100% rename from src/org/oscim/utils/Triangulator.java rename to vtm-extras/src/org/oscim/utils/Triangulator.java diff --git a/src/org/oscim/utils/osm/Bound.java b/vtm-extras/src/org/oscim/utils/osm/Bound.java similarity index 100% rename from src/org/oscim/utils/osm/Bound.java rename to vtm-extras/src/org/oscim/utils/osm/Bound.java diff --git a/src/org/oscim/utils/osm/OSMData.java b/vtm-extras/src/org/oscim/utils/osm/OSMData.java similarity index 100% rename from src/org/oscim/utils/osm/OSMData.java rename to vtm-extras/src/org/oscim/utils/osm/OSMData.java diff --git a/src/org/oscim/utils/osm/OSMElement.java b/vtm-extras/src/org/oscim/utils/osm/OSMElement.java similarity index 100% rename from src/org/oscim/utils/osm/OSMElement.java rename to vtm-extras/src/org/oscim/utils/osm/OSMElement.java diff --git a/src/org/oscim/utils/osm/OSMMember.java b/vtm-extras/src/org/oscim/utils/osm/OSMMember.java similarity index 100% rename from src/org/oscim/utils/osm/OSMMember.java rename to vtm-extras/src/org/oscim/utils/osm/OSMMember.java diff --git a/src/org/oscim/utils/osm/OSMNode.java b/vtm-extras/src/org/oscim/utils/osm/OSMNode.java similarity index 100% rename from src/org/oscim/utils/osm/OSMNode.java rename to vtm-extras/src/org/oscim/utils/osm/OSMNode.java diff --git a/src/org/oscim/utils/osm/OSMRelation.java b/vtm-extras/src/org/oscim/utils/osm/OSMRelation.java similarity index 100% rename from src/org/oscim/utils/osm/OSMRelation.java rename to vtm-extras/src/org/oscim/utils/osm/OSMRelation.java diff --git a/src/org/oscim/utils/osm/OSMWay.java b/vtm-extras/src/org/oscim/utils/osm/OSMWay.java similarity index 100% rename from src/org/oscim/utils/osm/OSMWay.java rename to vtm-extras/src/org/oscim/utils/osm/OSMWay.java diff --git a/src/org/oscim/utils/osmpbf/OsmPbfParser.java b/vtm-extras/src/org/oscim/utils/osmpbf/OsmPbfParser.java similarity index 100% rename from src/org/oscim/utils/osmpbf/OsmPbfParser.java rename to vtm-extras/src/org/oscim/utils/osmpbf/OsmPbfParser.java diff --git a/src/org/oscim/utils/osmpbf/OsmPbfReader.java b/vtm-extras/src/org/oscim/utils/osmpbf/OsmPbfReader.java similarity index 100% rename from src/org/oscim/utils/osmpbf/OsmPbfReader.java rename to vtm-extras/src/org/oscim/utils/osmpbf/OsmPbfReader.java diff --git a/src/org/oscim/utils/overpass/OverpassAPIReader.java b/vtm-extras/src/org/oscim/utils/overpass/OverpassAPIReader.java similarity index 100% rename from src/org/oscim/utils/overpass/OverpassAPIReader.java rename to vtm-extras/src/org/oscim/utils/overpass/OverpassAPIReader.java diff --git a/src/org/oscim/utils/wkb/Geometry.java b/vtm-extras/src/org/oscim/utils/wkb/Geometry.java similarity index 100% rename from src/org/oscim/utils/wkb/Geometry.java rename to vtm-extras/src/org/oscim/utils/wkb/Geometry.java diff --git a/src/org/oscim/utils/wkb/ValueGetter.java b/vtm-extras/src/org/oscim/utils/wkb/ValueGetter.java similarity index 100% rename from src/org/oscim/utils/wkb/ValueGetter.java rename to vtm-extras/src/org/oscim/utils/wkb/ValueGetter.java diff --git a/src/org/oscim/utils/wkb/WKBReader.java b/vtm-extras/src/org/oscim/utils/wkb/WKBReader.java similarity index 100% rename from src/org/oscim/utils/wkb/WKBReader.java rename to vtm-extras/src/org/oscim/utils/wkb/WKBReader.java diff --git a/vtm-gdx-android/.classpath b/vtm-gdx-android/.classpath new file mode 100644 index 00000000..346a9895 --- /dev/null +++ b/vtm-gdx-android/.classpath @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/vtm-gdx-android/.project b/vtm-gdx-android/.project new file mode 100644 index 00000000..73270fe9 --- /dev/null +++ b/vtm-gdx-android/.project @@ -0,0 +1,40 @@ + + + vtm-gdx-android + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + + + assets + 2 + /home/src/vtm/OpenScienceMap/vtm/assets + + + diff --git a/vtm-gdx-android/.settings/org.eclipse.jdt.core.prefs b/vtm-gdx-android/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..48ab4c6b --- /dev/null +++ b/vtm-gdx-android/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/vtm-gdx-android/AndroidManifest.xml b/vtm-gdx-android/AndroidManifest.xml new file mode 100644 index 00000000..e0a0fdf5 --- /dev/null +++ b/vtm-gdx-android/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vtm-gdx-android/proguard.cfg b/vtm-gdx-android/proguard.cfg new file mode 100644 index 00000000..b1cdf17b --- /dev/null +++ b/vtm-gdx-android/proguard.cfg @@ -0,0 +1,40 @@ +-optimizationpasses 5 +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-dontpreverify +-verbose +-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* + +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Application +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class * extends android.app.backup.BackupAgentHelper +-keep public class * extends android.preference.Preference +-keep public class com.android.vending.licensing.ILicensingService + +-keepclasseswithmembernames class * { + native ; +} + +-keepclasseswithmembers class * { + public (android.content.Context, android.util.AttributeSet); +} + +-keepclasseswithmembers class * { + public (android.content.Context, android.util.AttributeSet, int); +} + +-keepclassmembers class * extends android.app.Activity { + public void *(android.view.View); +} + +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} diff --git a/vtm-gdx-android/project.properties b/vtm-gdx-android/project.properties new file mode 100644 index 00000000..c4f09d2b --- /dev/null +++ b/vtm-gdx-android/project.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "ant.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-17 diff --git a/vtm-gdx-android/res/drawable-hdpi/ic_launcher.png b/vtm-gdx-android/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 00000000..8074c4c5 Binary files /dev/null and b/vtm-gdx-android/res/drawable-hdpi/ic_launcher.png differ diff --git a/vtm-gdx-android/res/drawable-ldpi/ic_launcher.png b/vtm-gdx-android/res/drawable-ldpi/ic_launcher.png new file mode 100644 index 00000000..1095584e Binary files /dev/null and b/vtm-gdx-android/res/drawable-ldpi/ic_launcher.png differ diff --git a/vtm-gdx-android/res/drawable-mdpi/ic_launcher.png b/vtm-gdx-android/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 00000000..a07c69fa Binary files /dev/null and b/vtm-gdx-android/res/drawable-mdpi/ic_launcher.png differ diff --git a/vtm-gdx-android/res/layout/main.xml b/vtm-gdx-android/res/layout/main.xml new file mode 100644 index 00000000..5c4fde29 --- /dev/null +++ b/vtm-gdx-android/res/layout/main.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/vtm-gdx-android/res/values/strings.xml b/vtm-gdx-android/res/values/strings.xml new file mode 100644 index 00000000..227659a0 --- /dev/null +++ b/vtm-gdx-android/res/values/strings.xml @@ -0,0 +1,4 @@ + + + My LibGDX Game + \ No newline at end of file diff --git a/src/org/oscim/backend/InputAdapter.java b/vtm-gdx-android/src/org/oscim/android/AndroidLog.java similarity index 60% rename from src/org/oscim/backend/InputAdapter.java rename to vtm-gdx-android/src/org/oscim/android/AndroidLog.java index 4f9679d5..1c785507 100644 --- a/src/org/oscim/backend/InputAdapter.java +++ b/vtm-gdx-android/src/org/oscim/android/AndroidLog.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 + * Copyright 2013 * * This program is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software @@ -12,8 +12,31 @@ * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ -package org.oscim.backend; +package org.oscim.android; -public class InputAdapter { +import android.util.Log; + + +public class AndroidLog implements org.oscim.backend.Log.Logger{ + + @Override + public void d(String tag, String msg) { + Log.d(tag, msg); + } + + @Override + public void w(String tag, String msg) { + Log.w(tag, msg); + } + + @Override + public void e(String tag, String msg) { + Log.e(tag, msg); + } + + @Override + public void i(String tag, String msg) { + Log.i(tag, msg); + } } diff --git a/vtm-gdx-android/src/org/oscim/android/canvas/AndroidBitmap.java b/vtm-gdx-android/src/org/oscim/android/canvas/AndroidBitmap.java new file mode 100644 index 00000000..ffd6f174 --- /dev/null +++ b/vtm-gdx-android/src/org/oscim/android/canvas/AndroidBitmap.java @@ -0,0 +1,86 @@ +/* + * Copyright 2013 Hannes Janetzek + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program. If not, see . + */ +package org.oscim.android.canvas; + +import java.io.InputStream; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.opengl.GLES20; +import android.opengl.GLUtils; + +public class AndroidBitmap implements org.oscim.backend.canvas.Bitmap { + final Bitmap mBitmap; + + public AndroidBitmap(InputStream inputStream) { + mBitmap = BitmapFactory.decodeStream(inputStream); + } + + /** + * @param format ignored always ARGB8888 + */ + public AndroidBitmap(int width, int height, int format){ + mBitmap = android.graphics.Bitmap + .createBitmap(width, height, android.graphics.Bitmap.Config.ARGB_8888); + } + AndroidBitmap(android.graphics.Bitmap bitmap){ + mBitmap = bitmap; + } + + @Override + public int getWidth() { + return mBitmap.getWidth(); + } + + @Override + public int getHeight() { + return mBitmap.getHeight(); + } + + @Override + public int[] getPixels() { + int width = getWidth(); + int height = getHeight(); + int[] colors = new int[width * height]; + mBitmap.getPixels(colors, 0, width, 0, 0, width, height); + return colors; + } + + @Override + public void eraseColor(int color) { + //int a = android.graphics.Color.TRANSPARENT; + mBitmap.eraseColor(color); + } + + @Override + public int uploadToTexture(boolean replace) { + + int format = GLUtils.getInternalFormat(mBitmap); + int type = GLUtils.getType(mBitmap); + + if (replace) + GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, mBitmap, format, + type); + else + GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, format, mBitmap, type, 0); + + return 0; + } + + @Override + public void recycle() { + mBitmap.recycle(); + } +} diff --git a/vtm-gdx-android/src/org/oscim/android/canvas/AndroidCanvas.java b/vtm-gdx-android/src/org/oscim/android/canvas/AndroidCanvas.java new file mode 100644 index 00000000..ac93ceac --- /dev/null +++ b/vtm-gdx-android/src/org/oscim/android/canvas/AndroidCanvas.java @@ -0,0 +1,29 @@ +package org.oscim.android.canvas; + +import org.oscim.backend.canvas.Bitmap; +import org.oscim.backend.canvas.Canvas; +import org.oscim.backend.canvas.Paint; + +public class AndroidCanvas implements Canvas { + final android.graphics.Canvas canvas; + + public AndroidCanvas() { + this.canvas = new android.graphics.Canvas(); + } + @Override + public void setBitmap(Bitmap bitmap) { + this.canvas.setBitmap(((AndroidBitmap)bitmap).mBitmap); + } + + @Override + public void drawText(String string, float x, float y, Paint stroke) { + this.canvas.drawText(string, x, y, ((AndroidPaint)stroke).mPaint); + + } + @Override + public void drawBitmap(Bitmap bitmap, float x, float y) { + this.canvas.drawBitmap(((AndroidBitmap)bitmap).mBitmap, x, y, null); + + } + +} diff --git a/vtm-gdx-android/src/org/oscim/android/canvas/AndroidGraphics.java b/vtm-gdx-android/src/org/oscim/android/canvas/AndroidGraphics.java new file mode 100644 index 00000000..e2ac5260 --- /dev/null +++ b/vtm-gdx-android/src/org/oscim/android/canvas/AndroidGraphics.java @@ -0,0 +1,117 @@ +/* + * Copyright 2010, 2011, 2012 mapsforge.org + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program. If not, see . + */ +package org.oscim.android.canvas; + +import java.io.InputStream; + +import org.oscim.backend.CanvasAdapter; +import org.oscim.backend.canvas.Bitmap; +import org.oscim.backend.canvas.Canvas; +import org.oscim.backend.canvas.Paint; +import org.oscim.layers.overlay.OverlayItem.HotspotPlace; +import org.oscim.layers.overlay.OverlayMarker; + +import android.content.res.Resources; +import android.graphics.Bitmap.Config; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; + +public final class AndroidGraphics extends CanvasAdapter { + public static final AndroidGraphics INSTANCE = new AndroidGraphics(); + + // public static android.graphics.Bitmap getAndroidBitmap(Bitmap bitmap) { + // return ((AndroidBitmap) bitmap).bitmap; + // } + + public static android.graphics.Paint getAndroidPaint(Paint paint) { + return ((AndroidPaint) paint).mPaint; + } + + private AndroidGraphics() { + // do nothing + } + + @Override + public Bitmap decodeBitmap(InputStream inputStream) { + return new AndroidBitmap(inputStream); + } + + @Override + public int getColor(Color color) { + switch (color) { + case BLACK: + return android.graphics.Color.BLACK; + + case CYAN: + return android.graphics.Color.CYAN; + + case TRANSPARENT: + return android.graphics.Color.TRANSPARENT; + + case WHITE: + return android.graphics.Color.WHITE; + } + + throw new IllegalArgumentException("unknown color value: " + color); + } + + @Override + public Paint getPaint() { + return new AndroidPaint(); + } + + @Override + public int parseColor(String colorString) { + return android.graphics.Color.parseColor(colorString); + } + + @Override + public Bitmap getBitmap(int width, int height, int format) { + return new AndroidBitmap(width, height, format); + } + + @Override + public Canvas getCanvas() { + return new AndroidCanvas(); + } + + //------------------------------------- + public static Bitmap drawableToBitmap(Drawable drawable) { + if (drawable instanceof BitmapDrawable) { + return new AndroidBitmap(((BitmapDrawable) drawable).getBitmap()); + } + + android.graphics.Bitmap bitmap = android.graphics.Bitmap.createBitmap( + drawable.getIntrinsicWidth(), + drawable.getIntrinsicHeight(), + Config.ARGB_8888); + + android.graphics.Canvas canvas = new android.graphics.Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + + return new AndroidBitmap(bitmap); + } + + public static OverlayMarker makeMarker(Resources res, int id, HotspotPlace place) { + + if (place == null) + place = HotspotPlace.CENTER; + + Drawable drawable = res.getDrawable(id); + + return new OverlayMarker(drawableToBitmap(drawable), place); + } +} diff --git a/vtm-gdx-android/src/org/oscim/android/canvas/AndroidPaint.java b/vtm-gdx-android/src/org/oscim/android/canvas/AndroidPaint.java new file mode 100644 index 00000000..0c112dba --- /dev/null +++ b/vtm-gdx-android/src/org/oscim/android/canvas/AndroidPaint.java @@ -0,0 +1,165 @@ +/* + * Copyright 2010, 2011, 2012 mapsforge.org + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program. If not, see . + */ +package org.oscim.android.canvas; + +import org.oscim.backend.canvas.Paint; + +import android.graphics.Bitmap.Config; +import android.graphics.BitmapShader; +import android.graphics.DashPathEffect; +import android.graphics.Paint.FontMetrics; +import android.graphics.PathEffect; +import android.graphics.Rect; +import android.graphics.Shader; +import android.graphics.Shader.TileMode; +import android.graphics.Typeface; + +class AndroidPaint implements Paint { + private static int getStyle(org.oscim.backend.canvas.Paint.FontStyle fontStyle) { + switch (fontStyle) { + case BOLD: + return 1; + case BOLD_ITALIC: + return 3; + case ITALIC: + return 2; + case NORMAL: + return 0; + } + + throw new IllegalArgumentException("unknown font style: " + fontStyle); + } + + private static Typeface getTypeface(org.oscim.backend.canvas.Paint.FontFamily fontFamily) { + switch (fontFamily) { + case DEFAULT: + return Typeface.DEFAULT; + case DEFAULT_BOLD: + return Typeface.DEFAULT_BOLD; + case MONOSPACE: + return Typeface.MONOSPACE; + case SANS_SERIF: + return Typeface.SANS_SERIF; + case SERIF: + return Typeface.SERIF; + } + + throw new IllegalArgumentException("unknown font family: " + fontFamily); + } + + final android.graphics.Paint mPaint; + + AndroidPaint() { + mPaint = new android.graphics.Paint( + android.graphics.Paint.ANTI_ALIAS_FLAG); + } + + @Override + public int getColor() { + return mPaint.getColor(); + } + + @Override + public int getTextHeight(String text) { + Rect rect = new Rect(); + mPaint.getTextBounds(text, 0, text.length(), rect); + return rect.height(); + } + + @Override + public int getTextWidth(String text) { + Rect rect = new Rect(); + mPaint.getTextBounds(text, 0, text.length(), rect); + return rect.width(); + } + + @Override + public void setBitmapShader(org.oscim.backend.canvas.Bitmap bitmap) { + if (bitmap == null) { + return; + } + + android.graphics.Bitmap androidBitmap = android.graphics.Bitmap + .createBitmap(bitmap.getPixels(), bitmap.getWidth(), + bitmap.getHeight(), Config.ARGB_8888); + Shader shader = new BitmapShader(androidBitmap, TileMode.REPEAT, + TileMode.REPEAT); + mPaint.setShader(shader); + } + + @Override + public void setColor(int color) { + mPaint.setColor(color); + } + + @Override + public void setDashPathEffect(float[] strokeDasharray) { + PathEffect pathEffect = new DashPathEffect(strokeDasharray, 0); + mPaint.setPathEffect(pathEffect); + } + + @Override + public void setStrokeCap(Cap cap) { + android.graphics.Paint.Cap androidCap = android.graphics.Paint.Cap + .valueOf(cap.name()); + mPaint.setStrokeCap(androidCap); + } + + @Override + public void setStrokeWidth(float width) { + mPaint.setStrokeWidth(width); + } + + @Override + public void setStyle(Style style) { + mPaint.setStyle(android.graphics.Paint.Style.valueOf(style.name())); + } + + @Override + public void setTextAlign(Align align) { + + mPaint.setTextAlign(android.graphics.Paint.Align.valueOf(align.name())); + } + + @Override + public void setTextSize(float textSize) { + mPaint.setTextSize(textSize); + } + + @Override + public void setTypeface(FontFamily fontFamily, FontStyle fontStyle) { + Typeface typeface = Typeface.create(getTypeface(fontFamily), + getStyle(fontStyle)); + mPaint.setTypeface(typeface); + } + + @Override + public float measureText(String text) { + return mPaint.measureText(text); + } + + @Override + public float getFontHeight() { + FontMetrics fm = mPaint.getFontMetrics(); + return (float) Math.ceil(Math.abs(fm.bottom) + Math.abs(fm.top)); + } + + @Override + public float getFontDescent() { + FontMetrics fm = mPaint.getFontMetrics(); + // //fontDescent = (float) Math.ceil(Math.abs(fm.descent)); + return Math.abs(fm.bottom); + } +} diff --git a/vtm-gdx-android/src/org/oscim/gdx/MainActivity.java b/vtm-gdx-android/src/org/oscim/gdx/MainActivity.java new file mode 100644 index 00000000..729eaa3d --- /dev/null +++ b/vtm-gdx-android/src/org/oscim/gdx/MainActivity.java @@ -0,0 +1,39 @@ +package org.oscim.gdx; + +import org.oscim.android.AndroidLog; +import org.oscim.android.canvas.AndroidGraphics; +import org.oscim.backend.AssetAdapter; +import org.oscim.backend.CanvasAdapter; +import org.oscim.backend.GL20; +import org.oscim.backend.GLAdapter; +import org.oscim.backend.Log; +import org.oscim.core.Tile; + +import android.os.Bundle; + +import com.badlogic.gdx.backends.android.AndroidApplication; +import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration; +import com.badlogic.gdx.backends.android.AndroidGL20; + +public class MainActivity extends AndroidApplication { + + private final class AndroidGLAdapter extends AndroidGL20 implements GL20{ + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // set our globals + CanvasAdapter.g = AndroidGraphics.INSTANCE; + GLAdapter.INSTANCE = new AndroidGLAdapter(); + Log.logger = new AndroidLog(); + // TODO make this dpi dependent + Tile.SIZE = 400; + + AndroidApplicationConfiguration cfg = new AndroidApplicationConfiguration(); + cfg.useGL20 = true; + + initialize(new GdxMap(), cfg); + } +} \ No newline at end of file diff --git a/vtm-gdx-desktop/.classpath b/vtm-gdx-desktop/.classpath new file mode 100644 index 00000000..b24402d4 --- /dev/null +++ b/vtm-gdx-desktop/.classpath @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/vtm-gdx-desktop/.project b/vtm-gdx-desktop/.project new file mode 100644 index 00000000..7bf43a15 --- /dev/null +++ b/vtm-gdx-desktop/.project @@ -0,0 +1,24 @@ + + + vtm-gdx-desktop + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + + + assets + 2 + /home/src/vtm/OpenScienceMap/vtm/assets + + + diff --git a/vtm-gdx-desktop/.settings/org.eclipse.jdt.core.prefs b/vtm-gdx-desktop/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..54e493c0 --- /dev/null +++ b/vtm-gdx-desktop/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/vtm-gdx-desktop/src/org/oscim/awt/AwtBitmap.java b/vtm-gdx-desktop/src/org/oscim/awt/AwtBitmap.java new file mode 100644 index 00000000..9895cec9 --- /dev/null +++ b/vtm-gdx-desktop/src/org/oscim/awt/AwtBitmap.java @@ -0,0 +1,85 @@ +package org.oscim.awt; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import java.nio.IntBuffer; + +import javax.imageio.ImageIO; + +import org.oscim.backend.canvas.Bitmap; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.GL20; +import com.badlogic.gdx.utils.BufferUtils; + +public class AwtBitmap implements Bitmap { + + BufferedImage bitmap; + int width; + int height; + + public AwtBitmap(int width, int height, int format) { + bitmap = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + this.width = width; + this.height = height; + } + + AwtBitmap(InputStream inputStream) throws IOException { + this.bitmap = ImageIO.read(inputStream); +} + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public int[] getPixels() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void eraseColor(int transparent) { + // TODO Auto-generated method stub + } + + private static IntBuffer tmpBuffer = BufferUtils.newIntBuffer(256 * 256); + private static int[] tmpPixel = new int[256 * 256]; + @Override + public int uploadToTexture(boolean replace) { + int[] pixels; + IntBuffer buffer; + + if (width == 256 && height == 256){ + pixels = tmpPixel; + buffer = tmpBuffer; + buffer.clear(); + }else{ + pixels = new int[width * height]; + buffer = BufferUtils.newIntBuffer(width * height); + } + + + bitmap.getRGB(0, 0, width, height, pixels, 0, width); + + buffer.put(pixels); + buffer.flip(); + + Gdx.gl20.glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL20.GL_RGBA, width, + height, 0, GL20.GL_RGBA, GL20.GL_UNSIGNED_BYTE, buffer); + + return 0; + } + + @Override + public void recycle() { + // TODO Auto-generated method stub + + } +} diff --git a/vtm-gdx-desktop/src/org/oscim/awt/AwtCanvas.java b/vtm-gdx-desktop/src/org/oscim/awt/AwtCanvas.java new file mode 100644 index 00000000..2452dff1 --- /dev/null +++ b/vtm-gdx-desktop/src/org/oscim/awt/AwtCanvas.java @@ -0,0 +1,63 @@ +package org.oscim.awt; + +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.RenderingHints; + +import org.oscim.backend.canvas.Bitmap; +import org.oscim.backend.canvas.Canvas; +import org.oscim.backend.canvas.Paint; + +public class AwtCanvas implements Canvas { + + Graphics2D canvas; + + public AwtCanvas() { + + } + + @Override + public void setBitmap(Bitmap bitmap) { + if (canvas != null) + canvas.dispose(); + + AwtBitmap awtBitamp = (AwtBitmap)bitmap; + + canvas = awtBitamp.bitmap.createGraphics(); + //awtBitamp.bitmap. + //bitmap.eraseColor(); + canvas.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0)); + //canvas.setBackground(new Color(1,1,1,1)); + canvas.setColor(Color.BLACK); + + //Gdx.app.log("set bitmap ", bitmap + " "+ bitmap.getWidth() + " " +bitmap.getHeight()); + canvas.fillRect(0,0,bitmap.getWidth(),bitmap.getHeight()); + //canvas.clearRect(0, 0, bitmap.getWidth(),bitmap.getHeight()); + + canvas.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f)); + + canvas.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON ); + //canvas.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); + //canvas.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + //canvas.setRenderingHint(RenderingHints.KEY_KERNING, RenderingHints.VALUE_RENDER_QUALITY); + + + } + + @Override + public void drawText(String string, float x, float y, Paint stroke) { + AwtPaint p = (AwtPaint)stroke; + + canvas.setFont(p.font); + canvas.setColor(p.color); + + canvas.drawString(string, (int)x, (int)y); + } + + @Override + public void drawBitmap(Bitmap bitmap, float x, float y) { + // TODO Auto-generated method stub + throw new UnknownError("not implemented"); + } +} diff --git a/vtm-gdx-desktop/src/org/oscim/awt/AwtGraphics.java b/vtm-gdx-desktop/src/org/oscim/awt/AwtGraphics.java new file mode 100644 index 00000000..944b2b28 --- /dev/null +++ b/vtm-gdx-desktop/src/org/oscim/awt/AwtGraphics.java @@ -0,0 +1,89 @@ +package org.oscim.awt; + +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; + +import org.oscim.backend.canvas.Bitmap; +import org.oscim.backend.canvas.Canvas; +import org.oscim.backend.CanvasAdapter; +import org.oscim.backend.canvas.Paint; + +public class AwtGraphics extends CanvasAdapter { + public static final AwtGraphics INSTANCE = new AwtGraphics(); + + private AwtGraphics() { + // do nothing + } + + @Override + public int getColor(Color color) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public Paint getPaint() { + return new AwtPaint(); + } + + @Override + public int parseColor(String colorString) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public Bitmap getBitmap(int width, int height, int format) { + return new AwtBitmap(width, height, format); + } + + @Override + public Canvas getCanvas() { + return new AwtCanvas(); + } + + static final BufferedImage image; + + static final Graphics2D canvas; + + static { + image = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB); + canvas = image.createGraphics(); + canvas.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON ); + //canvas.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); + //canvas.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + } + static synchronized FontMetrics getFontMetrics(Font font) { + canvas.setFont(font); + // get character measurements + FontMetrics fm = canvas.getFontMetrics(); + // int ascent = fm.getMaxAscent(); + // int descent = fm.getMaxDescent(); + // int advance = fm.charWidth('W'); // width of widest char, more + // reliable than getMaxAdvance(); + // int leading = fm.getLeading(); + // + return fm; + } + + static synchronized float getTextWidth(FontMetrics fm, String text) { + return (float)fm.getStringBounds(text, canvas).getWidth(); + } + + @Override + public Bitmap decodeBitmap(InputStream inputStream) { + try { + return new AwtBitmap(inputStream); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + +} diff --git a/vtm-gdx-desktop/src/org/oscim/awt/AwtPaint.java b/vtm-gdx-desktop/src/org/oscim/awt/AwtPaint.java new file mode 100644 index 00000000..8186240e --- /dev/null +++ b/vtm-gdx-desktop/src/org/oscim/awt/AwtPaint.java @@ -0,0 +1,149 @@ +package org.oscim.awt; + +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.font.TextAttribute; +import java.text.AttributedCharacterIterator.Attribute; +import java.util.HashMap; +import java.util.Map; + +//import org.oscim.graphics.Align; +import org.oscim.backend.canvas.Bitmap; +//import org.oscim.graphics.Cap; +//import org.oscim.graphics.FontFamily; +//import org.oscim.graphics.FontStyle; +import org.oscim.backend.canvas.Paint; +//import org.oscim.graphics.Style; + +public class AwtPaint implements Paint { + static final Font defaultFont; + static { + Map textAttributes = new HashMap(); + textAttributes.put(TextAttribute.KERNING, TextAttribute.KERNING_ON); + textAttributes.put(TextAttribute.FAMILY, "SansSerif"); + textAttributes.put(TextAttribute.SIZE, 13); + + defaultFont = Font.getFont(textAttributes); + + } + + Font font = defaultFont; //new Font("Default", Font.PLAIN, 13); + + FontMetrics fm; + Color color = new Color(0.1f,0.1f,0.1f,1); + + @Override + public int getColor() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getTextHeight(String text) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getTextWidth(String text) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void setBitmapShader(Bitmap bitmap) { + // TODO Auto-generated method stub + + } + + @Override + public void setColor(int color) { + this.color = new Color( + ((color >> 16) & 0xff)/255f, + ((color >> 8) & 0xff)/255f, + ((color >> 0) & 0xff)/255f, + ((color >> 24) & 0xff)/255f + ); + } + + @Override + public void setDashPathEffect(float[] strokeDasharray) { + // TODO Auto-generated method stub + + } + + @Override + public void setStrokeCap(Cap cap) { + // TODO Auto-generated method stub + + } + + @Override + public void setStrokeWidth(float width) { + //int size = font.getSize(); + //font = font.deriveFont(size + width * 4); + + // TODO Auto-generated method stub + + } + + @Override + public void setStyle(Style style) { + // TODO Auto-generated method stub + + } + + @Override + public void setTextAlign(Align align) { + // TODO Auto-generated method stub + + } + + @Override + public void setTextSize(float textSize) { + font = font.deriveFont(textSize - 4); + + } + + @Override + public void setTypeface(FontFamily fontFamily, FontStyle fontStyle) { + // TODO Auto-generated method stub + + } + + @Override + public float measureText(String text) { + if (fm == null) + fm = AwtGraphics.getFontMetrics(this.font); + float w = AwtGraphics.getTextWidth(fm, text); + //Gdx.app.log("text width:", text + " " + w); + return w; + //return fm.getStringBounds(text, A).getWidth(); + //return AwtGraphics.getTextWidth(fm, text); + //return fm.stringWidth(text); + } + + @Override + public float getFontHeight() { + if (fm == null) + fm = AwtGraphics.getFontMetrics(this.font); + + float height = fm.getHeight(); + + //Gdx.app.log("text height", " " + height); + return height; + } + + @Override + public float getFontDescent() { + if (fm == null) + fm = AwtGraphics.getFontMetrics(this.font); + + float desc = fm.getDescent(); + //Gdx.app.log("text descent", " " + desc); + + return desc; + } + +} diff --git a/vtm-gdx-desktop/src/org/oscim/gdx/GdxGLAdapter.java b/vtm-gdx-desktop/src/org/oscim/gdx/GdxGLAdapter.java new file mode 100644 index 00000000..64d7a6d0 --- /dev/null +++ b/vtm-gdx-desktop/src/org/oscim/gdx/GdxGLAdapter.java @@ -0,0 +1,742 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package org.oscim.gdx; + +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; + +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.EXTFramebufferObject; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL14; +import org.lwjgl.opengl.GL15; +import org.lwjgl.opengl.GL20; + +import com.badlogic.gdx.graphics.GL10; +import com.badlogic.gdx.utils.GdxRuntimeException; + +/** An implementation of the {@link GL20} interface based on LWJGL. Note that LWJGL shaders and OpenGL ES shaders will not be 100% + * compatible. Some glGetXXX methods are not implemented. + * + * @author mzechner */ +final class GdxGLAdapter implements org.oscim.backend.GL20 { + public void glActiveTexture (int texture) { + GL13.glActiveTexture(texture); + } + + public void glAttachShader (int program, int shader) { + GL20.glAttachShader(program, shader); + } + + public void glBindAttribLocation (int program, int index, String name) { + GL20.glBindAttribLocation(program, index, name); + } + + public void glBindBuffer (int target, int buffer) { + GL15.glBindBuffer(target, buffer); + } + + public void glBindFramebuffer (int target, int framebuffer) { + EXTFramebufferObject.glBindFramebufferEXT(target, framebuffer); + } + + public void glBindRenderbuffer (int target, int renderbuffer) { + EXTFramebufferObject.glBindRenderbufferEXT(target, renderbuffer); + } + + public void glBindTexture (int target, int texture) { + GL11.glBindTexture(target, texture); + } + + public void glBlendColor (float red, float green, float blue, float alpha) { + GL14.glBlendColor(red, green, blue, alpha); + } + + public void glBlendEquation (int mode) { + GL14.glBlendEquation(mode); + } + + public void glBlendEquationSeparate (int modeRGB, int modeAlpha) { + GL20.glBlendEquationSeparate(modeRGB, modeAlpha); + } + + public void glBlendFunc (int sfactor, int dfactor) { + GL11.glBlendFunc(sfactor, dfactor); + } + + public void glBlendFuncSeparate (int srcRGB, int dstRGB, int srcAlpha, int dstAlpha) { + GL14.glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); + } + + public void glBufferData (int target, int size, Buffer data, int usage) { + if(data == null) + throw new GdxRuntimeException("Using null for the data not possible, blame LWJGL"); + else if (data instanceof ByteBuffer) + GL15.glBufferData(target, (ByteBuffer)data, usage); + else if (data instanceof IntBuffer) + GL15.glBufferData(target, (IntBuffer)data, usage); + else if (data instanceof FloatBuffer) + GL15.glBufferData(target, (FloatBuffer)data, usage); + else if (data instanceof DoubleBuffer) + GL15.glBufferData(target, (DoubleBuffer)data, usage); + else if (data instanceof ShortBuffer) // + GL15.glBufferData(target, (ShortBuffer)data, usage); + } + + public void glBufferSubData (int target, int offset, int size, Buffer data) { + if(data == null) + throw new GdxRuntimeException("Using null for the data not possible, blame LWJGL"); + else if (data instanceof ByteBuffer) + GL15.glBufferSubData(target, offset, (ByteBuffer)data); + else if (data instanceof IntBuffer) + GL15.glBufferSubData(target, offset, (IntBuffer)data); + else if (data instanceof FloatBuffer) + GL15.glBufferSubData(target, offset, (FloatBuffer)data); + else if (data instanceof DoubleBuffer) + GL15.glBufferSubData(target, offset, (DoubleBuffer)data); + else if (data instanceof ShortBuffer) // + GL15.glBufferSubData(target, offset, (ShortBuffer)data); + } + + public int glCheckFramebufferStatus (int target) { + return EXTFramebufferObject.glCheckFramebufferStatusEXT(target); + } + + public void glClear (int mask) { + GL11.glClear(mask); + } + + public void glClearColor (float red, float green, float blue, float alpha) { + GL11.glClearColor(red, green, blue, alpha); + } + + public void glClearDepthf (float depth) { + GL11.glClearDepth(depth); + } + + public void glClearStencil (int s) { + GL11.glClearStencil(s); + } + + public void glColorMask (boolean red, boolean green, boolean blue, boolean alpha) { + GL11.glColorMask(red, green, blue, alpha); + } + + public void glCompileShader (int shader) { + GL20.glCompileShader(shader); + } + + public void glCompressedTexImage2D (int target, int level, int internalformat, int width, int height, int border, + int imageSize, Buffer data) { + if (data instanceof ByteBuffer) { + GL13.glCompressedTexImage2D(target, level, internalformat, width, height, border, (ByteBuffer)data); + } else { + throw new GdxRuntimeException("Can't use " + data.getClass().getName() + + " with this method. Use ByteBuffer instead."); + } + } + + public void glCompressedTexSubImage2D (int target, int level, int xoffset, int yoffset, int width, int height, int format, + int imageSize, Buffer data) { + throw new GdxRuntimeException("not implemented"); + } + + public void glCopyTexImage2D (int target, int level, int internalformat, int x, int y, int width, int height, int border) { + GL11.glCopyTexImage2D(target, level, internalformat, x, y, width, height, border); + } + + public void glCopyTexSubImage2D (int target, int level, int xoffset, int yoffset, int x, int y, int width, int height) { + GL11.glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); + } + + public int glCreateProgram () { + return GL20.glCreateProgram(); + } + + public int glCreateShader (int type) { + return GL20.glCreateShader(type); + } + + public void glCullFace (int mode) { + GL11.glCullFace(mode); + } + + public void glDeleteBuffers (int n, IntBuffer buffers) { + GL15.glDeleteBuffers(buffers); + } + + public void glDeleteFramebuffers (int n, IntBuffer framebuffers) { + EXTFramebufferObject.glDeleteFramebuffersEXT(framebuffers); + } + + public void glDeleteProgram (int program) { + GL20.glDeleteProgram(program); + } + + public void glDeleteRenderbuffers (int n, IntBuffer renderbuffers) { + EXTFramebufferObject.glDeleteRenderbuffersEXT(renderbuffers); + } + + public void glDeleteShader (int shader) { + GL20.glDeleteShader(shader); + } + + public void glDeleteTextures (int n, IntBuffer textures) { + GL11.glDeleteTextures(textures); + } + + public void glDepthFunc (int func) { + GL11.glDepthFunc(func); + } + + public void glDepthMask (boolean flag) { + GL11.glDepthMask(flag); + } + + public void glDepthRangef (float zNear, float zFar) { + GL11.glDepthRange(zNear, zFar); + } + + public void glDetachShader (int program, int shader) { + GL20.glDetachShader(program, shader); + } + + public void glDisable (int cap) { + GL11.glDisable(cap); + } + + public void glDisableVertexAttribArray (int index) { + GL20.glDisableVertexAttribArray(index); + } + + public void glDrawArrays (int mode, int first, int count) { + GL11.glDrawArrays(mode, first, count); + } + + public void glDrawElements (int mode, int count, int type, Buffer indices) { + if (indices instanceof ShortBuffer && type == GL10.GL_UNSIGNED_SHORT) + GL11.glDrawElements(mode, (ShortBuffer)indices); + else if (indices instanceof ByteBuffer && type == GL10.GL_UNSIGNED_SHORT) + GL11.glDrawElements(mode, ((ByteBuffer)indices).asShortBuffer()); // FIXME yay... + else if (indices instanceof ByteBuffer && type == GL10.GL_UNSIGNED_BYTE) + GL11.glDrawElements(mode, (ByteBuffer)indices); + else + throw new GdxRuntimeException("Can't use " + indices.getClass().getName() + + " with this method. Use ShortBuffer or ByteBuffer instead. Blame LWJGL"); + } + + public void glEnable (int cap) { + GL11.glEnable(cap); + } + + public void glEnableVertexAttribArray (int index) { + GL20.glEnableVertexAttribArray(index); + } + + public void glFinish () { + GL11.glFinish(); + } + + public void glFlush () { + GL11.glFlush(); + } + + public void glFramebufferRenderbuffer (int target, int attachment, int renderbuffertarget, int renderbuffer) { + EXTFramebufferObject.glFramebufferRenderbufferEXT(target, attachment, renderbuffertarget, renderbuffer); + } + + public void glFramebufferTexture2D (int target, int attachment, int textarget, int texture, int level) { + EXTFramebufferObject.glFramebufferTexture2DEXT(target, attachment, textarget, texture, level); + } + + public void glFrontFace (int mode) { + GL11.glFrontFace(mode); + } + + public void glGenBuffers (int n, IntBuffer buffers) { + GL15.glGenBuffers(buffers); + } + + public void glGenFramebuffers (int n, IntBuffer framebuffers) { + EXTFramebufferObject.glGenFramebuffersEXT(framebuffers); + } + + public void glGenRenderbuffers (int n, IntBuffer renderbuffers) { + EXTFramebufferObject.glGenRenderbuffersEXT(renderbuffers); + } + + public void glGenTextures (int n, IntBuffer textures) { + GL11.glGenTextures(textures); + } + + public void glGenerateMipmap (int target) { + EXTFramebufferObject.glGenerateMipmapEXT(target); + } + + public String glGetActiveAttrib (int program, int index, IntBuffer size, Buffer type) { + // FIXME this is less than ideal of course... + IntBuffer typeTmp = BufferUtils.createIntBuffer(2); + String name = GL20.glGetActiveAttrib(program, index, 256, typeTmp); + if (type instanceof IntBuffer) ((IntBuffer)type).put(typeTmp.get(0)); + return name; + } + + public String glGetActiveUniform (int program, int index, IntBuffer size, Buffer type) { + // FIXME this is less than ideal of course... + IntBuffer typeTmp = BufferUtils.createIntBuffer(2); + String name = GL20.glGetActiveUniform(program, index, 256, typeTmp); + if (type instanceof IntBuffer) ((IntBuffer)type).put(typeTmp.get(0)); + return name; + } + + public void glGetAttachedShaders (int program, int maxcount, Buffer count, IntBuffer shaders) { + GL20.glGetAttachedShaders(program, (IntBuffer)count, shaders); + } + + public int glGetAttribLocation (int program, String name) { + return GL20.glGetAttribLocation(program, name); + } + + public void glGetBooleanv (int pname, Buffer params) { + GL11.glGetBoolean(pname, (ByteBuffer)params); + } + + public void glGetBufferParameteriv (int target, int pname, IntBuffer params) { + GL15.glGetBufferParameter(target, pname, params); + } + + public int glGetError () { + return GL11.glGetError(); + } + + public void glGetFloatv (int pname, FloatBuffer params) { + GL11.glGetFloat(pname, params); + } + + public void glGetFramebufferAttachmentParameteriv (int target, int attachment, int pname, IntBuffer params) { + EXTFramebufferObject.glGetFramebufferAttachmentParameterEXT(target, attachment, pname, params); + } + + public void glGetIntegerv (int pname, IntBuffer params) { + GL11.glGetInteger(pname, params); + } + + public String glGetProgramInfoLog (int program) { + ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 10); + buffer.order(ByteOrder.nativeOrder()); + ByteBuffer tmp = ByteBuffer.allocateDirect(4); + tmp.order(ByteOrder.nativeOrder()); + IntBuffer intBuffer = tmp.asIntBuffer(); + + GL20.glGetProgramInfoLog(program, intBuffer, buffer); + int numBytes = intBuffer.get(0); + byte[] bytes = new byte[numBytes]; + buffer.get(bytes); + return new String(bytes); + } + + public void glGetProgramiv (int program, int pname, IntBuffer params) { + GL20.glGetProgram(program, pname, params); + } + + public void glGetRenderbufferParameteriv (int target, int pname, IntBuffer params) { + EXTFramebufferObject.glGetRenderbufferParameterEXT(target, pname, params); + } + + public String glGetShaderInfoLog (int shader) { + ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 10); + buffer.order(ByteOrder.nativeOrder()); + ByteBuffer tmp = ByteBuffer.allocateDirect(4); + tmp.order(ByteOrder.nativeOrder()); + IntBuffer intBuffer = tmp.asIntBuffer(); + + GL20.glGetShaderInfoLog(shader, intBuffer, buffer); + int numBytes = intBuffer.get(0); + byte[] bytes = new byte[numBytes]; + buffer.get(bytes); + return new String(bytes); + } + + public void glGetShaderPrecisionFormat (int shadertype, int precisiontype, IntBuffer range, IntBuffer precision) { + throw new UnsupportedOperationException("unsupported, won't implement"); + } + + public void glGetShaderSource (int shader, int bufsize, Buffer length, String source) { + throw new UnsupportedOperationException("unsupported, won't implement."); + } + + public void glGetShaderiv (int shader, int pname, IntBuffer params) { + GL20.glGetShader(shader, pname, params); + } + + public String glGetString (int name) { + return GL11.glGetString(name); + } + + public void glGetTexParameterfv (int target, int pname, FloatBuffer params) { + GL11.glGetTexParameter(target, pname, params); + } + + public void glGetTexParameteriv (int target, int pname, IntBuffer params) { + GL11.glGetTexParameter(target, pname, params); + } + + public int glGetUniformLocation (int program, String name) { + return GL20.glGetUniformLocation(program, name); + } + + public void glGetUniformfv (int program, int location, FloatBuffer params) { + GL20.glGetUniform(program, location, params); + } + + public void glGetUniformiv (int program, int location, IntBuffer params) { + GL20.glGetUniform(program, location, params); + } + + public void glGetVertexAttribPointerv (int index, int pname, Buffer pointer) { + throw new UnsupportedOperationException("unsupported, won't implement"); + } + + public void glGetVertexAttribfv (int index, int pname, FloatBuffer params) { + GL20.glGetVertexAttrib(index, pname, params); + } + + public void glGetVertexAttribiv (int index, int pname, IntBuffer params) { + GL20.glGetVertexAttrib(index, pname, params); + } + + public void glHint (int target, int mode) { + GL11.glHint(target, mode); + } + + public boolean glIsBuffer (int buffer) { + return GL15.glIsBuffer(buffer); + } + + public boolean glIsEnabled (int cap) { + return GL11.glIsEnabled(cap); + } + + public boolean glIsFramebuffer (int framebuffer) { + return EXTFramebufferObject.glIsFramebufferEXT(framebuffer); + } + + public boolean glIsProgram (int program) { + return GL20.glIsProgram(program); + } + + public boolean glIsRenderbuffer (int renderbuffer) { + return EXTFramebufferObject.glIsRenderbufferEXT(renderbuffer); + } + + public boolean glIsShader (int shader) { + return GL20.glIsShader(shader); + } + + public boolean glIsTexture (int texture) { + return GL11.glIsTexture(texture); + } + + public void glLineWidth (float width) { + GL11.glLineWidth(width); + } + + public void glLinkProgram (int program) { + GL20.glLinkProgram(program); + } + + public void glPixelStorei (int pname, int param) { + GL11.glPixelStorei(pname, param); + } + + public void glPolygonOffset (float factor, float units) { + GL11.glPolygonOffset(factor, units); + } + + public void glReadPixels (int x, int y, int width, int height, int format, int type, Buffer pixels) { + if (pixels instanceof ByteBuffer) + GL11.glReadPixels(x, y, width, height, format, type, (ByteBuffer)pixels); + else if (pixels instanceof ShortBuffer) + GL11.glReadPixels(x, y, width, height, format, type, (ShortBuffer)pixels); + else if (pixels instanceof IntBuffer) + GL11.glReadPixels(x, y, width, height, format, type, (IntBuffer)pixels); + else if (pixels instanceof FloatBuffer) + GL11.glReadPixels(x, y, width, height, format, type, (FloatBuffer)pixels); + else + throw new GdxRuntimeException("Can't use " + pixels.getClass().getName() + + " with this method. Use ByteBuffer, ShortBuffer, IntBuffer or FloatBuffer instead. Blame LWJGL"); + } + + public void glReleaseShaderCompiler () { + // nothing to do here + } + + public void glRenderbufferStorage (int target, int internalformat, int width, int height) { + EXTFramebufferObject.glRenderbufferStorageEXT(target, internalformat, width, height); + } + + public void glSampleCoverage (float value, boolean invert) { + GL13.glSampleCoverage(value, invert); + } + + public void glScissor (int x, int y, int width, int height) { + GL11.glScissor(x, y, width, height); + } + + public void glShaderBinary (int n, IntBuffer shaders, int binaryformat, Buffer binary, int length) { + throw new UnsupportedOperationException("unsupported, won't implement"); + } + + public void glShaderSource (int shader, String string) { + GL20.glShaderSource(shader, string); + } + + public void glStencilFunc (int func, int ref, int mask) { + GL11.glStencilFunc(func, ref, mask); + } + + public void glStencilFuncSeparate (int face, int func, int ref, int mask) { + GL20.glStencilFuncSeparate(face, func, ref, mask); + } + + public void glStencilMask (int mask) { + GL11.glStencilMask(mask); + } + + public void glStencilMaskSeparate (int face, int mask) { + GL20.glStencilMaskSeparate(face, mask); + } + + public void glStencilOp (int fail, int zfail, int zpass) { + GL11.glStencilOp(fail, zfail, zpass); + } + + public void glStencilOpSeparate (int face, int fail, int zfail, int zpass) { + GL20.glStencilOpSeparate(face, fail, zfail, zpass); + } + + public void glTexImage2D (int target, int level, int internalformat, int width, int height, int border, int format, int type, + Buffer pixels) { + if (pixels == null) + GL11.glTexImage2D(target, level, internalformat, width, height, border, format, type, (ByteBuffer)null); + else if (pixels instanceof ByteBuffer) + GL11.glTexImage2D(target, level, internalformat, width, height, border, format, type, (ByteBuffer)pixels); + else if (pixels instanceof ShortBuffer) + GL11.glTexImage2D(target, level, internalformat, width, height, border, format, type, (ShortBuffer)pixels); + else if (pixels instanceof IntBuffer) + GL11.glTexImage2D(target, level, internalformat, width, height, border, format, type, (IntBuffer)pixels); + else if (pixels instanceof FloatBuffer) + GL11.glTexImage2D(target, level, internalformat, width, height, border, format, type, (FloatBuffer)pixels); + else if (pixels instanceof DoubleBuffer) + GL11.glTexImage2D(target, level, internalformat, width, height, border, format, type, (DoubleBuffer)pixels); + else + throw new GdxRuntimeException("Can't use " + pixels.getClass().getName() + + " with this method. Use ByteBuffer, ShortBuffer, IntBuffer, FloatBuffer or DoubleBuffer instead. Blame LWJGL"); + } + + public void glTexParameterf (int target, int pname, float param) { + GL11.glTexParameterf(target, pname, param); + } + + public void glTexParameterfv (int target, int pname, FloatBuffer params) { + GL11.glTexParameter(target, pname, params); + } + + public void glTexParameteri (int target, int pname, int param) { + GL11.glTexParameteri(target, pname, param); + } + + public void glTexParameteriv (int target, int pname, IntBuffer params) { + GL11.glTexParameter(target, pname, params); + } + + public void glTexSubImage2D (int target, int level, int xoffset, int yoffset, int width, int height, int format, int type, + Buffer pixels) { + if (pixels instanceof ByteBuffer) + GL11.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, (ByteBuffer)pixels); + else if (pixels instanceof ShortBuffer) + GL11.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, (ShortBuffer)pixels); + else if (pixels instanceof IntBuffer) + GL11.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, (IntBuffer)pixels); + else if (pixels instanceof FloatBuffer) + GL11.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, (FloatBuffer)pixels); + else if (pixels instanceof DoubleBuffer) + GL11.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, (DoubleBuffer)pixels); + else + throw new GdxRuntimeException("Can't use " + pixels.getClass().getName() + + " with this method. Use ByteBuffer, ShortBuffer, IntBuffer, FloatBuffer or DoubleBuffer instead. Blame LWJGL"); + } + + public void glUniform1f (int location, float x) { + GL20.glUniform1f(location, x); + } + + public void glUniform1fv (int location, int count, FloatBuffer v) { + GL20.glUniform1(location, v); + } + + public void glUniform1i (int location, int x) { + GL20.glUniform1i(location, x); + } + + public void glUniform1iv (int location, int count, IntBuffer v) { + GL20.glUniform1(location, v); + } + + public void glUniform2f (int location, float x, float y) { + GL20.glUniform2f(location, x, y); + } + + public void glUniform2fv (int location, int count, FloatBuffer v) { + GL20.glUniform2(location, v); + } + + public void glUniform2i (int location, int x, int y) { + GL20.glUniform2i(location, x, y); + } + + public void glUniform2iv (int location, int count, IntBuffer v) { + GL20.glUniform2(location, v); + } + + public void glUniform3f (int location, float x, float y, float z) { + GL20.glUniform3f(location, x, y, z); + } + + public void glUniform3fv (int location, int count, FloatBuffer v) { + GL20.glUniform3(location, v); + } + + public void glUniform3i (int location, int x, int y, int z) { + GL20.glUniform3i(location, x, y, z); + } + + public void glUniform3iv (int location, int count, IntBuffer v) { + GL20.glUniform3(location, v); + } + + public void glUniform4f (int location, float x, float y, float z, float w) { + GL20.glUniform4f(location, x, y, z, w); + } + + public void glUniform4fv (int location, int count, FloatBuffer v) { + GL20.glUniform4(location, v); + } + + public void glUniform4i (int location, int x, int y, int z, int w) { + GL20.glUniform4i(location, x, y, z, w); + } + + public void glUniform4iv (int location, int count, IntBuffer v) { + GL20.glUniform4(location, v); + } + + public void glUniformMatrix2fv (int location, int count, boolean transpose, FloatBuffer value) { + GL20.glUniformMatrix2(location, transpose, value); + } + + public void glUniformMatrix3fv (int location, int count, boolean transpose, FloatBuffer value) { + GL20.glUniformMatrix3(location, transpose, value); + } + + public void glUniformMatrix4fv (int location, int count, boolean transpose, FloatBuffer value) { + GL20.glUniformMatrix4(location, transpose, value); + } + + public void glUseProgram (int program) { + GL20.glUseProgram(program); + } + + public void glValidateProgram (int program) { + GL20.glValidateProgram(program); + } + + public void glVertexAttrib1f (int indx, float x) { + GL20.glVertexAttrib1f(indx, x); + } + + public void glVertexAttrib1fv (int indx, FloatBuffer values) { + GL20.glVertexAttrib1f(indx, values.get()); + } + + public void glVertexAttrib2f (int indx, float x, float y) { + GL20.glVertexAttrib2f(indx, x, y); + } + + public void glVertexAttrib2fv (int indx, FloatBuffer values) { + GL20.glVertexAttrib2f(indx, values.get(), values.get()); + } + + public void glVertexAttrib3f (int indx, float x, float y, float z) { + GL20.glVertexAttrib3f(indx, x, y, z); + } + + public void glVertexAttrib3fv (int indx, FloatBuffer values) { + GL20.glVertexAttrib3f(indx, values.get(), values.get(), values.get()); + } + + public void glVertexAttrib4f (int indx, float x, float y, float z, float w) { + GL20.glVertexAttrib4f(indx, x, y, z, w); + } + + public void glVertexAttrib4fv (int indx, FloatBuffer values) { + GL20.glVertexAttrib4f(indx, values.get(), values.get(), values.get(), values.get()); + } + + public void glVertexAttribPointer (int indx, int size, int type, boolean normalized, int stride, Buffer buffer) { + if (buffer instanceof ByteBuffer) { + if (type == GL_BYTE) + GL20.glVertexAttribPointer(indx, size, false, normalized, stride, (ByteBuffer)buffer); + else if (type == GL_UNSIGNED_BYTE) + GL20.glVertexAttribPointer(indx, size, true, normalized, stride, (ByteBuffer)buffer); + else if (type == GL_SHORT) + GL20.glVertexAttribPointer(indx, size, false, normalized, stride, ((ByteBuffer)buffer).asShortBuffer()); + else if (type == GL_UNSIGNED_SHORT) + GL20.glVertexAttribPointer(indx, size, true, normalized, stride, ((ByteBuffer)buffer).asShortBuffer()); + else if (type == GL_FLOAT) + GL20.glVertexAttribPointer(indx, size, normalized, stride, ((ByteBuffer)buffer).asFloatBuffer()); + else + throw new GdxRuntimeException( + "Can't use " + + buffer.getClass().getName() + + " with type " + + type + + " with this method. Use ByteBuffer and one of GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT or GL_FLOAT for type. Blame LWJGL"); + } else + throw new GdxRuntimeException("Can't use " + buffer.getClass().getName() + + " with this method. Use ByteBuffer instead. Blame LWJGL"); + } + + public void glViewport (int x, int y, int width, int height) { + GL11.glViewport(x, y, width, height); + } + + public void glDrawElements (int mode, int count, int type, int indices) { + GL11.glDrawElements(mode, count, type, indices); + } + + public void glVertexAttribPointer (int indx, int size, int type, boolean normalized, int stride, int ptr) { + GL20.glVertexAttribPointer(indx, size, type, normalized, stride, ptr); + } + +} diff --git a/vtm-gdx-desktop/src/org/oscim/gdx/Main.java b/vtm-gdx-desktop/src/org/oscim/gdx/Main.java new file mode 100644 index 00000000..568bf1be --- /dev/null +++ b/vtm-gdx-desktop/src/org/oscim/gdx/Main.java @@ -0,0 +1,29 @@ +package org.oscim.gdx; + +import org.oscim.awt.AwtGraphics; +import org.oscim.backend.CanvasAdapter; +import org.oscim.backend.GLAdapter; +import org.oscim.core.Tile; + +import com.badlogic.gdx.backends.lwjgl.LwjglApplication; +import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration; + +public class Main { + + public static void main(String[] args) { + LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration(); + cfg.title = "vtm-gdx"; + cfg.useGL20 = true; + cfg.width = 1280; + cfg.height = 800; + cfg.stencil= 8; + //cfg.samples = 4; + + // set our globals + CanvasAdapter.g = AwtGraphics.INSTANCE; + GLAdapter.INSTANCE = new GdxGLAdapter(); + Tile.SIZE = 256; + + new LwjglApplication(new GdxMap(), cfg); + } +} diff --git a/vtm-gdx-html/.classpath b/vtm-gdx-html/.classpath new file mode 100644 index 00000000..f8a695fa --- /dev/null +++ b/vtm-gdx-html/.classpath @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/vtm-gdx-html/.project b/vtm-gdx-html/.project new file mode 100644 index 00000000..56fa9c33 --- /dev/null +++ b/vtm-gdx-html/.project @@ -0,0 +1,28 @@ + + + vtm-gdx-html + + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.google.gdt.eclipse.core.webAppProjectValidator + + + + + com.google.gwt.eclipse.core.gwtProjectValidator + + + + + + org.eclipse.jdt.core.javanature + com.google.gwt.eclipse.core.gwtNature + + diff --git a/vtm-gdx-html/.settings/com.google.gdt.eclipse.core.prefs b/vtm-gdx-html/.settings/com.google.gdt.eclipse.core.prefs new file mode 100644 index 00000000..11a59ec3 --- /dev/null +++ b/vtm-gdx-html/.settings/com.google.gdt.eclipse.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +jarsExcludedFromWebInfLib= +warSrcDir=war +warSrcDirIsOutput=true diff --git a/vtm-gdx-html/.settings/com.google.gwt.eclipse.core.prefs b/vtm-gdx-html/.settings/com.google.gwt.eclipse.core.prefs new file mode 100644 index 00000000..150d1cb6 --- /dev/null +++ b/vtm-gdx-html/.settings/com.google.gwt.eclipse.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +entryPointModules= +filesCopiedToWebInfLib=gwt-servlet.jar +gwtCompileSettings=PGd3dC1jb21waWxlLXNldHRpbmdzPjxsb2ctbGV2ZWw+SU5GTzwvbG9nLWxldmVsPjxvdXRwdXQtc3R5bGU+UFJFVFRZPC9vdXRwdXQtc3R5bGU+PGV4dHJhLWFyZ3M+PCFbQ0RBVEFbXV0+PC9leHRyYS1hcmdzPjx2bS1hcmdzPjwhW0NEQVRBWy1YbXg1MTJtXV0+PC92bS1hcmdzPjxlbnRyeS1wb2ludC1tb2R1bGU+b3JnLm9zY2ltLmdkeC5Hd3REZWZpbml0aW9uPC9lbnRyeS1wb2ludC1tb2R1bGU+PC9nd3QtY29tcGlsZS1zZXR0aW5ncz4\= diff --git a/vtm-gdx-html/src/org/oscim/gdx/GwtDefinition.gwt.xml b/vtm-gdx-html/src/org/oscim/gdx/GwtDefinition.gwt.xml new file mode 100644 index 00000000..774e9625 --- /dev/null +++ b/vtm-gdx-html/src/org/oscim/gdx/GwtDefinition.gwt.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/vtm-gdx-html/src/org/oscim/gdx/client/GwtLauncher.java b/vtm-gdx-html/src/org/oscim/gdx/client/GwtLauncher.java new file mode 100644 index 00000000..49f1dcee --- /dev/null +++ b/vtm-gdx-html/src/org/oscim/gdx/client/GwtLauncher.java @@ -0,0 +1,19 @@ +package org.oscim.gdx.client; + +import org.oscim.gdx.GdxMap; +import com.badlogic.gdx.ApplicationListener; +import com.badlogic.gdx.backends.gwt.GwtApplication; +import com.badlogic.gdx.backends.gwt.GwtApplicationConfiguration; + +public class GwtLauncher extends GwtApplication { + @Override + public GwtApplicationConfiguration getConfig () { + GwtApplicationConfiguration cfg = new GwtApplicationConfiguration(480, 320); + return cfg; + } + + @Override + public ApplicationListener getApplicationListener () { + return new GdxMap(); + } +} \ No newline at end of file diff --git a/vtm-gdx-html/war/WEB-INF/web.xml b/vtm-gdx-html/war/WEB-INF/web.xml new file mode 100644 index 00000000..a0e8eb7f --- /dev/null +++ b/vtm-gdx-html/war/WEB-INF/web.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/vtm-gdx-html/war/index.html b/vtm-gdx-html/war/index.html new file mode 100644 index 00000000..8dab95a6 --- /dev/null +++ b/vtm-gdx-html/war/index.html @@ -0,0 +1,35 @@ + + + + vtm-gdx + + + + + +
+ + + + + diff --git a/vtm-gdx/.classpath b/vtm-gdx/.classpath new file mode 100644 index 00000000..2e40bfda --- /dev/null +++ b/vtm-gdx/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/vtm-gdx/.project b/vtm-gdx/.project new file mode 100644 index 00000000..0b1ec7e5 --- /dev/null +++ b/vtm-gdx/.project @@ -0,0 +1,17 @@ + + + vtm-gdx + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/vtm-gdx/.settings/com.google.gwt.eclipse.core.prefs b/vtm-gdx/.settings/com.google.gwt.eclipse.core.prefs new file mode 100644 index 00000000..82c36afe --- /dev/null +++ b/vtm-gdx/.settings/com.google.gwt.eclipse.core.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +filesCopiedToWebInfLib= diff --git a/vtm-gdx/.settings/org.eclipse.jdt.core.prefs b/vtm-gdx/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..54e493c0 --- /dev/null +++ b/vtm-gdx/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/vtm-gdx/src/GdxMap.gwt.xml b/vtm-gdx/src/GdxMap.gwt.xml new file mode 100644 index 00000000..8adef5ef --- /dev/null +++ b/vtm-gdx/src/GdxMap.gwt.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/vtm-gdx/src/org/oscim/gdx/GdxAssetAdapter.java b/vtm-gdx/src/org/oscim/gdx/GdxAssetAdapter.java new file mode 100644 index 00000000..aac705e2 --- /dev/null +++ b/vtm-gdx/src/org/oscim/gdx/GdxAssetAdapter.java @@ -0,0 +1,20 @@ +package org.oscim.gdx; + +import java.io.InputStream; + +import org.oscim.backend.AssetAdapter; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.files.FileHandle; + +public class GdxAssetAdapter extends AssetAdapter{ + + @Override + public InputStream openFileAsStream(String fileName) { + FileHandle file = Gdx.files.internal(fileName); + if (file == null) + throw new IllegalArgumentException("missing file " + fileName); + + return file.read(); + } +} diff --git a/vtm-gdx/src/org/oscim/gdx/GdxLog.java b/vtm-gdx/src/org/oscim/gdx/GdxLog.java new file mode 100644 index 00000000..3741d5d7 --- /dev/null +++ b/vtm-gdx/src/org/oscim/gdx/GdxLog.java @@ -0,0 +1,30 @@ +package org.oscim.gdx; + +import org.oscim.backend.Log.Logger; + +import com.badlogic.gdx.Gdx; + +public class GdxLog implements Logger { + + @Override + public void d(String tag, String msg) { + Gdx.app.log(tag, msg); + + } + + @Override + public void w(String tag, String msg) { + Gdx.app.log(tag, msg); + } + + @Override + public void e(String tag, String msg) { + Gdx.app.log(tag, msg); + } + + @Override + public void i(String tag, String msg) { + Gdx.app.log(tag, msg); + } + +} diff --git a/vtm-gdx/src/org/oscim/gdx/GdxMap.java b/vtm-gdx/src/org/oscim/gdx/GdxMap.java new file mode 100644 index 00000000..ffc358f0 --- /dev/null +++ b/vtm-gdx/src/org/oscim/gdx/GdxMap.java @@ -0,0 +1,380 @@ +package org.oscim.gdx; + +import org.oscim.backend.AssetAdapter; +import org.oscim.backend.GL20; +import org.oscim.backend.Log; +import org.oscim.backend.input.MotionEvent; +import org.oscim.layers.tile.vector.MapTileLayer; +import org.oscim.renderer.GLRenderer; +import org.oscim.renderer.GLState; +import org.oscim.theme.InternalRenderTheme; +import org.oscim.tilesource.TileSource; +import org.oscim.tilesource.oscimap4.OSciMap4TileSource; +import org.oscim.view.MapRenderCallback; +import org.oscim.view.MapView; +import org.oscim.view.MapViewPosition; + +import com.badlogic.gdx.ApplicationListener; +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Input.Buttons; +import com.badlogic.gdx.InputProcessor; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.g2d.BitmapFont; +import com.badlogic.gdx.scenes.scene2d.Stage; +import com.badlogic.gdx.scenes.scene2d.ui.Label; +import com.badlogic.gdx.utils.SharedLibraryLoader; + +public class GdxMap implements ApplicationListener, MapRenderCallback { + + private final MapView mMapView; + private final GLRenderer mMapRenderer; + + public GdxMap() { + new SharedLibraryLoader().load("vtm-jni"); + + AssetAdapter.g = new GdxAssetAdapter(); + + mMapView = new MapView(this); + mMapRenderer = new GLRenderer(mMapView); + } + + Stage ui; + Label fps; + BitmapFont font; + + @Override + public void create() { + + // Gdx.graphics.setContinuousRendering(false); + + if (Log.logger == null) + Log.logger = new GdxLog(); + + Gdx.app.log("gdx says", "Hi!"); + Log.d("vtm says", "Hi!"); + + int w = Gdx.graphics.getWidth(); + int h = Gdx.graphics.getHeight(); + mWidth = w; + mHeight = h; + + // TileSource tileSource = new OSciMap2TileSource(); + // tileSource.setOption("url", + // "http://city.informatik.uni-bremen.de/osci/map-live"); + TileSource tileSource = new OSciMap4TileSource(); + tileSource.setOption("url", "http://city.informatik.uni-bremen.de/osci/testing"); + + MapTileLayer l = mMapView.setBaseMap(tileSource); + l.setRenderTheme(InternalRenderTheme.DEFAULT); + + // mMapView.getLayerManager().add(new GenericOverlay(mMapView, new + // GridRenderLayer(mMapView))); + + mMapView.getMapViewPosition().setViewport(w, h); + + mMapRenderer.onSurfaceCreated(); + mMapRenderer.onSurfaceChanged(w, h); + + Gdx.input.setInputProcessor(new TouchHandler()); + + ui = new Stage(w, h, false); + + font = new BitmapFont(false); + + fps = new Label("fps: 0", new Label.LabelStyle(font, Color.WHITE)); + fps.setPosition(10, 30); + fps.setColor(0, 1, 0, 1); + ui.addActor(fps); + } + + @Override + public void dispose() { + + } + private int fpsCnt = 0; + @Override + public void render() { + // GLState.enableVertexArrays(-1, -1); + // GLState.blend(false); + // GLState.test(false, false); + + mMapRenderer.onDrawFrame(); + + // Gdx.gl20.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0); + + // Gdx.gl20.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0); + + int f = Gdx.graphics.getFramesPerSecond(); + if (f != fpsCnt){ + Log.d("fps", ">" +f); + fpsCnt = f; + } + // fps.setText("fps: " + Gdx.graphics.getFramesPerSecond()); + // ui.draw(); + } + + @Override + public void resize(int w, int h) { + mWidth = w; + mHeight = h; + + mMapView.getMapViewPosition().setViewport(w, h); + mMapRenderer.onSurfaceChanged(w, h); + } + + @Override + public void pause() { + } + + @Override + public void resume() { + } + + /** + * Update all Layers on Main thread. + * + * @param forceRedraw + * also render frame FIXME (does nothing atm) + */ + void redrawMapInternal(boolean forceRedraw) { + + if (forceRedraw && !mClearMap) + Gdx.graphics.requestRendering(); + + mMapView.updateLayers(); + + if (mClearMap) { + Gdx.graphics.requestRendering(); + mClearMap = false; + } + } + + private boolean mClearMap; + + public void clearMap() { + mClearMap = true; + } + + /* private */boolean mWaitRedraw; + private final Runnable mRedrawRequest = new Runnable() { + @Override + public void run() { + mWaitRedraw = false; + redrawMapInternal(false); + } + }; + + @Override + public void updateMap(boolean forceRender) { + if (!mWaitRedraw) { + mWaitRedraw = true; + Gdx.app.postRunnable(mRedrawRequest); + } + } + + @Override + public void renderMap() { + if (mClearMap) + updateMap(false); + else + Gdx.graphics.requestRendering(); + } + + int mHeight, mWidth; + + @Override + public int getWidth() { + return mWidth; + } + + @Override + public int getHeight() { + return mHeight; + } + + class GdxMotionEvent extends MotionEvent { + + @Override + public int getAction() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public float getX() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public float getY() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public float getX(int idx) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public float getY(int idx) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getPointerCount() { + // TODO Auto-generated method stub + return 0; + } + + } + + class TouchHandler implements InputProcessor { + + private MapViewPosition mMapPosition; + + public TouchHandler() { + mMapPosition = mMapView.getMapViewPosition(); + } + + private boolean mActiveScale; + private boolean mActiveTilt; + private boolean mActiveRotate; + + private int mPosX, mPosY; + + @Override + public boolean keyDown(int keycode) { + // switch (keycode) { + // + // case Input.Keys.UP: + // mMapPosition.moveMap(0, -50); + // mMapView.redrawMap(true); + // break; + // case Input.Keys.DOWN: + // mMapPosition.moveMap(0, 50); + // mMapView.redrawMap(true); + // break; + // case Input.Keys.LEFT: + // mMapPosition.moveMap(-50, 0); + // mMapView.redrawMap(true); + // break; + // case Input.Keys.RIGHT: + // mMapPosition.moveMap(50, 0); + // mMapView.redrawMap(true); + // break; + // + // case Input.Keys.R: + // mMapView.setRenderTheme(InternalRenderTheme.DEFAULT); + // mMapView.redrawMap(true); + // break; + // + // case Input.Keys.T: + // mMapView.setRenderTheme(InternalRenderTheme.TRONRENDER); + // mMapView.redrawMap(true); + // break; + // + // } + return true; + } + + @Override + public boolean keyUp(int keycode) { + return false; + } + + @Override + public boolean keyTyped(char character) { + return false; + } + + @Override + public boolean touchDown(int screenX, int screenY, int pointer, + int button) { + + if (button == Buttons.MIDDLE) { + mActiveScale = true; + // mActiveTilt = true; + mPosY = screenY; + } else if (button == Buttons.RIGHT) { + mActiveRotate = true; + mPosX = screenX; + mPosY = screenY; + } + return false; + } + + @Override + public boolean touchUp(int screenX, int screenY, int pointer, int button) { + // Log.d(TAG, "touch up " + pointer); + mActiveScale = false; + mActiveTilt = false; + mActiveRotate = false; + + return false; + } + + @Override + public boolean touchDragged(int screenX, int screenY, int pointer) { + boolean changed = false; + + if (mActiveScale) { + // changed = mMapPosition.tilt((screenY - mStartY) / 5f); + changed = mMapPosition.scaleMap(1 - (screenY - mPosY) / 100f, + 0, 0); + mPosY = screenY; + return true; + } + + if (mActiveRotate) { + mMapPosition.rotateMap((screenX - mPosX) / 500f, 0, 0); + mPosX = screenX; + mMapPosition.tiltMap((screenY - mPosY) / 10f); + mPosY = screenY; + changed = true; + } + + if (!(mActiveRotate || mActiveTilt)) { + int dx = screenX - mPosX; + int dy = screenY - mPosY; + if (Math.abs(dx) > 0 || Math.abs(dy) > 0) { + mMapPosition.moveMap(dx, dy); + mPosX = screenX; + mPosY = screenY; + changed = true; + } + } + + if (changed) + updateMap(false); + + return true; + } + + @Override + public boolean mouseMoved(int screenX, int screenY) { + mPosX = screenX; + mPosY = screenY; + return false; + } + + @Override + public boolean scrolled(int amount) { + float fx = mPosX - mMapView.getWidth() / 2; + float fy = mPosY - mMapView.getHeight() / 2; + + if (amount > 0) + mMapPosition.scaleMap(0.9f, fx, fy); + else + mMapPosition.scaleMap(1.1f, fx, fy); + + updateMap(false); + + return true; + } + + } +} diff --git a/vtm/.classpath b/vtm/.classpath new file mode 100644 index 00000000..a07fe32a --- /dev/null +++ b/vtm/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/vtm/.cproject b/vtm/.cproject new file mode 100644 index 00000000..0d0f1cf4 --- /dev/null +++ b/vtm/.cproject @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vtm/.project b/vtm/.project new file mode 100644 index 00000000..99a31c7b --- /dev/null +++ b/vtm/.project @@ -0,0 +1,36 @@ + + + vtm + + + + + + org.eclipse.ui.externaltools.ExternalToolBuilder + full,incremental, + + + LaunchConfigHandle + <project>/.externalToolBuilders/org.eclipse.cdt.managedbuilder.core.genmakebuilder (1).launch + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/vtm/.settings/org.eclipse.cdt.codan.core.prefs b/vtm/.settings/org.eclipse.cdt.codan.core.prefs new file mode 100644 index 00000000..9c7ec59a --- /dev/null +++ b/vtm/.settings/org.eclipse.cdt.codan.core.prefs @@ -0,0 +1,66 @@ +eclipse.preferences.version=1 +org.eclipse.cdt.codan.checkers.errnoreturn=Warning +org.eclipse.cdt.codan.checkers.errnoreturn.params={implicit\=>false} +org.eclipse.cdt.codan.checkers.errreturnvalue=Error +org.eclipse.cdt.codan.checkers.errreturnvalue.params={} +org.eclipse.cdt.codan.checkers.noreturn=Error +org.eclipse.cdt.codan.checkers.noreturn.params={implicit\=>false} +org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation=-Error +org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem=-Error +org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem=Warning +org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem.params={} +org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem=Error +org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem.params={} +org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem=Warning +org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem.params={no_break_comment\=>"no break",last_case_param\=>true,empty_case_param\=>false} +org.eclipse.cdt.codan.internal.checkers.CatchByReference=Warning +org.eclipse.cdt.codan.internal.checkers.CatchByReference.params={unknown\=>false,exceptions\=>()} +org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem=-Error +org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem=-Error +org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem=-Error +org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.InvalidArguments=-Error +org.eclipse.cdt.codan.internal.checkers.InvalidArguments.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem=-Error +org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem=-Error +org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem=-Error +org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem=-Error +org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker=-Info +org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker.params={pattern\=>"^[a-z]",macro\=>true,exceptions\=>()} +org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=Warning +org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem.params={} +org.eclipse.cdt.codan.internal.checkers.OverloadProblem=-Error +org.eclipse.cdt.codan.internal.checkers.OverloadProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem=-Error +org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem=-Error +org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem=-Warning +org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem.params={} +org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem=-Warning +org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem.params={} +org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem=Warning +org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem.params={macro\=>true,exceptions\=>()} +org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem=Warning +org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem.params={paramNot\=>false} +org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem=Warning +org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem.params={else\=>false,afterelse\=>false} +org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem=-Error +org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem=Warning +org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem.params={macro\=>true} +org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem=Warning +org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem.params={macro\=>true} +org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem=Warning +org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem.params={macro\=>true,exceptions\=>("@(\#)","$Id")} +org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem=-Error +org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>false,RUN_ON_INC_BUILD\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +useParentScope=false diff --git a/src/org/oscim/theme/styles/carto.xml b/vtm/assets/styles/carto.xml similarity index 100% rename from src/org/oscim/theme/styles/carto.xml rename to vtm/assets/styles/carto.xml diff --git a/src/org/oscim/theme/styles/default.xml b/vtm/assets/styles/default.xml similarity index 100% rename from src/org/oscim/theme/styles/default.xml rename to vtm/assets/styles/default.xml diff --git a/src/org/oscim/theme/styles/maki.png b/vtm/assets/styles/maki.png similarity index 100% rename from src/org/oscim/theme/styles/maki.png rename to vtm/assets/styles/maki.png diff --git a/src/org/oscim/theme/styles/osmarender.xml b/vtm/assets/styles/osmarender.xml similarity index 100% rename from src/org/oscim/theme/styles/osmarender.xml rename to vtm/assets/styles/osmarender.xml diff --git a/src/org/oscim/theme/styles/symbols/airport.png b/vtm/assets/styles/symbols/airport.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/airport.png rename to vtm/assets/styles/symbols/airport.png diff --git a/src/org/oscim/theme/styles/symbols/alpine_hut.png b/vtm/assets/styles/symbols/alpine_hut.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/alpine_hut.png rename to vtm/assets/styles/symbols/alpine_hut.png diff --git a/src/org/oscim/theme/styles/symbols/atm.png b/vtm/assets/styles/symbols/atm.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/atm.png rename to vtm/assets/styles/symbols/atm.png diff --git a/src/org/oscim/theme/styles/symbols/bakery.png b/vtm/assets/styles/symbols/bakery.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/bakery.png rename to vtm/assets/styles/symbols/bakery.png diff --git a/src/org/oscim/theme/styles/symbols/bank.png b/vtm/assets/styles/symbols/bank.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/bank.png rename to vtm/assets/styles/symbols/bank.png diff --git a/src/org/oscim/theme/styles/symbols/bench.png b/vtm/assets/styles/symbols/bench.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/bench.png rename to vtm/assets/styles/symbols/bench.png diff --git a/src/org/oscim/theme/styles/symbols/bicycle_rental.png b/vtm/assets/styles/symbols/bicycle_rental.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/bicycle_rental.png rename to vtm/assets/styles/symbols/bicycle_rental.png diff --git a/src/org/oscim/theme/styles/symbols/bus.png b/vtm/assets/styles/symbols/bus.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/bus.png rename to vtm/assets/styles/symbols/bus.png diff --git a/src/org/oscim/theme/styles/symbols/bus_sta.png b/vtm/assets/styles/symbols/bus_sta.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/bus_sta.png rename to vtm/assets/styles/symbols/bus_sta.png diff --git a/src/org/oscim/theme/styles/symbols/cable_car.png b/vtm/assets/styles/symbols/cable_car.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/cable_car.png rename to vtm/assets/styles/symbols/cable_car.png diff --git a/src/org/oscim/theme/styles/symbols/cafe.png b/vtm/assets/styles/symbols/cafe.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/cafe.png rename to vtm/assets/styles/symbols/cafe.png diff --git a/src/org/oscim/theme/styles/symbols/campSite.png b/vtm/assets/styles/symbols/campSite.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/campSite.png rename to vtm/assets/styles/symbols/campSite.png diff --git a/src/org/oscim/theme/styles/symbols/cave_entrance.png b/vtm/assets/styles/symbols/cave_entrance.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/cave_entrance.png rename to vtm/assets/styles/symbols/cave_entrance.png diff --git a/src/org/oscim/theme/styles/symbols/chair_lift_2.png b/vtm/assets/styles/symbols/chair_lift_2.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/chair_lift_2.png rename to vtm/assets/styles/symbols/chair_lift_2.png diff --git a/src/org/oscim/theme/styles/symbols/church.png b/vtm/assets/styles/symbols/church.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/church.png rename to vtm/assets/styles/symbols/church.png diff --git a/src/org/oscim/theme/styles/symbols/cinema.png b/vtm/assets/styles/symbols/cinema.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/cinema.png rename to vtm/assets/styles/symbols/cinema.png diff --git a/src/org/oscim/theme/styles/symbols/drinking_water.png b/vtm/assets/styles/symbols/drinking_water.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/drinking_water.png rename to vtm/assets/styles/symbols/drinking_water.png diff --git a/src/org/oscim/theme/styles/symbols/fastfood.png b/vtm/assets/styles/symbols/fastfood.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/fastfood.png rename to vtm/assets/styles/symbols/fastfood.png diff --git a/src/org/oscim/theme/styles/symbols/firebrigade.png b/vtm/assets/styles/symbols/firebrigade.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/firebrigade.png rename to vtm/assets/styles/symbols/firebrigade.png diff --git a/src/org/oscim/theme/styles/symbols/florist.png b/vtm/assets/styles/symbols/florist.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/florist.png rename to vtm/assets/styles/symbols/florist.png diff --git a/src/org/oscim/theme/styles/symbols/fountain.png b/vtm/assets/styles/symbols/fountain.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/fountain.png rename to vtm/assets/styles/symbols/fountain.png diff --git a/src/org/oscim/theme/styles/symbols/gondola.png b/vtm/assets/styles/symbols/gondola.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/gondola.png rename to vtm/assets/styles/symbols/gondola.png diff --git a/src/org/oscim/theme/styles/symbols/helipad.png b/vtm/assets/styles/symbols/helipad.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/helipad.png rename to vtm/assets/styles/symbols/helipad.png diff --git a/src/org/oscim/theme/styles/symbols/hospital.png b/vtm/assets/styles/symbols/hospital.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/hospital.png rename to vtm/assets/styles/symbols/hospital.png diff --git a/src/org/oscim/theme/styles/symbols/hostel.png b/vtm/assets/styles/symbols/hostel.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/hostel.png rename to vtm/assets/styles/symbols/hostel.png diff --git a/src/org/oscim/theme/styles/symbols/hotel.png b/vtm/assets/styles/symbols/hotel.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/hotel.png rename to vtm/assets/styles/symbols/hotel.png diff --git a/src/org/oscim/theme/styles/symbols/information.png b/vtm/assets/styles/symbols/information.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/information.png rename to vtm/assets/styles/symbols/information.png diff --git a/src/org/oscim/theme/styles/symbols/kindergarten.png b/vtm/assets/styles/symbols/kindergarten.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/kindergarten.png rename to vtm/assets/styles/symbols/kindergarten.png diff --git a/src/org/oscim/theme/styles/symbols/library.png b/vtm/assets/styles/symbols/library.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/library.png rename to vtm/assets/styles/symbols/library.png diff --git a/src/org/oscim/theme/styles/symbols/mosque.png b/vtm/assets/styles/symbols/mosque.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/mosque.png rename to vtm/assets/styles/symbols/mosque.png diff --git a/src/org/oscim/theme/styles/symbols/oneway.png b/vtm/assets/styles/symbols/oneway.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/oneway.png rename to vtm/assets/styles/symbols/oneway.png diff --git a/src/org/oscim/theme/styles/symbols/parking.png b/vtm/assets/styles/symbols/parking.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/parking.png rename to vtm/assets/styles/symbols/parking.png diff --git a/src/org/oscim/theme/styles/symbols/peak.png b/vtm/assets/styles/symbols/peak.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/peak.png rename to vtm/assets/styles/symbols/peak.png diff --git a/src/org/oscim/theme/styles/symbols/petrolStation.png b/vtm/assets/styles/symbols/petrolStation.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/petrolStation.png rename to vtm/assets/styles/symbols/petrolStation.png diff --git a/src/org/oscim/theme/styles/symbols/pharmacy.png b/vtm/assets/styles/symbols/pharmacy.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/pharmacy.png rename to vtm/assets/styles/symbols/pharmacy.png diff --git a/src/org/oscim/theme/styles/symbols/playground.png b/vtm/assets/styles/symbols/playground.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/playground.png rename to vtm/assets/styles/symbols/playground.png diff --git a/src/org/oscim/theme/styles/symbols/postbox.png b/vtm/assets/styles/symbols/postbox.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/postbox.png rename to vtm/assets/styles/symbols/postbox.png diff --git a/src/org/oscim/theme/styles/symbols/postoffice.png b/vtm/assets/styles/symbols/postoffice.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/postoffice.png rename to vtm/assets/styles/symbols/postoffice.png diff --git a/src/org/oscim/theme/styles/symbols/pub.png b/vtm/assets/styles/symbols/pub.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/pub.png rename to vtm/assets/styles/symbols/pub.png diff --git a/src/org/oscim/theme/styles/symbols/railway-crossing-small.png b/vtm/assets/styles/symbols/railway-crossing-small.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/railway-crossing-small.png rename to vtm/assets/styles/symbols/railway-crossing-small.png diff --git a/src/org/oscim/theme/styles/symbols/railway-crossing.png b/vtm/assets/styles/symbols/railway-crossing.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/railway-crossing.png rename to vtm/assets/styles/symbols/railway-crossing.png diff --git a/src/org/oscim/theme/styles/symbols/recycling.png b/vtm/assets/styles/symbols/recycling.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/recycling.png rename to vtm/assets/styles/symbols/recycling.png diff --git a/src/org/oscim/theme/styles/symbols/restaurant.png b/vtm/assets/styles/symbols/restaurant.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/restaurant.png rename to vtm/assets/styles/symbols/restaurant.png diff --git a/src/org/oscim/theme/styles/symbols/school.png b/vtm/assets/styles/symbols/school.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/school.png rename to vtm/assets/styles/symbols/school.png diff --git a/src/org/oscim/theme/styles/symbols/shelter.png b/vtm/assets/styles/symbols/shelter.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/shelter.png rename to vtm/assets/styles/symbols/shelter.png diff --git a/src/org/oscim/theme/styles/symbols/soccer-borderless.png b/vtm/assets/styles/symbols/soccer-borderless.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/soccer-borderless.png rename to vtm/assets/styles/symbols/soccer-borderless.png diff --git a/src/org/oscim/theme/styles/symbols/supermarket.png b/vtm/assets/styles/symbols/supermarket.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/supermarket.png rename to vtm/assets/styles/symbols/supermarket.png diff --git a/src/org/oscim/theme/styles/symbols/synagogue.png b/vtm/assets/styles/symbols/synagogue.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/synagogue.png rename to vtm/assets/styles/symbols/synagogue.png diff --git a/src/org/oscim/theme/styles/symbols/telephone.png b/vtm/assets/styles/symbols/telephone.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/telephone.png rename to vtm/assets/styles/symbols/telephone.png diff --git a/src/org/oscim/theme/styles/symbols/tennis.png b/vtm/assets/styles/symbols/tennis.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/tennis.png rename to vtm/assets/styles/symbols/tennis.png diff --git a/src/org/oscim/theme/styles/symbols/theatre.png b/vtm/assets/styles/symbols/theatre.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/theatre.png rename to vtm/assets/styles/symbols/theatre.png diff --git a/src/org/oscim/theme/styles/symbols/toilets.png b/vtm/assets/styles/symbols/toilets.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/toilets.png rename to vtm/assets/styles/symbols/toilets.png diff --git a/src/org/oscim/theme/styles/symbols/traffic_signal.png b/vtm/assets/styles/symbols/traffic_signal.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/traffic_signal.png rename to vtm/assets/styles/symbols/traffic_signal.png diff --git a/src/org/oscim/theme/styles/symbols/tree.png b/vtm/assets/styles/symbols/tree.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/tree.png rename to vtm/assets/styles/symbols/tree.png diff --git a/src/org/oscim/theme/styles/symbols/university.png b/vtm/assets/styles/symbols/university.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/university.png rename to vtm/assets/styles/symbols/university.png diff --git a/src/org/oscim/theme/styles/symbols/viewpoint.png b/vtm/assets/styles/symbols/viewpoint.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/viewpoint.png rename to vtm/assets/styles/symbols/viewpoint.png diff --git a/src/org/oscim/theme/styles/symbols/vulcan.png b/vtm/assets/styles/symbols/vulcan.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/vulcan.png rename to vtm/assets/styles/symbols/vulcan.png diff --git a/src/org/oscim/theme/styles/symbols/windmill.png b/vtm/assets/styles/symbols/windmill.png similarity index 100% rename from src/org/oscim/theme/styles/symbols/windmill.png rename to vtm/assets/styles/symbols/windmill.png diff --git a/src/org/oscim/theme/styles/tronrender.xml b/vtm/assets/styles/tronrender.xml similarity index 100% rename from src/org/oscim/theme/styles/tronrender.xml rename to vtm/assets/styles/tronrender.xml diff --git a/vtm/jni/Android.mk b/vtm/jni/Android.mk new file mode 100644 index 00000000..3be634f1 --- /dev/null +++ b/vtm/jni/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := vtm-jni +LOCAL_C_INCLUDES := . + +LOCAL_CFLAGS := $(LOCAL_C_INCLUDES:%=-I%) -O2 -Wall -D__ANDROID__ -Wall -std=c99 -O2 -DTRILIBRARY -DREDUCED -DCDT_ONLY -DNO_TIMER +LOCAL_CPPFLAGS := $(LOCAL_C_INCLUDES:%=-I%) -O2 -Wall -D__ANDROID__ -Wall -std=c99 -O2 -DTRILIBRARY -DREDUCED -DCDT_ONLY -DNO_TIMER +LOCAL_LDLIBS := -lm -llog +LOCAL_ARM_MODE := arm + +LOCAL_SRC_FILES := gl/utils.c\ + triangle/triangle_dbg.c\ + triangle/TriangleJni.c\ + triangle/triangle.c + +include $(BUILD_SHARED_LIBRARY) diff --git a/vtm/jni/Application.mk b/vtm/jni/Application.mk new file mode 100644 index 00000000..d0a1e397 --- /dev/null +++ b/vtm/jni/Application.mk @@ -0,0 +1 @@ +APP_ABI := armeabi armeabi-v7a \ No newline at end of file diff --git a/vtm/jni/build-android32.xml b/vtm/jni/build-android32.xml new file mode 100644 index 00000000..4487f924 --- /dev/null +++ b/vtm/jni/build-android32.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + ndk_home: ${env.NDK_HOME} + + + + + + + + + + + + + + + + diff --git a/vtm/jni/build-linux64.xml b/vtm/jni/build-linux64.xml new file mode 100644 index 00000000..f04435e0 --- /dev/null +++ b/vtm/jni/build-linux64.xml @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vtm/jni/build.xml b/vtm/jni/build.xml new file mode 100644 index 00000000..a4fe3ec1 --- /dev/null +++ b/vtm/jni/build.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vtm/jni/builder/org/oscim/jni/JniBuilder.java b/vtm/jni/builder/org/oscim/jni/JniBuilder.java new file mode 100644 index 00000000..db6da38f --- /dev/null +++ b/vtm/jni/builder/org/oscim/jni/JniBuilder.java @@ -0,0 +1,80 @@ +package org.oscim.jni; + + +import com.badlogic.gdx.jnigen.AntScriptGenerator; +import com.badlogic.gdx.jnigen.BuildConfig; +import com.badlogic.gdx.jnigen.BuildTarget; +import com.badlogic.gdx.jnigen.BuildTarget.TargetOs; + +public class JniBuilder { + public static void main(String[] args) { + String[] headers = { "." }; + String[] sources = { "gl/utils.c", "triangle/TriangleJni.c", "triangle/triangle.c", "triangle/triangle_dbg.c" }; + String cflags = " -Wall -std=c99 -O2 -DTRILIBRARY -DREDUCED -DCDT_ONLY -DNO_TIMER"; // -Werror"; + + BuildTarget win32home = BuildTarget.newDefaultTarget(TargetOs.Windows, + false); + win32home.compilerPrefix = ""; + win32home.buildFileName = "build-windows32home.xml"; + win32home.excludeFromMasterBuildFile = true; + win32home.headerDirs = headers; + win32home.cIncludes = sources; + win32home.cFlags += cflags; + win32home.cppFlags += cflags; + + BuildTarget win32 = BuildTarget.newDefaultTarget(TargetOs.Windows, + false); + win32.headerDirs = headers; + win32.cIncludes = sources; + win32.cFlags += cflags; + win32.cppFlags += cflags; + + BuildTarget win64 = BuildTarget + .newDefaultTarget(TargetOs.Windows, true); + win64.headerDirs = headers; + win64.cIncludes = sources; + win64.cFlags += cflags; + win64.cppFlags += cflags; + + BuildTarget lin32 = BuildTarget.newDefaultTarget(TargetOs.Linux, false); + lin32.headerDirs = headers; + lin32.cIncludes = sources; + lin32.cFlags += cflags; + lin32.cppFlags += cflags; + + BuildTarget lin64 = BuildTarget.newDefaultTarget(TargetOs.Linux, true); + lin64.headerDirs = headers; + lin64.cIncludes = sources; + lin64.cFlags += cflags; + lin64.cppFlags += cflags; + + // BuildTarget mac = BuildTarget.newDefaultTarget(TargetOs.MacOsX, + // false); + // mac.headerDirs = headers; + // mac.cIncludes = sources; + // mac.cFlags += cflags; + // mac.cppFlags += cflags; + // mac.linkerFlags += " -framework CoreServices -framework Carbon"; + + BuildTarget android = BuildTarget.newDefaultTarget(TargetOs.Android, + false); + android.headerDirs = headers; + android.cIncludes = sources; + android.cFlags += cflags; + android.cppFlags += cflags; + android.linkerFlags += " -llog"; + // BuildTarget ios = BuildTarget.newDefaultTarget(TargetOs.IOS, false); + // ios.headerDirs = headers; + // ios.cIncludes = sources; + // ios.cFlags += cflags; + // ios.cppFlags += cflags; + + //new NativeCodeGenerator().generate(); + new AntScriptGenerator().generate(new BuildConfig("vtm-jni"), + //win32home, win32, win64, lin32, + lin64, android); +// BuildExecutor.executeAnt("jni/build-windows32home.xml", "-v clean"); +// BuildExecutor.executeAnt("jni/build-windows32home.xml", "-v"); +// BuildExecutor.executeAnt("jni/build.xml", "pack-natives -v"); + } +} diff --git a/vtm/jni/gl/utils.c b/vtm/jni/gl/utils.c new file mode 100644 index 00000000..5ae82195 --- /dev/null +++ b/vtm/jni/gl/utils.c @@ -0,0 +1,487 @@ +#include + +#include +#include +#include +#include +#include +#include + +//#ifndef uintptr_t +//typedef unsigned long uintptr_t; +//#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#if 0 +#ifdef __ANDROID__ +#include +#include +#include + +#define JNI(X) JNIEXPORT Java_org_oscim_utils_GlUtils_##X + + +#define COLOR_R(C) (((C >> 16) & 0xff) / 255.0f) +#define COLOR_G(C) (((C >> 8) & 0xff) / 255.0f) +#define COLOR_B(C) (((C >> 0) & 0xff) / 255.0f) +#define COLOR_A(C) (((C >> 24) & 0xff) / 255.0f) + +void JNI(setColor)(JNIEnv *env, jclass* clazz, jint location, jint c, jfloat alpha) +{ + + if (alpha >= 1) + alpha = COLOR_A(c); + else if (alpha < 0) + alpha = 0; + else + alpha *= COLOR_A(c); + + if (alpha == 1) + { + glUniform4f((GLint) location, + (GLfloat) COLOR_R(c), + (GLfloat) COLOR_G(c), + (GLfloat) COLOR_B(c), + (GLfloat) alpha); + } + else + { + glUniform4f((GLint) location, + (GLfloat) (COLOR_R(c) * alpha), + (GLfloat) (COLOR_G(c) * alpha), + (GLfloat) (COLOR_B(c) * alpha), + (GLfloat) alpha); + } +} + +void JNI(setColorBlend)(JNIEnv *env, jclass* clazz, jint location, jint c1, jint c2, jfloat mix) +{ + float a1 = COLOR_A(c1) * (1 - mix); + float a2 = COLOR_A(c2) * mix; + + glUniform4f((GLint) location, + (GLfloat) (COLOR_R(c1) * a1 + COLOR_R(c2) * a2), + (GLfloat) (COLOR_G(c1) * a1 + COLOR_G(c2) * a2), + (GLfloat) (COLOR_B(c1) * a1 + COLOR_B(c2) * a2), + (GLfloat) (a1 + a2)); +} +#endif // __ANDROID__ +#endif // 0 + +#undef JNI +#define JNI(X) JNIEXPORT Java_org_oscim_utils_Matrix4_##X + +#define CAST(x) (float *)(uintptr_t) x +#define MAT_SIZE 16 * sizeof(float) + +static const float identity[] = + { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; + +static inline void +multiplyMM(float* r, const float* lhs, const float* rhs); + +static inline void +setRotateM(float* rm, int rmOffset, float a, float x, float y, float z); + +static inline void +transposeM(float* mTrans, int mTransOffset, float* m, int mOffset); + +static inline void +matrix4_proj(float* mat, float* vec); + +jlong JNI(alloc)(JNIEnv *env, jclass* clazz) +{ + return (long) calloc(16, sizeof(float)); +} + +jobject JNI(getBuffer)(JNIEnv *env, jclass* clazz,jlong ptr){ + return (*env)->NewDirectByteBuffer(env,(char*)(uintptr_t)ptr, 16*sizeof(float)); +} + +void JNI(delete)(JNIEnv* env, jclass* clazz, jlong ptr) +{ + free(CAST(ptr)); +} +#if 0 +void JNI(setAsUniform)(JNIEnv* env, jclass* clazz, jlong ptr, jint location) +{ + float* m = CAST(ptr); + + glUniformMatrix4fv((GLint) location, (GLsizei) 1, (GLboolean) 0, (GLfloat *) m); +} +#endif + +void JNI(setValueAt)(JNIEnv* env, jclass* clazz, jlong ptr, jint pos, jfloat value) +{ + float* m = CAST(ptr); + if (pos > -1 && pos < 16) + m[pos] = value; +} + +void JNI(identity)(JNIEnv* env, jclass* clazz, jlong ptr) +{ + float* m = CAST(ptr); + memcpy(m, identity, MAT_SIZE); +} + +void JNI(setScale)(JNIEnv* env, jclass* clazz, jlong ptr, jfloat sx, jfloat sy, jfloat sz) +{ + float* m = CAST(ptr); + memcpy(m, identity, MAT_SIZE); + m[0] = sx; + m[5] = sy; + m[10] = sz; +} + +void JNI(setTranslation)(JNIEnv* env, jclass* clazz, jlong ptr, jfloat x, jfloat y, jfloat z) +{ + float* m = CAST(ptr); + memcpy(m, identity, MAT_SIZE); + m[12] = x; + m[13] = y; + m[14] = z; +} + +void JNI(setRotation)(JNIEnv* env, jclass* clazz, jlong ptr, jfloat a, jfloat x, jfloat y, jfloat z) +{ + float* m = CAST(ptr); + setRotateM(m, 0, a, x, y, z); +} + +void JNI(setTransScale)(JNIEnv* env, jclass* clazz, jlong ptr, jfloat tx, jfloat ty, jfloat scale) +{ + float* m = CAST(ptr); + memcpy(m, identity, MAT_SIZE); + m[0] = scale; + m[5] = scale; + m[12] = tx; + m[13] = ty; +} + +// set matrix from float array +void JNI(set)(JNIEnv* env, jclass* clazz, jlong ptr, jfloatArray obj_mat) +{ + float* m = CAST(ptr); + float* mat = (float*) (*env)->GetPrimitiveArrayCritical(env, obj_mat, 0); + + memcpy(m, mat, MAT_SIZE); + + (*env)->ReleasePrimitiveArrayCritical(env, obj_mat, mat, JNI_ABORT); +} + +// get float array from matrix +void JNI(get)(JNIEnv* env, jclass* clazz, jlong ptr, jfloatArray obj_mat) +{ + float* m = CAST(ptr); + float* mat = (float*) (*env)->GetPrimitiveArrayCritical(env, obj_mat, 0); + + memcpy(mat, m, MAT_SIZE); + + (*env)->ReleasePrimitiveArrayCritical(env, obj_mat, mat, 0); +} + +void JNI(mul)(JNIEnv* env, jclass* clazz, jlong ptr_a, jlong ptr_b) +{ + float* mata = CAST(ptr_a); + float* matb = CAST(ptr_b); + + multiplyMM(mata, mata, matb); +} + +void JNI(copy)(JNIEnv* env, jclass* clazz, jlong ptr_dst, jlong ptr_src) +{ + float* dst = CAST(ptr_dst); + float* src = CAST(ptr_src); + + memcpy(dst, src, MAT_SIZE); +} + +void JNI(smul)(JNIEnv* env, jclass* clazz, jlong ptr_r, jlong ptr_a, jlong ptr_b) +{ + float* matr = CAST(ptr_r); + float* mata = CAST(ptr_a); + float* matb = CAST(ptr_b); + + multiplyMM(matr, mata, matb); +} + +void JNI(smulrhs)(JNIEnv* env, jclass* clazz, jlong ptr_r, jlong ptr_rhs) +{ + float* matr = CAST(ptr_r); + float* mata = alloca(16 * sizeof(float)); + float* matb = CAST(ptr_rhs); + + memcpy(mata, matr, 16 * sizeof(float)); + + multiplyMM(matr, mata, matb); +} + +void JNI(smullhs)(JNIEnv* env, jclass* clazz, jlong ptr_r, jlong ptr_lhs) +{ + float* matr = CAST(ptr_r); + float* mata = CAST(ptr_lhs); + float* matb = alloca(16 * sizeof(float)); + + memcpy(matb, matr, 16 * sizeof(float)); + + multiplyMM(matr, mata, matb); +} + + +void JNI(strans)(JNIEnv* env, jclass* clazz, jlong ptr_r, jlong ptr_a) +{ + float* matr = CAST(ptr_r); + float* mata = CAST(ptr_a); + + transposeM(matr, 0, mata, 0); +} + +void JNI(prj)(JNIEnv* env, jclass* clazz, jlong ptr, jfloatArray obj_vec) +{ + float* m = CAST(ptr); + float* vec = (float*) (*env)->GetPrimitiveArrayCritical(env, obj_vec, 0); + + matrix4_proj(m, vec); + + (*env)->ReleasePrimitiveArrayCritical(env, obj_vec, vec, 0); +} + +static float someRandomEpsilon = 1.0f / (1 << 11); + +void JNI(addDepthOffset)(JNIEnv* env, jclass* clazz, jlong ptr, jint delta) +{ + float* m = CAST(ptr); + + // from http://www.mathfor3dgameprogramming.com/code/Listing9.1.cpp + // float n = MapViewPosition.VIEW_NEAR; + // float f = MapViewPosition.VIEW_FAR; + // float pz = 1; + // float epsilon = -2.0f * f * n * delta / ((f + n) * pz * (pz + delta)); + + m[10] *= 1.0f + someRandomEpsilon * delta; +} + +/* + * Copyright 2007, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// from android/platform_frameworks_base/blob/master/core/jni/android/opengl/util.cpp +#define I(_i, _j) ((_j)+ 4*(_i)) + +static inline void +multiplyMM(float* r, const float* lhs, const float* rhs) +{ + for (int i = 0; i < 4; i++) + { + register const float rhs_i0 = rhs[I(i,0)]; + register float ri0 = lhs[I(0,0)] * rhs_i0; + register float ri1 = lhs[I(0,1)] * rhs_i0; + register float ri2 = lhs[I(0,2)] * rhs_i0; + register float ri3 = lhs[I(0,3)] * rhs_i0; + for (int j = 1; j < 4; j++) + { + register const float rhs_ij = rhs[I(i,j)]; + ri0 += lhs[I(j,0)] * rhs_ij; + ri1 += lhs[I(j,1)] * rhs_ij; + ri2 += lhs[I(j,2)] * rhs_ij; + ri3 += lhs[I(j,3)] * rhs_ij; + } + r[I(i,0)] = ri0; + r[I(i,1)] = ri1; + r[I(i,2)] = ri2; + r[I(i,3)] = ri3; + } +} + +//static inline +//void +//mx4transform(float x, float y, float z, float w, const float* pM, float* pDest) +//{ +// pDest[0] = pM[0 + 4 * 0] * x + pM[0 + 4 * 1] * y + pM[0 + 4 * 2] * z + pM[0 + 4 * 3] * w; +// pDest[1] = pM[1 + 4 * 0] * x + pM[1 + 4 * 1] * y + pM[1 + 4 * 2] * z + pM[1 + 4 * 3] * w; +// pDest[2] = pM[2 + 4 * 0] * x + pM[2 + 4 * 1] * y + pM[2 + 4 * 2] * z + pM[2 + 4 * 3] * w; +// +// pDest[3] = pM[3 + 4 * 0] * x + pM[3 + 4 * 1] * y + pM[3 + 4 * 2] * z + pM[3 + 4 * 3] * w; +//} + +/** + * Computes the length of a vector + * + * @param x x coordinate of a vector + * @param y y coordinate of a vector + * @param z z coordinate of a vector + * @return the length of a vector + */ +static inline float +length(float x, float y, float z) +{ + return (float) sqrt(x * x + y * y + z * z); +} +/** + * Rotates matrix m by angle a (in degrees) around the axis (x, y, z) + * @param rm returns the result + * @param rmOffset index into rm where the result matrix starts + * @param a angle to rotate in degrees + * @param x scale factor x + * @param y scale factor y + * @param z scale factor z + */ + +static inline void +setRotateM(float* rm, int rmOffset, float a, float x, float y, float z) +{ + rm[rmOffset + 3] = 0; + rm[rmOffset + 7] = 0; + rm[rmOffset + 11] = 0; + rm[rmOffset + 12] = 0; + rm[rmOffset + 13] = 0; + rm[rmOffset + 14] = 0; + rm[rmOffset + 15] = 1; + a *= (float) (M_PI / 180.0f); + float s = (float) sin(a); + float c = (float) cos(a); + if (1.0f == x && 0.0f == y && 0.0f == z) + { + rm[rmOffset + 5] = c; + rm[rmOffset + 10] = c; + rm[rmOffset + 6] = s; + rm[rmOffset + 9] = -s; + rm[rmOffset + 1] = 0; + rm[rmOffset + 2] = 0; + rm[rmOffset + 4] = 0; + rm[rmOffset + 8] = 0; + rm[rmOffset + 0] = 1; + } + else if (0.0f == x && 1.0f == y && 0.0f == z) + { + rm[rmOffset + 0] = c; + rm[rmOffset + 10] = c; + rm[rmOffset + 8] = s; + rm[rmOffset + 2] = -s; + rm[rmOffset + 1] = 0; + rm[rmOffset + 4] = 0; + rm[rmOffset + 6] = 0; + rm[rmOffset + 9] = 0; + rm[rmOffset + 5] = 1; + } + else if (0.0f == x && 0.0f == y && 1.0f == z) + { + rm[rmOffset + 0] = c; + rm[rmOffset + 5] = c; + rm[rmOffset + 1] = s; + rm[rmOffset + 4] = -s; + rm[rmOffset + 2] = 0; + rm[rmOffset + 6] = 0; + rm[rmOffset + 8] = 0; + rm[rmOffset + 9] = 0; + rm[rmOffset + 10] = 1; + } + else + { + float len = length(x, y, z); + if (1.0f != len) + { + float recipLen = 1.0f / len; + x *= recipLen; + y *= recipLen; + z *= recipLen; + } + float nc = 1.0f - c; + float xy = x * y; + float yz = y * z; + float zx = z * x; + float xs = x * s; + float ys = y * s; + float zs = z * s; + rm[rmOffset + 0] = x * x * nc + c; + rm[rmOffset + 4] = xy * nc - zs; + rm[rmOffset + 8] = zx * nc + ys; + rm[rmOffset + 1] = xy * nc + zs; + rm[rmOffset + 5] = y * y * nc + c; + rm[rmOffset + 9] = yz * nc - xs; + rm[rmOffset + 2] = zx * nc - ys; + rm[rmOffset + 6] = yz * nc + xs; + rm[rmOffset + 10] = z * z * nc + c; + } +} + +/** + * Transposes a 4 x 4 matrix. + * + * @param mTrans the array that holds the output inverted matrix + * @param mTransOffset an offset into mInv where the inverted matrix is + * stored. + * @param m the input array + * @param mOffset an offset into m where the matrix is stored. + */ +static inline void +transposeM(float* mTrans, int mTransOffset, float* m, int mOffset) +{ + for (int i = 0; i < 4; i++) + { + int mBase = i * 4 + mOffset; + mTrans[i + mTransOffset] = m[mBase]; + mTrans[i + 4 + mTransOffset] = m[mBase + 1]; + mTrans[i + 8 + mTransOffset] = m[mBase + 2]; + mTrans[i + 12 + mTransOffset] = m[mBase + 3]; + } +} +/******************************************************************************* + * Copyright 2011 See libgdx AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +// from /gdx/src/com/badlogic/gdx/math/Matrix4.java +#define M00 0 +#define M01 4 +#define M02 8 +#define M03 12 +#define M10 1 +#define M11 5 +#define M12 9 +#define M13 13 +#define M20 2 +#define M21 6 +#define M22 10 +#define M23 14 +#define M30 3 +#define M31 7 +#define M32 11 +#define M33 15 + +static inline void +matrix4_proj(float* mat, float* vec) +{ + float inv_w = 1.0f / (vec[0] * mat[M30] + vec[1] * mat[M31] + vec[2] * mat[M32] + mat[M33]); + float x = (vec[0] * mat[M00] + vec[1] * mat[M01] + vec[2] * mat[M02] + mat[M03]) * inv_w; + float y = (vec[0] * mat[M10] + vec[1] * mat[M11] + vec[2] * mat[M12] + mat[M13]) * inv_w; + float z = (vec[0] * mat[M20] + vec[1] * mat[M21] + vec[2] * mat[M22] + mat[M23]) * inv_w; + vec[0] = x; + vec[1] = y; + vec[2] = z; +} diff --git a/vtm/jni/target/linux64/gl/utils.c b/vtm/jni/target/linux64/gl/utils.c new file mode 100644 index 00000000..5ae82195 --- /dev/null +++ b/vtm/jni/target/linux64/gl/utils.c @@ -0,0 +1,487 @@ +#include + +#include +#include +#include +#include +#include +#include + +//#ifndef uintptr_t +//typedef unsigned long uintptr_t; +//#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#if 0 +#ifdef __ANDROID__ +#include +#include +#include + +#define JNI(X) JNIEXPORT Java_org_oscim_utils_GlUtils_##X + + +#define COLOR_R(C) (((C >> 16) & 0xff) / 255.0f) +#define COLOR_G(C) (((C >> 8) & 0xff) / 255.0f) +#define COLOR_B(C) (((C >> 0) & 0xff) / 255.0f) +#define COLOR_A(C) (((C >> 24) & 0xff) / 255.0f) + +void JNI(setColor)(JNIEnv *env, jclass* clazz, jint location, jint c, jfloat alpha) +{ + + if (alpha >= 1) + alpha = COLOR_A(c); + else if (alpha < 0) + alpha = 0; + else + alpha *= COLOR_A(c); + + if (alpha == 1) + { + glUniform4f((GLint) location, + (GLfloat) COLOR_R(c), + (GLfloat) COLOR_G(c), + (GLfloat) COLOR_B(c), + (GLfloat) alpha); + } + else + { + glUniform4f((GLint) location, + (GLfloat) (COLOR_R(c) * alpha), + (GLfloat) (COLOR_G(c) * alpha), + (GLfloat) (COLOR_B(c) * alpha), + (GLfloat) alpha); + } +} + +void JNI(setColorBlend)(JNIEnv *env, jclass* clazz, jint location, jint c1, jint c2, jfloat mix) +{ + float a1 = COLOR_A(c1) * (1 - mix); + float a2 = COLOR_A(c2) * mix; + + glUniform4f((GLint) location, + (GLfloat) (COLOR_R(c1) * a1 + COLOR_R(c2) * a2), + (GLfloat) (COLOR_G(c1) * a1 + COLOR_G(c2) * a2), + (GLfloat) (COLOR_B(c1) * a1 + COLOR_B(c2) * a2), + (GLfloat) (a1 + a2)); +} +#endif // __ANDROID__ +#endif // 0 + +#undef JNI +#define JNI(X) JNIEXPORT Java_org_oscim_utils_Matrix4_##X + +#define CAST(x) (float *)(uintptr_t) x +#define MAT_SIZE 16 * sizeof(float) + +static const float identity[] = + { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; + +static inline void +multiplyMM(float* r, const float* lhs, const float* rhs); + +static inline void +setRotateM(float* rm, int rmOffset, float a, float x, float y, float z); + +static inline void +transposeM(float* mTrans, int mTransOffset, float* m, int mOffset); + +static inline void +matrix4_proj(float* mat, float* vec); + +jlong JNI(alloc)(JNIEnv *env, jclass* clazz) +{ + return (long) calloc(16, sizeof(float)); +} + +jobject JNI(getBuffer)(JNIEnv *env, jclass* clazz,jlong ptr){ + return (*env)->NewDirectByteBuffer(env,(char*)(uintptr_t)ptr, 16*sizeof(float)); +} + +void JNI(delete)(JNIEnv* env, jclass* clazz, jlong ptr) +{ + free(CAST(ptr)); +} +#if 0 +void JNI(setAsUniform)(JNIEnv* env, jclass* clazz, jlong ptr, jint location) +{ + float* m = CAST(ptr); + + glUniformMatrix4fv((GLint) location, (GLsizei) 1, (GLboolean) 0, (GLfloat *) m); +} +#endif + +void JNI(setValueAt)(JNIEnv* env, jclass* clazz, jlong ptr, jint pos, jfloat value) +{ + float* m = CAST(ptr); + if (pos > -1 && pos < 16) + m[pos] = value; +} + +void JNI(identity)(JNIEnv* env, jclass* clazz, jlong ptr) +{ + float* m = CAST(ptr); + memcpy(m, identity, MAT_SIZE); +} + +void JNI(setScale)(JNIEnv* env, jclass* clazz, jlong ptr, jfloat sx, jfloat sy, jfloat sz) +{ + float* m = CAST(ptr); + memcpy(m, identity, MAT_SIZE); + m[0] = sx; + m[5] = sy; + m[10] = sz; +} + +void JNI(setTranslation)(JNIEnv* env, jclass* clazz, jlong ptr, jfloat x, jfloat y, jfloat z) +{ + float* m = CAST(ptr); + memcpy(m, identity, MAT_SIZE); + m[12] = x; + m[13] = y; + m[14] = z; +} + +void JNI(setRotation)(JNIEnv* env, jclass* clazz, jlong ptr, jfloat a, jfloat x, jfloat y, jfloat z) +{ + float* m = CAST(ptr); + setRotateM(m, 0, a, x, y, z); +} + +void JNI(setTransScale)(JNIEnv* env, jclass* clazz, jlong ptr, jfloat tx, jfloat ty, jfloat scale) +{ + float* m = CAST(ptr); + memcpy(m, identity, MAT_SIZE); + m[0] = scale; + m[5] = scale; + m[12] = tx; + m[13] = ty; +} + +// set matrix from float array +void JNI(set)(JNIEnv* env, jclass* clazz, jlong ptr, jfloatArray obj_mat) +{ + float* m = CAST(ptr); + float* mat = (float*) (*env)->GetPrimitiveArrayCritical(env, obj_mat, 0); + + memcpy(m, mat, MAT_SIZE); + + (*env)->ReleasePrimitiveArrayCritical(env, obj_mat, mat, JNI_ABORT); +} + +// get float array from matrix +void JNI(get)(JNIEnv* env, jclass* clazz, jlong ptr, jfloatArray obj_mat) +{ + float* m = CAST(ptr); + float* mat = (float*) (*env)->GetPrimitiveArrayCritical(env, obj_mat, 0); + + memcpy(mat, m, MAT_SIZE); + + (*env)->ReleasePrimitiveArrayCritical(env, obj_mat, mat, 0); +} + +void JNI(mul)(JNIEnv* env, jclass* clazz, jlong ptr_a, jlong ptr_b) +{ + float* mata = CAST(ptr_a); + float* matb = CAST(ptr_b); + + multiplyMM(mata, mata, matb); +} + +void JNI(copy)(JNIEnv* env, jclass* clazz, jlong ptr_dst, jlong ptr_src) +{ + float* dst = CAST(ptr_dst); + float* src = CAST(ptr_src); + + memcpy(dst, src, MAT_SIZE); +} + +void JNI(smul)(JNIEnv* env, jclass* clazz, jlong ptr_r, jlong ptr_a, jlong ptr_b) +{ + float* matr = CAST(ptr_r); + float* mata = CAST(ptr_a); + float* matb = CAST(ptr_b); + + multiplyMM(matr, mata, matb); +} + +void JNI(smulrhs)(JNIEnv* env, jclass* clazz, jlong ptr_r, jlong ptr_rhs) +{ + float* matr = CAST(ptr_r); + float* mata = alloca(16 * sizeof(float)); + float* matb = CAST(ptr_rhs); + + memcpy(mata, matr, 16 * sizeof(float)); + + multiplyMM(matr, mata, matb); +} + +void JNI(smullhs)(JNIEnv* env, jclass* clazz, jlong ptr_r, jlong ptr_lhs) +{ + float* matr = CAST(ptr_r); + float* mata = CAST(ptr_lhs); + float* matb = alloca(16 * sizeof(float)); + + memcpy(matb, matr, 16 * sizeof(float)); + + multiplyMM(matr, mata, matb); +} + + +void JNI(strans)(JNIEnv* env, jclass* clazz, jlong ptr_r, jlong ptr_a) +{ + float* matr = CAST(ptr_r); + float* mata = CAST(ptr_a); + + transposeM(matr, 0, mata, 0); +} + +void JNI(prj)(JNIEnv* env, jclass* clazz, jlong ptr, jfloatArray obj_vec) +{ + float* m = CAST(ptr); + float* vec = (float*) (*env)->GetPrimitiveArrayCritical(env, obj_vec, 0); + + matrix4_proj(m, vec); + + (*env)->ReleasePrimitiveArrayCritical(env, obj_vec, vec, 0); +} + +static float someRandomEpsilon = 1.0f / (1 << 11); + +void JNI(addDepthOffset)(JNIEnv* env, jclass* clazz, jlong ptr, jint delta) +{ + float* m = CAST(ptr); + + // from http://www.mathfor3dgameprogramming.com/code/Listing9.1.cpp + // float n = MapViewPosition.VIEW_NEAR; + // float f = MapViewPosition.VIEW_FAR; + // float pz = 1; + // float epsilon = -2.0f * f * n * delta / ((f + n) * pz * (pz + delta)); + + m[10] *= 1.0f + someRandomEpsilon * delta; +} + +/* + * Copyright 2007, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// from android/platform_frameworks_base/blob/master/core/jni/android/opengl/util.cpp +#define I(_i, _j) ((_j)+ 4*(_i)) + +static inline void +multiplyMM(float* r, const float* lhs, const float* rhs) +{ + for (int i = 0; i < 4; i++) + { + register const float rhs_i0 = rhs[I(i,0)]; + register float ri0 = lhs[I(0,0)] * rhs_i0; + register float ri1 = lhs[I(0,1)] * rhs_i0; + register float ri2 = lhs[I(0,2)] * rhs_i0; + register float ri3 = lhs[I(0,3)] * rhs_i0; + for (int j = 1; j < 4; j++) + { + register const float rhs_ij = rhs[I(i,j)]; + ri0 += lhs[I(j,0)] * rhs_ij; + ri1 += lhs[I(j,1)] * rhs_ij; + ri2 += lhs[I(j,2)] * rhs_ij; + ri3 += lhs[I(j,3)] * rhs_ij; + } + r[I(i,0)] = ri0; + r[I(i,1)] = ri1; + r[I(i,2)] = ri2; + r[I(i,3)] = ri3; + } +} + +//static inline +//void +//mx4transform(float x, float y, float z, float w, const float* pM, float* pDest) +//{ +// pDest[0] = pM[0 + 4 * 0] * x + pM[0 + 4 * 1] * y + pM[0 + 4 * 2] * z + pM[0 + 4 * 3] * w; +// pDest[1] = pM[1 + 4 * 0] * x + pM[1 + 4 * 1] * y + pM[1 + 4 * 2] * z + pM[1 + 4 * 3] * w; +// pDest[2] = pM[2 + 4 * 0] * x + pM[2 + 4 * 1] * y + pM[2 + 4 * 2] * z + pM[2 + 4 * 3] * w; +// +// pDest[3] = pM[3 + 4 * 0] * x + pM[3 + 4 * 1] * y + pM[3 + 4 * 2] * z + pM[3 + 4 * 3] * w; +//} + +/** + * Computes the length of a vector + * + * @param x x coordinate of a vector + * @param y y coordinate of a vector + * @param z z coordinate of a vector + * @return the length of a vector + */ +static inline float +length(float x, float y, float z) +{ + return (float) sqrt(x * x + y * y + z * z); +} +/** + * Rotates matrix m by angle a (in degrees) around the axis (x, y, z) + * @param rm returns the result + * @param rmOffset index into rm where the result matrix starts + * @param a angle to rotate in degrees + * @param x scale factor x + * @param y scale factor y + * @param z scale factor z + */ + +static inline void +setRotateM(float* rm, int rmOffset, float a, float x, float y, float z) +{ + rm[rmOffset + 3] = 0; + rm[rmOffset + 7] = 0; + rm[rmOffset + 11] = 0; + rm[rmOffset + 12] = 0; + rm[rmOffset + 13] = 0; + rm[rmOffset + 14] = 0; + rm[rmOffset + 15] = 1; + a *= (float) (M_PI / 180.0f); + float s = (float) sin(a); + float c = (float) cos(a); + if (1.0f == x && 0.0f == y && 0.0f == z) + { + rm[rmOffset + 5] = c; + rm[rmOffset + 10] = c; + rm[rmOffset + 6] = s; + rm[rmOffset + 9] = -s; + rm[rmOffset + 1] = 0; + rm[rmOffset + 2] = 0; + rm[rmOffset + 4] = 0; + rm[rmOffset + 8] = 0; + rm[rmOffset + 0] = 1; + } + else if (0.0f == x && 1.0f == y && 0.0f == z) + { + rm[rmOffset + 0] = c; + rm[rmOffset + 10] = c; + rm[rmOffset + 8] = s; + rm[rmOffset + 2] = -s; + rm[rmOffset + 1] = 0; + rm[rmOffset + 4] = 0; + rm[rmOffset + 6] = 0; + rm[rmOffset + 9] = 0; + rm[rmOffset + 5] = 1; + } + else if (0.0f == x && 0.0f == y && 1.0f == z) + { + rm[rmOffset + 0] = c; + rm[rmOffset + 5] = c; + rm[rmOffset + 1] = s; + rm[rmOffset + 4] = -s; + rm[rmOffset + 2] = 0; + rm[rmOffset + 6] = 0; + rm[rmOffset + 8] = 0; + rm[rmOffset + 9] = 0; + rm[rmOffset + 10] = 1; + } + else + { + float len = length(x, y, z); + if (1.0f != len) + { + float recipLen = 1.0f / len; + x *= recipLen; + y *= recipLen; + z *= recipLen; + } + float nc = 1.0f - c; + float xy = x * y; + float yz = y * z; + float zx = z * x; + float xs = x * s; + float ys = y * s; + float zs = z * s; + rm[rmOffset + 0] = x * x * nc + c; + rm[rmOffset + 4] = xy * nc - zs; + rm[rmOffset + 8] = zx * nc + ys; + rm[rmOffset + 1] = xy * nc + zs; + rm[rmOffset + 5] = y * y * nc + c; + rm[rmOffset + 9] = yz * nc - xs; + rm[rmOffset + 2] = zx * nc - ys; + rm[rmOffset + 6] = yz * nc + xs; + rm[rmOffset + 10] = z * z * nc + c; + } +} + +/** + * Transposes a 4 x 4 matrix. + * + * @param mTrans the array that holds the output inverted matrix + * @param mTransOffset an offset into mInv where the inverted matrix is + * stored. + * @param m the input array + * @param mOffset an offset into m where the matrix is stored. + */ +static inline void +transposeM(float* mTrans, int mTransOffset, float* m, int mOffset) +{ + for (int i = 0; i < 4; i++) + { + int mBase = i * 4 + mOffset; + mTrans[i + mTransOffset] = m[mBase]; + mTrans[i + 4 + mTransOffset] = m[mBase + 1]; + mTrans[i + 8 + mTransOffset] = m[mBase + 2]; + mTrans[i + 12 + mTransOffset] = m[mBase + 3]; + } +} +/******************************************************************************* + * Copyright 2011 See libgdx AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +// from /gdx/src/com/badlogic/gdx/math/Matrix4.java +#define M00 0 +#define M01 4 +#define M02 8 +#define M03 12 +#define M10 1 +#define M11 5 +#define M12 9 +#define M13 13 +#define M20 2 +#define M21 6 +#define M22 10 +#define M23 14 +#define M30 3 +#define M31 7 +#define M32 11 +#define M33 15 + +static inline void +matrix4_proj(float* mat, float* vec) +{ + float inv_w = 1.0f / (vec[0] * mat[M30] + vec[1] * mat[M31] + vec[2] * mat[M32] + mat[M33]); + float x = (vec[0] * mat[M00] + vec[1] * mat[M01] + vec[2] * mat[M02] + mat[M03]) * inv_w; + float y = (vec[0] * mat[M10] + vec[1] * mat[M11] + vec[2] * mat[M12] + mat[M13]) * inv_w; + float z = (vec[0] * mat[M20] + vec[1] * mat[M21] + vec[2] * mat[M22] + mat[M23]) * inv_w; + vec[0] = x; + vec[1] = y; + vec[2] = z; +} diff --git a/vtm/jni/target/linux64/triangle/TriangleJni.c b/vtm/jni/target/linux64/triangle/TriangleJni.c new file mode 100644 index 00000000..56721f3a --- /dev/null +++ b/vtm/jni/target/linux64/triangle/TriangleJni.c @@ -0,0 +1,308 @@ +#include +#include +#include +#include +#include +#include "triangle.h" + +#ifdef __ANDROID__ +#include +#define printf(...) __android_log_print(ANDROID_LOG_DEBUG, "Triangle", __VA_ARGS__) +#endif + +// from www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html +#if 0 +int pnpoly(int nvert, float *vert, float testx, float testy) +{ + int i, j, c = 0; + for (i = 0, j = (nvert-1)*2; i < nvert * 2; j = i++) + { + if ( ((vert[i*2+1] > testy) != (vert[j*j+1] > testy)) && + (testx < (vert[j*2]-vert[i*2]) + * (testy - vert[i*2+1]) + / (vert[j*2+1]-vert[i*2+1]) + vert[i*2]) ) + c = !c; + } + return c; +} + +int compare_dups(const void *a, const void *b) { + int da = *((const long*) a); + int db = *((const long*) b); + return (da > db) - (da < db); +} + +void shiftSegment(TriangleIO *in, int *seg, int pos) { + int size = (in->numberofsegments - pos - 1) * sizeof(int) * 2; + printf("shift %d - %d %d\n", size, in->numberofsegments, pos); + if (size > 0) + memmove(seg, seg + 2, size); + + in->numberofsegments -= 1; +} +struct { + int p1; + int p2; +} segment; + +#endif + +static void printPoly(TriangleIO *in) { + // print poly format to check with triangle/showme + printf("%d 2 0 0\n", in->numberofpoints); + for (int j = 0; j < in->numberofpoints; j++) + printf("%d %f %f\n", j, in->pointlist[j*2], in->pointlist[j*2+1]); + + int *seg = in->segmentlist; + printf("%d 0\n", in->numberofsegments); + for (int j = 0; j < in->numberofsegments; j++, seg += 2) + printf("%d %d %d\n", j, *seg, *(seg+1)); + + printf("%d 0\n", in->numberofholes); + for (int j = 0; j < in->numberofholes; j++) { + printf("%d %f %f\n", j, in->holelist[j*2], in->holelist[j*2+1]); + } +} + +jint Java_org_oscim_renderer_sublayers_ExtrusionLayer_triangulate(JNIEnv *env, jclass c, + jfloatArray obj_points, jint pos, jint len, jint num_rings, jobject indice_buf, jint offset) { + + jshort* indices = (jshort*) (*env)->GetDirectBufferAddress(env, indice_buf); + jboolean isCopy; + + float* orig_points = (float*) (*env)->GetPrimitiveArrayCritical(env, obj_points, &isCopy); + if (orig_points == NULL) + return 0; + + float *points = orig_points + pos; + + TriangleIO in, out; + + memset(&in, 0, sizeof(TriangleIO)); + + in.numberofpoints = len >> 1; + in.pointlist = (float *) points; + + // check if explicitly closed + if (in.pointlist[0] == in.pointlist[indices[0] - 2] + && in.pointlist[1] == in.pointlist[indices[0] - 1]) { + int point = 0; + for (int i = 0; i < num_rings; i++) { + // remove last point in ring + indices[i] -= 2; + int last = point + (indices[i] >> 1); + + if (in.numberofpoints - last > 1) + memmove(in.pointlist + (last * 2), in.pointlist + ((last + 1) * 2), + (in.numberofpoints - last - 1) * 2 * sizeof(float)); + + in.numberofpoints--; + point = last; + } + } + + int dups = 0; + + float *i_points = points; + int *skip_list = NULL; + + // check for duplicate vertices and keep a list + // of dups and the first occurence + for (int i = 0; i < in.numberofpoints - 1; i++) { + float x = *i_points++; + float y = *i_points++; + float *j_points = i_points; + + for (int j = i + 1; j < in.numberofpoints; j++, j_points += 2) { + if ((*j_points == x) && (*(j_points + 1) == y)) { + skip_list = realloc(skip_list, (dups + 2) * 2 * sizeof(int)); + skip_list[dups * 2 + 0] = j; + skip_list[dups * 2 + 1] = i; + dups++; + } + } + } + + in.segmentlist = (int *) malloc(in.numberofpoints * 2 * sizeof(int)); + in.numberofsegments = in.numberofpoints; + in.numberofholes = num_rings - 1; + + int *rings = NULL; + if (in.numberofholes > 0) { + in.holelist = (float *) malloc(in.numberofholes * 2 * sizeof(float)); + rings = (int*) malloc(num_rings * sizeof(int)); + } + + int *seg = in.segmentlist; + float *hole = in.holelist; + + // counter going through all points + int point; + // counter going through all rings + int ring; + + // assign all points to segments for each ring + for (ring = 0, point = 0; ring < num_rings; ring++, point++) { + int len; + int num_points = indices[ring] >> 1; + + if (rings) + rings[ring] = num_points; + + // add holes: we need a point inside the hole... + // this is just a heuristic, assuming that two + // 'parallel' lines have a distance of at least + // 1 unit. you'll notice when things went wrong + // when the hole is rendered instead of the poly + if (ring > 0) { + int k = point * 2; + + float nx = in.pointlist[k++]; + float ny = in.pointlist[k++]; + + float cx = 0, cy = 0, vx = 0, vy = 0; + + // try to find a large enough segment + for (len = (point + num_points) * 2; k < len;) { + cx = nx; + cy = ny; + + nx = in.pointlist[k++]; + ny = in.pointlist[k++]; + + vx = nx - cx; + vy = ny - cy; + + if (vx > 4 || vx < -4 || vy > 4 || vy < -4) + break; + } + + float a = sqrt(vx * vx + vy * vy); + + float ux = -vy / a; + float uy = vx / a; + + float centerx = cx + vx / 2.0 - (ux * 0.1); + float centery = cy + vy / 2.0 - (uy * 0.1); + + *hole++ = centerx; + *hole++ = centery; + } + + // close ring + int last = point + (num_points - 1); + *seg++ = last; + *seg++ = point; + + for (len = point + num_points - 1; point < len; point++) { + *seg++ = point; + *seg++ = point + 1; + } + } + + if (dups) { + for (int i = 0; i < dups; i++) { + printf("duplicate points at %d, %d: %f,%f\n", + skip_list[i*2], skip_list[i*2+1], + in.pointlist[skip_list[i*2+1]*2], + in.pointlist[skip_list[i*2+1]*2+1]); + } + printPoly(&in); + + // replace duplicate positions with first occurence + for (int i = 0; i < dups; i++) { + // position of the duplicate vertex + int pos = skip_list[i * 2] - i; + // first vertex + int replacement = skip_list[i * 2 + 1]; + + seg = in.segmentlist; + for (int j = 0; j < in.numberofsegments * 2; j++, seg++) { + if (*seg == pos) { + printf("%d: %d <- %d", j, pos, replacement); + *seg = replacement; + } + } + } + } + + memset(&out, 0, sizeof(TriangleIO)); + out.trianglelist = (INDICE*) indices; + + // p - use polygon input, for CDT + // z - zero offset array offsets... + // P - no poly output + // N - no node output + // B - no bound output + // Q - be quiet! + + TriangleOptions opt; + memset(&opt, 0, sizeof(TriangleOptions)); + + opt.dwyer = 1; + opt.steiner = -1; + opt.order = 1; + opt.maxarea = -1.0; + + opt.poly = 1; + opt.usesegments = 1; + opt.nopolywritten = 1; + opt.nonodewritten = 1; + opt.nobound = 1; + opt.quiet = 1; + + triangulate(&opt, &in, &out, (TriangleIO *) NULL); + + if (in.numberofpoints < out.numberofpoints) { + // TODO rerun with 'nonodewritten = 0' + printf( "polygon input is bad! points in:%d out%d\n", in.numberofpoints, out.numberofpoints); + out.numberoftriangles = 0; + } + else if (out.trianglelist) + { + // scale to stride and add offset + short stride = 2; + + if (offset < 0) + offset = 0; + + INDICE *tri = out.trianglelist; + + for (int n = out.numberoftriangles * 3; n > 0; n--, tri++) + *tri = *tri * stride + offset; + + // when a ring has an odd number of points one (or rather two) + // additional vertices will be added. so the following rings + // needs extra offset... + int start = offset; + for (int j = 0, m = in.numberofholes; j < m; j++) { + start += rings[j] * stride; + + // even number of points? + if (!(rings[j] & 1)) + continue; + + tri = out.trianglelist; + int n = out.numberoftriangles * 3; + + for (; n-- > 0; tri++) + if (*tri >= start) + *tri += stride; + + start += stride; + } + } + else + { + printf( "triangle failed %d\n", out.numberofpoints); + } + + (*env)->ReleasePrimitiveArrayCritical(env, obj_points, orig_points, JNI_ABORT); + + free(in.segmentlist); + free(in.holelist); + free(rings); + free(skip_list); + + return out.numberoftriangles; +} diff --git a/vtm/jni/target/linux64/triangle/triangle.c b/vtm/jni/target/linux64/triangle/triangle.c new file mode 100644 index 00000000..c4e1a3ca --- /dev/null +++ b/vtm/jni/target/linux64/triangle/triangle.c @@ -0,0 +1,7390 @@ +/*****************************************************************************/ +/* */ +/* 888888888 ,o, / 888 */ +/* 888 88o88o " o8888o 88o8888o o88888o 888 o88888o */ +/* 888 888 888 88b 888 888 888 888 888 d888 88b */ +/* 888 888 888 o88^o888 888 888 "88888" 888 8888oo888 */ +/* 888 888 888 C888 888 888 888 / 888 q888 */ +/* 888 888 888 "88o^888 888 888 Cb 888 "88oooo" */ +/* "8oo8D */ +/* */ +/* A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator. */ +/* (triangle.c) */ +/* */ +/* Version 1.6 */ +/* July 28, 2005 */ +/* */ +/* Copyright 1993, 1995, 1997, 1998, 2002, 2005 */ +/* Jonathan Richard Shewchuk */ +/* 2360 Woolsey #H */ +/* Berkeley, California 94705-1927 */ +/* jrs@cs.berkeley.edu */ +/* */ +/* This program may be freely redistributed under the condition that the */ +/* copyright notices (including this entire header and the copyright */ +/* notice printed when the `-h' switch is selected) are not removed, and */ +/* no compensation is received. Private, research, and institutional */ +/* use is free. You may distribute modified versions of this code UNDER */ +/* THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT IN THE */ +/* SAME FILE REMAIN UNDER COPYRIGHT OF THE ORIGINAL AUTHOR, BOTH SOURCE */ +/* AND OBJECT CODE ARE MADE FREELY AVAILABLE WITHOUT CHARGE, AND CLEAR */ +/* NOTICE IS GIVEN OF THE MODIFICATIONS. Distribution of this code as */ +/* part of a commercial system is permissible ONLY BY DIRECT ARRANGEMENT */ +/* WITH THE AUTHOR. (If you are not directly supplying this code to a */ +/* customer, and you are instead telling them how they can obtain it for */ +/* free, then you are not required to make any arrangement with me.) */ +/* */ +/* Hypertext instructions for Triangle are available on the Web at */ +/* */ +/* http://www.cs.cmu.edu/~quake/triangle.html */ +/* */ +/* Disclaimer: Neither I nor Carnegie Mellon warrant this code in any way */ +/* whatsoever. This code is provided "as-is". Use at your own risk. */ +/* */ +/* Some of the references listed below are marked with an asterisk. [*] */ +/* These references are available for downloading from the Web page */ +/* */ +/* http://www.cs.cmu.edu/~quake/triangle.research.html */ +/* */ +/* Three papers discussing aspects of Triangle are available. A short */ +/* overview appears in "Triangle: Engineering a 2D Quality Mesh */ +/* Generator and Delaunay Triangulator," in Applied Computational */ +/* Geometry: Towards Geometric Engineering, Ming C. Lin and Dinesh */ +/* Manocha, editors, Lecture Notes in Computer Science volume 1148, */ +/* pages 203-222, Springer-Verlag, Berlin, May 1996 (from the First ACM */ +/* Workshop on Applied Computational Geometry). [*] */ +/* */ +/* The algorithms are discussed in the greatest detail in "Delaunay */ +/* Refinement Algorithms for Triangular Mesh Generation," Computational */ +/* Geometry: Theory and Applications 22(1-3):21-74, May 2002. [*] */ +/* */ +/* More detail about the data structures may be found in my dissertation: */ +/* "Delaunay Refinement Mesh Generation," Ph.D. thesis, Technical Report */ +/* CMU-CS-97-137, School of Computer Science, Carnegie Mellon University, */ +/* Pittsburgh, Pennsylvania, 18 May 1997. [*] */ +/* */ +/* Triangle was created as part of the Quake Project in the School of */ +/* Computer Science at Carnegie Mellon University. For further */ +/* information, see Hesheng Bao, Jacobo Bielak, Omar Ghattas, Loukas F. */ +/* Kallivokas, David R. O'Hallaron, Jonathan R. Shewchuk, and Jifeng Xu, */ +/* "Large-scale Simulation of Elastic Wave Propagation in Heterogeneous */ +/* Media on Parallel Computers," Computer Methods in Applied Mechanics */ +/* and Engineering 152(1-2):85-102, 22 January 1998. */ +/* */ +/* Triangle's Delaunay refinement algorithm for quality mesh generation is */ +/* a hybrid of one due to Jim Ruppert, "A Delaunay Refinement Algorithm */ +/* for Quality 2-Dimensional Mesh Generation," Journal of Algorithms */ +/* 18(3):548-585, May 1995 [*], and one due to L. Paul Chew, "Guaranteed- */ +/* Quality Mesh Generation for Curved Surfaces," Proceedings of the Ninth */ +/* Annual Symposium on Computational Geometry (San Diego, California), */ +/* pages 274-280, Association for Computing Machinery, May 1993, */ +/* http://portal.acm.org/citation.cfm?id=161150 . */ +/* */ +/* The Delaunay refinement algorithm has been modified so that it meshes */ +/* domains with small input angles well, as described in Gary L. Miller, */ +/* Steven E. Pav, and Noel J. Walkington, "When and Why Ruppert's */ +/* Algorithm Works," Twelfth International Meshing Roundtable, pages */ +/* 91-102, Sandia National Laboratories, September 2003. [*] */ +/* */ +/* My implementation of the divide-and-conquer and incremental Delaunay */ +/* triangulation algorithms follows closely the presentation of Guibas */ +/* and Stolfi, even though I use a triangle-based data structure instead */ +/* of their quad-edge data structure. (In fact, I originally implemented */ +/* Triangle using the quad-edge data structure, but the switch to a */ +/* triangle-based data structure sped Triangle by a factor of two.) The */ +/* mesh manipulation primitives and the two aforementioned Delaunay */ +/* triangulation algorithms are described by Leonidas J. Guibas and Jorge */ +/* Stolfi, "Primitives for the Manipulation of General Subdivisions and */ +/* the Computation of Voronoi Diagrams," ACM Transactions on Graphics */ +/* 4(2):74-123, April 1985, http://portal.acm.org/citation.cfm?id=282923 .*/ +/* */ +/* Their O(n log n) divide-and-conquer algorithm is adapted from Der-Tsai */ +/* Lee and Bruce J. Schachter, "Two Algorithms for Constructing the */ +/* Delaunay Triangulation," International Journal of Computer and */ +/* Information Science 9(3):219-242, 1980. Triangle's improvement of the */ +/* divide-and-conquer algorithm by alternating between vertical and */ +/* horizontal cuts was introduced by Rex A. Dwyer, "A Faster Divide-and- */ +/* Conquer Algorithm for Constructing Delaunay Triangulations," */ +/* Algorithmica 2(2):137-151, 1987. */ +/* */ +/* The incremental insertion algorithm was first proposed by C. L. Lawson, */ +/* "Software for C1 Surface Interpolation," in Mathematical Software III, */ +/* John R. Rice, editor, Academic Press, New York, pp. 161-194, 1977. */ +/* For point location, I use the algorithm of Ernst P. Mucke, Isaac */ +/* Saias, and Binhai Zhu, "Fast Randomized Point Location Without */ +/* Preprocessing in Two- and Three-Dimensional Delaunay Triangulations," */ +/* Proceedings of the Twelfth Annual Symposium on Computational Geometry, */ +/* ACM, May 1996. [*] If I were to randomize the order of vertex */ +/* insertion (I currently don't bother), their result combined with the */ +/* result of Kenneth L. Clarkson and Peter W. Shor, "Applications of */ +/* Random Sampling in Computational Geometry II," Discrete & */ +/* Computational Geometry 4(1):387-421, 1989, would yield an expected */ +/* O(n^{4/3}) bound on running time. */ +/* */ +/* The O(n log n) sweepline Delaunay triangulation algorithm is taken from */ +/* Steven Fortune, "A Sweepline Algorithm for Voronoi Diagrams", */ +/* Algorithmica 2(2):153-174, 1987. A random sample of edges on the */ +/* boundary of the triangulation are maintained in a splay tree for the */ +/* purpose of point location. Splay trees are described by Daniel */ +/* Dominic Sleator and Robert Endre Tarjan, "Self-Adjusting Binary Search */ +/* Trees," Journal of the ACM 32(3):652-686, July 1985, */ +/* http://portal.acm.org/citation.cfm?id=3835 . */ +/* */ +/* The algorithms for exact computation of the signs of determinants are */ +/* described in Jonathan Richard Shewchuk, "Adaptive Precision Floating- */ +/* Point Arithmetic and Fast Robust Geometric Predicates," Discrete & */ +/* Computational Geometry 18(3):305-363, October 1997. (Also available */ +/* as Technical Report CMU-CS-96-140, School of Computer Science, */ +/* Carnegie Mellon University, Pittsburgh, Pennsylvania, May 1996.) [*] */ +/* An abbreviated version appears as Jonathan Richard Shewchuk, "Robust */ +/* Adaptive Floating-Point Geometric Predicates," Proceedings of the */ +/* Twelfth Annual Symposium on Computational Geometry, ACM, May 1996. [*] */ +/* Many of the ideas for my exact arithmetic routines originate with */ +/* Douglas M. Priest, "Algorithms for Arbitrary Precision Floating Point */ +/* Arithmetic," Tenth Symposium on Computer Arithmetic, pp. 132-143, IEEE */ +/* Computer Society Press, 1991. [*] Many of the ideas for the correct */ +/* evaluation of the signs of determinants are taken from Steven Fortune */ +/* and Christopher J. Van Wyk, "Efficient Exact Arithmetic for Computa- */ +/* tional Geometry," Proceedings of the Ninth Annual Symposium on */ +/* Computational Geometry, ACM, pp. 163-172, May 1993, and from Steven */ +/* Fortune, "Numerical Stability of Algorithms for 2D Delaunay Triangu- */ +/* lations," International Journal of Computational Geometry & Applica- */ +/* tions 5(1-2):193-213, March-June 1995. */ +/* */ +/* The method of inserting new vertices off-center (not precisely at the */ +/* circumcenter of every poor-quality triangle) is from Alper Ungor, */ +/* "Off-centers: A New Type of Steiner Points for Computing Size-Optimal */ +/* Quality-Guaranteed Delaunay Triangulations," Proceedings of LATIN */ +/* 2004 (Buenos Aires, Argentina), April 2004. */ +/* */ +/* For definitions of and results involving Delaunay triangulations, */ +/* constrained and conforming versions thereof, and other aspects of */ +/* triangular mesh generation, see the excellent survey by Marshall Bern */ +/* and David Eppstein, "Mesh Generation and Optimal Triangulation," in */ +/* Computing and Euclidean Geometry, Ding-Zhu Du and Frank Hwang, */ +/* editors, World Scientific, Singapore, pp. 23-90, 1992. [*] */ +/* */ +/* The time for incrementally adding PSLG (planar straight line graph) */ +/* segments to create a constrained Delaunay triangulation is probably */ +/* O(t^2) per segment in the worst case and O(t) per segment in the */ +/* common case, where t is the number of triangles that intersect the */ +/* segment before it is inserted. This doesn't count point location, */ +/* which can be much more expensive. I could improve this to O(d log d) */ +/* time, but d is usually quite small, so it's not worth the bother. */ +/* (This note does not apply when the -s switch is used, invoking a */ +/* different method is used to insert segments.) */ +/* */ +/* The time for deleting a vertex from a Delaunay triangulation is O(d^2) */ +/* in the worst case and O(d) in the common case, where d is the degree */ +/* of the vertex being deleted. I could improve this to O(d log d) time, */ +/* but d is usually quite small, so it's not worth the bother. */ +/* */ +/* Ruppert's Delaunay refinement algorithm typically generates triangles */ +/* at a linear rate (constant time per triangle) after the initial */ +/* triangulation is formed. There may be pathological cases where */ +/* quadratic time is required, but these never arise in practice. */ +/* */ +/* The geometric predicates (circumcenter calculations, segment */ +/* intersection formulae, etc.) appear in my "Lecture Notes on Geometric */ +/* Robustness" at http://www.cs.berkeley.edu/~jrs/mesh . */ +/* */ +/* If you make any improvements to this code, please please please let me */ +/* know, so that I may obtain the improvements. Even if you don't change */ +/* the code, I'd still love to hear what it's being used for. */ +/* */ +/*****************************************************************************/ + +#include "triangle_private.h" + +/* Fast lookup arrays to speed some of the mesh manipulation primitives. */ + +int plus1mod3[3] = { 1, 2, 0 }; +int minus1mod3[3] = { 2, 0, 1 }; + +/********* User-defined triangle evaluation routine begins here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* triunsuitable() Determine if a triangle is unsuitable, and thus must */ +/* be further refined. */ +/* */ +/* You may write your own procedure that decides whether or not a selected */ +/* triangle is too big (and needs to be refined). There are two ways to do */ +/* this. */ +/* */ +/* (1) Modify the procedure `triunsuitable' below, then recompile */ +/* Triangle. */ +/* */ +/* (2) Define the symbol EXTERNAL_TEST (either by adding the definition */ +/* to this file, or by using the appropriate compiler switch). This way, */ +/* you can compile triangle.c separately from your test. Write your own */ +/* `triunsuitable' procedure in a separate C file (using the same prototype */ +/* as below). Compile it and link the object code with triangle.o. */ +/* */ +/* This procedure returns 1 if the triangle is too large and should be */ +/* refined; 0 otherwise. */ +/* */ +/*****************************************************************************/ + +#ifdef EXTERNAL_TEST + +int triunsuitable(); + +#else /* not EXTERNAL_TEST */ + +int triunsuitable(vertex triorg, vertex tridest, vertex triapex, REAL area) { + REAL dxoa, dxda, dxod; + REAL dyoa, dyda, dyod; + REAL oalen, dalen, odlen; + REAL maxlen; + + dxoa = triorg[0] - triapex[0]; + dyoa = triorg[1] - triapex[1]; + dxda = tridest[0] - triapex[0]; + dyda = tridest[1] - triapex[1]; + dxod = triorg[0] - tridest[0]; + dyod = triorg[1] - tridest[1]; + /* Find the squares of the lengths of the triangle's three edges. */ + oalen = dxoa * dxoa + dyoa * dyoa; + dalen = dxda * dxda + dyda * dyda; + odlen = dxod * dxod + dyod * dyod; + /* Find the square of the length of the longest edge. */ + maxlen = (dalen > oalen) ? dalen : oalen; + maxlen = (odlen > maxlen) ? odlen : maxlen; + + if (maxlen > 0.05 * (triorg[0] * triorg[0] + triorg[1] * triorg[1]) + 0.02) { + return 1; + } + else { + return 0; + } +} + +#endif /* not EXTERNAL_TEST */ + +/** **/ +/** **/ +/********* User-defined triangle evaluation routine ends here *********/ + +/********* Memory allocation and program exit wrappers begin here *********/ +/** **/ +/** **/ + +void triexit(int status) { + printf("Exit %d.\n", status); + + exit(status); +} + +VOID *trimalloc(int size) { + VOID *memptr; + + memptr = (VOID *) malloc((unsigned int) size); + if (memptr == (VOID *) NULL) { + printf("Error: Out of memory.\n"); + triexit(1); + } + return (memptr); +} + +void trifree(VOID *memptr) { + free(memptr); +} + +/** **/ +/** **/ +/********* Memory allocation and program exit wrappers end here *********/ + +/********* User interaction routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* internalerror() Ask the user to send me the defective product. Exit. */ +/* */ +/*****************************************************************************/ + +int error_set = 0; +void internalerror() { + error_set = 1; + printf("Triangle is going to quit its job now\n"); + //printf(" Please report this bug to jrs@cs.berkeley.edu\n"); + ///printf(" Include the message above, your input data set, and the exact\n"); + //printf(" command line you used to run Triangle.\n"); + //triexit(1); +} + +/*****************************************************************************/ +/* */ +/* parsecommandline() Read the command line, identify switches, and set */ +/* up options and file names. */ +/* */ +/*****************************************************************************/ + +void parsecommandline(int argc, char **argv, struct behavior *b) { + error_set = 0; + +#define STARTINDEX 0 + + int i, j; + + b->poly = b->refine = b->quality = 0; + b->vararea = b->fixedarea = b->usertest = 0; + b->regionattrib = b->convex = b->weighted = b->jettison = 0; + b->firstnumber = 1; + b->edgesout = b->voronoi = b->neighbors = b->geomview = 0; + b->nobound = b->nopolywritten = b->nonodewritten = b->noelewritten = 0; + b->noiterationnum = 0; + b->noholes = b->noexact = 0; + b->incremental = b->sweepline = 0; + b->dwyer = 1; + b->splitseg = 0; + b->docheck = 0; + b->nobisect = 0; + b->conformdel = 0; + b->steiner = -1; + b->order = 1; + b->minangle = 0.0; + b->maxarea = -1.0; + b->quiet = b->verbose = 0; + + for (i = STARTINDEX; i < argc; i++) { + for (j = STARTINDEX; argv[i][j] != '\0'; j++) { + if (argv[i][j] == 'p') { + b->poly = 1; + } +#ifndef CDT_ONLY + if (argv[i][j] == 'r') + { + b->refine = 1; + } + if (argv[i][j] == 'q') + { + b->quality = 1; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) + { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) + { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + b->minangle = (REAL) strtod(workstring, (char **) NULL); + } + else + { + b->minangle = 20.0; + } + } + if (argv[i][j] == 'a') + { + b->quality = 1; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) + { + b->fixedarea = 1; + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) + { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + b->maxarea = (REAL) strtod(workstring, (char **) NULL); + if (b->maxarea <= 0.0) + { + printf("Error: Maximum area must be greater than zero.\n"); + triexit(1); + } + } + else + { + b->vararea = 1; + } + } + if (argv[i][j] == 'u') + { + b->quality = 1; + b->usertest = 1; + } +#endif /* not CDT_ONLY */ + if (argv[i][j] == 'A') { + b->regionattrib = 1; + } + if (argv[i][j] == 'c') { + b->convex = 1; + } + if (argv[i][j] == 'w') { + b->weighted = 1; + } + if (argv[i][j] == 'W') { + b->weighted = 2; + } + if (argv[i][j] == 'j') { + b->jettison = 1; + } + if (argv[i][j] == 'z') { + b->firstnumber = 0; + } + if (argv[i][j] == 'e') { + b->edgesout = 1; + } + if (argv[i][j] == 'v') { + b->voronoi = 1; + } + if (argv[i][j] == 'n') { + b->neighbors = 1; + } + if (argv[i][j] == 'g') { + b->geomview = 1; + } + if (argv[i][j] == 'B') { + b->nobound = 1; + } + if (argv[i][j] == 'P') { + b->nopolywritten = 1; + } + if (argv[i][j] == 'N') { + b->nonodewritten = 1; + } + if (argv[i][j] == 'E') { + b->noelewritten = 1; + } + if (argv[i][j] == 'O') { + b->noholes = 1; + } + if (argv[i][j] == 'X') { + b->noexact = 1; + } + if (argv[i][j] == 'o') { + if (argv[i][j + 1] == '2') { + j++; + b->order = 2; + } + } +#ifndef CDT_ONLY + if (argv[i][j] == 'Y') + { + b->nobisect++; + } + if (argv[i][j] == 'S') + { + b->steiner = 0; + while ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) + { + j++; + b->steiner = b->steiner * 10 + (int) (argv[i][j] - '0'); + } + } +#endif /* not CDT_ONLY */ +#ifndef REDUCED + if (argv[i][j] == 'i') + { + b->incremental = 1; + } + if (argv[i][j] == 'F') + { + b->sweepline = 1; + } +#endif /* not REDUCED */ + if (argv[i][j] == 'l') { + b->dwyer = 0; + } +#ifndef REDUCED +#ifndef CDT_ONLY + if (argv[i][j] == 's') + { + b->splitseg = 1; + } + if ((argv[i][j] == 'D') || (argv[i][j] == 'L')) + { + b->quality = 1; + b->conformdel = 1; + } +#endif /* not CDT_ONLY */ + if (argv[i][j] == 'C') + { + b->docheck = 1; + } +#endif /* not REDUCED */ + if (argv[i][j] == 'Q') { + b->quiet = 1; + } + if (argv[i][j] == 'V') { + b->verbose++; + } + } + } + b->usesegments = b->poly || b->refine || b->quality || b->convex; + b->goodangle = cos(b->minangle * PI / 180.0); + if (b->goodangle == 1.0) { + b->offconstant = 0.0; + } + else { + b->offconstant = 0.475 * sqrt((1.0 + b->goodangle) / (1.0 - b->goodangle)); + } + b->goodangle *= b->goodangle; + if (b->refine && b->noiterationnum) { + printf( "Error: You cannot use the -I switch when refining a triangulation.\n"); + triexit(1); + } + /* Be careful not to allocate space for element area constraints that */ + /* will never be assigned any value (other than the default -1.0). */ + if (!b->refine && !b->poly) { + b->vararea = 0; + } + /* Be careful not to add an extra attribute to each element unless the */ + /* input supports it (PSLG in, but not refining a preexisting mesh). */ + if (b->refine || !b->poly) { + b->regionattrib = 0; + } + /* Regular/weighted triangulations are incompatible with PSLGs */ + /* and meshing. */ + if (b->weighted && (b->poly || b->quality)) { + b->weighted = 0; + if (!b->quiet) { + printf("Warning: weighted triangulations (-w, -W) are incompatible\n"); + printf(" with PSLGs (-p) and meshing (-q, -a, -u). Weights ignored.\n"); + } + } + if (b->jettison && b->nonodewritten && !b->quiet) { + printf("Warning: -j and -N switches are somewhat incompatible.\n"); + printf(" If any vertices are jettisoned, you will need the output\n"); + printf(" .node file to reconstruct the new node indices."); + } +} + +/** **/ +/** **/ +/********* User interaction routines begin here *********/ + +/********* Memory management routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* poolzero() Set all of a pool's fields to zero. */ +/* */ +/* This procedure should never be called on a pool that has any memory */ +/* allocated to it, as that memory would leak. */ +/* */ +/*****************************************************************************/ + +void poolzero(struct memorypool *pool) { + pool->firstblock = (VOID **) NULL; + pool->nowblock = (VOID **) NULL; + pool->nextitem = (VOID *) NULL; + pool->deaditemstack = (VOID *) NULL; + pool->pathblock = (VOID **) NULL; + pool->pathitem = (VOID *) NULL; + pool->alignbytes = 0; + pool->itembytes = 0; + pool->itemsperblock = 0; + pool->itemsfirstblock = 0; + pool->items = 0; + pool->maxitems = 0; + pool->unallocateditems = 0; + pool->pathitemsleft = 0; +} + +/*****************************************************************************/ +/* */ +/* poolrestart() Deallocate all items in a pool. */ +/* */ +/* The pool is returned to its starting state, except that no memory is */ +/* freed to the operating system. Rather, the previously allocated blocks */ +/* are ready to be reused. */ +/* */ +/*****************************************************************************/ + +void poolrestart(struct memorypool *pool) { + unsigned long alignptr; + + pool->items = 0; + pool->maxitems = 0; + + /* Set the currently active block. */ + pool->nowblock = pool->firstblock; + /* Find the first item in the pool. Increment by the size of (VOID *). */ + alignptr = (unsigned long) (pool->nowblock + 1); + /* Align the item on an `alignbytes'-byte boundary. */ + pool->nextitem = (VOID *) (alignptr + (unsigned long) pool->alignbytes + - (alignptr % (unsigned long) pool->alignbytes)); + /* There are lots of unallocated items left in this block. */ + pool->unallocateditems = pool->itemsfirstblock; + /* The stack of deallocated items is empty. */ + pool->deaditemstack = (VOID *) NULL; +} + +/*****************************************************************************/ +/* */ +/* poolinit() Initialize a pool of memory for allocation of items. */ +/* */ +/* This routine initializes the machinery for allocating items. A `pool' */ +/* is created whose records have size at least `bytecount'. Items will be */ +/* allocated in `itemcount'-item blocks. Each item is assumed to be a */ +/* collection of words, and either pointers or floating-point values are */ +/* assumed to be the "primary" word type. (The "primary" word type is used */ +/* to determine alignment of items.) If `alignment' isn't zero, all items */ +/* will be `alignment'-byte aligned in memory. `alignment' must be either */ +/* a multiple or a factor of the primary word size; powers of two are safe. */ +/* `alignment' is normally used to create a few unused bits at the bottom */ +/* of each item's pointer, in which information may be stored. */ +/* */ +/* Don't change this routine unless you understand it. */ +/* */ +/*****************************************************************************/ + +void poolinit(struct memorypool *pool, int bytecount, int itemcount, int firstitemcount, + int alignment) { + /* Find the proper alignment, which must be at least as large as: */ + /* - The parameter `alignment'. */ + /* - sizeof(VOID *), so the stack of dead items can be maintained */ + /* without unaligned accesses. */ + if (alignment > sizeof(VOID *)) { + pool->alignbytes = alignment; + } + else { + pool->alignbytes = sizeof(VOID *); + } + pool->itembytes = ((bytecount - 1) / pool->alignbytes + 1) * pool->alignbytes; + pool->itemsperblock = itemcount; + if (firstitemcount == 0) { + pool->itemsfirstblock = itemcount; + } + else { + pool->itemsfirstblock = firstitemcount; + } + + /* Allocate a block of items. Space for `itemsfirstblock' items and one */ + /* pointer (to point to the next block) are allocated, as well as space */ + /* to ensure alignment of the items. */ + pool->firstblock = (VOID **) trimalloc( + pool->itemsfirstblock * pool->itembytes + (int) sizeof(VOID *) + pool->alignbytes); + /* Set the next block pointer to NULL. */ + *(pool->firstblock) = (VOID *) NULL; + poolrestart(pool); +} + +/*****************************************************************************/ +/* */ +/* pooldeinit() Free to the operating system all memory taken by a pool. */ +/* */ +/*****************************************************************************/ + +void pooldeinit(struct memorypool *pool) { + while (pool->firstblock != (VOID **) NULL) { + pool->nowblock = (VOID **) *(pool->firstblock); + trifree((VOID *) pool->firstblock); + pool->firstblock = pool->nowblock; + } +} + +/*****************************************************************************/ +/* */ +/* poolalloc() Allocate space for an item. */ +/* */ +/*****************************************************************************/ + +VOID *poolalloc(struct memorypool *pool) { + VOID *newitem; + VOID **newblock; + unsigned long alignptr; + + /* First check the linked list of dead items. If the list is not */ + /* empty, allocate an item from the list rather than a fresh one. */ + if (pool->deaditemstack != (VOID *) NULL) { + newitem = pool->deaditemstack; /* Take first item in list. */ + pool->deaditemstack = *(VOID **) pool->deaditemstack; + } + else { + /* Check if there are any free items left in the current block. */ + if (pool->unallocateditems == 0) { + /* Check if another block must be allocated. */ + if (*(pool->nowblock) == (VOID *) NULL) { + /* Allocate a new block of items, pointed to by the previous block. */ + newblock = (VOID **) trimalloc( + pool->itemsperblock * pool->itembytes + (int) sizeof(VOID *) + pool->alignbytes); + *(pool->nowblock) = (VOID *) newblock; + /* The next block pointer is NULL. */ + *newblock = (VOID *) NULL; + } + + /* Move to the new block. */ + pool->nowblock = (VOID **) *(pool->nowblock); + /* Find the first item in the block. */ + /* Increment by the size of (VOID *). */ + alignptr = (unsigned long) (pool->nowblock + 1); + /* Align the item on an `alignbytes'-byte boundary. */ + pool->nextitem = (VOID *) (alignptr + (unsigned long) pool->alignbytes + - (alignptr % (unsigned long) pool->alignbytes)); + /* There are lots of unallocated items left in this block. */ + pool->unallocateditems = pool->itemsperblock; + } + + /* Allocate a new item. */ + newitem = pool->nextitem; + /* Advance `nextitem' pointer to next free item in block. */ + pool->nextitem = (VOID *) ((char *) pool->nextitem + pool->itembytes); + pool->unallocateditems--; + pool->maxitems++; + } + pool->items++; + return newitem; +} + +/*****************************************************************************/ +/* */ +/* pooldealloc() Deallocate space for an item. */ +/* */ +/* The deallocated space is stored in a queue for later reuse. */ +/* */ +/*****************************************************************************/ + +void pooldealloc(struct memorypool *pool, VOID *dyingitem) { + /* Push freshly killed item onto stack. */ + *((VOID **) dyingitem) = pool->deaditemstack; + pool->deaditemstack = dyingitem; + pool->items--; +} + +/*****************************************************************************/ +/* */ +/* traversalinit() Prepare to traverse the entire list of items. */ +/* */ +/* This routine is used in conjunction with traverse(). */ +/* */ +/*****************************************************************************/ + +void traversalinit(struct memorypool *pool) { + unsigned long alignptr; + + /* Begin the traversal in the first block. */ + pool->pathblock = pool->firstblock; + /* Find the first item in the block. Increment by the size of (VOID *). */ + alignptr = (unsigned long) (pool->pathblock + 1); + /* Align with item on an `alignbytes'-byte boundary. */ + pool->pathitem = (VOID *) (alignptr + (unsigned long) pool->alignbytes + - (alignptr % (unsigned long) pool->alignbytes)); + /* Set the number of items left in the current block. */ + pool->pathitemsleft = pool->itemsfirstblock; +} + +/*****************************************************************************/ +/* */ +/* traverse() Find the next item in the list. */ +/* */ +/* This routine is used in conjunction with traversalinit(). Be forewarned */ +/* that this routine successively returns all items in the list, including */ +/* deallocated ones on the deaditemqueue. It's up to you to figure out */ +/* which ones are actually dead. Why? I don't want to allocate extra */ +/* space just to demarcate dead items. It can usually be done more */ +/* space-efficiently by a routine that knows something about the structure */ +/* of the item. */ +/* */ +/*****************************************************************************/ + +VOID *traverse(struct memorypool *pool) { + VOID *newitem; + unsigned long alignptr; + + /* Stop upon exhausting the list of items. */ + if (pool->pathitem == pool->nextitem) { + return (VOID *) NULL; + } + + /* Check whether any untraversed items remain in the current block. */ + if (pool->pathitemsleft == 0) { + /* Find the next block. */ + pool->pathblock = (VOID **) *(pool->pathblock); + /* Find the first item in the block. Increment by the size of (VOID *). */ + alignptr = (unsigned long) (pool->pathblock + 1); + /* Align with item on an `alignbytes'-byte boundary. */ + pool->pathitem = (VOID *) (alignptr + (unsigned long) pool->alignbytes + - (alignptr % (unsigned long) pool->alignbytes)); + /* Set the number of items left in the current block. */ + pool->pathitemsleft = pool->itemsperblock; + } + + newitem = pool->pathitem; + /* Find the next item in the block. */ + pool->pathitem = (VOID *) ((char *) pool->pathitem + pool->itembytes); + pool->pathitemsleft--; + return newitem; +} + +/*****************************************************************************/ +/* */ +/* dummyinit() Initialize the triangle that fills "outer space" and the */ +/* omnipresent subsegment. */ +/* */ +/* The triangle that fills "outer space," called `dummytri', is pointed to */ +/* by every triangle and subsegment on a boundary (be it outer or inner) of */ +/* the triangulation. Also, `dummytri' points to one of the triangles on */ +/* the convex hull (until the holes and concavities are carved), making it */ +/* possible to find a starting triangle for point location. */ +/* */ +/* The omnipresent subsegment, `dummysub', is pointed to by every triangle */ +/* or subsegment that doesn't have a full complement of real subsegments */ +/* to point to. */ +/* */ +/* `dummytri' and `dummysub' are generally required to fulfill only a few */ +/* invariants: their vertices must remain NULL and `dummytri' must always */ +/* be bonded (at offset zero) to some triangle on the convex hull of the */ +/* mesh, via a boundary edge. Otherwise, the connections of `dummytri' and */ +/* `dummysub' may change willy-nilly. This makes it possible to avoid */ +/* writing a good deal of special-case code (in the edge flip, for example) */ +/* for dealing with the boundary of the mesh, places where no subsegment is */ +/* present, and so forth. Other entities are frequently bonded to */ +/* `dummytri' and `dummysub' as if they were real mesh entities, with no */ +/* harm done. */ +/* */ +/*****************************************************************************/ + +void dummyinit(struct mesh *m, struct behavior *b, int trianglebytes, int subsegbytes) { + unsigned long alignptr; + + /* Set up `dummytri', the `triangle' that occupies "outer space." */ + m->dummytribase = (triangle *) trimalloc(trianglebytes + m->triangles.alignbytes); + /* Align `dummytri' on a `triangles.alignbytes'-byte boundary. */ + alignptr = (unsigned long) m->dummytribase; + m->dummytri = (triangle *) (alignptr + (unsigned long) m->triangles.alignbytes + - (alignptr % (unsigned long) m->triangles.alignbytes)); + /* Initialize the three adjoining triangles to be "outer space." These */ + /* will eventually be changed by various bonding operations, but their */ + /* values don't really matter, as long as they can legally be */ + /* dereferenced. */ + m->dummytri[0] = (triangle) m->dummytri; + m->dummytri[1] = (triangle) m->dummytri; + m->dummytri[2] = (triangle) m->dummytri; + /* Three NULL vertices. */ + m->dummytri[3] = (triangle) NULL; + m->dummytri[4] = (triangle) NULL; + m->dummytri[5] = (triangle) NULL; + + if (b->usesegments) { + /* Set up `dummysub', the omnipresent subsegment pointed to by any */ + /* triangle side or subsegment end that isn't attached to a real */ + /* subsegment. */ + m->dummysubbase = (subseg *) trimalloc(subsegbytes + m->subsegs.alignbytes); + /* Align `dummysub' on a `subsegs.alignbytes'-byte boundary. */ + alignptr = (unsigned long) m->dummysubbase; + m->dummysub = (subseg *) (alignptr + (unsigned long) m->subsegs.alignbytes + - (alignptr % (unsigned long) m->subsegs.alignbytes)); + /* Initialize the two adjoining subsegments to be the omnipresent */ + /* subsegment. These will eventually be changed by various bonding */ + /* operations, but their values don't really matter, as long as they */ + /* can legally be dereferenced. */ + m->dummysub[0] = (subseg) m->dummysub; + m->dummysub[1] = (subseg) m->dummysub; + /* Four NULL vertices. */ + m->dummysub[2] = (subseg) NULL; + m->dummysub[3] = (subseg) NULL; + m->dummysub[4] = (subseg) NULL; + m->dummysub[5] = (subseg) NULL; + /* Initialize the two adjoining triangles to be "outer space." */ + m->dummysub[6] = (subseg) m->dummytri; + m->dummysub[7] = (subseg) m->dummytri; + /* Set the boundary marker to zero. */ + *(int *) (m->dummysub + 8) = 0; + + /* Initialize the three adjoining subsegments of `dummytri' to be */ + /* the omnipresent subsegment. */ + m->dummytri[6] = (triangle) m->dummysub; + m->dummytri[7] = (triangle) m->dummysub; + m->dummytri[8] = (triangle) m->dummysub; + } +} + +/*****************************************************************************/ +/* */ +/* initializevertexpool() Calculate the size of the vertex data structure */ +/* and initialize its memory pool. */ +/* */ +/* This routine also computes the `vertexmarkindex' and `vertex2triindex' */ +/* indices used to find values within each vertex. */ +/* */ +/*****************************************************************************/ + +void initializevertexpool(struct mesh *m, struct behavior *b) { + int vertexsize; + + /* The index within each vertex at which the boundary marker is found, */ + /* followed by the vertex type. Ensure the vertex marker is aligned to */ + /* a sizeof(int)-byte address. */ + m->vertexmarkindex = ((m->mesh_dim + m->nextras) * sizeof(REAL) + sizeof(int) - 1) / sizeof(int); + vertexsize = (m->vertexmarkindex + 2) * sizeof(int); + if (b->poly) { + /* The index within each vertex at which a triangle pointer is found. */ + /* Ensure the pointer is aligned to a sizeof(triangle)-byte address. */ + m->vertex2triindex = (vertexsize + sizeof(triangle) - 1) / sizeof(triangle); + vertexsize = (m->vertex2triindex + 1) * sizeof(triangle); + } + + /* Initialize the pool of vertices. */ + poolinit(&m->vertices, vertexsize, VERTEXPERBLOCK, + m->invertices > VERTEXPERBLOCK ? m->invertices : VERTEXPERBLOCK, + sizeof(REAL)); +} + +/*****************************************************************************/ +/* */ +/* initializetrisubpools() Calculate the sizes of the triangle and */ +/* subsegment data structures and initialize */ +/* their memory pools. */ +/* */ +/* This routine also computes the `highorderindex', `elemattribindex', and */ +/* `areaboundindex' indices used to find values within each triangle. */ +/* */ +/*****************************************************************************/ + +void initializetrisubpools(struct mesh *m, struct behavior *b) { + int trisize; + + /* The index within each triangle at which the extra nodes (above three) */ + /* associated with high order elements are found. There are three */ + /* pointers to other triangles, three pointers to corners, and possibly */ + /* three pointers to subsegments before the extra nodes. */ + m->highorderindex = 6 + (b->usesegments * 3); + /* The number of bytes occupied by a triangle. */ + trisize = ((b->order + 1) * (b->order + 2) / 2 + (m->highorderindex - 3)) * sizeof(triangle); + /* The index within each triangle at which its attributes are found, */ + /* where the index is measured in REALs. */ + m->elemattribindex = (trisize + sizeof(REAL) - 1) / sizeof(REAL); + /* The index within each triangle at which the maximum area constraint */ + /* is found, where the index is measured in REALs. Note that if the */ + /* `regionattrib' flag is set, an additional attribute will be added. */ + m->areaboundindex = m->elemattribindex + m->eextras + b->regionattrib; + /* If triangle attributes or an area bound are needed, increase the number */ + /* of bytes occupied by a triangle. */ + if (b->vararea) { + trisize = (m->areaboundindex + 1) * sizeof(REAL); + } + else if (m->eextras + b->regionattrib > 0) { + trisize = m->areaboundindex * sizeof(REAL); + } + /* If a Voronoi diagram or triangle neighbor graph is requested, make */ + /* sure there's room to store an integer index in each triangle. This */ + /* integer index can occupy the same space as the subsegment pointers */ + /* or attributes or area constraint or extra nodes. */ + if ((b->voronoi || b->neighbors) && (trisize < 6 * sizeof(triangle) + sizeof(int))) { + trisize = 6 * sizeof(triangle) + sizeof(int); + } + + /* Having determined the memory size of a triangle, initialize the pool. */ + poolinit(&m->triangles, trisize, TRIPERBLOCK, + (2 * m->invertices - 2) > TRIPERBLOCK ? (2 * m->invertices - 2) : TRIPERBLOCK, 4); + + if (b->usesegments) { + /* Initialize the pool of subsegments. Take into account all eight */ + /* pointers and one boundary marker. */ + poolinit(&m->subsegs, 8 * sizeof(triangle) + sizeof(int), SUBSEGPERBLOCK, SUBSEGPERBLOCK, 4); + + /* Initialize the "outer space" triangle and omnipresent subsegment. */ + dummyinit(m, b, m->triangles.itembytes, m->subsegs.itembytes); + } + else { + /* Initialize the "outer space" triangle. */ + dummyinit(m, b, m->triangles.itembytes, 0); + } +} + +/*****************************************************************************/ +/* */ +/* triangledealloc() Deallocate space for a triangle, marking it dead. */ +/* */ +/*****************************************************************************/ + +void triangledealloc(struct mesh *m, triangle *dyingtriangle) { + /* Mark the triangle as dead. This makes it possible to detect dead */ + /* triangles when traversing the list of all triangles. */ + killtri(dyingtriangle); + pooldealloc(&m->triangles, (VOID *) dyingtriangle); +} + +/*****************************************************************************/ +/* */ +/* triangletraverse() Traverse the triangles, skipping dead ones. */ +/* */ +/*****************************************************************************/ + +triangle *triangletraverse(struct mesh *m) { + triangle *newtriangle; + + do { + newtriangle = (triangle *) traverse(&m->triangles); + if (newtriangle == (triangle *) NULL) { + return (triangle *) NULL; + } + } while (deadtri(newtriangle)); /* Skip dead ones. */ + return newtriangle; +} + +/*****************************************************************************/ +/* */ +/* subsegdealloc() Deallocate space for a subsegment, marking it dead. */ +/* */ +/*****************************************************************************/ + +void subsegdealloc(struct mesh *m, subseg *dyingsubseg) { + /* Mark the subsegment as dead. This makes it possible to detect dead */ + /* subsegments when traversing the list of all subsegments. */ + killsubseg(dyingsubseg); + pooldealloc(&m->subsegs, (VOID *) dyingsubseg); +} + +/*****************************************************************************/ +/* */ +/* subsegtraverse() Traverse the subsegments, skipping dead ones. */ +/* */ +/*****************************************************************************/ + +subseg *subsegtraverse(struct mesh *m) { + subseg *newsubseg; + + do { + newsubseg = (subseg *) traverse(&m->subsegs); + if (newsubseg == (subseg *) NULL) { + return (subseg *) NULL; + } + } while (deadsubseg(newsubseg)); /* Skip dead ones. */ + return newsubseg; +} + +/*****************************************************************************/ +/* */ +/* vertexdealloc() Deallocate space for a vertex, marking it dead. */ +/* */ +/*****************************************************************************/ + +void vertexdealloc(struct mesh *m, vertex dyingvertex) { + /* Mark the vertex as dead. This makes it possible to detect dead */ + /* vertices when traversing the list of all vertices. */ + setvertextype(dyingvertex, DEADVERTEX); + pooldealloc(&m->vertices, (VOID *) dyingvertex); +} + +/*****************************************************************************/ +/* */ +/* vertextraverse() Traverse the vertices, skipping dead ones. */ +/* */ +/*****************************************************************************/ + +vertex vertextraverse(struct mesh *m) { + vertex newvertex; + + do { + newvertex = (vertex) traverse(&m->vertices); + if (newvertex == (vertex) NULL) { + return (vertex) NULL; + } + } while (vertextype(newvertex) == DEADVERTEX); /* Skip dead ones. */ + return newvertex; +} + +/*****************************************************************************/ +/* */ +/* getvertex() Get a specific vertex, by number, from the list. */ +/* */ +/* The first vertex is number 'firstnumber'. */ +/* */ +/* Note that this takes O(n) time (with a small constant, if VERTEXPERBLOCK */ +/* is large). I don't care to take the trouble to make it work in constant */ +/* time. */ +/* */ +/*****************************************************************************/ + +vertex getvertex(struct mesh *m, struct behavior *b, int number) { + VOID **getblock; + char *foundvertex; + unsigned long alignptr; + int current; + + getblock = m->vertices.firstblock; + current = b->firstnumber; + + /* Find the right block. */ + if (current + m->vertices.itemsfirstblock <= number) { + getblock = (VOID **) *getblock; + current += m->vertices.itemsfirstblock; + while (current + m->vertices.itemsperblock <= number) { + getblock = (VOID **) *getblock; + current += m->vertices.itemsperblock; + } + } + + /* Now find the right vertex. */ + alignptr = (unsigned long) (getblock + 1); + foundvertex = (char *) (alignptr + (unsigned long) m->vertices.alignbytes + - (alignptr % (unsigned long) m->vertices.alignbytes)); + return (vertex) (foundvertex + m->vertices.itembytes * (number - current)); +} + +/*****************************************************************************/ +/* */ +/* triangledeinit() Free all remaining allocated memory. */ +/* */ +/*****************************************************************************/ + +void triangledeinit(struct mesh *m, struct behavior *b) { + pooldeinit(&m->triangles); + trifree((VOID *) m->dummytribase); + if (b->usesegments) { + pooldeinit(&m->subsegs); + trifree((VOID *) m->dummysubbase); + } + pooldeinit(&m->vertices); +#ifndef CDT_ONLY + if (b->quality) + { + pooldeinit(&m->badsubsegs); + if ((b->minangle > 0.0) || b->vararea || b->fixedarea || b->usertest) + { + pooldeinit(&m->badtriangles); + pooldeinit(&m->flipstackers); + } + } +#endif /* not CDT_ONLY */ +} + +/** **/ +/** **/ +/********* Memory management routines end here *********/ + +/********* Constructors begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* maketriangle() Create a new triangle with orientation zero. */ +/* */ +/*****************************************************************************/ + +void maketriangle(struct mesh *m, struct behavior *b, struct otri *newotri) { + int i; + + newotri->tri = (triangle *) poolalloc(&m->triangles); + /* Initialize the three adjoining triangles to be "outer space". */ + newotri->tri[0] = (triangle) m->dummytri; + newotri->tri[1] = (triangle) m->dummytri; + newotri->tri[2] = (triangle) m->dummytri; + /* Three NULL vertices. */ + newotri->tri[3] = (triangle) NULL; + newotri->tri[4] = (triangle) NULL; + newotri->tri[5] = (triangle) NULL; + if (b->usesegments) { + /* Initialize the three adjoining subsegments to be the omnipresent */ + /* subsegment. */ + newotri->tri[6] = (triangle) m->dummysub; + newotri->tri[7] = (triangle) m->dummysub; + newotri->tri[8] = (triangle) m->dummysub; + } + for (i = 0; i < m->eextras; i++) { + setelemattribute(*newotri, i, 0.0); + } + if (b->vararea) { + setareabound(*newotri, -1.0); + } + + newotri->orient = 0; +} + +/*****************************************************************************/ +/* */ +/* makesubseg() Create a new subsegment with orientation zero. */ +/* */ +/*****************************************************************************/ + +void makesubseg(struct mesh *m, struct osub *newsubseg) { + newsubseg->ss = (subseg *) poolalloc(&m->subsegs); + /* Initialize the two adjoining subsegments to be the omnipresent */ + /* subsegment. */ + newsubseg->ss[0] = (subseg) m->dummysub; + newsubseg->ss[1] = (subseg) m->dummysub; + /* Four NULL vertices. */ + newsubseg->ss[2] = (subseg) NULL; + newsubseg->ss[3] = (subseg) NULL; + newsubseg->ss[4] = (subseg) NULL; + newsubseg->ss[5] = (subseg) NULL; + /* Initialize the two adjoining triangles to be "outer space." */ + newsubseg->ss[6] = (subseg) m->dummytri; + newsubseg->ss[7] = (subseg) m->dummytri; + /* Set the boundary marker to zero. */ + setmark(*newsubseg, 0); + + newsubseg->ssorient = 0; +} + +/** **/ +/** **/ +/********* Constructors end here *********/ + +/********* Geometric primitives begin here *********/ +/** **/ +/** **/ + +/* The adaptive exact arithmetic geometric predicates implemented herein are */ +/* described in detail in my paper, "Adaptive Precision Floating-Point */ +/* Arithmetic and Fast Robust Geometric Predicates." See the header for a */ +/* full citation. */ + +/* Which of the following two methods of finding the absolute values is */ +/* fastest is compiler-dependent. A few compilers can inline and optimize */ +/* the fabs() call; but most will incur the overhead of a function call, */ +/* which is disastrously slow. A faster way on IEEE machines might be to */ +/* mask the appropriate bit, but that's difficult to do in C without */ +/* forcing the value to be stored to memory (rather than be kept in the */ +/* register to which the optimizer assigned it). */ + +#define Absolute(a) ((a) >= 0.0 ? (a) : -(a)) +/* #define Absolute(a) fabs(a) */ + +/* Many of the operations are broken up into two pieces, a main part that */ +/* performs an approximate operation, and a "tail" that computes the */ +/* roundoff error of that operation. */ +/* */ +/* The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(), */ +/* Split(), and Two_Product() are all implemented as described in the */ +/* reference. Each of these macros requires certain variables to be */ +/* defined in the calling routine. The variables `bvirt', `c', `abig', */ +/* `_i', `_j', `_k', `_l', `_m', and `_n' are declared `' because */ +/* they store the result of an operation that may incur roundoff error. */ +/* The input parameter `x' (or the highest numbered `x_' parameter) must */ +/* also be declared `'. */ + +#define Fast_Two_Sum_Tail(a, b, x, y) \ + bvirt = x - a; \ + y = b - bvirt + +#define Fast_Two_Sum(a, b, x, y) \ + x = (REAL) (a + b); \ + Fast_Two_Sum_Tail(a, b, x, y) + +#define Two_Sum_Tail(a, b, x, y) \ + bvirt = (REAL) (x - a); \ + avirt = x - bvirt; \ + bround = b - bvirt; \ + around = a - avirt; \ + y = around + bround + +#define Two_Sum(a, b, x, y) \ + x = (REAL) (a + b); \ + Two_Sum_Tail(a, b, x, y) + +#define Two_Diff_Tail(a, b, x, y) \ + bvirt = (REAL) (a - x); \ + avirt = x + bvirt; \ + bround = bvirt - b; \ + around = a - avirt; \ + y = around + bround + +#define Two_Diff(a, b, x, y) \ + x = (REAL) (a - b); \ + Two_Diff_Tail(a, b, x, y) + +#define Split(a, ahi, alo) \ + c = (REAL) (splitter * a); \ + abig = (REAL) (c - a); \ + ahi = c - abig; \ + alo = a - ahi + +#define Two_Product_Tail(a, b, x, y) \ + Split(a, ahi, alo); \ + Split(b, bhi, blo); \ + err1 = x - (ahi * bhi); \ + err2 = err1 - (alo * bhi); \ + err3 = err2 - (ahi * blo); \ + y = (alo * blo) - err3 + +#define Two_Product(a, b, x, y) \ + x = (REAL) (a * b); \ + Two_Product_Tail(a, b, x, y) + +/* Two_Product_Presplit() is Two_Product() where one of the inputs has */ +/* already been split. Avoids redundant splitting. */ + +#define Two_Product_Presplit(a, b, bhi, blo, x, y) \ + x = (REAL) (a * b); \ + Split(a, ahi, alo); \ + err1 = x - (ahi * bhi); \ + err2 = err1 - (alo * bhi); \ + err3 = err2 - (ahi * blo); \ + y = (alo * blo) - err3 + +/* Square() can be done more quickly than Two_Product(). */ + +#define Square_Tail(a, x, y) \ + Split(a, ahi, alo); \ + err1 = x - (ahi * ahi); \ + err3 = err1 - ((ahi + ahi) * alo); \ + y = (alo * alo) - err3 + +#define Square(a, x, y) \ + x = (REAL) (a * a); \ + Square_Tail(a, x, y) + +/* Macros for summing expansions of various fixed lengths. These are all */ +/* unrolled versions of Expansion_Sum(). */ + +#define Two_One_Sum(a1, a0, b, x2, x1, x0) \ + Two_Sum(a0, b , _i, x0); \ + Two_Sum(a1, _i, x2, x1) + +#define Two_One_Diff(a1, a0, b, x2, x1, x0) \ + Two_Diff(a0, b , _i, x0); \ + Two_Sum( a1, _i, x2, x1) + +#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \ + Two_One_Sum(a1, a0, b0, _j, _0, x0); \ + Two_One_Sum(_j, _0, b1, x3, x2, x1) + +#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \ + Two_One_Diff(a1, a0, b0, _j, _0, x0); \ + Two_One_Diff(_j, _0, b1, x3, x2, x1) + +/* Macro for multiplying a two-component expansion by a single component. */ + +#define Two_One_Product(a1, a0, b, x3, x2, x1, x0) \ + Split(b, bhi, blo); \ + Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \ + Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x1); \ + Fast_Two_Sum(_j, _k, x3, x2) + +/*****************************************************************************/ +/* */ +/* exactinit() Initialize the variables used for exact arithmetic. */ +/* */ +/* `epsilon' is the largest power of two such that 1.0 + epsilon = 1.0 in */ +/* floating-point arithmetic. `epsilon' bounds the relative roundoff */ +/* error. It is used for floating-point error analysis. */ +/* */ +/* `splitter' is used to split floating-point numbers into two half- */ +/* length significands for exact multiplication. */ +/* */ +/* I imagine that a highly optimizing compiler might be too smart for its */ +/* own good, and somehow cause this routine to fail, if it pretends that */ +/* floating-point arithmetic is too much like real arithmetic. */ +/* */ +/* Don't change this routine unless you fully understand it. */ +/* */ +/*****************************************************************************/ + +void exactinit() { + REAL half; + REAL check, lastcheck; + int every_other; +#ifdef LINUX + int cword; +#endif /* LINUX */ + +#ifdef CPU86 +#ifdef SINGLE + _control87(_PC_24, _MCW_PC); /* Set FPU control word for single precision. */ +#else /* not SINGLE */ + _control87(_PC_53, _MCW_PC); /* Set FPU control word for double precision. */ +#endif /* not SINGLE */ +#endif /* CPU86 */ +#ifdef LINUX +#ifdef SINGLE + /* cword = 4223; */ + cword = 4210; /* set FPU control word for single precision */ +#else /* not SINGLE */ + /* cword = 4735; */ + cword = 4722; /* set FPU control word for double precision */ +#endif /* not SINGLE */ + _FPU_SETCW(cword); +#endif /* LINUX */ + + every_other = 1; + half = 0.5; + epsilon = 1.0; + splitter = 1.0; + check = 1.0; + /* Repeatedly divide `epsilon' by two until it is too small to add to */ + /* one without causing roundoff. (Also check if the sum is equal to */ + /* the previous sum, for machines that round up instead of using exact */ + /* rounding. Not that these routines will work on such machines.) */ + do { + lastcheck = check; + epsilon *= half; + if (every_other) { + splitter *= 2.0; + } + every_other = !every_other; + check = 1.0 + epsilon; + } while ((check != 1.0) && (check != lastcheck)); + splitter += 1.0; + /* Error bounds for orientation and incircle tests. */ + resulterrbound = (3.0 + 8.0 * epsilon) * epsilon; + ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon; + ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon; + ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon; + iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon; + iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon; + iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon; + o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon; + o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon; + o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon; +} + +/*****************************************************************************/ +/* */ +/* fast_expansion_sum_zeroelim() Sum two expansions, eliminating zero */ +/* components from the output expansion. */ +/* */ +/* Sets h = e + f. See my Robust Predicates paper for details. */ +/* */ +/* If round-to-even is used (as with IEEE 754), maintains the strongly */ +/* nonoverlapping property. (That is, if e is strongly nonoverlapping, h */ +/* will be also.) Does NOT maintain the nonoverlapping or nonadjacent */ +/* properties. */ +/* */ +/*****************************************************************************/ + +int fast_expansion_sum_zeroelim(int elen, REAL *e, int flen, REAL *f, REAL *h) { + REAL Q; + REAL Qnew; + REAL hh; + REAL bvirt; + REAL avirt, bround, around; + int eindex, findex, hindex; + REAL enow, fnow; + + enow = e[0]; + fnow = f[0]; + eindex = findex = 0; + if ((fnow > enow) == (fnow > -enow)) { + Q = enow; + enow = e[++eindex]; + } + else { + Q = fnow; + fnow = f[++findex]; + } + hindex = 0; + if ((eindex < elen) && (findex < flen)) { + if ((fnow > enow) == (fnow > -enow)) { + Fast_Two_Sum(enow, Q, Qnew, hh); + enow = e[++eindex]; + } + else { + Fast_Two_Sum(fnow, Q, Qnew, hh); + fnow = f[++findex]; + } + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + while ((eindex < elen) && (findex < flen)) { + if ((fnow > enow) == (fnow > -enow)) { + Two_Sum(Q, enow, Qnew, hh); + enow = e[++eindex]; + } + else { + Two_Sum(Q, fnow, Qnew, hh); + fnow = f[++findex]; + } + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + } + while (eindex < elen) { + Two_Sum(Q, enow, Qnew, hh); + enow = e[++eindex]; + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + while (findex < flen) { + Two_Sum(Q, fnow, Qnew, hh); + fnow = f[++findex]; + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + if ((Q != 0.0) || (hindex == 0)) { + h[hindex++] = Q; + } + return hindex; +} + +/*****************************************************************************/ +/* */ +/* scale_expansion_zeroelim() Multiply an expansion by a scalar, */ +/* eliminating zero components from the */ +/* output expansion. */ +/* */ +/* Sets h = be. See my Robust Predicates paper for details. */ +/* */ +/* Maintains the nonoverlapping property. If round-to-even is used (as */ +/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */ +/* properties as well. (That is, if e has one of these properties, so */ +/* will h.) */ +/* */ +/*****************************************************************************/ + +int scale_expansion_zeroelim(int elen, REAL *e, REAL b, REAL *h) { + REAL Q, sum; + REAL hh; + REAL product1; + REAL product0; + int eindex, hindex; + REAL enow; + REAL bvirt; + REAL avirt, bround, around; + REAL c; + REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + + Split(b, bhi, blo); + Two_Product_Presplit(e[0], b, bhi, blo, Q, hh); + hindex = 0; + if (hh != 0) { + h[hindex++] = hh; + } + for (eindex = 1; eindex < elen; eindex++) { + enow = e[eindex]; + Two_Product_Presplit(enow, b, bhi, blo, product1, product0); + Two_Sum(Q, product0, sum, hh); + if (hh != 0) { + h[hindex++] = hh; + } + Fast_Two_Sum(product1, sum, Q, hh); + if (hh != 0) { + h[hindex++] = hh; + } + } + if ((Q != 0.0) || (hindex == 0)) { + h[hindex++] = Q; + } + return hindex; +} + +/*****************************************************************************/ +/* */ +/* estimate() Produce a one-word estimate of an expansion's value. */ +/* */ +/* See my Robust Predicates paper for details. */ +/* */ +/*****************************************************************************/ + +REAL estimate(int elen, REAL *e) { + REAL Q; + int eindex; + + Q = e[0]; + for (eindex = 1; eindex < elen; eindex++) { + Q += e[eindex]; + } + return Q; +} + +/*****************************************************************************/ +/* */ +/* counterclockwise() Return a positive value if the points pa, pb, and */ +/* pc occur in counterclockwise order; a negative */ +/* value if they occur in clockwise order; and zero */ +/* if they are collinear. The result is also a rough */ +/* approximation of twice the signed area of the */ +/* triangle defined by the three points. */ +/* */ +/* Uses exact arithmetic if necessary to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. This determinant is */ +/* computed adaptively, in the sense that exact arithmetic is used only to */ +/* the degree it is needed to ensure that the returned value has the */ +/* correct sign. Hence, this function is usually quite fast, but will run */ +/* more slowly when the input points are collinear or nearly so. */ +/* */ +/* See my Robust Predicates paper for details. */ +/* */ +/*****************************************************************************/ + +REAL counterclockwiseadapt(vertex pa, vertex pb, vertex pc, REAL detsum) { + REAL acx, acy, bcx, bcy; + REAL acxtail, acytail, bcxtail, bcytail; + REAL detleft, detright; + REAL detlefttail, detrighttail; + REAL det, errbound; + REAL B[4], C1[8], C2[12], D[16]; + REAL B3; + int C1length, C2length, Dlength; + REAL u[4]; + REAL u3; + REAL s1, t1; + REAL s0, t0; + + REAL bvirt; + REAL avirt, bround, around; + REAL c; + REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + REAL _i, _j; + REAL _0; + + acx = (REAL) (pa[0] - pc[0]); + bcx = (REAL) (pb[0] - pc[0]); + acy = (REAL) (pa[1] - pc[1]); + bcy = (REAL) (pb[1] - pc[1]); + + Two_Product(acx, bcy, detleft, detlefttail); + Two_Product(acy, bcx, detright, detrighttail); + + Two_Two_Diff(detleft, detlefttail, detright, detrighttail, B3, B[2], B[1], B[0]); + B[3] = B3; + + det = estimate(4, B); + errbound = ccwerrboundB * detsum; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pc[0], acx, acxtail); + Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail); + Two_Diff_Tail(pa[1], pc[1], acy, acytail); + Two_Diff_Tail(pb[1], pc[1], bcy, bcytail); + + if ((acxtail == 0.0) && (acytail == 0.0) && (bcxtail == 0.0) && (bcytail == 0.0)) { + return det; + } + + errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det); + det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Product(acxtail, bcy, s1, s0); + Two_Product(acytail, bcx, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1); + + Two_Product(acx, bcytail, s1, s0); + Two_Product(acy, bcxtail, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2); + + Two_Product(acxtail, bcytail, s1, s0); + Two_Product(acytail, bcxtail, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D); + + return (D[Dlength - 1]); +} + +REAL counterclockwise(struct mesh *m, struct behavior *b, vertex pa, vertex pb, vertex pc) { + REAL detleft, detright, det; + REAL detsum, errbound; + + m->counterclockcount++; + + detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]); + detright = (pa[1] - pc[1]) * (pb[0] - pc[0]); + det = detleft - detright; + + if (b->noexact) { + return det; + } + + if (detleft > 0.0) { + if (detright <= 0.0) { + return det; + } + else { + detsum = detleft + detright; + } + } + else if (detleft < 0.0) { + if (detright >= 0.0) { + return det; + } + else { + detsum = -detleft - detright; + } + } + else { + return det; + } + + errbound = ccwerrboundA * detsum; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + return counterclockwiseadapt(pa, pb, pc, detsum); +} + +/*****************************************************************************/ +/* */ +/* incircle() Return a positive value if the point pd lies inside the */ +/* circle passing through pa, pb, and pc; a negative value if */ +/* it lies outside; and zero if the four points are cocircular.*/ +/* The points pa, pb, and pc must be in counterclockwise */ +/* order, or the sign of the result will be reversed. */ +/* */ +/* Uses exact arithmetic if necessary to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. This determinant is */ +/* computed adaptively, in the sense that exact arithmetic is used only to */ +/* the degree it is needed to ensure that the returned value has the */ +/* correct sign. Hence, this function is usually quite fast, but will run */ +/* more slowly when the input points are cocircular or nearly so. */ +/* */ +/* See my Robust Predicates paper for details. */ +/* */ +/*****************************************************************************/ + +REAL incircleadapt(vertex pa, vertex pb, vertex pc, vertex pd, REAL permanent) { + REAL adx, bdx, cdx, ady, bdy, cdy; + REAL det, errbound; + + REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; + REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0; + REAL bc[4], ca[4], ab[4]; + REAL bc3, ca3, ab3; + REAL axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32]; + int axbclen, axxbclen, aybclen, ayybclen, alen; + REAL bxca[8], bxxca[16], byca[8], byyca[16], bdet[32]; + int bxcalen, bxxcalen, bycalen, byycalen, blen; + REAL cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32]; + int cxablen, cxxablen, cyablen, cyyablen, clen; + REAL abdet[64]; + int ablen; + REAL fin1[1152], fin2[1152]; + REAL *finnow, *finother, *finswap; + int finlength; + + REAL adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail; + REAL adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1; + REAL adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0; + REAL aa[4], bb[4], cc[4]; + REAL aa3, bb3, cc3; + REAL ti1, tj1; + REAL ti0, tj0; + REAL u[4], v[4]; + REAL u3, v3; + REAL temp8[8], temp16a[16], temp16b[16], temp16c[16]; + REAL temp32a[32], temp32b[32], temp48[48], temp64[64]; + int temp8len, temp16alen, temp16blen, temp16clen; + int temp32alen, temp32blen, temp48len, temp64len; + REAL axtbb[8], axtcc[8], aytbb[8], aytcc[8]; + int axtbblen, axtcclen, aytbblen, aytcclen; + REAL bxtaa[8], bxtcc[8], bytaa[8], bytcc[8]; + int bxtaalen, bxtcclen, bytaalen, bytcclen; + REAL cxtaa[8], cxtbb[8], cytaa[8], cytbb[8]; + int cxtaalen, cxtbblen, cytaalen, cytbblen; + REAL axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8]; + int axtbclen, aytbclen, bxtcalen, bytcalen, cxtablen, cytablen; + REAL axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16]; + int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen; + REAL axtbctt[8], aytbctt[8], bxtcatt[8]; + REAL bytcatt[8], cxtabtt[8], cytabtt[8]; + int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen; + REAL abt[8], bct[8], cat[8]; + int abtlen, bctlen, catlen; + REAL abtt[4], bctt[4], catt[4]; + int abttlen, bcttlen, cattlen; + REAL abtt3, bctt3, catt3; + REAL negate; + + REAL bvirt; + REAL avirt, bround, around; + REAL c; + REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + REAL _i, _j; + REAL _0; + + adx = (REAL) (pa[0] - pd[0]); + bdx = (REAL) (pb[0] - pd[0]); + cdx = (REAL) (pc[0] - pd[0]); + ady = (REAL) (pa[1] - pd[1]); + bdy = (REAL) (pb[1] - pd[1]); + cdy = (REAL) (pc[1] - pd[1]); + + Two_Product(bdx, cdy, bdxcdy1, bdxcdy0); + Two_Product(cdx, bdy, cdxbdy1, cdxbdy0); + Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + axbclen = scale_expansion_zeroelim(4, bc, adx, axbc); + axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc); + aybclen = scale_expansion_zeroelim(4, bc, ady, aybc); + ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc); + alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet); + + Two_Product(cdx, ady, cdxady1, cdxady0); + Two_Product(adx, cdy, adxcdy1, adxcdy0); + Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); + ca[3] = ca3; + bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca); + bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca); + bycalen = scale_expansion_zeroelim(4, ca, bdy, byca); + byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca); + blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet); + + Two_Product(adx, bdy, adxbdy1, adxbdy0); + Two_Product(bdx, ady, bdxady1, bdxady0); + Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab); + cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab); + cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab); + cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab); + clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1); + + det = estimate(finlength, fin1); + errbound = iccerrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pd[0], adx, adxtail); + Two_Diff_Tail(pa[1], pd[1], ady, adytail); + Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); + Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); + Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); + Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); + if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) && (adytail == 0.0) + && (bdytail == 0.0) && (cdytail == 0.0)) { + return det; + } + + errbound = iccerrboundC * permanent + resulterrbound * Absolute(det); + det += ((adx * adx + ady * ady) + * ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail)) + + 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx)) + + ((bdx * bdx + bdy * bdy) + * ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail)) + + 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx)) + + ((cdx * cdx + cdy * cdy) + * ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail)) + + 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx)); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + finnow = fin1; + finother = fin2; + + if ((bdxtail != 0.0) || (bdytail != 0.0) || (cdxtail != 0.0) || (cdytail != 0.0)) { + Square(adx, adxadx1, adxadx0); + Square(ady, adyady1, adyady0); + Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]); + aa[3] = aa3; + } + if ((cdxtail != 0.0) || (cdytail != 0.0) || (adxtail != 0.0) || (adytail != 0.0)) { + Square(bdx, bdxbdx1, bdxbdx0); + Square(bdy, bdybdy1, bdybdy0); + Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]); + bb[3] = bb3; + } + if ((adxtail != 0.0) || (adytail != 0.0) || (bdxtail != 0.0) || (bdytail != 0.0)) { + Square(cdx, cdxcdx1, cdxcdx0); + Square(cdy, cdycdy1, cdycdy0); + Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]); + cc[3] = cc3; + } + + if (adxtail != 0.0) { + axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc); + temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx, temp16a); + + axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc); + temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b); + + axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb); + temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (adytail != 0.0) { + aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc); + temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady, temp16a); + + aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb); + temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b); + + aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc); + temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdxtail != 0.0) { + bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca); + temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx, temp16a); + + bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa); + temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b); + + bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc); + temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdytail != 0.0) { + bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca); + temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy, temp16a); + + bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc); + temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b); + + bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa); + temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdxtail != 0.0) { + cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab); + temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx, temp16a); + + cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb); + temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b); + + cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa); + temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdytail != 0.0) { + cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab); + temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy, temp16a); + + cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa); + temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b); + + cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb); + temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + if ((adxtail != 0.0) || (adytail != 0.0)) { + if ((bdxtail != 0.0) || (bdytail != 0.0) || (cdxtail != 0.0) || (cdytail != 0.0)) { + Two_Product(bdxtail, cdy, ti1, ti0); + Two_Product(bdx, cdytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -bdy; + Two_Product(cdxtail, negate, ti1, ti0); + negate = -bdytail; + Two_Product(cdx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct); + + Two_Product(bdxtail, cdytail, ti1, ti0); + Two_Product(cdxtail, bdytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]); + bctt[3] = bctt3; + bcttlen = 4; + } + else { + bct[0] = 0.0; + bctlen = 1; + bctt[0] = 0.0; + bcttlen = 1; + } + + if (adxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a); + axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct); + temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (bdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, + finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, + finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail, temp32a); + axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt); + temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx, temp16a); + temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, + temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (adytail != 0.0) { + temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a); + aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct); + temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail, temp32a); + aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt); + temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady, temp16a); + temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, + temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + if ((bdxtail != 0.0) || (bdytail != 0.0)) { + if ((cdxtail != 0.0) || (cdytail != 0.0) || (adxtail != 0.0) || (adytail != 0.0)) { + Two_Product(cdxtail, ady, ti1, ti0); + Two_Product(cdx, adytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -cdy; + Two_Product(adxtail, negate, ti1, ti0); + negate = -cdytail; + Two_Product(adx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat); + + Two_Product(cdxtail, adytail, ti1, ti0); + Two_Product(adxtail, cdytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]); + catt[3] = catt3; + cattlen = 4; + } + else { + cat[0] = 0.0; + catlen = 1; + catt[0] = 0.0; + cattlen = 1; + } + + if (bdxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a); + bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat); + temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (cdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, + finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (adytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, + finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail, temp32a); + bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt); + temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx, temp16a); + temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, + temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdytail != 0.0) { + temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a); + bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat); + temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail, temp32a); + bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt); + temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy, temp16a); + temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, + temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + if ((cdxtail != 0.0) || (cdytail != 0.0)) { + if ((adxtail != 0.0) || (adytail != 0.0) || (bdxtail != 0.0) || (bdytail != 0.0)) { + Two_Product(adxtail, bdy, ti1, ti0); + Two_Product(adx, bdytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -ady; + Two_Product(bdxtail, negate, ti1, ti0); + negate = -adytail; + Two_Product(bdx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt); + + Two_Product(adxtail, bdytail, ti1, ti0); + Two_Product(bdxtail, adytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]); + abtt[3] = abtt3; + abttlen = 4; + } + else { + abt[0] = 0.0; + abtlen = 1; + abtt[0] = 0.0; + abttlen = 1; + } + + if (cdxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a); + cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt); + temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (adytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, + finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, + finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail, temp32a); + cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt); + temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx, temp16a); + temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, + temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdytail != 0.0) { + temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a); + cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt); + temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail, temp32a); + cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt); + temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy, temp16a); + temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, + temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + + return finnow[finlength - 1]; +} + +REAL incircle(struct mesh *m, struct behavior *b, vertex pa, vertex pb, vertex pc, vertex pd) { + REAL adx, bdx, cdx, ady, bdy, cdy; + REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; + REAL alift, blift, clift; + REAL det; + REAL permanent, errbound; + + m->incirclecount++; + + adx = pa[0] - pd[0]; + bdx = pb[0] - pd[0]; + cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; + cdy = pc[1] - pd[1]; + + bdxcdy = bdx * cdy; + cdxbdy = cdx * bdy; + alift = adx * adx + ady * ady; + + cdxady = cdx * ady; + adxcdy = adx * cdy; + blift = bdx * bdx + bdy * bdy; + + adxbdy = adx * bdy; + bdxady = bdx * ady; + clift = cdx * cdx + cdy * cdy; + + det = alift * (bdxcdy - cdxbdy) + blift * (cdxady - adxcdy) + clift * (adxbdy - bdxady); + + if (b->noexact) { + return det; + } + + permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift + + (Absolute(cdxady) + Absolute(adxcdy)) * blift + + (Absolute(adxbdy) + Absolute(bdxady)) * clift; + errbound = iccerrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + return det; + } + + return incircleadapt(pa, pb, pc, pd, permanent); +} + +/*****************************************************************************/ +/* */ +/* orient3d() Return a positive value if the point pd lies below the */ +/* plane passing through pa, pb, and pc; "below" is defined so */ +/* that pa, pb, and pc appear in counterclockwise order when */ +/* viewed from above the plane. Returns a negative value if */ +/* pd lies above the plane. Returns zero if the points are */ +/* coplanar. The result is also a rough approximation of six */ +/* times the signed volume of the tetrahedron defined by the */ +/* four points. */ +/* */ +/* Uses exact arithmetic if necessary to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. This determinant is */ +/* computed adaptively, in the sense that exact arithmetic is used only to */ +/* the degree it is needed to ensure that the returned value has the */ +/* correct sign. Hence, this function is usually quite fast, but will run */ +/* more slowly when the input points are coplanar or nearly so. */ +/* */ +/* See my Robust Predicates paper for details. */ +/* */ +/*****************************************************************************/ + +REAL orient3dadapt(vertex pa, vertex pb, vertex pc, vertex pd, REAL aheight, REAL bheight, + REAL cheight, REAL dheight, REAL permanent) { + REAL adx, bdx, cdx, ady, bdy, cdy, adheight, bdheight, cdheight; + REAL det, errbound; + + REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; + REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0; + REAL bc[4], ca[4], ab[4]; + REAL bc3, ca3, ab3; + REAL adet[8], bdet[8], cdet[8]; + int alen, blen, clen; + REAL abdet[16]; + int ablen; + REAL *finnow, *finother, *finswap; + REAL fin1[192], fin2[192]; + int finlength; + + REAL adxtail, bdxtail, cdxtail; + REAL adytail, bdytail, cdytail; + REAL adheighttail, bdheighttail, cdheighttail; + REAL at_blarge, at_clarge; + REAL bt_clarge, bt_alarge; + REAL ct_alarge, ct_blarge; + REAL at_b[4], at_c[4], bt_c[4], bt_a[4], ct_a[4], ct_b[4]; + int at_blen, at_clen, bt_clen, bt_alen, ct_alen, ct_blen; + REAL bdxt_cdy1, cdxt_bdy1, cdxt_ady1; + REAL adxt_cdy1, adxt_bdy1, bdxt_ady1; + REAL bdxt_cdy0, cdxt_bdy0, cdxt_ady0; + REAL adxt_cdy0, adxt_bdy0, bdxt_ady0; + REAL bdyt_cdx1, cdyt_bdx1, cdyt_adx1; + REAL adyt_cdx1, adyt_bdx1, bdyt_adx1; + REAL bdyt_cdx0, cdyt_bdx0, cdyt_adx0; + REAL adyt_cdx0, adyt_bdx0, bdyt_adx0; + REAL bct[8], cat[8], abt[8]; + int bctlen, catlen, abtlen; + REAL bdxt_cdyt1, cdxt_bdyt1, cdxt_adyt1; + REAL adxt_cdyt1, adxt_bdyt1, bdxt_adyt1; + REAL bdxt_cdyt0, cdxt_bdyt0, cdxt_adyt0; + REAL adxt_cdyt0, adxt_bdyt0, bdxt_adyt0; + REAL u[4], v[12], w[16]; + REAL u3; + int vlength, wlength; + REAL negate; + + REAL bvirt; + REAL avirt, bround, around; + REAL c; + REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + REAL _i, _j, _k; + REAL _0; + + adx = (REAL) (pa[0] - pd[0]); + bdx = (REAL) (pb[0] - pd[0]); + cdx = (REAL) (pc[0] - pd[0]); + ady = (REAL) (pa[1] - pd[1]); + bdy = (REAL) (pb[1] - pd[1]); + cdy = (REAL) (pc[1] - pd[1]); + adheight = (REAL) (aheight - dheight); + bdheight = (REAL) (bheight - dheight); + cdheight = (REAL) (cheight - dheight); + + Two_Product(bdx, cdy, bdxcdy1, bdxcdy0); + Two_Product(cdx, bdy, cdxbdy1, cdxbdy0); + Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + alen = scale_expansion_zeroelim(4, bc, adheight, adet); + + Two_Product(cdx, ady, cdxady1, cdxady0); + Two_Product(adx, cdy, adxcdy1, adxcdy0); + Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); + ca[3] = ca3; + blen = scale_expansion_zeroelim(4, ca, bdheight, bdet); + + Two_Product(adx, bdy, adxbdy1, adxbdy0); + Two_Product(bdx, ady, bdxady1, bdxady0); + Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + clen = scale_expansion_zeroelim(4, ab, cdheight, cdet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1); + + det = estimate(finlength, fin1); + errbound = o3derrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pd[0], adx, adxtail); + Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); + Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); + Two_Diff_Tail(pa[1], pd[1], ady, adytail); + Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); + Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); + Two_Diff_Tail(aheight, dheight, adheight, adheighttail); + Two_Diff_Tail(bheight, dheight, bdheight, bdheighttail); + Two_Diff_Tail(cheight, dheight, cdheight, cdheighttail); + + if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) && (adytail == 0.0) + && (bdytail == 0.0) && (cdytail == 0.0) && (adheighttail == 0.0) && (bdheighttail == 0.0) + && (cdheighttail == 0.0)) { + return det; + } + + errbound = o3derrboundC * permanent + resulterrbound * Absolute(det); + det += (adheight * ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail)) + + adheighttail * (bdx * cdy - bdy * cdx)) + + (bdheight * ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail)) + + bdheighttail * (cdx * ady - cdy * adx)) + + (cdheight * ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail)) + + cdheighttail * (adx * bdy - ady * bdx)); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + finnow = fin1; + finother = fin2; + + if (adxtail == 0.0) { + if (adytail == 0.0) { + at_b[0] = 0.0; + at_blen = 1; + at_c[0] = 0.0; + at_clen = 1; + } + else { + negate = -adytail; + Two_Product(negate, bdx, at_blarge, at_b[0]); + at_b[1] = at_blarge; + at_blen = 2; + Two_Product(adytail, cdx, at_clarge, at_c[0]); + at_c[1] = at_clarge; + at_clen = 2; + } + } + else { + if (adytail == 0.0) { + Two_Product(adxtail, bdy, at_blarge, at_b[0]); + at_b[1] = at_blarge; + at_blen = 2; + negate = -adxtail; + Two_Product(negate, cdy, at_clarge, at_c[0]); + at_c[1] = at_clarge; + at_clen = 2; + } + else { + Two_Product(adxtail, bdy, adxt_bdy1, adxt_bdy0); + Two_Product(adytail, bdx, adyt_bdx1, adyt_bdx0); + Two_Two_Diff(adxt_bdy1, adxt_bdy0, adyt_bdx1, adyt_bdx0, at_blarge, at_b[2], at_b[1], + at_b[0]); + at_b[3] = at_blarge; + at_blen = 4; + Two_Product(adytail, cdx, adyt_cdx1, adyt_cdx0); + Two_Product(adxtail, cdy, adxt_cdy1, adxt_cdy0); + Two_Two_Diff(adyt_cdx1, adyt_cdx0, adxt_cdy1, adxt_cdy0, at_clarge, at_c[2], at_c[1], + at_c[0]); + at_c[3] = at_clarge; + at_clen = 4; + } + } + if (bdxtail == 0.0) { + if (bdytail == 0.0) { + bt_c[0] = 0.0; + bt_clen = 1; + bt_a[0] = 0.0; + bt_alen = 1; + } + else { + negate = -bdytail; + Two_Product(negate, cdx, bt_clarge, bt_c[0]); + bt_c[1] = bt_clarge; + bt_clen = 2; + Two_Product(bdytail, adx, bt_alarge, bt_a[0]); + bt_a[1] = bt_alarge; + bt_alen = 2; + } + } + else { + if (bdytail == 0.0) { + Two_Product(bdxtail, cdy, bt_clarge, bt_c[0]); + bt_c[1] = bt_clarge; + bt_clen = 2; + negate = -bdxtail; + Two_Product(negate, ady, bt_alarge, bt_a[0]); + bt_a[1] = bt_alarge; + bt_alen = 2; + } + else { + Two_Product(bdxtail, cdy, bdxt_cdy1, bdxt_cdy0); + Two_Product(bdytail, cdx, bdyt_cdx1, bdyt_cdx0); + Two_Two_Diff(bdxt_cdy1, bdxt_cdy0, bdyt_cdx1, bdyt_cdx0, bt_clarge, bt_c[2], bt_c[1], + bt_c[0]); + bt_c[3] = bt_clarge; + bt_clen = 4; + Two_Product(bdytail, adx, bdyt_adx1, bdyt_adx0); + Two_Product(bdxtail, ady, bdxt_ady1, bdxt_ady0); + Two_Two_Diff(bdyt_adx1, bdyt_adx0, bdxt_ady1, bdxt_ady0, bt_alarge, bt_a[2], bt_a[1], + bt_a[0]); + bt_a[3] = bt_alarge; + bt_alen = 4; + } + } + if (cdxtail == 0.0) { + if (cdytail == 0.0) { + ct_a[0] = 0.0; + ct_alen = 1; + ct_b[0] = 0.0; + ct_blen = 1; + } + else { + negate = -cdytail; + Two_Product(negate, adx, ct_alarge, ct_a[0]); + ct_a[1] = ct_alarge; + ct_alen = 2; + Two_Product(cdytail, bdx, ct_blarge, ct_b[0]); + ct_b[1] = ct_blarge; + ct_blen = 2; + } + } + else { + if (cdytail == 0.0) { + Two_Product(cdxtail, ady, ct_alarge, ct_a[0]); + ct_a[1] = ct_alarge; + ct_alen = 2; + negate = -cdxtail; + Two_Product(negate, bdy, ct_blarge, ct_b[0]); + ct_b[1] = ct_blarge; + ct_blen = 2; + } + else { + Two_Product(cdxtail, ady, cdxt_ady1, cdxt_ady0); + Two_Product(cdytail, adx, cdyt_adx1, cdyt_adx0); + Two_Two_Diff(cdxt_ady1, cdxt_ady0, cdyt_adx1, cdyt_adx0, ct_alarge, ct_a[2], ct_a[1], + ct_a[0]); + ct_a[3] = ct_alarge; + ct_alen = 4; + Two_Product(cdytail, bdx, cdyt_bdx1, cdyt_bdx0); + Two_Product(cdxtail, bdy, cdxt_bdy1, cdxt_bdy0); + Two_Two_Diff(cdyt_bdx1, cdyt_bdx0, cdxt_bdy1, cdxt_bdy0, ct_blarge, ct_b[2], ct_b[1], + ct_b[0]); + ct_b[3] = ct_blarge; + ct_blen = 4; + } + } + + bctlen = fast_expansion_sum_zeroelim(bt_clen, bt_c, ct_blen, ct_b, bct); + wlength = scale_expansion_zeroelim(bctlen, bct, adheight, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + catlen = fast_expansion_sum_zeroelim(ct_alen, ct_a, at_clen, at_c, cat); + wlength = scale_expansion_zeroelim(catlen, cat, bdheight, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + abtlen = fast_expansion_sum_zeroelim(at_blen, at_b, bt_alen, bt_a, abt); + wlength = scale_expansion_zeroelim(abtlen, abt, cdheight, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + if (adheighttail != 0.0) { + vlength = scale_expansion_zeroelim(4, bc, adheighttail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdheighttail != 0.0) { + vlength = scale_expansion_zeroelim(4, ca, bdheighttail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdheighttail != 0.0) { + vlength = scale_expansion_zeroelim(4, ab, cdheighttail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + if (adxtail != 0.0) { + if (bdytail != 0.0) { + Two_Product(adxtail, bdytail, adxt_bdyt1, adxt_bdyt0); + Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdheight, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (cdheighttail != 0.0) { + Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdheighttail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + if (cdytail != 0.0) { + negate = -adxtail; + Two_Product(negate, cdytail, adxt_cdyt1, adxt_cdyt0); + Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdheight, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (bdheighttail != 0.0) { + Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdheighttail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + } + if (bdxtail != 0.0) { + if (cdytail != 0.0) { + Two_Product(bdxtail, cdytail, bdxt_cdyt1, bdxt_cdyt0); + Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adheight, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (adheighttail != 0.0) { + Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adheighttail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + if (adytail != 0.0) { + negate = -bdxtail; + Two_Product(negate, adytail, bdxt_adyt1, bdxt_adyt0); + Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdheight, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (cdheighttail != 0.0) { + Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdheighttail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + } + if (cdxtail != 0.0) { + if (adytail != 0.0) { + Two_Product(cdxtail, adytail, cdxt_adyt1, cdxt_adyt0); + Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdheight, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (bdheighttail != 0.0) { + Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdheighttail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + if (bdytail != 0.0) { + negate = -cdxtail; + Two_Product(negate, bdytail, cdxt_bdyt1, cdxt_bdyt0); + Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adheight, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (adheighttail != 0.0) { + Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adheighttail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + } + + if (adheighttail != 0.0) { + wlength = scale_expansion_zeroelim(bctlen, bct, adheighttail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdheighttail != 0.0) { + wlength = scale_expansion_zeroelim(catlen, cat, bdheighttail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdheighttail != 0.0) { + wlength = scale_expansion_zeroelim(abtlen, abt, cdheighttail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + return finnow[finlength - 1]; +} + +REAL orient3d(struct mesh *m, struct behavior *b, vertex pa, vertex pb, vertex pc, vertex pd, + REAL aheight, REAL bheight, REAL cheight, REAL dheight) { + REAL adx, bdx, cdx, ady, bdy, cdy, adheight, bdheight, cdheight; + REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; + REAL det; + REAL permanent, errbound; + + m->orient3dcount++; + + adx = pa[0] - pd[0]; + bdx = pb[0] - pd[0]; + cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; + cdy = pc[1] - pd[1]; + adheight = aheight - dheight; + bdheight = bheight - dheight; + cdheight = cheight - dheight; + + bdxcdy = bdx * cdy; + cdxbdy = cdx * bdy; + + cdxady = cdx * ady; + adxcdy = adx * cdy; + + adxbdy = adx * bdy; + bdxady = bdx * ady; + + det = adheight * (bdxcdy - cdxbdy) + bdheight * (cdxady - adxcdy) + cdheight * (adxbdy - bdxady); + + if (b->noexact) { + return det; + } + + permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adheight) + + (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdheight) + + (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdheight); + errbound = o3derrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + return det; + } + + return orient3dadapt(pa, pb, pc, pd, aheight, bheight, cheight, dheight, permanent); +} + +/*****************************************************************************/ +/* */ +/* nonregular() Return a positive value if the point pd is incompatible */ +/* with the circle or plane passing through pa, pb, and pc */ +/* (meaning that pd is inside the circle or below the */ +/* plane); a negative value if it is compatible; and zero if */ +/* the four points are cocircular/coplanar. The points pa, */ +/* pb, and pc must be in counterclockwise order, or the sign */ +/* of the result will be reversed. */ +/* */ +/* If the -w switch is used, the points are lifted onto the parabolic */ +/* lifting map, then they are dropped according to their weights, then the */ +/* 3D orientation test is applied. If the -W switch is used, the points' */ +/* heights are already provided, so the 3D orientation test is applied */ +/* directly. If neither switch is used, the incircle test is applied. */ +/* */ +/*****************************************************************************/ + +REAL nonregular(struct mesh *m, struct behavior *b, vertex pa, vertex pb, vertex pc, vertex pd) { + if (b->weighted == 0) { + return incircle(m, b, pa, pb, pc, pd); + } + else if (b->weighted == 1) { + return orient3d(m, b, pa, pb, pc, pd, pa[0] * pa[0] + pa[1] * pa[1] - pa[2], + pb[0] * pb[0] + pb[1] * pb[1] - pb[2], pc[0] * pc[0] + pc[1] * pc[1] - pc[2], + pd[0] * pd[0] + pd[1] * pd[1] - pd[2]); + } + else { + return orient3d(m, b, pa, pb, pc, pd, pa[2], pb[2], pc[2], pd[2]); + } +} + +/*****************************************************************************/ +/* */ +/* findcircumcenter() Find the circumcenter of a triangle. */ +/* */ +/* The result is returned both in terms of x-y coordinates and xi-eta */ +/* (barycentric) coordinates. The xi-eta coordinate system is defined in */ +/* terms of the triangle: the origin of the triangle is the origin of the */ +/* coordinate system; the destination of the triangle is one unit along the */ +/* xi axis; and the apex of the triangle is one unit along the eta axis. */ +/* This procedure also returns the square of the length of the triangle's */ +/* shortest edge. */ +/* */ +/*****************************************************************************/ + +void findcircumcenter(struct mesh *m, struct behavior *b, vertex torg, vertex tdest, vertex tapex, + vertex circumcenter, REAL *xi, REAL *eta, int offcenter) { + REAL xdo, ydo, xao, yao; + REAL dodist, aodist, dadist; + REAL denominator; + REAL dx, dy, dxoff, dyoff; + + m->circumcentercount++; + + /* Compute the circumcenter of the triangle. */ + xdo = tdest[0] - torg[0]; + ydo = tdest[1] - torg[1]; + xao = tapex[0] - torg[0]; + yao = tapex[1] - torg[1]; + dodist = xdo * xdo + ydo * ydo; + aodist = xao * xao + yao * yao; + dadist = (tdest[0] - tapex[0]) * (tdest[0] - tapex[0]) + + (tdest[1] - tapex[1]) * (tdest[1] - tapex[1]); + if (b->noexact) { + denominator = 0.5 / (xdo * yao - xao * ydo); + } + else { + /* Use the counterclockwise() routine to ensure a positive (and */ + /* reasonably accurate) result, avoiding any possibility of */ + /* division by zero. */ + denominator = 0.5 / counterclockwise(m, b, tdest, tapex, torg); + /* Don't count the above as an orientation test. */ + m->counterclockcount--; + } + dx = (yao * dodist - ydo * aodist) * denominator; + dy = (xdo * aodist - xao * dodist) * denominator; + + /* Find the (squared) length of the triangle's shortest edge. This */ + /* serves as a conservative estimate of the insertion radius of the */ + /* circumcenter's parent. The estimate is used to ensure that */ + /* the algorithm terminates even if very small angles appear in */ + /* the input PSLG. */ + if ((dodist < aodist) && (dodist < dadist)) { + if (offcenter && (b->offconstant > 0.0)) { + /* Find the position of the off-center, as described by Alper Ungor. */ + dxoff = 0.5 * xdo - b->offconstant * ydo; + dyoff = 0.5 * ydo + b->offconstant * xdo; + /* If the off-center is closer to the origin than the */ + /* circumcenter, use the off-center instead. */ + if (dxoff * dxoff + dyoff * dyoff < dx * dx + dy * dy) { + dx = dxoff; + dy = dyoff; + } + } + } + else if (aodist < dadist) { + if (offcenter && (b->offconstant > 0.0)) { + dxoff = 0.5 * xao + b->offconstant * yao; + dyoff = 0.5 * yao - b->offconstant * xao; + /* If the off-center is closer to the origin than the */ + /* circumcenter, use the off-center instead. */ + if (dxoff * dxoff + dyoff * dyoff < dx * dx + dy * dy) { + dx = dxoff; + dy = dyoff; + } + } + } + else { + if (offcenter && (b->offconstant > 0.0)) { + dxoff = 0.5 * (tapex[0] - tdest[0]) - b->offconstant * (tapex[1] - tdest[1]); + dyoff = 0.5 * (tapex[1] - tdest[1]) + b->offconstant * (tapex[0] - tdest[0]); + /* If the off-center is closer to the destination than the */ + /* circumcenter, use the off-center instead. */ + if (dxoff * dxoff + dyoff * dyoff < (dx - xdo) * (dx - xdo) + (dy - ydo) * (dy - ydo)) { + dx = xdo + dxoff; + dy = ydo + dyoff; + } + } + } + + circumcenter[0] = torg[0] + dx; + circumcenter[1] = torg[1] + dy; + + /* To interpolate vertex attributes for the new vertex inserted at */ + /* the circumcenter, define a coordinate system with a xi-axis, */ + /* directed from the triangle's origin to its destination, and */ + /* an eta-axis, directed from its origin to its apex. */ + /* Calculate the xi and eta coordinates of the circumcenter. */ + *xi = (yao * dx - xao * dy) * (2.0 * denominator); + *eta = (xdo * dy - ydo * dx) * (2.0 * denominator); +} + +/** **/ +/** **/ +/********* Geometric primitives end here *********/ + +/*****************************************************************************/ +/* */ +/* triangleinit() Initialize some variables. */ +/* */ +/*****************************************************************************/ + +void triangleinit(struct mesh *m) { + poolzero(&m->vertices); + poolzero(&m->triangles); + poolzero(&m->subsegs); + poolzero(&m->viri); + poolzero(&m->badsubsegs); + poolzero(&m->badtriangles); + poolzero(&m->flipstackers); + poolzero(&m->splaynodes); + + m->recenttri.tri = (triangle *) NULL; /* No triangle has been visited yet. */ + m->undeads = 0; /* No eliminated input vertices yet. */ + m->samples = 1; /* Point location should take at least one sample. */ + m->checksegments = 0; /* There are no segments in the triangulation yet. */ + m->checkquality = 0; /* The quality triangulation stage has not begun. */ + m->incirclecount = m->counterclockcount = m->orient3dcount = 0; + m->hyperbolacount = m->circletopcount = m->circumcentercount = 0; + randomseed = 1; + + exactinit(); /* Initialize exact arithmetic constants. */ +} + +/*****************************************************************************/ +/* */ +/* randomnation() Generate a random number between 0 and `choices' - 1. */ +/* */ +/* This is a simple linear congruential random number generator. Hence, it */ +/* is a bad random number generator, but good enough for most randomized */ +/* geometric algorithms. */ +/* */ +/*****************************************************************************/ + +unsigned long randomnation(unsigned int choices) { + randomseed = (randomseed * 1366l + 150889l) % 714025l; + return randomseed / (714025l / choices + 1); +} + +/********* Point location routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* makevertexmap() Construct a mapping from vertices to triangles to */ +/* improve the speed of point location for segment */ +/* insertion. */ +/* */ +/* Traverses all the triangles, and provides each corner of each triangle */ +/* with a pointer to that triangle. Of course, pointers will be */ +/* overwritten by other pointers because (almost) each vertex is a corner */ +/* of several triangles, but in the end every vertex will point to some */ +/* triangle that contains it. */ +/* */ +/*****************************************************************************/ + +void makevertexmap(struct mesh *m, struct behavior *b) { + struct otri triangleloop; + vertex triorg; + + if (b->verbose) { + printf(" Constructing mapping from vertices to triangles.\n"); + } + traversalinit(&m->triangles); + triangleloop.tri = triangletraverse(m); + while (triangleloop.tri != (triangle *) NULL) { + /* Check all three vertices of the triangle. */ + for (triangleloop.orient = 0; triangleloop.orient < 3; triangleloop.orient++) { + org(triangleloop, triorg); + setvertex2tri(triorg, encode(triangleloop)); + } + triangleloop.tri = triangletraverse(m); + } +} + +/*****************************************************************************/ +/* */ +/* preciselocate() Find a triangle or edge containing a given point. */ +/* */ +/* Begins its search from `searchtri'. It is important that `searchtri' */ +/* be a handle with the property that `searchpoint' is strictly to the left */ +/* of the edge denoted by `searchtri', or is collinear with that edge and */ +/* does not intersect that edge. (In particular, `searchpoint' should not */ +/* be the origin or destination of that edge.) */ +/* */ +/* These conditions are imposed because preciselocate() is normally used in */ +/* one of two situations: */ +/* */ +/* (1) To try to find the location to insert a new point. Normally, we */ +/* know an edge that the point is strictly to the left of. In the */ +/* incremental Delaunay algorithm, that edge is a bounding box edge. */ +/* In Ruppert's Delaunay refinement algorithm for quality meshing, */ +/* that edge is the shortest edge of the triangle whose circumcenter */ +/* is being inserted. */ +/* */ +/* (2) To try to find an existing point. In this case, any edge on the */ +/* convex hull is a good starting edge. You must screen out the */ +/* possibility that the vertex sought is an endpoint of the starting */ +/* edge before you call preciselocate(). */ +/* */ +/* On completion, `searchtri' is a triangle that contains `searchpoint'. */ +/* */ +/* This implementation differs from that given by Guibas and Stolfi. It */ +/* walks from triangle to triangle, crossing an edge only if `searchpoint' */ +/* is on the other side of the line containing that edge. After entering */ +/* a triangle, there are two edges by which one can leave that triangle. */ +/* If both edges are valid (`searchpoint' is on the other side of both */ +/* edges), one of the two is chosen by drawing a line perpendicular to */ +/* the entry edge (whose endpoints are `forg' and `fdest') passing through */ +/* `fapex'. Depending on which side of this perpendicular `searchpoint' */ +/* falls on, an exit edge is chosen. */ +/* */ +/* This implementation is empirically faster than the Guibas and Stolfi */ +/* point location routine (which I originally used), which tends to spiral */ +/* in toward its target. */ +/* */ +/* Returns ONVERTEX if the point lies on an existing vertex. `searchtri' */ +/* is a handle whose origin is the existing vertex. */ +/* */ +/* Returns ONEDGE if the point lies on a mesh edge. `searchtri' is a */ +/* handle whose primary edge is the edge on which the point lies. */ +/* */ +/* Returns INTRIANGLE if the point lies strictly within a triangle. */ +/* `searchtri' is a handle on the triangle that contains the point. */ +/* */ +/* Returns OUTSIDE if the point lies outside the mesh. `searchtri' is a */ +/* handle whose primary edge the point is to the right of. This might */ +/* occur when the circumcenter of a triangle falls just slightly outside */ +/* the mesh due to floating-point roundoff error. It also occurs when */ +/* seeking a hole or region point that a foolish user has placed outside */ +/* the mesh. */ +/* */ +/* If `stopatsubsegment' is nonzero, the search will stop if it tries to */ +/* walk through a subsegment, and will return OUTSIDE. */ +/* */ +/* WARNING: This routine is designed for convex triangulations, and will */ +/* not generally work after the holes and concavities have been carved. */ +/* However, it can still be used to find the circumcenter of a triangle, as */ +/* long as the search is begun from the triangle in question. */ +/* */ +/*****************************************************************************/ + +enum locateresult preciselocate(struct mesh *m, struct behavior *b, vertex searchpoint, + struct otri *searchtri, int stopatsubsegment) { + struct otri backtracktri; + struct osub checkedge; + vertex forg, fdest, fapex; + REAL orgorient, destorient; + int moveleft; + triangle ptr; /* Temporary variable used by sym(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + if (b->verbose > 2) { + printf(" Searching for point (%.12g, %.12g).\n", searchpoint[0], searchpoint[1]); + } + /* Where are we? */ + org(*searchtri, forg); + dest(*searchtri, fdest); + apex(*searchtri, fapex); + while (1) { + if (b->verbose > 2) { + printf( + " At (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", forg[0], forg[1], fdest[0], fdest[1], fapex[0], fapex[1]); + } + /* Check whether the apex is the point we seek. */ + if ((fapex[0] == searchpoint[0]) && (fapex[1] == searchpoint[1])) { + lprevself(*searchtri); + return ONVERTEX; + } + /* Does the point lie on the other side of the line defined by the */ + /* triangle edge opposite the triangle's destination? */ + destorient = counterclockwise(m, b, forg, fapex, searchpoint); + /* Does the point lie on the other side of the line defined by the */ + /* triangle edge opposite the triangle's origin? */ + orgorient = counterclockwise(m, b, fapex, fdest, searchpoint); + if (destorient > 0.0) { + if (orgorient > 0.0) { + /* Move left if the inner product of (fapex - searchpoint) and */ + /* (fdest - forg) is positive. This is equivalent to drawing */ + /* a line perpendicular to the line (forg, fdest) and passing */ + /* through `fapex', and determining which side of this line */ + /* `searchpoint' falls on. */ + moveleft = (fapex[0] - searchpoint[0]) * (fdest[0] - forg[0]) + + (fapex[1] - searchpoint[1]) * (fdest[1] - forg[1]) > 0.0; + } + else { + moveleft = 1; + } + } + else { + if (orgorient > 0.0) { + moveleft = 0; + } + else { + /* The point we seek must be on the boundary of or inside this */ + /* triangle. */ + if (destorient == 0.0) { + lprevself(*searchtri); + return ONEDGE; + } + if (orgorient == 0.0) { + lnextself(*searchtri); + return ONEDGE; + } + return INTRIANGLE; + } + } + + /* Move to another triangle. Leave a trace `backtracktri' in case */ + /* floating-point roundoff or some such bogey causes us to walk */ + /* off a boundary of the triangulation. */ + if (moveleft) { + lprev(*searchtri, backtracktri); + fdest = fapex; + } + else { + lnext(*searchtri, backtracktri); + forg = fapex; + } + sym(backtracktri, *searchtri); + + if (m->checksegments && stopatsubsegment) { + /* Check for walking through a subsegment. */ + tspivot(backtracktri, checkedge); + if (checkedge.ss != m->dummysub) { + /* Go back to the last triangle. */ + otricopy(backtracktri, *searchtri); + return OUTSIDE; + } + } + /* Check for walking right out of the triangulation. */ + if (searchtri->tri == m->dummytri) { + /* Go back to the last triangle. */ + otricopy(backtracktri, *searchtri); + return OUTSIDE; + } + + apex(*searchtri, fapex); + } +} + +/*****************************************************************************/ +/* */ +/* locate() Find a triangle or edge containing a given point. */ +/* */ +/* Searching begins from one of: the input `searchtri', a recently */ +/* encountered triangle `recenttri', or from a triangle chosen from a */ +/* random sample. The choice is made by determining which triangle's */ +/* origin is closest to the point we are searching for. Normally, */ +/* `searchtri' should be a handle on the convex hull of the triangulation. */ +/* */ +/* Details on the random sampling method can be found in the Mucke, Saias, */ +/* and Zhu paper cited in the header of this code. */ +/* */ +/* On completion, `searchtri' is a triangle that contains `searchpoint'. */ +/* */ +/* Returns ONVERTEX if the point lies on an existing vertex. `searchtri' */ +/* is a handle whose origin is the existing vertex. */ +/* */ +/* Returns ONEDGE if the point lies on a mesh edge. `searchtri' is a */ +/* handle whose primary edge is the edge on which the point lies. */ +/* */ +/* Returns INTRIANGLE if the point lies strictly within a triangle. */ +/* `searchtri' is a handle on the triangle that contains the point. */ +/* */ +/* Returns OUTSIDE if the point lies outside the mesh. `searchtri' is a */ +/* handle whose primary edge the point is to the right of. This might */ +/* occur when the circumcenter of a triangle falls just slightly outside */ +/* the mesh due to floating-point roundoff error. It also occurs when */ +/* seeking a hole or region point that a foolish user has placed outside */ +/* the mesh. */ +/* */ +/* WARNING: This routine is designed for convex triangulations, and will */ +/* not generally work after the holes and concavities have been carved. */ +/* */ +/*****************************************************************************/ + +enum locateresult locate(struct mesh *m, struct behavior *b, vertex searchpoint, + struct otri *searchtri) { + VOID **sampleblock; + char *firsttri; + struct otri sampletri; + vertex torg, tdest; + unsigned long alignptr; + REAL searchdist, dist; + REAL ahead; + long samplesperblock, totalsamplesleft, samplesleft; + long population, totalpopulation; + triangle ptr; /* Temporary variable used by sym(). */ + + if (b->verbose > 2) { + printf( + " Randomly sampling for a triangle near point (%.12g, %.12g).\n", searchpoint[0], searchpoint[1]); + } + /* Record the distance from the suggested starting triangle to the */ + /* point we seek. */ + org(*searchtri, torg); + searchdist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) + + (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]); + if (b->verbose > 2) { + printf(" Boundary triangle has origin (%.12g, %.12g).\n", torg[0], torg[1]); + } + + /* If a recently encountered triangle has been recorded and has not been */ + /* deallocated, test it as a good starting point. */ + if (m->recenttri.tri != (triangle *) NULL) { + if (!deadtri(m->recenttri.tri)) { + org(m->recenttri, torg); + if ((torg[0] == searchpoint[0]) && (torg[1] == searchpoint[1])) { + otricopy(m->recenttri, *searchtri); + return ONVERTEX; + } + dist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) + + (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]); + if (dist < searchdist) { + otricopy(m->recenttri, *searchtri); + searchdist = dist; + if (b->verbose > 2) { + printf( + " Choosing recent triangle with origin (%.12g, %.12g).\n", torg[0], torg[1]); + } + } + } + } + + /* The number of random samples taken is proportional to the cube root of */ + /* the number of triangles in the mesh. The next bit of code assumes */ + /* that the number of triangles increases monotonically (or at least */ + /* doesn't decrease enough to matter). */ + while (SAMPLEFACTOR * m->samples * m->samples * m->samples < m->triangles.items) { + m->samples++; + } + + /* We'll draw ceiling(samples * TRIPERBLOCK / maxitems) random samples */ + /* from each block of triangles (except the first)--until we meet the */ + /* sample quota. The ceiling means that blocks at the end might be */ + /* neglected, but I don't care. */ + samplesperblock = (m->samples * TRIPERBLOCK - 1) / m->triangles.maxitems + 1; + /* We'll draw ceiling(samples * itemsfirstblock / maxitems) random samples */ + /* from the first block of triangles. */ + samplesleft = (m->samples * m->triangles.itemsfirstblock - 1) / m->triangles.maxitems + 1; + totalsamplesleft = m->samples; + population = m->triangles.itemsfirstblock; + totalpopulation = m->triangles.maxitems; + sampleblock = m->triangles.firstblock; + sampletri.orient = 0; + while (totalsamplesleft > 0) { + /* If we're in the last block, `population' needs to be corrected. */ + if (population > totalpopulation) { + population = totalpopulation; + } + /* Find a pointer to the first triangle in the block. */ + alignptr = (unsigned long) (sampleblock + 1); + firsttri = (char *) (alignptr + (unsigned long) m->triangles.alignbytes + - (alignptr % (unsigned long) m->triangles.alignbytes)); + + /* Choose `samplesleft' randomly sampled triangles in this block. */ + do { + sampletri.tri = (triangle *) (firsttri + + (randomnation((unsigned int) population) * m->triangles.itembytes)); + if (!deadtri(sampletri.tri)) { + org(sampletri, torg); + dist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) + + (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]); + if (dist < searchdist) { + otricopy(sampletri, *searchtri); + searchdist = dist; + if (b->verbose > 2) { + printf(" Choosing triangle with origin (%.12g, %.12g).\n", torg[0], torg[1]); + } + } + } + + samplesleft--; + totalsamplesleft--; + } while ((samplesleft > 0) && (totalsamplesleft > 0)); + + if (totalsamplesleft > 0) { + sampleblock = (VOID **) *sampleblock; + samplesleft = samplesperblock; + totalpopulation -= population; + population = TRIPERBLOCK; + } + } + + /* Where are we? */ + org(*searchtri, torg); + dest(*searchtri, tdest); + /* Check the starting triangle's vertices. */ + if ((torg[0] == searchpoint[0]) && (torg[1] == searchpoint[1])) { + return ONVERTEX; + } + if ((tdest[0] == searchpoint[0]) && (tdest[1] == searchpoint[1])) { + lnextself(*searchtri); + return ONVERTEX; + } + /* Orient `searchtri' to fit the preconditions of calling preciselocate(). */ + ahead = counterclockwise(m, b, torg, tdest, searchpoint); + if (ahead < 0.0) { + /* Turn around so that `searchpoint' is to the left of the */ + /* edge specified by `searchtri'. */ + symself(*searchtri); + } + else if (ahead == 0.0) { + /* Check if `searchpoint' is between `torg' and `tdest'. */ + if (((torg[0] < searchpoint[0]) == (searchpoint[0] < tdest[0])) + && ((torg[1] < searchpoint[1]) == (searchpoint[1] < tdest[1]))) { + return ONEDGE; + } + } + return preciselocate(m, b, searchpoint, searchtri, 0); +} + +/** **/ +/** **/ +/********* Point location routines end here *********/ + +/********* Mesh transformation routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* insertsubseg() Create a new subsegment and insert it between two */ +/* triangles. */ +/* */ +/* The new subsegment is inserted at the edge described by the handle */ +/* `tri'. Its vertices are properly initialized. The marker `subsegmark' */ +/* is applied to the subsegment and, if appropriate, its vertices. */ +/* */ +/*****************************************************************************/ + +void insertsubseg(struct mesh *m, struct behavior *b, struct otri *tri, int subsegmark) { + struct otri oppotri; + struct osub newsubseg; + vertex triorg, tridest; + triangle ptr; /* Temporary variable used by sym(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + org(*tri, triorg); + dest(*tri, tridest); + /* Mark vertices if possible. */ + if (vertexmark(triorg) == 0) { + setvertexmark(triorg, subsegmark); + } + if (vertexmark(tridest) == 0) { + setvertexmark(tridest, subsegmark); + } + /* Check if there's already a subsegment here. */ + tspivot(*tri, newsubseg); + if (newsubseg.ss == m->dummysub) { + /* Make new subsegment and initialize its vertices. */ + makesubseg(m, &newsubseg); + setsorg(newsubseg, tridest); + setsdest(newsubseg, triorg); + setsegorg(newsubseg, tridest); + setsegdest(newsubseg, triorg); + /* Bond new subsegment to the two triangles it is sandwiched between. */ + /* Note that the facing triangle `oppotri' might be equal to */ + /* `dummytri' (outer space), but the new subsegment is bonded to it */ + /* all the same. */ + tsbond(*tri, newsubseg); + sym(*tri, oppotri); + ssymself(newsubseg); + tsbond(oppotri, newsubseg); + setmark(newsubseg, subsegmark); + if (b->verbose > 2) { + printf(" Inserting new "); + printsubseg(m, b, &newsubseg); + } + } + else { + if (mark(newsubseg) == 0) { + setmark(newsubseg, subsegmark); + } + } +} + +/*****************************************************************************/ +/* */ +/* Terminology */ +/* */ +/* A "local transformation" replaces a small set of triangles with another */ +/* set of triangles. This may or may not involve inserting or deleting a */ +/* vertex. */ +/* */ +/* The term "casing" is used to describe the set of triangles that are */ +/* attached to the triangles being transformed, but are not transformed */ +/* themselves. Think of the casing as a fixed hollow structure inside */ +/* which all the action happens. A "casing" is only defined relative to */ +/* a single transformation; each occurrence of a transformation will */ +/* involve a different casing. */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* */ +/* flip() Transform two triangles to two different triangles by flipping */ +/* an edge counterclockwise within a quadrilateral. */ +/* */ +/* Imagine the original triangles, abc and bad, oriented so that the */ +/* shared edge ab lies in a horizontal plane, with the vertex b on the left */ +/* and the vertex a on the right. The vertex c lies below the edge, and */ +/* the vertex d lies above the edge. The `flipedge' handle holds the edge */ +/* ab of triangle abc, and is directed left, from vertex a to vertex b. */ +/* */ +/* The triangles abc and bad are deleted and replaced by the triangles cdb */ +/* and dca. The triangles that represent abc and bad are NOT deallocated; */ +/* they are reused for dca and cdb, respectively. Hence, any handles that */ +/* may have held the original triangles are still valid, although not */ +/* directed as they were before. */ +/* */ +/* Upon completion of this routine, the `flipedge' handle holds the edge */ +/* dc of triangle dca, and is directed down, from vertex d to vertex c. */ +/* (Hence, the two triangles have rotated counterclockwise.) */ +/* */ +/* WARNING: This transformation is geometrically valid only if the */ +/* quadrilateral adbc is convex. Furthermore, this transformation is */ +/* valid only if there is not a subsegment between the triangles abc and */ +/* bad. This routine does not check either of these preconditions, and */ +/* it is the responsibility of the calling routine to ensure that they are */ +/* met. If they are not, the streets shall be filled with wailing and */ +/* gnashing of teeth. */ +/* */ +/*****************************************************************************/ + +void flip(struct mesh *m, struct behavior *b, struct otri *flipedge) { + struct otri botleft, botright; + struct otri topleft, topright; + struct otri top; + struct otri botlcasing, botrcasing; + struct otri toplcasing, toprcasing; + struct osub botlsubseg, botrsubseg; + struct osub toplsubseg, toprsubseg; + vertex leftvertex, rightvertex, botvertex; + vertex farvertex; + triangle ptr; /* Temporary variable used by sym(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + /* Identify the vertices of the quadrilateral. */ + org(*flipedge, rightvertex); + dest(*flipedge, leftvertex); + apex(*flipedge, botvertex); + sym(*flipedge, top); +#ifdef SELF_CHECK + if (top.tri == m->dummytri) + { + printf("Internal error in flip(): Attempt to flip on boundary.\n"); + lnextself(*flipedge); + return; + } + if (m->checksegments) + { + tspivot(*flipedge, toplsubseg); + if (toplsubseg.ss != m->dummysub) + { + printf("Internal error in flip(): Attempt to flip a segment.\n"); + lnextself(*flipedge); + return; + } + } +#endif /* SELF_CHECK */ + apex(top, farvertex); + + /* Identify the casing of the quadrilateral. */ + lprev(top, topleft); + sym(topleft, toplcasing); + lnext(top, topright); + sym(topright, toprcasing); + lnext(*flipedge, botleft); + sym(botleft, botlcasing); + lprev(*flipedge, botright); + sym(botright, botrcasing); + /* Rotate the quadrilateral one-quarter turn counterclockwise. */ + bond(topleft, botlcasing); + bond(botleft, botrcasing); + bond(botright, toprcasing); + bond(topright, toplcasing); + + if (m->checksegments) { + /* Check for subsegments and rebond them to the quadrilateral. */ + tspivot(topleft, toplsubseg); + tspivot(botleft, botlsubseg); + tspivot(botright, botrsubseg); + tspivot(topright, toprsubseg); + if (toplsubseg.ss == m->dummysub) { + tsdissolve(topright); + } + else { + tsbond(topright, toplsubseg); + } + if (botlsubseg.ss == m->dummysub) { + tsdissolve(topleft); + } + else { + tsbond(topleft, botlsubseg); + } + if (botrsubseg.ss == m->dummysub) { + tsdissolve(botleft); + } + else { + tsbond(botleft, botrsubseg); + } + if (toprsubseg.ss == m->dummysub) { + tsdissolve(botright); + } + else { + tsbond(botright, toprsubseg); + } + } + + /* New vertex assignments for the rotated quadrilateral. */ + setorg(*flipedge, farvertex); + setdest(*flipedge, botvertex); + setapex(*flipedge, rightvertex); + setorg(top, botvertex); + setdest(top, farvertex); + setapex(top, leftvertex); + if (b->verbose > 2) { + printf(" Edge flip results in left "); + printtriangle(m, b, &top); + printf(" and right "); + printtriangle(m, b, flipedge); + } +} + +/*****************************************************************************/ +/* */ +/* unflip() Transform two triangles to two different triangles by */ +/* flipping an edge clockwise within a quadrilateral. Reverses */ +/* the flip() operation so that the data structures representing */ +/* the triangles are back where they were before the flip(). */ +/* */ +/* Imagine the original triangles, abc and bad, oriented so that the */ +/* shared edge ab lies in a horizontal plane, with the vertex b on the left */ +/* and the vertex a on the right. The vertex c lies below the edge, and */ +/* the vertex d lies above the edge. The `flipedge' handle holds the edge */ +/* ab of triangle abc, and is directed left, from vertex a to vertex b. */ +/* */ +/* The triangles abc and bad are deleted and replaced by the triangles cdb */ +/* and dca. The triangles that represent abc and bad are NOT deallocated; */ +/* they are reused for cdb and dca, respectively. Hence, any handles that */ +/* may have held the original triangles are still valid, although not */ +/* directed as they were before. */ +/* */ +/* Upon completion of this routine, the `flipedge' handle holds the edge */ +/* cd of triangle cdb, and is directed up, from vertex c to vertex d. */ +/* (Hence, the two triangles have rotated clockwise.) */ +/* */ +/* WARNING: This transformation is geometrically valid only if the */ +/* quadrilateral adbc is convex. Furthermore, this transformation is */ +/* valid only if there is not a subsegment between the triangles abc and */ +/* bad. This routine does not check either of these preconditions, and */ +/* it is the responsibility of the calling routine to ensure that they are */ +/* met. If they are not, the streets shall be filled with wailing and */ +/* gnashing of teeth. */ +/* */ +/*****************************************************************************/ + +void unflip(struct mesh *m, struct behavior *b, struct otri *flipedge) { + struct otri botleft, botright; + struct otri topleft, topright; + struct otri top; + struct otri botlcasing, botrcasing; + struct otri toplcasing, toprcasing; + struct osub botlsubseg, botrsubseg; + struct osub toplsubseg, toprsubseg; + vertex leftvertex, rightvertex, botvertex; + vertex farvertex; + triangle ptr; /* Temporary variable used by sym(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + /* Identify the vertices of the quadrilateral. */ + org(*flipedge, rightvertex); + dest(*flipedge, leftvertex); + apex(*flipedge, botvertex); + sym(*flipedge, top); +#ifdef SELF_CHECK + if (top.tri == m->dummytri) + { + printf("Internal error in unflip(): Attempt to flip on boundary.\n"); + lnextself(*flipedge); + return; + } + if (m->checksegments) + { + tspivot(*flipedge, toplsubseg); + if (toplsubseg.ss != m->dummysub) + { + printf("Internal error in unflip(): Attempt to flip a subsegment.\n"); + lnextself(*flipedge); + return; + } + } +#endif /* SELF_CHECK */ + apex(top, farvertex); + + /* Identify the casing of the quadrilateral. */ + lprev(top, topleft); + sym(topleft, toplcasing); + lnext(top, topright); + sym(topright, toprcasing); + lnext(*flipedge, botleft); + sym(botleft, botlcasing); + lprev(*flipedge, botright); + sym(botright, botrcasing); + /* Rotate the quadrilateral one-quarter turn clockwise. */ + bond(topleft, toprcasing); + bond(botleft, toplcasing); + bond(botright, botlcasing); + bond(topright, botrcasing); + + if (m->checksegments) { + /* Check for subsegments and rebond them to the quadrilateral. */ + tspivot(topleft, toplsubseg); + tspivot(botleft, botlsubseg); + tspivot(botright, botrsubseg); + tspivot(topright, toprsubseg); + if (toplsubseg.ss == m->dummysub) { + tsdissolve(botleft); + } + else { + tsbond(botleft, toplsubseg); + } + if (botlsubseg.ss == m->dummysub) { + tsdissolve(botright); + } + else { + tsbond(botright, botlsubseg); + } + if (botrsubseg.ss == m->dummysub) { + tsdissolve(topright); + } + else { + tsbond(topright, botrsubseg); + } + if (toprsubseg.ss == m->dummysub) { + tsdissolve(topleft); + } + else { + tsbond(topleft, toprsubseg); + } + } + + /* New vertex assignments for the rotated quadrilateral. */ + setorg(*flipedge, botvertex); + setdest(*flipedge, farvertex); + setapex(*flipedge, leftvertex); + setorg(top, farvertex); + setdest(top, botvertex); + setapex(top, rightvertex); + if (b->verbose > 2) { + printf(" Edge unflip results in left "); + printtriangle(m, b, flipedge); + printf(" and right "); + printtriangle(m, b, &top); + } +} + +/*****************************************************************************/ +/* */ +/* insertvertex() Insert a vertex into a Delaunay triangulation, */ +/* performing flips as necessary to maintain the Delaunay */ +/* property. */ +/* */ +/* The point `insertvertex' is located. If `searchtri.tri' is not NULL, */ +/* the search for the containing triangle begins from `searchtri'. If */ +/* `searchtri.tri' is NULL, a full point location procedure is called. */ +/* If `insertvertex' is found inside a triangle, the triangle is split into */ +/* three; if `insertvertex' lies on an edge, the edge is split in two, */ +/* thereby splitting the two adjacent triangles into four. Edge flips are */ +/* used to restore the Delaunay property. If `insertvertex' lies on an */ +/* existing vertex, no action is taken, and the value DUPLICATEVERTEX is */ +/* returned. On return, `searchtri' is set to a handle whose origin is the */ +/* existing vertex. */ +/* */ +/* Normally, the parameter `splitseg' is set to NULL, implying that no */ +/* subsegment should be split. In this case, if `insertvertex' is found to */ +/* lie on a segment, no action is taken, and the value VIOLATINGVERTEX is */ +/* returned. On return, `searchtri' is set to a handle whose primary edge */ +/* is the violated subsegment. */ +/* */ +/* If the calling routine wishes to split a subsegment by inserting a */ +/* vertex in it, the parameter `splitseg' should be that subsegment. In */ +/* this case, `searchtri' MUST be the triangle handle reached by pivoting */ +/* from that subsegment; no point location is done. */ +/* */ +/* `segmentflaws' and `triflaws' are flags that indicate whether or not */ +/* there should be checks for the creation of encroached subsegments or bad */ +/* quality triangles. If a newly inserted vertex encroaches upon */ +/* subsegments, these subsegments are added to the list of subsegments to */ +/* be split if `segmentflaws' is set. If bad triangles are created, these */ +/* are added to the queue if `triflaws' is set. */ +/* */ +/* If a duplicate vertex or violated segment does not prevent the vertex */ +/* from being inserted, the return value will be ENCROACHINGVERTEX if the */ +/* vertex encroaches upon a subsegment (and checking is enabled), or */ +/* SUCCESSFULVERTEX otherwise. In either case, `searchtri' is set to a */ +/* handle whose origin is the newly inserted vertex. */ +/* */ +/* insertvertex() does not use flip() for reasons of speed; some */ +/* information can be reused from edge flip to edge flip, like the */ +/* locations of subsegments. */ +/* */ +/*****************************************************************************/ + +enum insertvertexresult insertvertex(struct mesh *m, struct behavior *b, vertex newvertex, + struct otri *searchtri, struct osub *splitseg, int segmentflaws, int triflaws) { + struct otri horiz; + struct otri top; + struct otri botleft, botright; + struct otri topleft, topright; + struct otri newbotleft, newbotright; + struct otri newtopright; + struct otri botlcasing, botrcasing; + struct otri toplcasing, toprcasing; + struct otri testtri; + struct osub botlsubseg, botrsubseg; + struct osub toplsubseg, toprsubseg; + struct osub brokensubseg; + struct osub checksubseg; + struct osub rightsubseg; + struct osub newsubseg; + struct badsubseg *encroached; + struct flipstacker *newflip; + vertex first; + vertex leftvertex, rightvertex, botvertex, topvertex, farvertex; + vertex segmentorg, segmentdest; + REAL attrib; + REAL area; + enum insertvertexresult success; + enum locateresult intersect; + int doflip; + int mirrorflag; + int enq; + int i; + triangle ptr; /* Temporary variable used by sym(). */ + subseg sptr; /* Temporary variable used by spivot() and tspivot(). */ + + if (b->verbose > 1) { + printf(" Inserting (%.12g, %.12g).\n", newvertex[0], newvertex[1]); + } + + if (splitseg == (struct osub *) NULL) { + /* Find the location of the vertex to be inserted. Check if a good */ + /* starting triangle has already been provided by the caller. */ + if (searchtri->tri == m->dummytri) { + /* Find a boundary triangle. */ + horiz.tri = m->dummytri; + horiz.orient = 0; + symself(horiz); + /* Search for a triangle containing `newvertex'. */ + intersect = locate(m, b, newvertex, &horiz); + } + else { + /* Start searching from the triangle provided by the caller. */ + otricopy(*searchtri, horiz); + intersect = preciselocate(m, b, newvertex, &horiz, 1); + } + } + else { + /* The calling routine provides the subsegment in which */ + /* the vertex is inserted. */ + otricopy(*searchtri, horiz); + intersect = ONEDGE; + } + + if (intersect == ONVERTEX) { + /* There's already a vertex there. Return in `searchtri' a triangle */ + /* whose origin is the existing vertex. */ + otricopy(horiz, *searchtri); + otricopy(horiz, m->recenttri); + return DUPLICATEVERTEX; + } + if ((intersect == ONEDGE) || (intersect == OUTSIDE)) { + /* The vertex falls on an edge or boundary. */ + if (m->checksegments && (splitseg == (struct osub *) NULL)) { + /* Check whether the vertex falls on a subsegment. */ + tspivot(horiz, brokensubseg); + if (brokensubseg.ss != m->dummysub) { + /* The vertex falls on a subsegment, and hence will not be inserted. */ + if (segmentflaws) { + enq = b->nobisect != 2; + if (enq && (b->nobisect == 1)) { + /* This subsegment may be split only if it is an */ + /* internal boundary. */ + sym(horiz, testtri); + enq = testtri.tri != m->dummytri; + } + if (enq) { + /* Add the subsegment to the list of encroached subsegments. */ + encroached = (struct badsubseg *) poolalloc(&m->badsubsegs); + encroached->encsubseg = sencode(brokensubseg); + sorg(brokensubseg, encroached->subsegorg); + sdest(brokensubseg, encroached->subsegdest); + if (b->verbose > 2) { + printf( + " Queueing encroached subsegment (%.12g, %.12g) (%.12g, %.12g).\n", encroached->subsegorg[0], encroached->subsegorg[1], encroached->subsegdest[0], encroached->subsegdest[1]); + } + } + } + /* Return a handle whose primary edge contains the vertex, */ + /* which has not been inserted. */ + otricopy(horiz, *searchtri); + otricopy(horiz, m->recenttri); + return VIOLATINGVERTEX; + } + } + + /* Insert the vertex on an edge, dividing one triangle into two (if */ + /* the edge lies on a boundary) or two triangles into four. */ + lprev(horiz, botright); + sym(botright, botrcasing); + sym(horiz, topright); + /* Is there a second triangle? (Or does this edge lie on a boundary?) */ + mirrorflag = topright.tri != m->dummytri; + if (mirrorflag) { + lnextself(topright); + sym(topright, toprcasing); + maketriangle(m, b, &newtopright); + } + else { + /* Splitting a boundary edge increases the number of boundary edges. */ + m->hullsize++; + } + maketriangle(m, b, &newbotright); + + /* Set the vertices of changed and new triangles. */ + org(horiz, rightvertex); + dest(horiz, leftvertex); + apex(horiz, botvertex); + setorg(newbotright, botvertex); + setdest(newbotright, rightvertex); + setapex(newbotright, newvertex); + setorg(horiz, newvertex); + for (i = 0; i < m->eextras; i++) { + /* Set the element attributes of a new triangle. */ + setelemattribute(newbotright, i, elemattribute(botright, i)); + } + if (b->vararea) { + /* Set the area constraint of a new triangle. */ + setareabound(newbotright, areabound(botright)); + } + if (mirrorflag) { + dest(topright, topvertex); + setorg(newtopright, rightvertex); + setdest(newtopright, topvertex); + setapex(newtopright, newvertex); + setorg(topright, newvertex); + for (i = 0; i < m->eextras; i++) { + /* Set the element attributes of another new triangle. */ + setelemattribute(newtopright, i, elemattribute(topright, i)); + } + if (b->vararea) { + /* Set the area constraint of another new triangle. */ + setareabound(newtopright, areabound(topright)); + } + } + + /* There may be subsegments that need to be bonded */ + /* to the new triangle(s). */ + if (m->checksegments) { + tspivot(botright, botrsubseg); + if (botrsubseg.ss != m->dummysub) { + tsdissolve(botright); + tsbond(newbotright, botrsubseg); + } + if (mirrorflag) { + tspivot(topright, toprsubseg); + if (toprsubseg.ss != m->dummysub) { + tsdissolve(topright); + tsbond(newtopright, toprsubseg); + } + } + } + + /* Bond the new triangle(s) to the surrounding triangles. */ + bond(newbotright, botrcasing); + lprevself(newbotright); + bond(newbotright, botright); + lprevself(newbotright); + if (mirrorflag) { + bond(newtopright, toprcasing); + lnextself(newtopright); + bond(newtopright, topright); + lnextself(newtopright); + bond(newtopright, newbotright); + } + + if (splitseg != (struct osub *) NULL) { + /* Split the subsegment into two. */ + setsdest(*splitseg, newvertex); + segorg(*splitseg, segmentorg); + segdest(*splitseg, segmentdest); + ssymself(*splitseg); + spivot(*splitseg, rightsubseg); + insertsubseg(m, b, &newbotright, mark(*splitseg)); + tspivot(newbotright, newsubseg); + setsegorg(newsubseg, segmentorg); + setsegdest(newsubseg, segmentdest); + sbond(*splitseg, newsubseg); + ssymself(newsubseg); + sbond(newsubseg, rightsubseg); + ssymself(*splitseg); + /* Transfer the subsegment's boundary marker to the vertex */ + /* if required. */ + if (vertexmark(newvertex) == 0) { + setvertexmark(newvertex, mark(*splitseg)); + } + } + + if (m->checkquality) { + poolrestart(&m->flipstackers); + m->lastflip = (struct flipstacker *) poolalloc(&m->flipstackers); + m->lastflip->flippedtri = encode(horiz); + m->lastflip->prevflip = (struct flipstacker *) &insertvertex; + } + +#ifdef SELF_CHECK + if (counterclockwise(m, b, rightvertex, leftvertex, botvertex) < 0.0) + { + printf("Internal error in insertvertex():\n"); + printf( + " Clockwise triangle prior to edge vertex insertion (bottom).\n"); + } + if (mirrorflag) + { + if (counterclockwise(m, b, leftvertex, rightvertex, topvertex) < 0.0) + { + printf("Internal error in insertvertex():\n"); + printf(" Clockwise triangle prior to edge vertex insertion (top).\n"); + } + if (counterclockwise(m, b, rightvertex, topvertex, newvertex) < 0.0) + { + printf("Internal error in insertvertex():\n"); + printf( + " Clockwise triangle after edge vertex insertion (top right).\n"); + } + if (counterclockwise(m, b, topvertex, leftvertex, newvertex) < 0.0) + { + printf("Internal error in insertvertex():\n"); + printf( + " Clockwise triangle after edge vertex insertion (top left).\n"); + } + } + if (counterclockwise(m, b, leftvertex, botvertex, newvertex) < 0.0) + { + printf("Internal error in insertvertex():\n"); + printf( + " Clockwise triangle after edge vertex insertion (bottom left).\n"); + } + if (counterclockwise(m, b, botvertex, rightvertex, newvertex) < 0.0) + { + printf("Internal error in insertvertex():\n"); + printf( + " Clockwise triangle after edge vertex insertion (bottom right).\n"); + } +#endif /* SELF_CHECK */ + if (b->verbose > 2) { + printf(" Updating bottom left "); + printtriangle(m, b, &botright); + if (mirrorflag) { + printf(" Updating top left "); + printtriangle(m, b, &topright); + printf(" Creating top right "); + printtriangle(m, b, &newtopright); + } + printf(" Creating bottom right "); + printtriangle(m, b, &newbotright); + } + + /* Position `horiz' on the first edge to check for */ + /* the Delaunay property. */ + lnextself(horiz); + } + else { + /* Insert the vertex in a triangle, splitting it into three. */ + lnext(horiz, botleft); + lprev(horiz, botright); + sym(botleft, botlcasing); + sym(botright, botrcasing); + maketriangle(m, b, &newbotleft); + maketriangle(m, b, &newbotright); + + /* Set the vertices of changed and new triangles. */ + org(horiz, rightvertex); + dest(horiz, leftvertex); + apex(horiz, botvertex); + setorg(newbotleft, leftvertex); + setdest(newbotleft, botvertex); + setapex(newbotleft, newvertex); + setorg(newbotright, botvertex); + setdest(newbotright, rightvertex); + setapex(newbotright, newvertex); + setapex(horiz, newvertex); + for (i = 0; i < m->eextras; i++) { + /* Set the element attributes of the new triangles. */ + attrib = elemattribute(horiz, i); + setelemattribute(newbotleft, i, attrib); + setelemattribute(newbotright, i, attrib); + } + if (b->vararea) { + /* Set the area constraint of the new triangles. */ + area = areabound(horiz); + setareabound(newbotleft, area); + setareabound(newbotright, area); + } + + /* There may be subsegments that need to be bonded */ + /* to the new triangles. */ + if (m->checksegments) { + tspivot(botleft, botlsubseg); + if (botlsubseg.ss != m->dummysub) { + tsdissolve(botleft); + tsbond(newbotleft, botlsubseg); + } + tspivot(botright, botrsubseg); + if (botrsubseg.ss != m->dummysub) { + tsdissolve(botright); + tsbond(newbotright, botrsubseg); + } + } + + /* Bond the new triangles to the surrounding triangles. */ + bond(newbotleft, botlcasing); + bond(newbotright, botrcasing); + lnextself(newbotleft); + lprevself(newbotright); + bond(newbotleft, newbotright); + lnextself(newbotleft); + bond(botleft, newbotleft); + lprevself(newbotright); + bond(botright, newbotright); + + if (m->checkquality) { + poolrestart(&m->flipstackers); + m->lastflip = (struct flipstacker *) poolalloc(&m->flipstackers); + m->lastflip->flippedtri = encode(horiz); + m->lastflip->prevflip = (struct flipstacker *) NULL; + } + +#ifdef SELF_CHECK + if (counterclockwise(m, b, rightvertex, leftvertex, botvertex) < 0.0) + { + printf("Internal error in insertvertex():\n"); + printf(" Clockwise triangle prior to vertex insertion.\n"); + } + if (counterclockwise(m, b, rightvertex, leftvertex, newvertex) < 0.0) + { + printf("Internal error in insertvertex():\n"); + printf(" Clockwise triangle after vertex insertion (top).\n"); + } + if (counterclockwise(m, b, leftvertex, botvertex, newvertex) < 0.0) + { + printf("Internal error in insertvertex():\n"); + printf(" Clockwise triangle after vertex insertion (left).\n"); + } + if (counterclockwise(m, b, botvertex, rightvertex, newvertex) < 0.0) + { + printf("Internal error in insertvertex():\n"); + printf(" Clockwise triangle after vertex insertion (right).\n"); + } +#endif /* SELF_CHECK */ + if (b->verbose > 2) { + printf(" Updating top "); + printtriangle(m, b, &horiz); + printf(" Creating left "); + printtriangle(m, b, &newbotleft); + printf(" Creating right "); + printtriangle(m, b, &newbotright); + } + } + + /* The insertion is successful by default, unless an encroached */ + /* subsegment is found. */ + success = SUCCESSFULVERTEX; + /* Circle around the newly inserted vertex, checking each edge opposite */ + /* it for the Delaunay property. Non-Delaunay edges are flipped. */ + /* `horiz' is always the edge being checked. `first' marks where to */ + /* stop circling. */ + org(horiz, first); + rightvertex = first; + dest(horiz, leftvertex); + /* Circle until finished. */ + while (1) { + /* By default, the edge will be flipped. */ + doflip = 1; + + if (m->checksegments) { + /* Check for a subsegment, which cannot be flipped. */ + tspivot(horiz, checksubseg); + if (checksubseg.ss != m->dummysub) { + /* The edge is a subsegment and cannot be flipped. */ + doflip = 0; +#ifndef CDT_ONLY + if (segmentflaws) + { + /* Does the new vertex encroach upon this subsegment? */ + if (checkseg4encroach(m, b, &checksubseg)) + { + success = ENCROACHINGVERTEX; + } + } +#endif /* not CDT_ONLY */ + } + } + + if (doflip) { + /* Check if the edge is a boundary edge. */ + sym(horiz, top); + if (top.tri == m->dummytri) { + /* The edge is a boundary edge and cannot be flipped. */ + doflip = 0; + } + else { + /* Find the vertex on the other side of the edge. */ + apex(top, farvertex); + /* In the incremental Delaunay triangulation algorithm, any of */ + /* `leftvertex', `rightvertex', and `farvertex' could be vertices */ + /* of the triangular bounding box. These vertices must be */ + /* treated as if they are infinitely distant, even though their */ + /* "coordinates" are not. */ + if ((leftvertex == m->infvertex1) || (leftvertex == m->infvertex2) + || (leftvertex == m->infvertex3)) { + /* `leftvertex' is infinitely distant. Check the convexity of */ + /* the boundary of the triangulation. 'farvertex' might be */ + /* infinite as well, but trust me, this same condition should */ + /* be applied. */ + doflip = counterclockwise(m, b, newvertex, rightvertex, farvertex) > 0.0; + } + else if ((rightvertex == m->infvertex1) || (rightvertex == m->infvertex2) + || (rightvertex == m->infvertex3)) { + /* `rightvertex' is infinitely distant. Check the convexity of */ + /* the boundary of the triangulation. 'farvertex' might be */ + /* infinite as well, but trust me, this same condition should */ + /* be applied. */ + doflip = counterclockwise(m, b, farvertex, leftvertex, newvertex) > 0.0; + } + else if ((farvertex == m->infvertex1) || (farvertex == m->infvertex2) + || (farvertex == m->infvertex3)) { + /* `farvertex' is infinitely distant and cannot be inside */ + /* the circumcircle of the triangle `horiz'. */ + doflip = 0; + } + else { + /* Test whether the edge is locally Delaunay. */ + doflip = incircle(m, b, leftvertex, newvertex, rightvertex, farvertex) > 0.0; + } + if (doflip) { + /* We made it! Flip the edge `horiz' by rotating its containing */ + /* quadrilateral (the two triangles adjacent to `horiz'). */ + /* Identify the casing of the quadrilateral. */ + lprev(top, topleft); + sym(topleft, toplcasing); + lnext(top, topright); + sym(topright, toprcasing); + lnext(horiz, botleft); + sym(botleft, botlcasing); + lprev(horiz, botright); + sym(botright, botrcasing); + /* Rotate the quadrilateral one-quarter turn counterclockwise. */ + bond(topleft, botlcasing); + bond(botleft, botrcasing); + bond(botright, toprcasing); + bond(topright, toplcasing); + if (m->checksegments) { + /* Check for subsegments and rebond them to the quadrilateral. */ + tspivot(topleft, toplsubseg); + tspivot(botleft, botlsubseg); + tspivot(botright, botrsubseg); + tspivot(topright, toprsubseg); + if (toplsubseg.ss == m->dummysub) { + tsdissolve(topright); + } + else { + tsbond(topright, toplsubseg); + } + if (botlsubseg.ss == m->dummysub) { + tsdissolve(topleft); + } + else { + tsbond(topleft, botlsubseg); + } + if (botrsubseg.ss == m->dummysub) { + tsdissolve(botleft); + } + else { + tsbond(botleft, botrsubseg); + } + if (toprsubseg.ss == m->dummysub) { + tsdissolve(botright); + } + else { + tsbond(botright, toprsubseg); + } + } + /* New vertex assignments for the rotated quadrilateral. */ + setorg(horiz, farvertex); + setdest(horiz, newvertex); + setapex(horiz, rightvertex); + setorg(top, newvertex); + setdest(top, farvertex); + setapex(top, leftvertex); + for (i = 0; i < m->eextras; i++) { + /* Take the average of the two triangles' attributes. */ + attrib = 0.5 * (elemattribute(top, i) + elemattribute(horiz, i)); + setelemattribute(top, i, attrib); + setelemattribute(horiz, i, attrib); + } + if (b->vararea) { + if ((areabound(top) <= 0.0) || (areabound(horiz) <= 0.0)) { + area = -1.0; + } + else { + /* Take the average of the two triangles' area constraints. */ + /* This prevents small area constraints from migrating a */ + /* long, long way from their original location due to flips. */ + area = 0.5 * (areabound(top) + areabound(horiz)); + } + setareabound(top, area); + setareabound(horiz, area); + } + + if (m->checkquality) { + newflip = (struct flipstacker *) poolalloc(&m->flipstackers); + newflip->flippedtri = encode(horiz); + newflip->prevflip = m->lastflip; + m->lastflip = newflip; + } + +#ifdef SELF_CHECK + if (newvertex != (vertex) NULL) + { + if (counterclockwise(m, b, leftvertex, newvertex, rightvertex) < + 0.0) + { + printf("Internal error in insertvertex():\n"); + printf(" Clockwise triangle prior to edge flip (bottom).\n"); + } + /* The following test has been removed because constrainededge() */ + /* sometimes generates inverted triangles that insertvertex() */ + /* removes. */ + /* + if (counterclockwise(m, b, rightvertex, farvertex, leftvertex) < + 0.0) { + printf("Internal error in insertvertex():\n"); + printf(" Clockwise triangle prior to edge flip (top).\n"); + } + */ + if (counterclockwise(m, b, farvertex, leftvertex, newvertex) < + 0.0) + { + printf("Internal error in insertvertex():\n"); + printf(" Clockwise triangle after edge flip (left).\n"); + } + if (counterclockwise(m, b, newvertex, rightvertex, farvertex) < + 0.0) + { + printf("Internal error in insertvertex():\n"); + printf(" Clockwise triangle after edge flip (right).\n"); + } + } +#endif /* SELF_CHECK */ + if (b->verbose > 2) { + printf(" Edge flip results in left "); + lnextself(topleft); + printtriangle(m, b, &topleft); + printf(" and right "); + printtriangle(m, b, &horiz); + } + /* On the next iterations, consider the two edges that were */ + /* exposed (this is, are now visible to the newly inserted */ + /* vertex) by the edge flip. */ + lprevself(horiz); + leftvertex = farvertex; + } + } + } + if (!doflip) { + /* The handle `horiz' is accepted as locally Delaunay. */ +#ifndef CDT_ONLY + if (triflaws) + { + /* Check the triangle `horiz' for quality. */ + testtriangle(m, b, &horiz); + } +#endif /* not CDT_ONLY */ + /* Look for the next edge around the newly inserted vertex. */ + lnextself(horiz); + sym(horiz, testtri); + /* Check for finishing a complete revolution about the new vertex, or */ + /* falling outside of the triangulation. The latter will happen */ + /* when a vertex is inserted at a boundary. */ + if ((leftvertex == first) || (testtri.tri == m->dummytri)) { + /* We're done. Return a triangle whose origin is the new vertex. */ + lnext(horiz, *searchtri); + lnext(horiz, m->recenttri); + return success; + } + /* Finish finding the next edge around the newly inserted vertex. */ + lnext(testtri, horiz); + rightvertex = leftvertex; + dest(horiz, leftvertex); + } + } +} + +/********* Divide-and-conquer Delaunay triangulation begins here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* The divide-and-conquer bounding box */ +/* */ +/* I originally implemented the divide-and-conquer and incremental Delaunay */ +/* triangulations using the edge-based data structure presented by Guibas */ +/* and Stolfi. Switching to a triangle-based data structure doubled the */ +/* speed. However, I had to think of a few extra tricks to maintain the */ +/* elegance of the original algorithms. */ +/* */ +/* The "bounding box" used by my variant of the divide-and-conquer */ +/* algorithm uses one triangle for each edge of the convex hull of the */ +/* triangulation. These bounding triangles all share a common apical */ +/* vertex, which is represented by NULL and which represents nothing. */ +/* The bounding triangles are linked in a circular fan about this NULL */ +/* vertex, and the edges on the convex hull of the triangulation appear */ +/* opposite the NULL vertex. You might find it easiest to imagine that */ +/* the NULL vertex is a point in 3D space behind the center of the */ +/* triangulation, and that the bounding triangles form a sort of cone. */ +/* */ +/* This bounding box makes it easy to represent degenerate cases. For */ +/* instance, the triangulation of two vertices is a single edge. This edge */ +/* is represented by two bounding box triangles, one on each "side" of the */ +/* edge. These triangles are also linked together in a fan about the NULL */ +/* vertex. */ +/* */ +/* The bounding box also makes it easy to traverse the convex hull, as the */ +/* divide-and-conquer algorithm needs to do. */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* */ +/* vertexsort() Sort an array of vertices by x-coordinate, using the */ +/* y-coordinate as a secondary key. */ +/* */ +/* Uses quicksort. Randomized O(n log n) time. No, I did not make any of */ +/* the usual quicksort mistakes. */ +/* */ +/*****************************************************************************/ + +void vertexsort(vertex *sortarray, int arraysize) { + int left, right; + int pivot; + REAL pivotx, pivoty; + vertex temp; + + if (arraysize == 2) { + /* Recursive base case. */ + if ((sortarray[0][0] > sortarray[1][0]) + || ((sortarray[0][0] == sortarray[1][0]) && (sortarray[0][1] > sortarray[1][1]))) { + temp = sortarray[1]; + sortarray[1] = sortarray[0]; + sortarray[0] = temp; + } + return; + } + /* Choose a random pivot to split the array. */ + pivot = (int) randomnation((unsigned int) arraysize); + pivotx = sortarray[pivot][0]; + pivoty = sortarray[pivot][1]; + /* Split the array. */ + left = -1; + right = arraysize; + while (left < right) { + /* Search for a vertex whose x-coordinate is too large for the left. */ + do { + left++; + } while ((left <= right) + && ((sortarray[left][0] < pivotx) + || ((sortarray[left][0] == pivotx) && (sortarray[left][1] < pivoty)))); + /* Search for a vertex whose x-coordinate is too small for the right. */ + do { + right--; + } while ((left <= right) + && ((sortarray[right][0] > pivotx) + || ((sortarray[right][0] == pivotx) && (sortarray[right][1] > pivoty)))); + if (left < right) { + /* Swap the left and right vertices. */ + temp = sortarray[left]; + sortarray[left] = sortarray[right]; + sortarray[right] = temp; + } + } + if (left > 1) { + /* Recursively sort the left subset. */ + vertexsort(sortarray, left); + } + if (right < arraysize - 2) { + /* Recursively sort the right subset. */ + vertexsort(&sortarray[right + 1], arraysize - right - 1); + } +} + +/*****************************************************************************/ +/* */ +/* vertexmedian() An order statistic algorithm, almost. Shuffles an */ +/* array of vertices so that the first `median' vertices */ +/* occur lexicographically before the remaining vertices. */ +/* */ +/* Uses the x-coordinate as the primary key if axis == 0; the y-coordinate */ +/* if axis == 1. Very similar to the vertexsort() procedure, but runs in */ +/* randomized linear time. */ +/* */ +/*****************************************************************************/ + +void vertexmedian(vertex *sortarray, int arraysize, int median, int axis) { + int left, right; + int pivot; + REAL pivot1, pivot2; + vertex temp; + + if (arraysize == 2) { + /* Recursive base case. */ + if ((sortarray[0][axis] > sortarray[1][axis]) + || ((sortarray[0][axis] == sortarray[1][axis]) + && (sortarray[0][1 - axis] > sortarray[1][1 - axis]))) { + temp = sortarray[1]; + sortarray[1] = sortarray[0]; + sortarray[0] = temp; + } + return; + } + /* Choose a random pivot to split the array. */ + pivot = (int) randomnation((unsigned int) arraysize); + pivot1 = sortarray[pivot][axis]; + pivot2 = sortarray[pivot][1 - axis]; + /* Split the array. */ + left = -1; + right = arraysize; + while (left < right) { + /* Search for a vertex whose x-coordinate is too large for the left. */ + do { + left++; + } while ((left <= right) + && ((sortarray[left][axis] < pivot1) + || ((sortarray[left][axis] == pivot1) && (sortarray[left][1 - axis] < pivot2)))); + /* Search for a vertex whose x-coordinate is too small for the right. */ + do { + right--; + } while ((left <= right) + && ((sortarray[right][axis] > pivot1) + || ((sortarray[right][axis] == pivot1) && (sortarray[right][1 - axis] > pivot2)))); + if (left < right) { + /* Swap the left and right vertices. */ + temp = sortarray[left]; + sortarray[left] = sortarray[right]; + sortarray[right] = temp; + } + } + /* Unlike in vertexsort(), at most one of the following */ + /* conditionals is true. */ + if (left > median) { + /* Recursively shuffle the left subset. */ + vertexmedian(sortarray, left, median, axis); + } + if (right < median - 1) { + /* Recursively shuffle the right subset. */ + vertexmedian(&sortarray[right + 1], arraysize - right - 1, median - right - 1, axis); + } +} + +/*****************************************************************************/ +/* */ +/* alternateaxes() Sorts the vertices as appropriate for the divide-and- */ +/* conquer algorithm with alternating cuts. */ +/* */ +/* Partitions by x-coordinate if axis == 0; by y-coordinate if axis == 1. */ +/* For the base case, subsets containing only two or three vertices are */ +/* always sorted by x-coordinate. */ +/* */ +/*****************************************************************************/ + +void alternateaxes(vertex *sortarray, int arraysize, int axis) { + int divider; + + divider = arraysize >> 1; + if (arraysize <= 3) { + /* Recursive base case: subsets of two or three vertices will be */ + /* handled specially, and should always be sorted by x-coordinate. */ + axis = 0; + } + /* Partition with a horizontal or vertical cut. */ + vertexmedian(sortarray, arraysize, divider, axis); + /* Recursively partition the subsets with a cross cut. */ + if (arraysize - divider >= 2) { + if (divider >= 2) { + alternateaxes(sortarray, divider, 1 - axis); + } + alternateaxes(&sortarray[divider], arraysize - divider, 1 - axis); + } +} + +/*****************************************************************************/ +/* */ +/* mergehulls() Merge two adjacent Delaunay triangulations into a */ +/* single Delaunay triangulation. */ +/* */ +/* This is similar to the algorithm given by Guibas and Stolfi, but uses */ +/* a triangle-based, rather than edge-based, data structure. */ +/* */ +/* The algorithm walks up the gap between the two triangulations, knitting */ +/* them together. As they are merged, some of their bounding triangles */ +/* are converted into real triangles of the triangulation. The procedure */ +/* pulls each hull's bounding triangles apart, then knits them together */ +/* like the teeth of two gears. The Delaunay property determines, at each */ +/* step, whether the next "tooth" is a bounding triangle of the left hull */ +/* or the right. When a bounding triangle becomes real, its apex is */ +/* changed from NULL to a real vertex. */ +/* */ +/* Only two new triangles need to be allocated. These become new bounding */ +/* triangles at the top and bottom of the seam. They are used to connect */ +/* the remaining bounding triangles (those that have not been converted */ +/* into real triangles) into a single fan. */ +/* */ +/* On entry, `farleft' and `innerleft' are bounding triangles of the left */ +/* triangulation. The origin of `farleft' is the leftmost vertex, and */ +/* the destination of `innerleft' is the rightmost vertex of the */ +/* triangulation. Similarly, `innerright' and `farright' are bounding */ +/* triangles of the right triangulation. The origin of `innerright' and */ +/* destination of `farright' are the leftmost and rightmost vertices. */ +/* */ +/* On completion, the origin of `farleft' is the leftmost vertex of the */ +/* merged triangulation, and the destination of `farright' is the rightmost */ +/* vertex. */ +/* */ +/*****************************************************************************/ + +void mergehulls(struct mesh *m, struct behavior *b, struct otri *farleft, struct otri *innerleft, + struct otri *innerright, struct otri *farright, int axis) { + struct otri leftcand, rightcand; + struct otri baseedge; + struct otri nextedge; + struct otri sidecasing, topcasing, outercasing; + struct otri checkedge; + vertex innerleftdest; + vertex innerrightorg; + vertex innerleftapex, innerrightapex; + vertex farleftpt, farrightpt; + vertex farleftapex, farrightapex; + vertex lowerleft, lowerright; + vertex upperleft, upperright; + vertex nextapex; + vertex checkvertex; + int changemade; + int badedge; + int leftfinished, rightfinished; + triangle ptr; /* Temporary variable used by sym(). */ + + dest(*innerleft, innerleftdest); + apex(*innerleft, innerleftapex); + org(*innerright, innerrightorg); + apex(*innerright, innerrightapex); + /* Special treatment for horizontal cuts. */ + if (b->dwyer && (axis == 1)) { + org(*farleft, farleftpt); + apex(*farleft, farleftapex); + dest(*farright, farrightpt); + apex(*farright, farrightapex); + /* The pointers to the extremal vertices are shifted to point to the */ + /* topmost and bottommost vertex of each hull, rather than the */ + /* leftmost and rightmost vertices. */ + while (farleftapex[1] < farleftpt[1]) { + lnextself(*farleft); + symself(*farleft); + farleftpt = farleftapex; + apex(*farleft, farleftapex); + } + sym(*innerleft, checkedge); + apex(checkedge, checkvertex); + while (checkvertex[1] > innerleftdest[1]) { + lnext(checkedge, *innerleft); + innerleftapex = innerleftdest; + innerleftdest = checkvertex; + sym(*innerleft, checkedge); + apex(checkedge, checkvertex); + } + while (innerrightapex[1] < innerrightorg[1]) { + lnextself(*innerright); + symself(*innerright); + innerrightorg = innerrightapex; + apex(*innerright, innerrightapex); + } + sym(*farright, checkedge); + apex(checkedge, checkvertex); + while (checkvertex[1] > farrightpt[1]) { + lnext(checkedge, *farright); + farrightapex = farrightpt; + farrightpt = checkvertex; + sym(*farright, checkedge); + apex(checkedge, checkvertex); + } + } + /* Find a line tangent to and below both hulls. */ + do { + changemade = 0; + /* Make innerleftdest the "bottommost" vertex of the left hull. */ + if (counterclockwise(m, b, innerleftdest, innerleftapex, innerrightorg) > 0.0) { + lprevself(*innerleft); + symself(*innerleft); + innerleftdest = innerleftapex; + apex(*innerleft, innerleftapex); + changemade = 1; + } + /* Make innerrightorg the "bottommost" vertex of the right hull. */ + if (counterclockwise(m, b, innerrightapex, innerrightorg, innerleftdest) > 0.0) { + lnextself(*innerright); + symself(*innerright); + innerrightorg = innerrightapex; + apex(*innerright, innerrightapex); + changemade = 1; + } + } while (changemade); + /* Find the two candidates to be the next "gear tooth." */ + sym(*innerleft, leftcand); + sym(*innerright, rightcand); + /* Create the bottom new bounding triangle. */ + maketriangle(m, b, &baseedge); + /* Connect it to the bounding boxes of the left and right triangulations. */ + bond(baseedge, *innerleft); + lnextself(baseedge); + bond(baseedge, *innerright); + lnextself(baseedge); + setorg(baseedge, innerrightorg); + setdest(baseedge, innerleftdest); + /* Apex is intentionally left NULL. */ + if (b->verbose > 2) { + printf(" Creating base bounding "); + printtriangle(m, b, &baseedge); + } + /* Fix the extreme triangles if necessary. */ + org(*farleft, farleftpt); + if (innerleftdest == farleftpt) { + lnext(baseedge, *farleft); + } + dest(*farright, farrightpt); + if (innerrightorg == farrightpt) { + lprev(baseedge, *farright); + } + /* The vertices of the current knitting edge. */ + lowerleft = innerleftdest; + lowerright = innerrightorg; + /* The candidate vertices for knitting. */ + apex(leftcand, upperleft); + apex(rightcand, upperright); + /* Walk up the gap between the two triangulations, knitting them together. */ + while (1) { + /* Have we reached the top? (This isn't quite the right question, */ + /* because even though the left triangulation might seem finished now, */ + /* moving up on the right triangulation might reveal a new vertex of */ + /* the left triangulation. And vice-versa.) */ + leftfinished = counterclockwise(m, b, upperleft, lowerleft, lowerright) <= 0.0; + rightfinished = counterclockwise(m, b, upperright, lowerleft, lowerright) <= 0.0; + if (leftfinished && rightfinished) { + /* Create the top new bounding triangle. */ + maketriangle(m, b, &nextedge); + setorg(nextedge, lowerleft); + setdest(nextedge, lowerright); + /* Apex is intentionally left NULL. */ + /* Connect it to the bounding boxes of the two triangulations. */ + bond(nextedge, baseedge); + lnextself(nextedge); + bond(nextedge, rightcand); + lnextself(nextedge); + bond(nextedge, leftcand); + if (b->verbose > 2) { + printf(" Creating top bounding "); + printtriangle(m, b, &nextedge); + } + /* Special treatment for horizontal cuts. */ + if (b->dwyer && (axis == 1)) { + org(*farleft, farleftpt); + apex(*farleft, farleftapex); + dest(*farright, farrightpt); + apex(*farright, farrightapex); + sym(*farleft, checkedge); + apex(checkedge, checkvertex); + /* The pointers to the extremal vertices are restored to the */ + /* leftmost and rightmost vertices (rather than topmost and */ + /* bottommost). */ + while (checkvertex[0] < farleftpt[0]) { + lprev(checkedge, *farleft); + farleftapex = farleftpt; + farleftpt = checkvertex; + sym(*farleft, checkedge); + apex(checkedge, checkvertex); + } + while (farrightapex[0] > farrightpt[0]) { + lprevself(*farright); + symself(*farright); + farrightpt = farrightapex; + apex(*farright, farrightapex); + } + } + return; + } + /* Consider eliminating edges from the left triangulation. */ + if (!leftfinished) { + /* What vertex would be exposed if an edge were deleted? */ + lprev(leftcand, nextedge); + symself(nextedge); + apex(nextedge, nextapex); + /* If nextapex is NULL, then no vertex would be exposed; the */ + /* triangulation would have been eaten right through. */ + if (nextapex != (vertex) NULL) { + /* Check whether the edge is Delaunay. */ + badedge = incircle(m, b, lowerleft, lowerright, upperleft, nextapex) > 0.0; + while (badedge) { + /* Eliminate the edge with an edge flip. As a result, the */ + /* left triangulation will have one more boundary triangle. */ + lnextself(nextedge); + sym(nextedge, topcasing); + lnextself(nextedge); + sym(nextedge, sidecasing); + bond(nextedge, topcasing); + bond(leftcand, sidecasing); + lnextself(leftcand); + sym(leftcand, outercasing); + lprevself(nextedge); + bond(nextedge, outercasing); + /* Correct the vertices to reflect the edge flip. */ + setorg(leftcand, lowerleft); + setdest(leftcand, NULL); + setapex(leftcand, nextapex); + setorg(nextedge, NULL); + setdest(nextedge, upperleft); + setapex(nextedge, nextapex); + /* Consider the newly exposed vertex. */ + upperleft = nextapex; + /* What vertex would be exposed if another edge were deleted? */ + otricopy(sidecasing, nextedge); + apex(nextedge, nextapex); + if (nextapex != (vertex) NULL) { + /* Check whether the edge is Delaunay. */ + badedge = incircle(m, b, lowerleft, lowerright, upperleft, nextapex) > 0.0; + } + else { + /* Avoid eating right through the triangulation. */ + badedge = 0; + } + } + } + } + /* Consider eliminating edges from the right triangulation. */ + if (!rightfinished) { + /* What vertex would be exposed if an edge were deleted? */ + lnext(rightcand, nextedge); + symself(nextedge); + apex(nextedge, nextapex); + /* If nextapex is NULL, then no vertex would be exposed; the */ + /* triangulation would have been eaten right through. */ + if (nextapex != (vertex) NULL) { + /* Check whether the edge is Delaunay. */ + badedge = incircle(m, b, lowerleft, lowerright, upperright, nextapex) > 0.0; + while (badedge) { + /* Eliminate the edge with an edge flip. As a result, the */ + /* right triangulation will have one more boundary triangle. */ + lprevself(nextedge); + sym(nextedge, topcasing); + lprevself(nextedge); + sym(nextedge, sidecasing); + bond(nextedge, topcasing); + bond(rightcand, sidecasing); + lprevself(rightcand); + sym(rightcand, outercasing); + lnextself(nextedge); + bond(nextedge, outercasing); + /* Correct the vertices to reflect the edge flip. */ + setorg(rightcand, NULL); + setdest(rightcand, lowerright); + setapex(rightcand, nextapex); + setorg(nextedge, upperright); + setdest(nextedge, NULL); + setapex(nextedge, nextapex); + /* Consider the newly exposed vertex. */ + upperright = nextapex; + /* What vertex would be exposed if another edge were deleted? */ + otricopy(sidecasing, nextedge); + apex(nextedge, nextapex); + if (nextapex != (vertex) NULL) { + /* Check whether the edge is Delaunay. */ + badedge = incircle(m, b, lowerleft, lowerright, upperright, nextapex) > 0.0; + } + else { + /* Avoid eating right through the triangulation. */ + badedge = 0; + } + } + } + } + if (leftfinished + || (!rightfinished + && (incircle(m, b, upperleft, lowerleft, lowerright, upperright) > 0.0))) { + /* Knit the triangulations, adding an edge from `lowerleft' */ + /* to `upperright'. */ + bond(baseedge, rightcand); + lprev(rightcand, baseedge); + setdest(baseedge, lowerleft); + lowerright = upperright; + sym(baseedge, rightcand); + apex(rightcand, upperright); + } + else { + /* Knit the triangulations, adding an edge from `upperleft' */ + /* to `lowerright'. */ + bond(baseedge, leftcand); + lnext(leftcand, baseedge); + setorg(baseedge, lowerright); + lowerleft = upperleft; + sym(baseedge, leftcand); + apex(leftcand, upperleft); + } + if (b->verbose > 2) { + printf(" Connecting "); + printtriangle(m, b, &baseedge); + } + } +} + +/*****************************************************************************/ +/* */ +/* divconqrecurse() Recursively form a Delaunay triangulation by the */ +/* divide-and-conquer method. */ +/* */ +/* Recursively breaks down the problem into smaller pieces, which are */ +/* knitted together by mergehulls(). The base cases (problems of two or */ +/* three vertices) are handled specially here. */ +/* */ +/* On completion, `farleft' and `farright' are bounding triangles such that */ +/* the origin of `farleft' is the leftmost vertex (breaking ties by */ +/* choosing the highest leftmost vertex), and the destination of */ +/* `farright' is the rightmost vertex (breaking ties by choosing the */ +/* lowest rightmost vertex). */ +/* */ +/*****************************************************************************/ + +void divconqrecurse(struct mesh *m, struct behavior *b, vertex *sortarray, int vertices, int axis, + struct otri *farleft, struct otri *farright) { + struct otri midtri, tri1, tri2, tri3; + struct otri innerleft, innerright; + REAL area; + int divider; + + if (b->verbose > 2) { + printf(" Triangulating %d vertices.\n", vertices); + } + if (vertices == 2) { + /* The triangulation of two vertices is an edge. An edge is */ + /* represented by two bounding triangles. */ + maketriangle(m, b, farleft); + setorg(*farleft, sortarray[0]); + setdest(*farleft, sortarray[1]); + /* The apex is intentionally left NULL. */ + maketriangle(m, b, farright); + setorg(*farright, sortarray[1]); + setdest(*farright, sortarray[0]); + /* The apex is intentionally left NULL. */ + bond(*farleft, *farright); + lprevself(*farleft); + lnextself(*farright); + bond(*farleft, *farright); + lprevself(*farleft); + lnextself(*farright); + bond(*farleft, *farright); + if (b->verbose > 2) { + printf(" Creating "); + printtriangle(m, b, farleft); + printf(" Creating "); + printtriangle(m, b, farright); + } + /* Ensure that the origin of `farleft' is sortarray[0]. */ + lprev(*farright, *farleft); + return; + } + else if (vertices == 3) { + /* The triangulation of three vertices is either a triangle (with */ + /* three bounding triangles) or two edges (with four bounding */ + /* triangles). In either case, four triangles are created. */ + maketriangle(m, b, &midtri); + maketriangle(m, b, &tri1); + maketriangle(m, b, &tri2); + maketriangle(m, b, &tri3); + area = counterclockwise(m, b, sortarray[0], sortarray[1], sortarray[2]); + if (area == 0.0) { + /* Three collinear vertices; the triangulation is two edges. */ + setorg(midtri, sortarray[0]); + setdest(midtri, sortarray[1]); + setorg(tri1, sortarray[1]); + setdest(tri1, sortarray[0]); + setorg(tri2, sortarray[2]); + setdest(tri2, sortarray[1]); + setorg(tri3, sortarray[1]); + setdest(tri3, sortarray[2]); + /* All apices are intentionally left NULL. */ + bond(midtri, tri1); + bond(tri2, tri3); + lnextself(midtri); + lprevself(tri1); + lnextself(tri2); + lprevself(tri3); + bond(midtri, tri3); + bond(tri1, tri2); + lnextself(midtri); + lprevself(tri1); + lnextself(tri2); + lprevself(tri3); + bond(midtri, tri1); + bond(tri2, tri3); + /* Ensure that the origin of `farleft' is sortarray[0]. */ + otricopy(tri1, *farleft); + /* Ensure that the destination of `farright' is sortarray[2]. */ + otricopy(tri2, *farright); + } + else { + /* The three vertices are not collinear; the triangulation is one */ + /* triangle, namely `midtri'. */ + setorg(midtri, sortarray[0]); + setdest(tri1, sortarray[0]); + setorg(tri3, sortarray[0]); + /* Apices of tri1, tri2, and tri3 are left NULL. */ + if (area > 0.0) { + /* The vertices are in counterclockwise order. */ + setdest(midtri, sortarray[1]); + setorg(tri1, sortarray[1]); + setdest(tri2, sortarray[1]); + setapex(midtri, sortarray[2]); + setorg(tri2, sortarray[2]); + setdest(tri3, sortarray[2]); + } + else { + /* The vertices are in clockwise order. */ + setdest(midtri, sortarray[2]); + setorg(tri1, sortarray[2]); + setdest(tri2, sortarray[2]); + setapex(midtri, sortarray[1]); + setorg(tri2, sortarray[1]); + setdest(tri3, sortarray[1]); + } + /* The topology does not depend on how the vertices are ordered. */ + bond(midtri, tri1); + lnextself(midtri); + bond(midtri, tri2); + lnextself(midtri); + bond(midtri, tri3); + lprevself(tri1); + lnextself(tri2); + bond(tri1, tri2); + lprevself(tri1); + lprevself(tri3); + bond(tri1, tri3); + lnextself(tri2); + lprevself(tri3); + bond(tri2, tri3); + /* Ensure that the origin of `farleft' is sortarray[0]. */ + otricopy(tri1, *farleft); + /* Ensure that the destination of `farright' is sortarray[2]. */ + if (area > 0.0) { + otricopy(tri2, *farright); + } + else { + lnext(*farleft, *farright); + } + } + if (b->verbose > 2) { + printf(" Creating "); + printtriangle(m, b, &midtri); + printf(" Creating "); + printtriangle(m, b, &tri1); + printf(" Creating "); + printtriangle(m, b, &tri2); + printf(" Creating "); + printtriangle(m, b, &tri3); + } + return; + } + else { + /* Split the vertices in half. */ + divider = vertices >> 1; + /* Recursively triangulate each half. */ + divconqrecurse(m, b, sortarray, divider, 1 - axis, farleft, &innerleft); + divconqrecurse(m, b, &sortarray[divider], vertices - divider, 1 - axis, &innerright, + farright); + if (b->verbose > 1) { + printf(" Joining triangulations with %d and %d vertices.\n", divider, vertices - divider); + } + /* Merge the two triangulations into one. */ + mergehulls(m, b, farleft, &innerleft, &innerright, farright, axis); + } +} + +long removeghosts(struct mesh *m, struct behavior *b, struct otri *startghost) { + struct otri searchedge; + struct otri dissolveedge; + struct otri deadtriangle; + vertex markorg; + long hullsize; + triangle ptr; /* Temporary variable used by sym(). */ + + if (b->verbose) { + printf(" Removing ghost triangles.\n"); + } + /* Find an edge on the convex hull to start point location from. */ + lprev(*startghost, searchedge); + symself(searchedge); + m->dummytri[0] = encode(searchedge); + /* Remove the bounding box and count the convex hull edges. */ + otricopy(*startghost, dissolveedge); + hullsize = 0; + do { + hullsize++; + lnext(dissolveedge, deadtriangle); + lprevself(dissolveedge); + symself(dissolveedge); + /* If no PSLG is involved, set the boundary markers of all the vertices */ + /* on the convex hull. If a PSLG is used, this step is done later. */ + if (!b->poly) { + /* Watch out for the case where all the input vertices are collinear. */ + if (dissolveedge.tri != m->dummytri) { + org(dissolveedge, markorg); + if (vertexmark(markorg) == 0) { + setvertexmark(markorg, 1); + } + } + } + /* Remove a bounding triangle from a convex hull triangle. */ + dissolve(dissolveedge); + /* Find the next bounding triangle. */ + sym(deadtriangle, dissolveedge); + /* Delete the bounding triangle. */ + triangledealloc(m, deadtriangle.tri); + } while (!otriequal(dissolveedge, *startghost)); + return hullsize; +} + +/*****************************************************************************/ +/* */ +/* divconqdelaunay() Form a Delaunay triangulation by the divide-and- */ +/* conquer method. */ +/* */ +/* Sorts the vertices, calls a recursive procedure to triangulate them, and */ +/* removes the bounding box, setting boundary markers as appropriate. */ +/* */ +/*****************************************************************************/ + +long divconqdelaunay(struct mesh *m, struct behavior *b) { + vertex *sortarray; + struct otri hullleft, hullright; + int divider; + int i, j; + + if (b->verbose) { + printf(" Sorting vertices.\n"); + } + + /* Allocate an array of pointers to vertices for sorting. */ + sortarray = (vertex *) trimalloc(m->invertices * (int) sizeof(vertex)); + traversalinit(&m->vertices); + for (i = 0; i < m->invertices; i++) { + sortarray[i] = vertextraverse(m); + } + /* Sort the vertices. */ + vertexsort(sortarray, m->invertices); + /* Discard duplicate vertices, which can really mess up the algorithm. */ + i = 0; + for (j = 1; j < m->invertices; j++) { + if ((sortarray[i][0] == sortarray[j][0]) && (sortarray[i][1] == sortarray[j][1])) { + if (!b->quiet) { + printf( + "Warning: A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n", sortarray[j][0], sortarray[j][1]); + } + setvertextype(sortarray[j], UNDEADVERTEX); + m->undeads++; + } + else { + i++; + sortarray[i] = sortarray[j]; + } + } + i++; + if (b->dwyer) { + /* Re-sort the array of vertices to accommodate alternating cuts. */ + divider = i >> 1; + if (i - divider >= 2) { + if (divider >= 2) { + alternateaxes(sortarray, divider, 1); + } + alternateaxes(&sortarray[divider], i - divider, 1); + } + } + + if (b->verbose) { + printf(" Forming triangulation.\n"); + } + + /* Form the Delaunay triangulation. */ + divconqrecurse(m, b, sortarray, i, 0, &hullleft, &hullright); + trifree((VOID *) sortarray); + + return removeghosts(m, b, &hullleft); +} + +/** **/ +/** **/ +/********* Divide-and-conquer Delaunay triangulation ends here *********/ + +/********* General mesh construction routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* delaunay() Form a Delaunay triangulation. */ +/* */ +/*****************************************************************************/ + +long delaunay(struct mesh *m, struct behavior *b) { + long hulledges; + + m->eextras = 0; + initializetrisubpools(m, b); + +#ifdef REDUCED + if (!b->quiet) { + printf( "Constructing Delaunay triangulation by divide-and-conquer method.\n"); + } + hulledges = divconqdelaunay(m, b); +#else /* not REDUCED */ + if (!b->quiet) + { + printf("Constructing Delaunay triangulation "); + if (b->incremental) + { + printf("by incremental method.\n"); + } + else if (b->sweepline) + { + printf("by sweepline method.\n"); + } + else + { + printf("by divide-and-conquer method.\n"); + } + } + if (b->incremental) + { + hulledges = incrementaldelaunay(m, b); + } + else if (b->sweepline) + { + hulledges = sweeplinedelaunay(m, b); + } + else + { + hulledges = divconqdelaunay(m, b); + } +#endif /* not REDUCED */ + + if (m->triangles.items == 0) { + /* The input vertices were all collinear, so there are no triangles. */ + return 0l; + } + else { + return hulledges; + } +} + +/** **/ +/** **/ +/********* General mesh construction routines end here *********/ + +/********* Segment insertion begins here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* finddirection() Find the first triangle on the path from one point */ +/* to another. */ +/* */ +/* Finds the triangle that intersects a line segment drawn from the */ +/* origin of `searchtri' to the point `searchpoint', and returns the result */ +/* in `searchtri'. The origin of `searchtri' does not change, even though */ +/* the triangle returned may differ from the one passed in. This routine */ +/* is used to find the direction to move in to get from one point to */ +/* another. */ +/* */ +/* The return value notes whether the destination or apex of the found */ +/* triangle is collinear with the two points in question. */ +/* */ +/*****************************************************************************/ + +enum finddirectionresult finddirection(struct mesh *m, struct behavior *b, struct otri *searchtri, + vertex searchpoint) { + struct otri checktri; + vertex startvertex; + vertex leftvertex, rightvertex; + REAL leftccw, rightccw; + int leftflag, rightflag; + triangle ptr; /* Temporary variable used by onext() and oprev(). */ + + org(*searchtri, startvertex); + dest(*searchtri, rightvertex); + apex(*searchtri, leftvertex); + /* Is `searchpoint' to the left? */ + leftccw = counterclockwise(m, b, searchpoint, startvertex, leftvertex); + leftflag = leftccw > 0.0; + /* Is `searchpoint' to the right? */ + rightccw = counterclockwise(m, b, startvertex, searchpoint, rightvertex); + rightflag = rightccw > 0.0; + if (leftflag && rightflag) { + /* `searchtri' faces directly away from `searchpoint'. We could go left */ + /* or right. Ask whether it's a triangle or a boundary on the left. */ + onext(*searchtri, checktri); + if (checktri.tri == m->dummytri) { + leftflag = 0; + } + else { + rightflag = 0; + } + } + while (leftflag) { + /* Turn left until satisfied. */ + onextself(*searchtri); + if (searchtri->tri == m->dummytri) { + printf("Internal error in finddirection(): Unable to find a\n"); + printf(" triangle leading from (%.12g, %.12g) to", startvertex[0], startvertex[1]); + printf(" (%.12g, %.12g).\n", searchpoint[0], searchpoint[1]); + internalerror(); + } + apex(*searchtri, leftvertex); + rightccw = leftccw; + leftccw = counterclockwise(m, b, searchpoint, startvertex, leftvertex); + leftflag = leftccw > 0.0; + } + while (rightflag) { + /* Turn right until satisfied. */ + oprevself(*searchtri); + if (searchtri->tri == m->dummytri) { + printf("Internal error in finddirection(): Unable to find a\n"); + printf(" triangle leading from (%.12g, %.12g) to", startvertex[0], startvertex[1]); + printf(" (%.12g, %.12g).\n", searchpoint[0], searchpoint[1]); + internalerror(); + } + dest(*searchtri, rightvertex); + leftccw = rightccw; + rightccw = counterclockwise(m, b, startvertex, searchpoint, rightvertex); + rightflag = rightccw > 0.0; + } + if (leftccw == 0.0) { + return LEFTCOLLINEAR; + } + else if (rightccw == 0.0) { + return RIGHTCOLLINEAR; + } + else { + return WITHIN; + } +} + +/*****************************************************************************/ +/* */ +/* segmentintersection() Find the intersection of an existing segment */ +/* and a segment that is being inserted. Insert */ +/* a vertex at the intersection, splitting an */ +/* existing subsegment. */ +/* */ +/* The segment being inserted connects the apex of splittri to endpoint2. */ +/* splitsubseg is the subsegment being split, and MUST adjoin splittri. */ +/* Hence, endpoints of the subsegment being split are the origin and */ +/* destination of splittri. */ +/* */ +/* On completion, splittri is a handle having the newly inserted */ +/* intersection point as its origin, and endpoint1 as its destination. */ +/* */ +/*****************************************************************************/ + +void segmentintersection(struct mesh *m, struct behavior *b, struct otri *splittri, + struct osub *splitsubseg, vertex endpoint2) { + struct osub opposubseg; + vertex endpoint1; + vertex torg, tdest; + vertex leftvertex, rightvertex; + vertex newvertex; + enum insertvertexresult success; + //enum finddirectionresult collinear; + REAL ex, ey; + REAL tx, ty; + REAL etx, ety; + REAL split, denom; + int i; + triangle ptr; /* Temporary variable used by onext(). */ + subseg sptr; /* Temporary variable used by snext(). */ + + /* Find the other three segment endpoints. */ + apex(*splittri, endpoint1); + org(*splittri, torg); + dest(*splittri, tdest); + /* Segment intersection formulae; see the Antonio reference. */ + tx = tdest[0] - torg[0]; + ty = tdest[1] - torg[1]; + ex = endpoint2[0] - endpoint1[0]; + ey = endpoint2[1] - endpoint1[1]; + etx = torg[0] - endpoint2[0]; + ety = torg[1] - endpoint2[1]; + denom = ty * ex - tx * ey; + if (denom == 0.0) { + printf("Internal error in segmentintersection():"); + printf(" Attempt to find intersection of parallel segments.\n"); + internalerror(); + return; + } + split = (ey * etx - ex * ety) / denom; + /* Create the new vertex. */ + newvertex = (vertex) poolalloc(&m->vertices); + /* Interpolate its coordinate and attributes. */ + for (i = 0; i < 2 + m->nextras; i++) { + newvertex[i] = torg[i] + split * (tdest[i] - torg[i]); + } + setvertexmark(newvertex, mark(*splitsubseg)); + setvertextype(newvertex, INPUTVERTEX); + if (b->verbose > 1) { + printf( + " Splitting subsegment (%.12g, %.12g) (%.12g, %.12g) at (%.12g, %.12g).\n", torg[0], torg[1], tdest[0], tdest[1], newvertex[0], newvertex[1]); + } + /* Insert the intersection vertex. This should always succeed. */ + success = insertvertex(m, b, newvertex, splittri, splitsubseg, 0, 0); + if (success != SUCCESSFULVERTEX) { + printf("Internal error in segmentintersection():\n"); + printf(" Failure to split a segment.\n"); + internalerror(); + return; + } + /* Record a triangle whose origin is the new vertex. */ + setvertex2tri(newvertex, encode(*splittri)); + if (m->steinerleft > 0) { + m->steinerleft--; + } + + /* Divide the segment into two, and correct the segment endpoints. */ + ssymself(*splitsubseg); + spivot(*splitsubseg, opposubseg); + sdissolve(*splitsubseg); + sdissolve(opposubseg); + do { + setsegorg(*splitsubseg, newvertex); + snextself(*splitsubseg); + } while (splitsubseg->ss != m->dummysub); + do { + setsegorg(opposubseg, newvertex); + snextself(opposubseg); + } while (opposubseg.ss != m->dummysub); + + /* Inserting the vertex may have caused edge flips. We wish to rediscover */ + /* the edge connecting endpoint1 to the new intersection vertex. */ + + // FIXME collinear = + finddirection(m, b, splittri, endpoint1); + + dest(*splittri, rightvertex); + apex(*splittri, leftvertex); + if ((leftvertex[0] == endpoint1[0]) && (leftvertex[1] == endpoint1[1])) { + onextself(*splittri); + } + else if ((rightvertex[0] != endpoint1[0]) || (rightvertex[1] != endpoint1[1])) { + printf("Internal error in segmentintersection():\n"); + printf(" Topological inconsistency after splitting a segment.\n"); + internalerror(); + return; + } + /* `splittri' should have destination endpoint1. */ +} + +/*****************************************************************************/ +/* */ +/* scoutsegment() Scout the first triangle on the path from one endpoint */ +/* to another, and check for completion (reaching the */ +/* second endpoint), a collinear vertex, or the */ +/* intersection of two segments. */ +/* */ +/* Returns one if the entire segment is successfully inserted, and zero if */ +/* the job must be finished by conformingedge() or constrainededge(). */ +/* */ +/* If the first triangle on the path has the second endpoint as its */ +/* destination or apex, a subsegment is inserted and the job is done. */ +/* */ +/* If the first triangle on the path has a destination or apex that lies on */ +/* the segment, a subsegment is inserted connecting the first endpoint to */ +/* the collinear vertex, and the search is continued from the collinear */ +/* vertex. */ +/* */ +/* If the first triangle on the path has a subsegment opposite its origin, */ +/* then there is a segment that intersects the segment being inserted. */ +/* Their intersection vertex is inserted, splitting the subsegment. */ +/* */ +/*****************************************************************************/ + +int scoutsegment(struct mesh *m, struct behavior *b, struct otri *searchtri, vertex endpoint2, + int newmark) { + struct otri crosstri; + struct osub crosssubseg; + vertex leftvertex, rightvertex; + enum finddirectionresult collinear; + subseg sptr; /* Temporary variable used by tspivot(). */ + + collinear = finddirection(m, b, searchtri, endpoint2); + dest(*searchtri, rightvertex); + apex(*searchtri, leftvertex); + if (((leftvertex[0] == endpoint2[0]) && (leftvertex[1] == endpoint2[1])) + || ((rightvertex[0] == endpoint2[0]) && (rightvertex[1] == endpoint2[1]))) { + /* The segment is already an edge in the mesh. */ + if ((leftvertex[0] == endpoint2[0]) && (leftvertex[1] == endpoint2[1])) { + lprevself(*searchtri); + } + /* Insert a subsegment, if there isn't already one there. */ + insertsubseg(m, b, searchtri, newmark); + return 1; + } + else if (collinear == LEFTCOLLINEAR) { + /* We've collided with a vertex between the segment's endpoints. */ + /* Make the collinear vertex be the triangle's origin. */ + lprevself(*searchtri); + insertsubseg(m, b, searchtri, newmark); + /* Insert the remainder of the segment. */ + return scoutsegment(m, b, searchtri, endpoint2, newmark); + } + else if (collinear == RIGHTCOLLINEAR) { + /* We've collided with a vertex between the segment's endpoints. */ + insertsubseg(m, b, searchtri, newmark); + /* Make the collinear vertex be the triangle's origin. */ + lnextself(*searchtri); + /* Insert the remainder of the segment. */ + return scoutsegment(m, b, searchtri, endpoint2, newmark); + } + else { + lnext(*searchtri, crosstri); + tspivot(crosstri, crosssubseg); + /* Check for a crossing segment. */ + if (crosssubseg.ss == m->dummysub) { + return 0; + } + else { + /* Insert a vertex at the intersection. */ + segmentintersection(m, b, &crosstri, &crosssubseg, endpoint2); + if (error_set) + return -1; + otricopy(crosstri, *searchtri); + insertsubseg(m, b, searchtri, newmark); + /* Insert the remainder of the segment. */ + return scoutsegment(m, b, searchtri, endpoint2, newmark); + } + } +} + +/*****************************************************************************/ +/* */ +/* delaunayfixup() Enforce the Delaunay condition at an edge, fanning out */ +/* recursively from an existing vertex. Pay special */ +/* attention to stacking inverted triangles. */ +/* */ +/* This is a support routine for inserting segments into a constrained */ +/* Delaunay triangulation. */ +/* */ +/* The origin of fixuptri is treated as if it has just been inserted, and */ +/* the local Delaunay condition needs to be enforced. It is only enforced */ +/* in one sector, however, that being the angular range defined by */ +/* fixuptri. */ +/* */ +/* This routine also needs to make decisions regarding the "stacking" of */ +/* triangles. (Read the description of constrainededge() below before */ +/* reading on here, so you understand the algorithm.) If the position of */ +/* the new vertex (the origin of fixuptri) indicates that the vertex before */ +/* it on the polygon is a reflex vertex, then "stack" the triangle by */ +/* doing nothing. (fixuptri is an inverted triangle, which is how stacked */ +/* triangles are identified.) */ +/* */ +/* Otherwise, check whether the vertex before that was a reflex vertex. */ +/* If so, perform an edge flip, thereby eliminating an inverted triangle */ +/* (popping it off the stack). The edge flip may result in the creation */ +/* of a new inverted triangle, depending on whether or not the new vertex */ +/* is visible to the vertex three edges behind on the polygon. */ +/* */ +/* If neither of the two vertices behind the new vertex are reflex */ +/* vertices, fixuptri and fartri, the triangle opposite it, are not */ +/* inverted; hence, ensure that the edge between them is locally Delaunay. */ +/* */ +/* `leftside' indicates whether or not fixuptri is to the left of the */ +/* segment being inserted. (Imagine that the segment is pointing up from */ +/* endpoint1 to endpoint2.) */ +/* */ +/*****************************************************************************/ + +void delaunayfixup(struct mesh *m, struct behavior *b, struct otri *fixuptri, int leftside) { + struct otri neartri; + struct otri fartri; + struct osub faredge; + vertex nearvertex, leftvertex, rightvertex, farvertex; + triangle ptr; /* Temporary variable used by sym(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + lnext(*fixuptri, neartri); + sym(neartri, fartri); + /* Check if the edge opposite the origin of fixuptri can be flipped. */ + if (fartri.tri == m->dummytri) { + return; + } + tspivot(neartri, faredge); + if (faredge.ss != m->dummysub) { + return; + } + /* Find all the relevant vertices. */ + apex(neartri, nearvertex); + org(neartri, leftvertex); + dest(neartri, rightvertex); + apex(fartri, farvertex); + /* Check whether the previous polygon vertex is a reflex vertex. */ + if (leftside) { + if (counterclockwise(m, b, nearvertex, leftvertex, farvertex) <= 0.0) { + /* leftvertex is a reflex vertex too. Nothing can */ + /* be done until a convex section is found. */ + return; + } + } + else { + if (counterclockwise(m, b, farvertex, rightvertex, nearvertex) <= 0.0) { + /* rightvertex is a reflex vertex too. Nothing can */ + /* be done until a convex section is found. */ + return; + } + } + if (counterclockwise(m, b, rightvertex, leftvertex, farvertex) > 0.0) { + /* fartri is not an inverted triangle, and farvertex is not a reflex */ + /* vertex. As there are no reflex vertices, fixuptri isn't an */ + /* inverted triangle, either. Hence, test the edge between the */ + /* triangles to ensure it is locally Delaunay. */ + if (incircle(m, b, leftvertex, farvertex, rightvertex, nearvertex) <= 0.0) { + return; + } + /* Not locally Delaunay; go on to an edge flip. */ + } /* else fartri is inverted; remove it from the stack by flipping. */ + flip(m, b, &neartri); + lprevself(*fixuptri); + /* Restore the origin of fixuptri after the flip. */ + /* Recursively process the two triangles that result from the flip. */ + delaunayfixup(m, b, fixuptri, leftside); + delaunayfixup(m, b, &fartri, leftside); +} + +/*****************************************************************************/ +/* */ +/* constrainededge() Force a segment into a constrained Delaunay */ +/* triangulation by deleting the triangles it */ +/* intersects, and triangulating the polygons that */ +/* form on each side of it. */ +/* */ +/* Generates a single subsegment connecting `endpoint1' to `endpoint2'. */ +/* The triangle `starttri' has `endpoint1' as its origin. `newmark' is the */ +/* boundary marker of the segment. */ +/* */ +/* To insert a segment, every triangle whose interior intersects the */ +/* segment is deleted. The union of these deleted triangles is a polygon */ +/* (which is not necessarily monotone, but is close enough), which is */ +/* divided into two polygons by the new segment. This routine's task is */ +/* to generate the Delaunay triangulation of these two polygons. */ +/* */ +/* You might think of this routine's behavior as a two-step process. The */ +/* first step is to walk from endpoint1 to endpoint2, flipping each edge */ +/* encountered. This step creates a fan of edges connected to endpoint1, */ +/* including the desired edge to endpoint2. The second step enforces the */ +/* Delaunay condition on each side of the segment in an incremental manner: */ +/* proceeding along the polygon from endpoint1 to endpoint2 (this is done */ +/* independently on each side of the segment), each vertex is "enforced" */ +/* as if it had just been inserted, but affecting only the previous */ +/* vertices. The result is the same as if the vertices had been inserted */ +/* in the order they appear on the polygon, so the result is Delaunay. */ +/* */ +/* In truth, constrainededge() interleaves these two steps. The procedure */ +/* walks from endpoint1 to endpoint2, and each time an edge is encountered */ +/* and flipped, the newly exposed vertex (at the far end of the flipped */ +/* edge) is "enforced" upon the previously flipped edges, usually affecting */ +/* only one side of the polygon (depending upon which side of the segment */ +/* the vertex falls on). */ +/* */ +/* The algorithm is complicated by the need to handle polygons that are not */ +/* convex. Although the polygon is not necessarily monotone, it can be */ +/* triangulated in a manner similar to the stack-based algorithms for */ +/* monotone polygons. For each reflex vertex (local concavity) of the */ +/* polygon, there will be an inverted triangle formed by one of the edge */ +/* flips. (An inverted triangle is one with negative area - that is, its */ +/* vertices are arranged in clockwise order - and is best thought of as a */ +/* wrinkle in the fabric of the mesh.) Each inverted triangle can be */ +/* thought of as a reflex vertex pushed on the stack, waiting to be fixed */ +/* later. */ +/* */ +/* A reflex vertex is popped from the stack when a vertex is inserted that */ +/* is visible to the reflex vertex. (However, if the vertex behind the */ +/* reflex vertex is not visible to the reflex vertex, a new inverted */ +/* triangle will take its place on the stack.) These details are handled */ +/* by the delaunayfixup() routine above. */ +/* */ +/*****************************************************************************/ + +void constrainededge(struct mesh *m, struct behavior *b, struct otri *starttri, vertex endpoint2, + int newmark) { + struct otri fixuptri, fixuptri2; + struct osub crosssubseg; + vertex endpoint1; + vertex farvertex; + REAL area; + int collision; + int done; + triangle ptr; /* Temporary variable used by sym() and oprev(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + org(*starttri, endpoint1); + lnext(*starttri, fixuptri); + flip(m, b, &fixuptri); + /* `collision' indicates whether we have found a vertex directly */ + /* between endpoint1 and endpoint2. */ + collision = 0; + done = 0; + do { + org(fixuptri, farvertex); + /* `farvertex' is the extreme point of the polygon we are "digging" */ + /* to get from endpoint1 to endpoint2. */ + if ((farvertex[0] == endpoint2[0]) && (farvertex[1] == endpoint2[1])) { + oprev(fixuptri, fixuptri2); + /* Enforce the Delaunay condition around endpoint2. */ + delaunayfixup(m, b, &fixuptri, 0); + delaunayfixup(m, b, &fixuptri2, 1); + done = 1; + } + else { + /* Check whether farvertex is to the left or right of the segment */ + /* being inserted, to decide which edge of fixuptri to dig */ + /* through next. */ + area = counterclockwise(m, b, endpoint1, endpoint2, farvertex); + if (area == 0.0) { + /* We've collided with a vertex between endpoint1 and endpoint2. */ + collision = 1; + oprev(fixuptri, fixuptri2); + /* Enforce the Delaunay condition around farvertex. */ + delaunayfixup(m, b, &fixuptri, 0); + delaunayfixup(m, b, &fixuptri2, 1); + done = 1; + } + else { + if (area > 0.0) { /* farvertex is to the left of the segment. */ + oprev(fixuptri, fixuptri2); + /* Enforce the Delaunay condition around farvertex, on the */ + /* left side of the segment only. */ + delaunayfixup(m, b, &fixuptri2, 1); + /* Flip the edge that crosses the segment. After the edge is */ + /* flipped, one of its endpoints is the fan vertex, and the */ + /* destination of fixuptri is the fan vertex. */ + lprevself(fixuptri); + } + else { /* farvertex is to the right of the segment. */ + delaunayfixup(m, b, &fixuptri, 0); + /* Flip the edge that crosses the segment. After the edge is */ + /* flipped, one of its endpoints is the fan vertex, and the */ + /* destination of fixuptri is the fan vertex. */ + oprevself(fixuptri); + } + /* Check for two intersecting segments. */ + tspivot(fixuptri, crosssubseg); + if (crosssubseg.ss == m->dummysub) { + flip(m, b, &fixuptri); /* May create inverted triangle at left. */ + } + else { + /* We've collided with a segment between endpoint1 and endpoint2. */ + collision = 1; + /* Insert a vertex at the intersection. */ + segmentintersection(m, b, &fixuptri, &crosssubseg, endpoint2); + done = 1; + } + } + } + } while (!done); + /* Insert a subsegment to make the segment permanent. */ + insertsubseg(m, b, &fixuptri, newmark); + /* If there was a collision with an interceding vertex, install another */ + /* segment connecting that vertex with endpoint2. */ + if (collision) { + /* Insert the remainder of the segment. */ + if (!scoutsegment(m, b, &fixuptri, endpoint2, newmark)) { + constrainededge(m, b, &fixuptri, endpoint2, newmark); + } + } +} + +/*****************************************************************************/ +/* */ +/* insertsegment() Insert a PSLG segment into a triangulation. */ +/* */ +/*****************************************************************************/ + +void insertsegment(struct mesh *m, struct behavior *b, vertex endpoint1, vertex endpoint2, + int newmark) { + struct otri searchtri1, searchtri2; + triangle encodedtri; + vertex checkvertex; + triangle ptr; /* Temporary variable used by sym(). */ + + if (b->verbose > 1) { + printf( " Connecting (%.12g, %.12g) to (%.12g, %.12g).\n", + endpoint1[0], endpoint1[1], endpoint2[0], endpoint2[1]); + } + + /* Find a triangle whose origin is the segment's first endpoint. */ + checkvertex = (vertex) NULL; + encodedtri = vertex2tri(endpoint1); + if (encodedtri != (triangle) NULL) { + decode(encodedtri, searchtri1); + org(searchtri1, checkvertex); + } + if (checkvertex != endpoint1) { + /* Find a boundary triangle to search from. */ + searchtri1.tri = m->dummytri; + searchtri1.orient = 0; + symself(searchtri1); + /* Search for the segment's first endpoint by point location. */ + if (locate(m, b, endpoint1, &searchtri1) != ONVERTEX) { + printf( "Internal error in insertsegment(): Unable to locate PSLG vertex\n"); + printf(" (%.12g, %.12g) in triangulation.\n", endpoint1[0], endpoint1[1]); + internalerror(); + } + } + /* Remember this triangle to improve subsequent point location. */ + otricopy(searchtri1, m->recenttri); + /* Scout the beginnings of a path from the first endpoint */ + /* toward the second. */ + if (scoutsegment(m, b, &searchtri1, endpoint2, newmark)) { + /* The segment was easily inserted. */ + return; + } + /* The first endpoint may have changed if a collision with an intervening */ + /* vertex on the segment occurred. */ + org(searchtri1, endpoint1); + + /* Find a triangle whose origin is the segment's second endpoint. */ + checkvertex = (vertex) NULL; + encodedtri = vertex2tri(endpoint2); + if (encodedtri != (triangle) NULL) { + decode(encodedtri, searchtri2); + org(searchtri2, checkvertex); + } + if (checkvertex != endpoint2) { + /* Find a boundary triangle to search from. */ + searchtri2.tri = m->dummytri; + searchtri2.orient = 0; + symself(searchtri2); + /* Search for the segment's second endpoint by point location. */ + if (locate(m, b, endpoint2, &searchtri2) != ONVERTEX) { + printf( "Internal error in insertsegment(): Unable to locate PSLG vertex\n"); + printf(" (%.12g, %.12g) in triangulation.\n", endpoint2[0], endpoint2[1]); + internalerror(); + } + } + /* Remember this triangle to improve subsequent point location. */ + otricopy(searchtri2, m->recenttri); + /* Scout the beginnings of a path from the second endpoint */ + /* toward the first. */ + if (scoutsegment(m, b, &searchtri2, endpoint1, newmark)) { + /* The segment was easily inserted. */ + return; + } + /* The second endpoint may have changed if a collision with an intervening */ + /* vertex on the segment occurred. */ + org(searchtri2, endpoint2); + +#ifndef REDUCED +#ifndef CDT_ONLY + if (b->splitseg) + { + /* Insert vertices to force the segment into the triangulation. */ + conformingedge(m, b, endpoint1, endpoint2, newmark); + } + else + { +#endif /* not CDT_ONLY */ +#endif /* not REDUCED */ + /* Insert the segment directly into the triangulation. */ + constrainededge(m, b, &searchtri1, endpoint2, newmark); +#ifndef REDUCED +#ifndef CDT_ONLY +} +#endif /* not CDT_ONLY */ +#endif /* not REDUCED */ +} + +/*****************************************************************************/ +/* */ +/* markhull() Cover the convex hull of a triangulation with subsegments. */ +/* */ +/*****************************************************************************/ + +void markhull(struct mesh *m, struct behavior *b) { + struct otri hulltri; + struct otri nexttri; + struct otri starttri; + triangle ptr; /* Temporary variable used by sym() and oprev(). */ + + /* Find a triangle handle on the hull. */ + hulltri.tri = m->dummytri; + hulltri.orient = 0; + symself(hulltri); + /* Remember where we started so we know when to stop. */ + otricopy(hulltri, starttri); + /* Go once counterclockwise around the convex hull. */ + do { + /* Create a subsegment if there isn't already one here. */ + insertsubseg(m, b, &hulltri, 1); + /* To find the next hull edge, go clockwise around the next vertex. */ + lnextself(hulltri); + oprev(hulltri, nexttri); + while (nexttri.tri != m->dummytri) { + otricopy(nexttri, hulltri); + oprev(hulltri, nexttri); + } + } while (!otriequal(hulltri, starttri)); +} + +/*****************************************************************************/ +/* */ +/* formskeleton() Create the segments of a triangulation, including PSLG */ +/* segments and edges on the convex hull. */ +/* */ +/* The PSLG segments are read from a .poly file. The return value is the */ +/* number of segments in the file. */ +/* */ +/*****************************************************************************/ + +void formskeleton(struct mesh *m, struct behavior *b, int *segmentlist, int *segmentmarkerlist, + int numberofsegments) { + char polyfilename[6]; + int index; + vertex endpoint1, endpoint2; + int segmentmarkers; + int end1, end2; + int boundmarker; + int i; + + if (b->poly) { + if (!b->quiet) { + printf("Recovering segments in Delaunay triangulation.\n"); + } + strcpy(polyfilename, "input"); + m->insegments = numberofsegments; + segmentmarkers = segmentmarkerlist != (int *) NULL; + index = 0; + /* If the input vertices are collinear, there is no triangulation, */ + /* so don't try to insert segments. */ + if (m->triangles.items == 0) { + return; + } + + /* If segments are to be inserted, compute a mapping */ + /* from vertices to triangles. */ + if (m->insegments > 0) { + makevertexmap(m, b); + if (b->verbose) { + printf(" Recovering PSLG segments.\n"); + } + } + + boundmarker = 0; + /* Read and insert the segments. */ + for (i = 0; i < m->insegments; i++) { + end1 = segmentlist[index++]; + end2 = segmentlist[index++]; + if (segmentmarkers) { + boundmarker = segmentmarkerlist[i]; + } + if ((end1 < b->firstnumber) || (end1 >= b->firstnumber + m->invertices)) { + if (!b->quiet) { + printf( "Warning: Invalid first endpoint of segment %d in %s.\n", + b->firstnumber + i, polyfilename); + } + } + else if ((end2 < b->firstnumber) || (end2 >= b->firstnumber + m->invertices)) { + if (!b->quiet) { + printf( "Warning: Invalid second endpoint of segment %d in %s.\n", + b->firstnumber + i, polyfilename); + } + } + else { + /* Find the vertices numbered `end1' and `end2'. */ + endpoint1 = getvertex(m, b, end1); + endpoint2 = getvertex(m, b, end2); + if ((endpoint1[0] == endpoint2[0]) && (endpoint1[1] == endpoint2[1])) { + if (!b->quiet) { + printf( "Warning: Endpoints of segment %d are coincident in %s.\n", + b->firstnumber + i, polyfilename); + } + } + else { + insertsegment(m, b, endpoint1, endpoint2, boundmarker); + } + } + } + } + else { + m->insegments = 0; + } + if (b->convex || !b->poly) { + /* Enclose the convex hull with subsegments. */ + if (b->verbose) { + printf(" Enclosing convex hull with segments.\n"); + } + markhull(m, b); + } +} + +/** **/ +/** **/ +/********* Segment insertion ends here *********/ + +/********* Carving out holes and concavities begins here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* infecthull() Virally infect all of the triangles of the convex hull */ +/* that are not protected by subsegments. Where there are */ +/* subsegments, set boundary markers as appropriate. */ +/* */ +/*****************************************************************************/ + +void infecthull(struct mesh *m, struct behavior *b) { + struct otri hulltri; + struct otri nexttri; + struct otri starttri; + struct osub hullsubseg; + triangle **deadtriangle; + vertex horg, hdest; + triangle ptr; /* Temporary variable used by sym(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + if (b->verbose) { + printf(" Marking concavities (external triangles) for elimination.\n"); + } + /* Find a triangle handle on the hull. */ + hulltri.tri = m->dummytri; + hulltri.orient = 0; + symself(hulltri); + /* Remember where we started so we know when to stop. */ + otricopy(hulltri, starttri); + /* Go once counterclockwise around the convex hull. */ + do { + /* Ignore triangles that are already infected. */ + if (!infected(hulltri)) { + /* Is the triangle protected by a subsegment? */ + tspivot(hulltri, hullsubseg); + if (hullsubseg.ss == m->dummysub) { + /* The triangle is not protected; infect it. */ + if (!infected(hulltri)) { + infect(hulltri); + deadtriangle = (triangle **) poolalloc(&m->viri); + *deadtriangle = hulltri.tri; + } + } + else { + /* The triangle is protected; set boundary markers if appropriate. */ + if (mark(hullsubseg) == 0) { + setmark(hullsubseg, 1); + org(hulltri, horg); + dest(hulltri, hdest); + if (vertexmark(horg) == 0) { + setvertexmark(horg, 1); + } + if (vertexmark(hdest) == 0) { + setvertexmark(hdest, 1); + } + } + } + } + /* To find the next hull edge, go clockwise around the next vertex. */ + lnextself(hulltri); + oprev(hulltri, nexttri); + while (nexttri.tri != m->dummytri) { + otricopy(nexttri, hulltri); + oprev(hulltri, nexttri); + } + } while (!otriequal(hulltri, starttri)); +} + +/*****************************************************************************/ +/* */ +/* plague() Spread the virus from all infected triangles to any neighbors */ +/* not protected by subsegments. Delete all infected triangles. */ +/* */ +/* This is the procedure that actually creates holes and concavities. */ +/* */ +/* This procedure operates in two phases. The first phase identifies all */ +/* the triangles that will die, and marks them as infected. They are */ +/* marked to ensure that each triangle is added to the virus pool only */ +/* once, so the procedure will terminate. */ +/* */ +/* The second phase actually eliminates the infected triangles. It also */ +/* eliminates orphaned vertices. */ +/* */ +/*****************************************************************************/ + +void plague(struct mesh *m, struct behavior *b) { + struct otri testtri; + struct otri neighbor; + triangle **virusloop; + triangle **deadtriangle; + struct osub neighborsubseg; + vertex testvertex; + vertex norg, ndest; + vertex deadorg, deaddest, deadapex; + int killorg; + triangle ptr; /* Temporary variable used by sym() and onext(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + if (b->verbose) { + printf(" Marking neighbors of marked triangles.\n"); + } + /* Loop through all the infected triangles, spreading the virus to */ + /* their neighbors, then to their neighbors' neighbors. */ + traversalinit(&m->viri); + virusloop = (triangle **) traverse(&m->viri); + while (virusloop != (triangle **) NULL) { + testtri.tri = *virusloop; + /* A triangle is marked as infected by messing with one of its pointers */ + /* to subsegments, setting it to an illegal value. Hence, we have to */ + /* temporarily uninfect this triangle so that we can examine its */ + /* adjacent subsegments. */ + uninfect(testtri); + if (b->verbose > 2) { + /* Assign the triangle an orientation for convenience in */ + /* checking its vertices. */ + testtri.orient = 0; + org(testtri, deadorg); + dest(testtri, deaddest); + apex(testtri, deadapex); + printf( + " Checking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", deadorg[0], deadorg[1], deaddest[0], deaddest[1], deadapex[0], deadapex[1]); + } + /* Check each of the triangle's three neighbors. */ + for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { + /* Find the neighbor. */ + sym(testtri, neighbor); + /* Check for a subsegment between the triangle and its neighbor. */ + tspivot(testtri, neighborsubseg); + /* Check if the neighbor is nonexistent or already infected. */ + if ((neighbor.tri == m->dummytri) || infected(neighbor)) { + if (neighborsubseg.ss != m->dummysub) { + /* There is a subsegment separating the triangle from its */ + /* neighbor, but both triangles are dying, so the subsegment */ + /* dies too. */ + subsegdealloc(m, neighborsubseg.ss); + if (neighbor.tri != m->dummytri) { + /* Make sure the subsegment doesn't get deallocated again */ + /* later when the infected neighbor is visited. */ + uninfect(neighbor); + tsdissolve(neighbor); + infect(neighbor); + } + } + } + else { /* The neighbor exists and is not infected. */ + if (neighborsubseg.ss == m->dummysub) { + /* There is no subsegment protecting the neighbor, so */ + /* the neighbor becomes infected. */ + if (b->verbose > 2) { + org(neighbor, deadorg); + dest(neighbor, deaddest); + apex(neighbor, deadapex); + printf( + " Marking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", deadorg[0], deadorg[1], deaddest[0], deaddest[1], deadapex[0], deadapex[1]); + } + infect(neighbor); + /* Ensure that the neighbor's neighbors will be infected. */ + deadtriangle = (triangle **) poolalloc(&m->viri); + *deadtriangle = neighbor.tri; + } + else { /* The neighbor is protected by a subsegment. */ + /* Remove this triangle from the subsegment. */ + stdissolve(neighborsubseg); + /* The subsegment becomes a boundary. Set markers accordingly. */ + if (mark(neighborsubseg) == 0) { + setmark(neighborsubseg, 1); + } + org(neighbor, norg); + dest(neighbor, ndest); + if (vertexmark(norg) == 0) { + setvertexmark(norg, 1); + } + if (vertexmark(ndest) == 0) { + setvertexmark(ndest, 1); + } + } + } + } + /* Remark the triangle as infected, so it doesn't get added to the */ + /* virus pool again. */ + infect(testtri); + virusloop = (triangle **) traverse(&m->viri); + } + + if (b->verbose) { + printf(" Deleting marked triangles.\n"); + } + + traversalinit(&m->viri); + virusloop = (triangle **) traverse(&m->viri); + while (virusloop != (triangle **) NULL) { + testtri.tri = *virusloop; + + /* Check each of the three corners of the triangle for elimination. */ + /* This is done by walking around each vertex, checking if it is */ + /* still connected to at least one live triangle. */ + for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { + org(testtri, testvertex); + /* Check if the vertex has already been tested. */ + if (testvertex != (vertex) NULL) { + killorg = 1; + /* Mark the corner of the triangle as having been tested. */ + setorg(testtri, NULL); + /* Walk counterclockwise about the vertex. */ + onext(testtri, neighbor); + /* Stop upon reaching a boundary or the starting triangle. */ + while ((neighbor.tri != m->dummytri) && (!otriequal(neighbor, testtri))) { + if (infected(neighbor)) { + /* Mark the corner of this triangle as having been tested. */ + setorg(neighbor, NULL); + } + else { + /* A live triangle. The vertex survives. */ + killorg = 0; + } + /* Walk counterclockwise about the vertex. */ + onextself(neighbor); + } + /* If we reached a boundary, we must walk clockwise as well. */ + if (neighbor.tri == m->dummytri) { + /* Walk clockwise about the vertex. */ + oprev(testtri, neighbor); + /* Stop upon reaching a boundary. */ + while (neighbor.tri != m->dummytri) { + if (infected(neighbor)) { + /* Mark the corner of this triangle as having been tested. */ + setorg(neighbor, NULL); + } + else { + /* A live triangle. The vertex survives. */ + killorg = 0; + } + /* Walk clockwise about the vertex. */ + oprevself(neighbor); + } + } + if (killorg) { + if (b->verbose > 1) { + printf(" Deleting vertex (%.12g, %.12g)\n", testvertex[0], testvertex[1]); + } + setvertextype(testvertex, UNDEADVERTEX); + m->undeads++; + } + } + } + + /* Record changes in the number of boundary edges, and disconnect */ + /* dead triangles from their neighbors. */ + for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { + sym(testtri, neighbor); + if (neighbor.tri == m->dummytri) { + /* There is no neighboring triangle on this edge, so this edge */ + /* is a boundary edge. This triangle is being deleted, so this */ + /* boundary edge is deleted. */ + m->hullsize--; + } + else { + /* Disconnect the triangle from its neighbor. */ + dissolve(neighbor); + /* There is a neighboring triangle on this edge, so this edge */ + /* becomes a boundary edge when this triangle is deleted. */ + m->hullsize++; + } + } + /* Return the dead triangle to the pool of triangles. */ + triangledealloc(m, testtri.tri); + virusloop = (triangle **) traverse(&m->viri); + } + /* Empty the virus pool. */ + poolrestart(&m->viri); +} + +/*****************************************************************************/ +/* */ +/* regionplague() Spread regional attributes and/or area constraints */ +/* (from a .poly file) throughout the mesh. */ +/* */ +/* This procedure operates in two phases. The first phase spreads an */ +/* attribute and/or an area constraint through a (segment-bounded) region. */ +/* The triangles are marked to ensure that each triangle is added to the */ +/* virus pool only once, so the procedure will terminate. */ +/* */ +/* The second phase uninfects all infected triangles, returning them to */ +/* normal. */ +/* */ +/*****************************************************************************/ + +void regionplague(struct mesh *m, struct behavior *b, REAL attribute, REAL area) { + struct otri testtri; + struct otri neighbor; + triangle **virusloop; + triangle **regiontri; + struct osub neighborsubseg; + vertex regionorg, regiondest, regionapex; + triangle ptr; /* Temporary variable used by sym() and onext(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + if (b->verbose > 1) { + printf(" Marking neighbors of marked triangles.\n"); + } + /* Loop through all the infected triangles, spreading the attribute */ + /* and/or area constraint to their neighbors, then to their neighbors' */ + /* neighbors. */ + traversalinit(&m->viri); + virusloop = (triangle **) traverse(&m->viri); + while (virusloop != (triangle **) NULL) { + testtri.tri = *virusloop; + /* A triangle is marked as infected by messing with one of its pointers */ + /* to subsegments, setting it to an illegal value. Hence, we have to */ + /* temporarily uninfect this triangle so that we can examine its */ + /* adjacent subsegments. */ + uninfect(testtri); + if (b->regionattrib) { + /* Set an attribute. */ + setelemattribute(testtri, m->eextras, attribute); + } + if (b->vararea) { + /* Set an area constraint. */ + setareabound(testtri, area); + } + if (b->verbose > 2) { + /* Assign the triangle an orientation for convenience in */ + /* checking its vertices. */ + testtri.orient = 0; + org(testtri, regionorg); + dest(testtri, regiondest); + apex(testtri, regionapex); + printf( " Checking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", + regionorg[0], regionorg[1], regiondest[0], regiondest[1], + regionapex[0], regionapex[1]); + } + /* Check each of the triangle's three neighbors. */ + for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { + /* Find the neighbor. */ + sym(testtri, neighbor); + /* Check for a subsegment between the triangle and its neighbor. */ + tspivot(testtri, neighborsubseg); + /* Make sure the neighbor exists, is not already infected, and */ + /* isn't protected by a subsegment. */ + if ((neighbor.tri != m->dummytri) && !infected(neighbor) + && (neighborsubseg.ss == m->dummysub)) { + if (b->verbose > 2) { + org(neighbor, regionorg); + dest(neighbor, regiondest); + apex(neighbor, regionapex); + printf( " Marking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", + regionorg[0], regionorg[1], regiondest[0], regiondest[1], + regionapex[0], regionapex[1]); + } + /* Infect the neighbor. */ + infect(neighbor); + /* Ensure that the neighbor's neighbors will be infected. */ + regiontri = (triangle **) poolalloc(&m->viri); + *regiontri = neighbor.tri; + } + } + /* Remark the triangle as infected, so it doesn't get added to the */ + /* virus pool again. */ + infect(testtri); + virusloop = (triangle **) traverse(&m->viri); + } + + /* Uninfect all triangles. */ + if (b->verbose > 1) { + printf(" Unmarking marked triangles.\n"); + } + traversalinit(&m->viri); + virusloop = (triangle **) traverse(&m->viri); + while (virusloop != (triangle **) NULL) { + testtri.tri = *virusloop; + uninfect(testtri); + virusloop = (triangle **) traverse(&m->viri); + } + /* Empty the virus pool. */ + poolrestart(&m->viri); +} + +/*****************************************************************************/ +/* */ +/* carveholes() Find the holes and infect them. Find the area */ +/* constraints and infect them. Infect the convex hull. */ +/* Spread the infection and kill triangles. Spread the */ +/* area constraints. */ +/* */ +/* This routine mainly calls other routines to carry out all these */ +/* functions. */ +/* */ +/*****************************************************************************/ + +void carveholes(struct mesh *m, struct behavior *b, REAL *holelist, int holes, REAL *regionlist, + int regions) { + struct otri searchtri; + struct otri triangleloop; + struct otri *regiontris; + triangle **holetri; + triangle **regiontri; + vertex searchorg, searchdest; + enum locateresult intersect; + int i; + triangle ptr; /* Temporary variable used by sym(). */ + + if (!(b->quiet || (b->noholes && b->convex))) { + printf("Removing unwanted triangles.\n"); + if (b->verbose && (holes > 0)) { + printf(" Marking holes for elimination.\n"); + } + } + + if (regions > 0) { + /* Allocate storage for the triangles in which region points fall. */ + regiontris = (struct otri *) trimalloc(regions * (int) sizeof(struct otri)); + } + else { + regiontris = (struct otri *) NULL; + } + + if (((holes > 0) && !b->noholes) || !b->convex || (regions > 0)) { + /* Initialize a pool of viri to be used for holes, concavities, */ + /* regional attributes, and/or regional area constraints. */ + poolinit(&m->viri, sizeof(triangle *), VIRUSPERBLOCK, VIRUSPERBLOCK, 0); + } + + if (!b->convex) { + /* Mark as infected any unprotected triangles on the boundary. */ + /* This is one way by which concavities are created. */ + infecthull(m, b); + } + + if ((holes > 0) && !b->noholes) { + /* Infect each triangle in which a hole lies. */ + for (i = 0; i < 2 * holes; i += 2) { + /* Ignore holes that aren't within the bounds of the mesh. */ + if ((holelist[i] >= m->xmin) && (holelist[i] <= m->xmax) && (holelist[i + 1] >= m->ymin) + && (holelist[i + 1] <= m->ymax)) { + /* Start searching from some triangle on the outer boundary. */ + searchtri.tri = m->dummytri; + searchtri.orient = 0; + symself(searchtri); + /* Ensure that the hole is to the left of this boundary edge; */ + /* otherwise, locate() will falsely report that the hole */ + /* falls within the starting triangle. */ + org(searchtri, searchorg); + dest(searchtri, searchdest); + if (counterclockwise(m, b, searchorg, searchdest, &holelist[i]) > 0.0) { + /* Find a triangle that contains the hole. */ + intersect = locate(m, b, &holelist[i], &searchtri); + if ((intersect != OUTSIDE) && (!infected(searchtri))) { + /* Infect the triangle. This is done by marking the triangle */ + /* as infected and including the triangle in the virus pool. */ + infect(searchtri); + holetri = (triangle **) poolalloc(&m->viri); + *holetri = searchtri.tri; + } + } + } + } + } + + /* Now, we have to find all the regions BEFORE we carve the holes, because */ + /* locate() won't work when the triangulation is no longer convex. */ + /* (Incidentally, this is the reason why regional attributes and area */ + /* constraints can't be used when refining a preexisting mesh, which */ + /* might not be convex; they can only be used with a freshly */ + /* triangulated PSLG.) */ + if (regions > 0) { + /* Find the starting triangle for each region. */ + for (i = 0; i < regions; i++) { + regiontris[i].tri = m->dummytri; + /* Ignore region points that aren't within the bounds of the mesh. */ + if ((regionlist[4 * i] >= m->xmin) && (regionlist[4 * i] <= m->xmax) + && (regionlist[4 * i + 1] >= m->ymin) && (regionlist[4 * i + 1] <= m->ymax)) { + /* Start searching from some triangle on the outer boundary. */ + searchtri.tri = m->dummytri; + searchtri.orient = 0; + symself(searchtri); + /* Ensure that the region point is to the left of this boundary */ + /* edge; otherwise, locate() will falsely report that the */ + /* region point falls within the starting triangle. */ + org(searchtri, searchorg); + dest(searchtri, searchdest); + if (counterclockwise(m, b, searchorg, searchdest, ®ionlist[4 * i]) > 0.0) { + /* Find a triangle that contains the region point. */ + intersect = locate(m, b, ®ionlist[4 * i], &searchtri); + if ((intersect != OUTSIDE) && (!infected(searchtri))) { + /* Record the triangle for processing after the */ + /* holes have been carved. */ + otricopy(searchtri, regiontris[i]); + } + } + } + } + } + + if (m->viri.items > 0) { + /* Carve the holes and concavities. */ + plague(m, b); + } + /* The virus pool should be empty now. */ + + if (regions > 0) { + if (!b->quiet) { + if (b->regionattrib) { + if (b->vararea) { + printf("Spreading regional attributes and area constraints.\n"); + } + else { + printf("Spreading regional attributes.\n"); + } + } + else { + printf("Spreading regional area constraints.\n"); + } + } + if (b->regionattrib && !b->refine) { + /* Assign every triangle a regional attribute of zero. */ + traversalinit(&m->triangles); + triangleloop.orient = 0; + triangleloop.tri = triangletraverse(m); + while (triangleloop.tri != (triangle *) NULL) { + setelemattribute(triangleloop, m->eextras, 0.0); + triangleloop.tri = triangletraverse(m); + } + } + for (i = 0; i < regions; i++) { + if (regiontris[i].tri != m->dummytri) { + /* Make sure the triangle under consideration still exists. */ + /* It may have been eaten by the virus. */ + if (!deadtri(regiontris[i].tri)) { + /* Put one triangle in the virus pool. */ + infect(regiontris[i]); + regiontri = (triangle **) poolalloc(&m->viri); + *regiontri = regiontris[i].tri; + /* Apply one region's attribute and/or area constraint. */ + regionplague(m, b, regionlist[4 * i + 2], regionlist[4 * i + 3]); + /* The virus pool should be empty now. */ + } + } + } + if (b->regionattrib && !b->refine) { + /* Note the fact that each triangle has an additional attribute. */ + m->eextras++; + } + } + + /* Free up memory. */ + if (((holes > 0) && !b->noholes) || !b->convex || (regions > 0)) { + pooldeinit(&m->viri); + } + if (regions > 0) { + trifree((VOID *) regiontris); + } +} + +/** **/ +/** **/ +/********* Carving out holes and concavities ends here *********/ + +/*****************************************************************************/ +/* */ +/* highorder() Create extra nodes for quadratic subparametric elements. */ +/* */ +/*****************************************************************************/ + +void highorder(struct mesh *m, struct behavior *b) { + struct otri triangleloop, trisym; + struct osub checkmark; + vertex newvertex; + vertex torg, tdest; + int i; + triangle ptr; /* Temporary variable used by sym(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + if (!b->quiet) { + printf("Adding vertices for second-order triangles.\n"); + } + /* The following line ensures that dead items in the pool of nodes */ + /* cannot be allocated for the extra nodes associated with high */ + /* order elements. This ensures that the primary nodes (at the */ + /* corners of elements) will occur earlier in the output files, and */ + /* have lower indices, than the extra nodes. */ + m->vertices.deaditemstack = (VOID *) NULL; + + traversalinit(&m->triangles); + triangleloop.tri = triangletraverse(m); + /* To loop over the set of edges, loop over all triangles, and look at */ + /* the three edges of each triangle. If there isn't another triangle */ + /* adjacent to the edge, operate on the edge. If there is another */ + /* adjacent triangle, operate on the edge only if the current triangle */ + /* has a smaller pointer than its neighbor. This way, each edge is */ + /* considered only once. */ + while (triangleloop.tri != (triangle *) NULL) { + for (triangleloop.orient = 0; triangleloop.orient < 3; triangleloop.orient++) { + sym(triangleloop, trisym); + if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) { + org(triangleloop, torg); + dest(triangleloop, tdest); + /* Create a new node in the middle of the edge. Interpolate */ + /* its attributes. */ + newvertex = (vertex) poolalloc(&m->vertices); + for (i = 0; i < 2 + m->nextras; i++) { + newvertex[i] = 0.5 * (torg[i] + tdest[i]); + } + /* Set the new node's marker to zero or one, depending on */ + /* whether it lies on a boundary. */ + setvertexmark(newvertex, trisym.tri == m->dummytri); + setvertextype(newvertex, trisym.tri == m->dummytri ? FREEVERTEX : SEGMENTVERTEX); + if (b->usesegments) { + tspivot(triangleloop, checkmark); + /* If this edge is a segment, transfer the marker to the new node. */ + if (checkmark.ss != m->dummysub) { + setvertexmark(newvertex, mark(checkmark)); + setvertextype(newvertex, SEGMENTVERTEX); + } + } + if (b->verbose > 1) { + printf(" Creating (%.12g, %.12g).\n", newvertex[0], newvertex[1]); + } + /* Record the new node in the (one or two) adjacent elements. */ + triangleloop.tri[m->highorderindex + triangleloop.orient] = (triangle) newvertex; + if (trisym.tri != m->dummytri) { + trisym.tri[m->highorderindex + trisym.orient] = (triangle) newvertex; + } + } + } + triangleloop.tri = triangletraverse(m); + } +} + +/*****************************************************************************/ +/* */ +/* transfernodes() Read the vertices from memory. */ +/* */ +/*****************************************************************************/ + +void transfernodes(struct mesh *m, struct behavior *b, REAL *pointlist, REAL *pointattriblist, + int *pointmarkerlist, int numberofpoints, int numberofpointattribs) { + vertex vertexloop; + REAL x, y; + int i, j; + int coordindex; + int attribindex; + + m->invertices = numberofpoints; + m->mesh_dim = 2; + m->nextras = numberofpointattribs; + m->readnodefile = 0; + if (m->invertices < 3) { + printf("Error: Input must have at least three input vertices.\n"); + triexit(1); + } + if (m->nextras == 0) { + b->weighted = 0; + } + + initializevertexpool(m, b); + + /* Read the vertices. */ + coordindex = 0; + attribindex = 0; + for (i = 0; i < m->invertices; i++) { + vertexloop = (vertex) poolalloc(&m->vertices); + /* Read the vertex coordinates. */ + x = vertexloop[0] = pointlist[coordindex++]; + y = vertexloop[1] = pointlist[coordindex++]; + /* Read the vertex attributes. */ + for (j = 0; j < numberofpointattribs; j++) { + vertexloop[2 + j] = pointattriblist[attribindex++]; + } + if (pointmarkerlist != (int *) NULL) { + /* Read a vertex marker. */ + setvertexmark(vertexloop, pointmarkerlist[i]); + } + else { + /* If no markers are specified, they default to zero. */ + setvertexmark(vertexloop, 0); + } + + // ---------------------------------------------- + for (j = (i - 1) * 2; j >= 0; j -= 2){ + if (x == pointlist[j] && y == pointlist[j+1]){ + printf("skip duplicate %d\n", j >> 1); + setvertextype(vertexloop, UNDEADVERTEX); + vertexloop[0] = 0xffffffff; + vertexloop[1] = 0xffffffff; + break; + } + } + if (j >= 0) + continue; + // ---------------------------------------------- + + setvertextype(vertexloop, INPUTVERTEX); + /* Determine the smallest and largest x and y coordinates. */ + if (i == 0) { + m->xmin = m->xmax = x; + m->ymin = m->ymax = y; + } + else { + m->xmin = (x < m->xmin) ? x : m->xmin; + m->xmax = (x > m->xmax) ? x : m->xmax; + m->ymin = (y < m->ymin) ? y : m->ymin; + m->ymax = (y > m->ymax) ? y : m->ymax; + } + } + + /* Nonexistent x value used as a flag to mark circle events in sweepline */ + /* Delaunay algorithm. */ + m->xminextreme = 10 * m->xmin - 9 * m->xmax; +} + +/*****************************************************************************/ +/* */ +/* writenodes() Number the vertices and write them to a .node file. */ +/* */ +/* To save memory, the vertex numbers are written over the boundary markers */ +/* after the vertices are written to a file. */ +/* */ +/*****************************************************************************/ + +void writenodes(struct mesh *m, struct behavior *b, REAL **pointlist, REAL **pointattriblist, + int **pointmarkerlist) { + REAL *plist; + REAL *palist; + int *pmlist; + int coordindex; + int attribindex; + vertex vertexloop; + long outvertices; + int vertexnumber; + int i; + + if (b->jettison) { + outvertices = m->vertices.items - m->undeads; + } + else { + outvertices = m->vertices.items; + } + + if (!b->quiet) { + printf("Writing vertices.\n"); + } + /* Allocate memory for output vertices if necessary. */ + if (*pointlist == (REAL *) NULL) { + *pointlist = (REAL *) trimalloc((int) (outvertices * 2 * sizeof(REAL))); + } + /* Allocate memory for output vertex attributes if necessary. */ + if ((m->nextras > 0) && (*pointattriblist == (REAL *) NULL)) { + *pointattriblist = (REAL *) trimalloc((int) (outvertices * m->nextras * sizeof(REAL))); + } + /* Allocate memory for output vertex markers if necessary. */ + if (!b->nobound && (*pointmarkerlist == (int *) NULL)) { + *pointmarkerlist = (int *) trimalloc((int) (outvertices * sizeof(int))); + } + plist = *pointlist; + palist = *pointattriblist; + pmlist = *pointmarkerlist; + coordindex = 0; + attribindex = 0; + + traversalinit(&m->vertices); + vertexnumber = b->firstnumber; + vertexloop = vertextraverse(m); + while (vertexloop != (vertex) NULL) { + if (!b->jettison || (vertextype(vertexloop) != UNDEADVERTEX)) { + /* X and y coordinates. */ + plist[coordindex++] = vertexloop[0]; + plist[coordindex++] = vertexloop[1]; + /* Vertex attributes. */ + for (i = 0; i < m->nextras; i++) { + palist[attribindex++] = vertexloop[2 + i]; + } + if (!b->nobound) { + /* Copy the boundary marker. */ + pmlist[vertexnumber - b->firstnumber] = vertexmark(vertexloop); + } + setvertexmark(vertexloop, vertexnumber); + vertexnumber++; + } + vertexloop = vertextraverse(m); + } +} + +/*****************************************************************************/ +/* */ +/* numbernodes() Number the vertices. */ +/* */ +/* Each vertex is assigned a marker equal to its number. */ +/* */ +/* Used when writenodes() is not called because no .node file is written. */ +/* */ +/*****************************************************************************/ + +void numbernodes(struct mesh *m, struct behavior *b) { + vertex vertexloop; + int vertexnumber; + + traversalinit(&m->vertices); + vertexnumber = b->firstnumber; + vertexloop = vertextraverse(m); + while (vertexloop != (vertex) NULL) { + setvertexmark(vertexloop, vertexnumber); + if (!b->jettison || (vertextype(vertexloop) != UNDEADVERTEX)) { + vertexnumber++; + } + vertexloop = vertextraverse(m); + } +} + +/*****************************************************************************/ +/* */ +/* writeelements() Write the triangles to an .ele file. */ +/* */ +/*****************************************************************************/ + +void writeelements(struct mesh *m, struct behavior *b, INDICE **trianglelist, + REAL **triangleattriblist) { + INDICE *tlist; + REAL *talist; + int vertexindex; + int attribindex; + struct otri triangleloop; + vertex p1, p2, p3; + vertex mid1, mid2, mid3; + long elementnumber; + int i; + + if (!b->quiet) { + printf("Writing triangles.\n"); + } + /* Allocate memory for output triangles if necessary. */ + if (*trianglelist == (INDICE *) NULL) { + *trianglelist = (INDICE *) trimalloc( + (INDICE) (m->triangles.items * ((b->order + 1) * (b->order + 2) / 2) * sizeof(int))); + } + /* Allocate memory for output triangle attributes if necessary. */ + if ((m->eextras > 0) && (*triangleattriblist == (REAL *) NULL)) { + *triangleattriblist = (REAL *) trimalloc( + (int) (m->triangles.items * m->eextras * sizeof(REAL))); + } + tlist = *trianglelist; + talist = *triangleattriblist; + vertexindex = 0; + attribindex = 0; + + traversalinit(&m->triangles); + triangleloop.tri = triangletraverse(m); + triangleloop.orient = 0; + elementnumber = b->firstnumber; + while (triangleloop.tri != (triangle *) NULL) { + org(triangleloop, p1); + dest(triangleloop, p2); + apex(triangleloop, p3); + if (b->order == 1) { + tlist[vertexindex++] = vertexmark(p1); + tlist[vertexindex++] = vertexmark(p2); + tlist[vertexindex++] = vertexmark(p3); + } + else { + mid1 = (vertex) triangleloop.tri[m->highorderindex + 1]; + mid2 = (vertex) triangleloop.tri[m->highorderindex + 2]; + mid3 = (vertex) triangleloop.tri[m->highorderindex]; + tlist[vertexindex++] = vertexmark(p1); + tlist[vertexindex++] = vertexmark(p2); + tlist[vertexindex++] = vertexmark(p3); + tlist[vertexindex++] = vertexmark(mid1); + tlist[vertexindex++] = vertexmark(mid2); + tlist[vertexindex++] = vertexmark(mid3); + } + + for (i = 0; i < m->eextras; i++) { + talist[attribindex++] = elemattribute(triangleloop, i); + } + + triangleloop.tri = triangletraverse(m); + elementnumber++; + } +} + +/*****************************************************************************/ +/* */ +/* writepoly() Write the segments and holes to a .poly file. */ +/* */ +/*****************************************************************************/ + +void writepoly(struct mesh *m, struct behavior *b, int **segmentlist, int **segmentmarkerlist) { + int *slist; + int *smlist; + int index; + struct osub subsegloop; + vertex endpoint1, endpoint2; + long subsegnumber; + + if (!b->quiet) { + printf("Writing segments.\n"); + } + /* Allocate memory for output segments if necessary. */ + if (*segmentlist == (int *) NULL) { + *segmentlist = (int *) trimalloc((int) (m->subsegs.items * 2 * sizeof(int))); + } + /* Allocate memory for output segment markers if necessary. */ + if (!b->nobound && (*segmentmarkerlist == (int *) NULL)) { + *segmentmarkerlist = (int *) trimalloc((int) (m->subsegs.items * sizeof(int))); + } + slist = *segmentlist; + smlist = *segmentmarkerlist; + index = 0; + + traversalinit(&m->subsegs); + subsegloop.ss = subsegtraverse(m); + subsegloop.ssorient = 0; + subsegnumber = b->firstnumber; + while (subsegloop.ss != (subseg *) NULL) { + sorg(subsegloop, endpoint1); + sdest(subsegloop, endpoint2); + /* Copy indices of the segment's two endpoints. */ + slist[index++] = vertexmark(endpoint1); + slist[index++] = vertexmark(endpoint2); + if (!b->nobound) { + /* Copy the boundary marker. */ + smlist[subsegnumber - b->firstnumber] = mark(subsegloop); + } + + subsegloop.ss = subsegtraverse(m); + subsegnumber++; + } +} + +/*****************************************************************************/ +/* */ +/* writeedges() Write the edges to an .edge file. */ +/* */ +/*****************************************************************************/ + +void writeedges(struct mesh *m, struct behavior *b, int **edgelist, int **edgemarkerlist) { + int *elist; + int *emlist; + int index; + struct otri triangleloop, trisym; + struct osub checkmark; + vertex p1, p2; + long edgenumber; + triangle ptr; /* Temporary variable used by sym(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + if (!b->quiet) { + printf("Writing edges.\n"); + } + /* Allocate memory for edges if necessary. */ + if (*edgelist == (int *) NULL) { + *edgelist = (int *) trimalloc((int) (m->edges * 2 * sizeof(int))); + } + /* Allocate memory for edge markers if necessary. */ + if (!b->nobound && (*edgemarkerlist == (int *) NULL)) { + *edgemarkerlist = (int *) trimalloc((int) (m->edges * sizeof(int))); + } + elist = *edgelist; + emlist = *edgemarkerlist; + index = 0; + + traversalinit(&m->triangles); + triangleloop.tri = triangletraverse(m); + edgenumber = b->firstnumber; + /* To loop over the set of edges, loop over all triangles, and look at */ + /* the three edges of each triangle. If there isn't another triangle */ + /* adjacent to the edge, operate on the edge. If there is another */ + /* adjacent triangle, operate on the edge only if the current triangle */ + /* has a smaller pointer than its neighbor. This way, each edge is */ + /* considered only once. */ + while (triangleloop.tri != (triangle *) NULL) { + for (triangleloop.orient = 0; triangleloop.orient < 3; triangleloop.orient++) { + sym(triangleloop, trisym); + if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) { + org(triangleloop, p1); + dest(triangleloop, p2); + elist[index++] = vertexmark(p1); + elist[index++] = vertexmark(p2); + if (b->nobound) { + } + else { + /* Edge number, indices of two endpoints, and a boundary marker. */ + /* If there's no subsegment, the boundary marker is zero. */ + if (b->usesegments) { + tspivot(triangleloop, checkmark); + if (checkmark.ss == m->dummysub) { + emlist[edgenumber - b->firstnumber] = 0; + } + else { + emlist[edgenumber - b->firstnumber] = mark(checkmark); + } + } + else { + emlist[edgenumber - b->firstnumber] = trisym.tri == m->dummytri; + } + } + edgenumber++; + } + } + triangleloop.tri = triangletraverse(m); + } +} + +/*****************************************************************************/ +/* */ +/* writevoronoi() Write the Voronoi diagram to a .v.node and .v.edge */ +/* file. */ +/* */ +/* The Voronoi diagram is the geometric dual of the Delaunay triangulation. */ +/* Hence, the Voronoi vertices are listed by traversing the Delaunay */ +/* triangles, and the Voronoi edges are listed by traversing the Delaunay */ +/* edges. */ +/* */ +/* WARNING: In order to assign numbers to the Voronoi vertices, this */ +/* procedure messes up the subsegments or the extra nodes of every */ +/* element. Hence, you should call this procedure last. */ +/* */ +/*****************************************************************************/ + +void writevoronoi(struct mesh *m, struct behavior *b, REAL **vpointlist, REAL **vpointattriblist, + int **vpointmarkerlist, int **vedgelist, int **vedgemarkerlist, REAL **vnormlist) { + REAL *plist; + REAL *palist; + int *elist; + REAL *normlist; + int coordindex; + int attribindex; + struct otri triangleloop, trisym; + vertex torg, tdest, tapex; + REAL circumcenter[2]; + REAL xi, eta; + long vnodenumber, vedgenumber; + int p1, p2; + int i; + triangle ptr; /* Temporary variable used by sym(). */ + + if (!b->quiet) { + printf("Writing Voronoi vertices.\n"); + } + /* Allocate memory for Voronoi vertices if necessary. */ + if (*vpointlist == (REAL *) NULL) { + *vpointlist = (REAL *) trimalloc((int) (m->triangles.items * 2 * sizeof(REAL))); + } + /* Allocate memory for Voronoi vertex attributes if necessary. */ + if (*vpointattriblist == (REAL *) NULL) { + *vpointattriblist = (REAL *) trimalloc( + (int) (m->triangles.items * m->nextras * sizeof(REAL))); + } + *vpointmarkerlist = (int *) NULL; + plist = *vpointlist; + palist = *vpointattriblist; + coordindex = 0; + attribindex = 0; + + traversalinit(&m->triangles); + triangleloop.tri = triangletraverse(m); + triangleloop.orient = 0; + vnodenumber = b->firstnumber; + while (triangleloop.tri != (triangle *) NULL) { + org(triangleloop, torg); + dest(triangleloop, tdest); + apex(triangleloop, tapex); + findcircumcenter(m, b, torg, tdest, tapex, circumcenter, &xi, &eta, 0); + /* X and y coordinates. */ + plist[coordindex++] = circumcenter[0]; + plist[coordindex++] = circumcenter[1]; + for (i = 2; i < 2 + m->nextras; i++) { + /* Interpolate the vertex attributes at the circumcenter. */ + palist[attribindex++] = torg[i] + xi * (tdest[i] - torg[i]) + eta * (tapex[i] - torg[i]); + } + + *(int *) (triangleloop.tri + 6) = (int) vnodenumber; + triangleloop.tri = triangletraverse(m); + vnodenumber++; + } + + if (!b->quiet) { + printf("Writing Voronoi edges.\n"); + } + /* Allocate memory for output Voronoi edges if necessary. */ + if (*vedgelist == (int *) NULL) { + *vedgelist = (int *) trimalloc((int) (m->edges * 2 * sizeof(int))); + } + *vedgemarkerlist = (int *) NULL; + /* Allocate memory for output Voronoi norms if necessary. */ + if (*vnormlist == (REAL *) NULL) { + *vnormlist = (REAL *) trimalloc((int) (m->edges * 2 * sizeof(REAL))); + } + elist = *vedgelist; + normlist = *vnormlist; + coordindex = 0; + + traversalinit(&m->triangles); + triangleloop.tri = triangletraverse(m); + vedgenumber = b->firstnumber; + /* To loop over the set of edges, loop over all triangles, and look at */ + /* the three edges of each triangle. If there isn't another triangle */ + /* adjacent to the edge, operate on the edge. If there is another */ + /* adjacent triangle, operate on the edge only if the current triangle */ + /* has a smaller pointer than its neighbor. This way, each edge is */ + /* considered only once. */ + while (triangleloop.tri != (triangle *) NULL) { + for (triangleloop.orient = 0; triangleloop.orient < 3; triangleloop.orient++) { + sym(triangleloop, trisym); + if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) { + /* Find the number of this triangle (and Voronoi vertex). */ + p1 = *(int *) (triangleloop.tri + 6); + if (trisym.tri == m->dummytri) { + org(triangleloop, torg); + dest(triangleloop, tdest); + /* Copy an infinite ray. Index of one endpoint, and -1. */ + elist[coordindex] = p1; + normlist[coordindex++] = tdest[1] - torg[1]; + elist[coordindex] = -1; + normlist[coordindex++] = torg[0] - tdest[0]; + } + else { + /* Find the number of the adjacent triangle (and Voronoi vertex). */ + p2 = *(int *) (trisym.tri + 6); + /* Finite edge. Write indices of two endpoints. */ + elist[coordindex] = p1; + normlist[coordindex++] = 0.0; + elist[coordindex] = p2; + normlist[coordindex++] = 0.0; + } + vedgenumber++; + } + } + triangleloop.tri = triangletraverse(m); + } +} + +void writeneighbors(struct mesh *m, struct behavior *b, int **neighborlist) { + int *nlist; + int index; + struct otri triangleloop, trisym; + long elementnumber; + int neighbor1, neighbor2, neighbor3; + triangle ptr; /* Temporary variable used by sym(). */ + + if (!b->quiet) { + printf("Writing neighbors.\n"); + } + /* Allocate memory for neighbors if necessary. */ + if (*neighborlist == (int *) NULL) { + *neighborlist = (int *) trimalloc((int) (m->triangles.items * 3 * sizeof(int))); + } + nlist = *neighborlist; + index = 0; + + traversalinit(&m->triangles); + triangleloop.tri = triangletraverse(m); + triangleloop.orient = 0; + elementnumber = b->firstnumber; + while (triangleloop.tri != (triangle *) NULL) { + *(int *) (triangleloop.tri + 6) = (int) elementnumber; + triangleloop.tri = triangletraverse(m); + elementnumber++; + } + *(int *) (m->dummytri + 6) = -1; + + traversalinit(&m->triangles); + triangleloop.tri = triangletraverse(m); + elementnumber = b->firstnumber; + while (triangleloop.tri != (triangle *) NULL) { + triangleloop.orient = 1; + sym(triangleloop, trisym); + neighbor1 = *(int *) (trisym.tri + 6); + triangleloop.orient = 2; + sym(triangleloop, trisym); + neighbor2 = *(int *) (trisym.tri + 6); + triangleloop.orient = 0; + sym(triangleloop, trisym); + neighbor3 = *(int *) (trisym.tri + 6); + nlist[index++] = neighbor1; + nlist[index++] = neighbor2; + nlist[index++] = neighbor3; + + triangleloop.tri = triangletraverse(m); + elementnumber++; + } +} + +/** **/ +/** **/ +/********* File I/O routines end here *********/ + +/*****************************************************************************/ +/* */ +/* main() or triangulate() Gosh, do everything. */ +/* */ +/* The sequence is roughly as follows. Many of these steps can be skipped, */ +/* depending on the command line switches. */ +/* */ +/* - Initialize constants and parse the command line. */ +/* - Read the vertices from a file and either */ +/* - triangulate them (no -r), or */ +/* - read an old mesh from files and reconstruct it (-r). */ +/* - Insert the PSLG segments (-p), and possibly segments on the convex */ +/* hull (-c). */ +/* - Read the holes (-p), regional attributes (-pA), and regional area */ +/* constraints (-pa). Carve the holes and concavities, and spread the */ +/* regional attributes and area constraints. */ +/* - Enforce the constraints on minimum angle (-q) and maximum area (-a). */ +/* Also enforce the conforming Delaunay property (-q and -a). */ +/* - Compute the number of edges in the resulting mesh. */ +/* - Promote the mesh's linear triangles to higher order elements (-o). */ +/* - Write the output files and print the statistics. */ +/* - Check the consistency and Delaunay property of the mesh (-C). */ +/* */ +/*****************************************************************************/ + +void triangulate(struct behavior *command, struct triangulateio *in, struct triangulateio *out, + struct triangulateio *vorout) { + struct mesh m; + struct behavior *b = command; + REAL *holearray; /* Array of holes. */ + REAL *regionarray; /* Array of regional attributes and area constraints. */ + + triangleinit(&m); + //parsecommandline(1, &triswitches, &b); + m.steinerleft = b->steiner; + + transfernodes(&m, b, in->pointlist, in->pointattributelist, in->pointmarkerlist, + in->numberofpoints, in->numberofpointattributes); + +#ifdef CDT_ONLY + m.hullsize = delaunay(&m, b); /* Triangulate the vertices. */ +#else /* not CDT_ONLY */ + if (b->refine) + { + /* Read and reconstruct a mesh. */ + m.hullsize = reconstruct(&m, b, in->trianglelist, + in->triangleattributelist, in->trianglearealist, + in->numberoftriangles, in->numberofcorners, + in->numberoftriangleattributes, + in->segmentlist, in->segmentmarkerlist, + in->numberofsegments); + } + else + { + m.hullsize = delaunay(&m, b); /* Triangulate the vertices. */ + } +#endif /* not CDT_ONLY */ + + /* Ensure that no vertex can be mistaken for a triangular bounding */ + /* box vertex in insertvertex(). */ + m.infvertex1 = (vertex) NULL; + m.infvertex2 = (vertex) NULL; + m.infvertex3 = (vertex) NULL; + + if (b->usesegments) { + m.checksegments = 1; /* Segments will be introduced next. */ + if (!b->refine) { + /* Insert PSLG segments and/or convex hull segments. */ + formskeleton(&m, b, in->segmentlist, in->segmentmarkerlist, in->numberofsegments); + } + } + + if (b->poly && (m.triangles.items > 0)) { + holearray = in->holelist; + m.holes = in->numberofholes; + regionarray = in->regionlist; + m.regions = in->numberofregions; + if (!b->refine) { + /* Carve out holes and concavities. */ + carveholes(&m, b, holearray, m.holes, regionarray, m.regions); + } + } + else { + /* Without a PSLG, there can be no holes or regional attributes */ + /* or area constraints. The following are set to zero to avoid */ + /* an accidental free() later. */ + m.holes = 0; + m.regions = 0; + } + +#ifndef CDT_ONLY + if (b->quality && (m.triangles.items > 0)) + { + enforcequality(&m, b); /* Enforce angle and area constraints. */ + } +#endif /* not CDT_ONLY */ + +#ifndef CDT_ONLY + if (b->quality) + { + printf("Quality milliseconds: %ld\n", + 1000l * (tv5.tv_sec - tv4.tv_sec) + + (tv5.tv_usec - tv4.tv_usec) / 1000l); + } +#endif /* not CDT_ONLY */ + + /* Calculate the number of edges. */ + m.edges = (3l * m.triangles.items + m.hullsize) / 2l; + + if (b->order > 1) { + highorder(&m, b); /* Promote elements to higher polynomial order. */ + } + if (!b->quiet) { + printf("\n"); + } + + if (b->jettison) { + out->numberofpoints = m.vertices.items - m.undeads; + } + else { + out->numberofpoints = m.vertices.items; + } + out->numberofpointattributes = m.nextras; + out->numberoftriangles = m.triangles.items; + out->numberofcorners = (b->order + 1) * (b->order + 2) / 2; + out->numberoftriangleattributes = m.eextras; + out->numberofedges = m.edges; + if (b->usesegments) { + out->numberofsegments = m.subsegs.items; + } + else { + out->numberofsegments = m.hullsize; + } + if (vorout != (struct triangulateio *) NULL) { + vorout->numberofpoints = m.triangles.items; + vorout->numberofpointattributes = m.nextras; + vorout->numberofedges = m.edges; + } + + /* If not using iteration numbers, don't write a .node file if one was */ + /* read, because the original one would be overwritten! */ + if (b->nonodewritten || (b->noiterationnum && m.readnodefile)) { + if (!b->quiet) { + printf("NOT writing vertices.\n"); + } + numbernodes(&m, b); /* We must remember to number the vertices. */ + } + else { + /* writenodes() numbers the vertices too. */ + writenodes(&m, b, &out->pointlist, &out->pointattributelist, &out->pointmarkerlist); + } + if (b->noelewritten) { + if (!b->quiet) { + printf("NOT writing triangles.\n"); + } + } + else { + writeelements(&m, b, &out->trianglelist, &out->triangleattributelist); + } + /* The -c switch (convex switch) causes a PSLG to be written */ + /* even if none was read. */ + if (b->poly || b->convex) { + /* If not using iteration numbers, don't overwrite the .poly file. */ + if (b->nopolywritten || b->noiterationnum) { + if (!b->quiet) { + printf("NOT writing segments.\n"); + } + } + else { + writepoly(&m, b, &out->segmentlist, &out->segmentmarkerlist); + out->numberofholes = m.holes; + out->numberofregions = m.regions; + if (b->poly) { + out->holelist = in->holelist; + out->regionlist = in->regionlist; + } + else { + out->holelist = (REAL *) NULL; + out->regionlist = (REAL *) NULL; + } + } + } + if (b->edgesout) { + writeedges(&m, b, &out->edgelist, &out->edgemarkerlist); + } + if (b->voronoi) { + writevoronoi(&m, b, &vorout->pointlist, &vorout->pointattributelist, &vorout->pointmarkerlist, + &vorout->edgelist, &vorout->edgemarkerlist, &vorout->normlist); + } + if (b->neighbors) { + writeneighbors(&m, b, &out->neighborlist); + } + + if (!b->quiet) { + statistics(&m, b); + } + +#ifndef REDUCED + if (b->docheck) + { + checkmesh(&m, b); + checkdelaunay(&m, b); + } +#endif /* not REDUCED */ + + triangledeinit(&m, b); +} diff --git a/vtm/jni/target/linux64/triangle/triangle_dbg.c b/vtm/jni/target/linux64/triangle/triangle_dbg.c new file mode 100644 index 00000000..f6374ba3 --- /dev/null +++ b/vtm/jni/target/linux64/triangle/triangle_dbg.c @@ -0,0 +1,441 @@ +#include "triangle_private.h" + +/*****************************************************************************/ +/* */ +/* quality_statistics() Print statistics about the quality of the mesh. */ +/* */ +/*****************************************************************************/ + +void quality_statistics(struct mesh *m, struct behavior *b) { + struct otri triangleloop; + vertex p[3]; + REAL cossquaretable[8]; + REAL ratiotable[16]; + REAL dx[3], dy[3]; + REAL edgelength[3]; + REAL dotproduct; + REAL cossquare; + REAL triarea; + REAL shortest, longest; + REAL trilongest2; + REAL smallestarea, biggestarea; + REAL triminaltitude2; + REAL minaltitude; + REAL triaspect2; + REAL worstaspect; + REAL smallestangle, biggestangle; + REAL radconst, degconst; + int angletable[18]; + int aspecttable[16]; + int aspectindex; + int tendegree; + int acutebiggest; + int i, ii, j, k; + + printf("Mesh quality statistics:\n\n"); + radconst = PI / 18.0; + degconst = 180.0 / PI; + for (i = 0; i < 8; i++) { + cossquaretable[i] = cos(radconst * (REAL) (i + 1)); + cossquaretable[i] = cossquaretable[i] * cossquaretable[i]; + } + for (i = 0; i < 18; i++) { + angletable[i] = 0; + } + + ratiotable[0] = 1.5; + ratiotable[1] = 2.0; + ratiotable[2] = 2.5; + ratiotable[3] = 3.0; + ratiotable[4] = 4.0; + ratiotable[5] = 6.0; + ratiotable[6] = 10.0; + ratiotable[7] = 15.0; + ratiotable[8] = 25.0; + ratiotable[9] = 50.0; + ratiotable[10] = 100.0; + ratiotable[11] = 300.0; + ratiotable[12] = 1000.0; + ratiotable[13] = 10000.0; + ratiotable[14] = 100000.0; + ratiotable[15] = 0.0; + for (i = 0; i < 16; i++) { + aspecttable[i] = 0; + } + + worstaspect = 0.0; + minaltitude = m->xmax - m->xmin + m->ymax - m->ymin; + minaltitude = minaltitude * minaltitude; + shortest = minaltitude; + longest = 0.0; + smallestarea = minaltitude; + biggestarea = 0.0; + worstaspect = 0.0; + smallestangle = 0.0; + biggestangle = 2.0; + acutebiggest = 1; + + traversalinit(&m->triangles); + triangleloop.tri = triangletraverse(m); + triangleloop.orient = 0; + while (triangleloop.tri != (triangle *) NULL) { + org(triangleloop, p[0]); + dest(triangleloop, p[1]); + apex(triangleloop, p[2]); + trilongest2 = 0.0; + + for (i = 0; i < 3; i++) { + j = plus1mod3[i]; + k = minus1mod3[i]; + dx[i] = p[j][0] - p[k][0]; + dy[i] = p[j][1] - p[k][1]; + edgelength[i] = dx[i] * dx[i] + dy[i] * dy[i]; + if (edgelength[i] > trilongest2) { + trilongest2 = edgelength[i]; + } + if (edgelength[i] > longest) { + longest = edgelength[i]; + } + if (edgelength[i] < shortest) { + shortest = edgelength[i]; + } + } + + triarea = counterclockwise(m, b, p[0], p[1], p[2]); + if (triarea < smallestarea) { + smallestarea = triarea; + } + if (triarea > biggestarea) { + biggestarea = triarea; + } + triminaltitude2 = triarea * triarea / trilongest2; + if (triminaltitude2 < minaltitude) { + minaltitude = triminaltitude2; + } + triaspect2 = trilongest2 / triminaltitude2; + if (triaspect2 > worstaspect) { + worstaspect = triaspect2; + } + aspectindex = 0; + while ((triaspect2 > ratiotable[aspectindex] * ratiotable[aspectindex]) && (aspectindex < 15)) { + aspectindex++; + } + aspecttable[aspectindex]++; + + for (i = 0; i < 3; i++) { + j = plus1mod3[i]; + k = minus1mod3[i]; + dotproduct = dx[j] * dx[k] + dy[j] * dy[k]; + cossquare = dotproduct * dotproduct / (edgelength[j] * edgelength[k]); + tendegree = 8; + for (ii = 7; ii >= 0; ii--) { + if (cossquare > cossquaretable[ii]) { + tendegree = ii; + } + } + if (dotproduct <= 0.0) { + angletable[tendegree]++; + if (cossquare > smallestangle) { + smallestangle = cossquare; + } + if (acutebiggest && (cossquare < biggestangle)) { + biggestangle = cossquare; + } + } + else { + angletable[17 - tendegree]++; + if (acutebiggest || (cossquare > biggestangle)) { + biggestangle = cossquare; + acutebiggest = 0; + } + } + } + triangleloop.tri = triangletraverse(m); + } + + shortest = sqrt(shortest); + longest = sqrt(longest); + minaltitude = sqrt(minaltitude); + worstaspect = sqrt(worstaspect); + smallestarea *= 0.5; + biggestarea *= 0.5; + if (smallestangle >= 1.0) { + smallestangle = 0.0; + } + else { + smallestangle = degconst * acos(sqrt(smallestangle)); + } + if (biggestangle >= 1.0) { + biggestangle = 180.0; + } + else { + if (acutebiggest) { + biggestangle = degconst * acos(sqrt(biggestangle)); + } + else { + biggestangle = 180.0 - degconst * acos(sqrt(biggestangle)); + } + } + + printf(" Smallest area: %16.5g | Largest area: %16.5g\n", smallestarea, biggestarea); + printf(" Shortest edge: %16.5g | Longest edge: %16.5g\n", shortest, longest); + printf( + " Shortest altitude: %12.5g | Largest aspect ratio: %8.5g\n\n", minaltitude, worstaspect); + + printf(" Triangle aspect ratio histogram:\n"); + printf( + " 1.1547 - %-6.6g : %8d | %6.6g - %-6.6g : %8d\n", ratiotable[0], aspecttable[0], ratiotable[7], ratiotable[8], aspecttable[8]); + for (i = 1; i < 7; i++) { + printf( + " %6.6g - %-6.6g : %8d | %6.6g - %-6.6g : %8d\n", ratiotable[i - 1], ratiotable[i], aspecttable[i], ratiotable[i + 7], ratiotable[i + 8], aspecttable[i + 8]); + } + printf( + " %6.6g - %-6.6g : %8d | %6.6g - : %8d\n", ratiotable[6], ratiotable[7], aspecttable[7], ratiotable[14], aspecttable[15]); + printf(" (Aspect ratio is longest edge divided by shortest altitude)\n\n"); + + printf(" Smallest angle: %15.5g | Largest angle: %15.5g\n\n", smallestangle, biggestangle); + + printf(" Angle histogram:\n"); + for (i = 0; i < 9; i++) { + printf( + " %3d - %3d degrees: %8d | %3d - %3d degrees: %8d\n", i * 10, i * 10 + 10, angletable[i], i * 10 + 90, i * 10 + 100, angletable[i + 9]); + } + printf("\n"); +} + +/*****************************************************************************/ +/* */ +/* statistics() Print all sorts of cool facts. */ +/* */ +/*****************************************************************************/ + +void statistics(struct mesh *m, struct behavior *b) { + printf("\nStatistics:\n\n"); + printf(" Input vertices: %d\n", m->invertices); + if (b->refine) { + printf(" Input triangles: %d\n", m->inelements); + } + if (b->poly) { + printf(" Input segments: %d\n", m->insegments); + if (!b->refine) { + printf(" Input holes: %d\n", m->holes); + } + } + + printf("\n Mesh vertices: %ld\n", m->vertices.items - m->undeads); + printf(" Mesh triangles: %ld\n", m->triangles.items); + printf(" Mesh edges: %ld\n", m->edges); + printf(" Mesh exterior boundary edges: %ld\n", m->hullsize); + if (b->poly || b->refine) { + printf(" Mesh interior boundary edges: %ld\n", m->subsegs.items - m->hullsize); + printf(" Mesh subsegments (constrained edges): %ld\n", m->subsegs.items); + } + printf("\n"); + + if (b->verbose) { + quality_statistics(m, b); + printf("Memory allocation statistics:\n\n"); + printf(" Maximum number of vertices: %ld\n", m->vertices.maxitems); + printf(" Maximum number of triangles: %ld\n", m->triangles.maxitems); + if (m->subsegs.maxitems > 0) { + printf(" Maximum number of subsegments: %ld\n", m->subsegs.maxitems); + } + if (m->viri.maxitems > 0) { + printf(" Maximum number of viri: %ld\n", m->viri.maxitems); + } + if (m->badsubsegs.maxitems > 0) { + printf(" Maximum number of encroached subsegments: %ld\n", m->badsubsegs.maxitems); + } + if (m->badtriangles.maxitems > 0) { + printf(" Maximum number of bad triangles: %ld\n", m->badtriangles.maxitems); + } + if (m->flipstackers.maxitems > 0) { + printf(" Maximum number of stacked triangle flips: %ld\n", m->flipstackers.maxitems); + } + if (m->splaynodes.maxitems > 0) { + printf(" Maximum number of splay tree nodes: %ld\n", m->splaynodes.maxitems); + } + printf( + " Approximate heap memory use (bytes): %ld\n\n", m->vertices.maxitems * m->vertices.itembytes + m->triangles.maxitems * m->triangles.itembytes + m->subsegs.maxitems * m->subsegs.itembytes + m->viri.maxitems * m->viri.itembytes + m->badsubsegs.maxitems * m->badsubsegs.itembytes + m->badtriangles.maxitems * m->badtriangles.itembytes + m->flipstackers.maxitems * m->flipstackers.itembytes + m->splaynodes.maxitems * m->splaynodes.itembytes); + + printf("Algorithmic statistics:\n\n"); + if (!b->weighted) { + printf(" Number of incircle tests: %ld\n", m->incirclecount); + } + else { + printf(" Number of 3D orientation tests: %ld\n", m->orient3dcount); + } + printf(" Number of 2D orientation tests: %ld\n", m->counterclockcount); + if (m->hyperbolacount > 0) { + printf(" Number of right-of-hyperbola tests: %ld\n", m->hyperbolacount); + } + if (m->circletopcount > 0) { + printf(" Number of circle top computations: %ld\n", m->circletopcount); + } + if (m->circumcentercount > 0) { + printf(" Number of triangle circumcenter computations: %ld\n", m->circumcentercount); + } + printf("\n"); + } +} + +/********* Debugging routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* printtriangle() Print out the details of an oriented triangle. */ +/* */ +/* I originally wrote this procedure to simplify debugging; it can be */ +/* called directly from the debugger, and presents information about an */ +/* oriented triangle in digestible form. It's also used when the */ +/* highest level of verbosity (`-VVV') is specified. */ +/* */ +/*****************************************************************************/ + +void printtriangle(struct mesh *m, struct behavior *b, struct otri *t) { + struct otri printtri; + struct osub printsh; + vertex printvertex; + + printf("triangle x%lx with orientation %d:\n", (unsigned long) t->tri, t->orient); + decode(t->tri[0], printtri); + if (printtri.tri == m->dummytri) { + printf(" [0] = Outer space\n"); + } + else { + printf(" [0] = x%lx %d\n", (unsigned long) printtri.tri, printtri.orient); + } + decode(t->tri[1], printtri); + if (printtri.tri == m->dummytri) { + printf(" [1] = Outer space\n"); + } + else { + printf(" [1] = x%lx %d\n", (unsigned long) printtri.tri, printtri.orient); + } + decode(t->tri[2], printtri); + if (printtri.tri == m->dummytri) { + printf(" [2] = Outer space\n"); + } + else { + printf(" [2] = x%lx %d\n", (unsigned long) printtri.tri, printtri.orient); + } + + org(*t, printvertex); + if (printvertex == (vertex) NULL) + printf(" Origin[%d] = NULL\n", (t->orient + 1) % 3 + 3); + else + printf( + " Origin[%d] = x%lx (%.12g, %.12g)\n", (t->orient + 1) % 3 + 3, (unsigned long) printvertex, printvertex[0], printvertex[1]); + dest(*t, printvertex); + if (printvertex == (vertex) NULL) + printf(" Dest [%d] = NULL\n", (t->orient + 2) % 3 + 3); + else + printf( + " Dest [%d] = x%lx (%.12g, %.12g)\n", (t->orient + 2) % 3 + 3, (unsigned long) printvertex, printvertex[0], printvertex[1]); + apex(*t, printvertex); + if (printvertex == (vertex) NULL) + printf(" Apex [%d] = NULL\n", t->orient + 3); + else + printf( + " Apex [%d] = x%lx (%.12g, %.12g)\n", t->orient + 3, (unsigned long) printvertex, printvertex[0], printvertex[1]); + + if (b->usesegments) { + sdecode(t->tri[6], printsh); + if (printsh.ss != m->dummysub) { + printf(" [6] = x%lx %d\n", (unsigned long) printsh.ss, printsh.ssorient); + } + sdecode(t->tri[7], printsh); + if (printsh.ss != m->dummysub) { + printf(" [7] = x%lx %d\n", (unsigned long) printsh.ss, printsh.ssorient); + } + sdecode(t->tri[8], printsh); + if (printsh.ss != m->dummysub) { + printf(" [8] = x%lx %d\n", (unsigned long) printsh.ss, printsh.ssorient); + } + } + + if (b->vararea) { + printf(" Area constraint: %.4g\n", areabound(*t)); + } +} + +/*****************************************************************************/ +/* */ +/* printsubseg() Print out the details of an oriented subsegment. */ +/* */ +/* I originally wrote this procedure to simplify debugging; it can be */ +/* called directly from the debugger, and presents information about an */ +/* oriented subsegment in digestible form. It's also used when the highest */ +/* level of verbosity (`-VVV') is specified. */ +/* */ +/*****************************************************************************/ + +void printsubseg(struct mesh *m, struct behavior *b, struct osub *s) { + struct osub printsh; + struct otri printtri; + vertex printvertex; + + printf( + "subsegment x%lx with orientation %d and mark %d:\n", (unsigned long) s->ss, s->ssorient, mark(*s)); + sdecode(s->ss[0], printsh); + if (printsh.ss == m->dummysub) { + printf(" [0] = No subsegment\n"); + } + else { + printf(" [0] = x%lx %d\n", (unsigned long) printsh.ss, printsh.ssorient); + } + sdecode(s->ss[1], printsh); + if (printsh.ss == m->dummysub) { + printf(" [1] = No subsegment\n"); + } + else { + printf(" [1] = x%lx %d\n", (unsigned long) printsh.ss, printsh.ssorient); + } + + sorg(*s, printvertex); + if (printvertex == (vertex) NULL) + printf(" Origin[%d] = NULL\n", 2 + s->ssorient); + else + printf( + " Origin[%d] = x%lx (%.12g, %.12g)\n", 2 + s->ssorient, (unsigned long) printvertex, printvertex[0], printvertex[1]); + sdest(*s, printvertex); + if (printvertex == (vertex) NULL) + printf(" Dest [%d] = NULL\n", 3 - s->ssorient); + else + printf( + " Dest [%d] = x%lx (%.12g, %.12g)\n", 3 - s->ssorient, (unsigned long) printvertex, printvertex[0], printvertex[1]); + + decode(s->ss[6], printtri); + if (printtri.tri == m->dummytri) { + printf(" [6] = Outer space\n"); + } + else { + printf(" [6] = x%lx %d\n", (unsigned long) printtri.tri, printtri.orient); + } + decode(s->ss[7], printtri); + if (printtri.tri == m->dummytri) { + printf(" [7] = Outer space\n"); + } + else { + printf(" [7] = x%lx %d\n", (unsigned long) printtri.tri, printtri.orient); + } + + segorg(*s, printvertex); + if (printvertex == (vertex) NULL) + printf(" Segment origin[%d] = NULL\n", 4 + s->ssorient); + else + printf( + " Segment origin[%d] = x%lx (%.12g, %.12g)\n", 4 + s->ssorient, (unsigned long) printvertex, printvertex[0], printvertex[1]); + segdest(*s, printvertex); + if (printvertex == (vertex) NULL) + printf(" Segment dest [%d] = NULL\n", 5 - s->ssorient); + else + printf( + " Segment dest [%d] = x%lx (%.12g, %.12g)\n", 5 - s->ssorient, (unsigned long) printvertex, printvertex[0], printvertex[1]); +} + +/** **/ +/** **/ +/********* Debugging routines end here *********/ diff --git a/vtm/jni/tessellate/LICENSE b/vtm/jni/tessellate/LICENSE new file mode 100644 index 00000000..fd57cadc --- /dev/null +++ b/vtm/jni/tessellate/LICENSE @@ -0,0 +1,37 @@ +Copyright notice and license for the libtess files (all source files besides +tessellate.[ch] and main.c): + +SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) +Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice including the dates of first publication and +either this permission notice or a reference to +http://oss.sgi.com/projects/FreeB/ +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Except as contained in this notice, the name of Silicon Graphics, Inc. +shall not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization from +Silicon Graphics, Inc. + +-------------------------------------------------------------------------------- + +Copyright notice for the other files: + +SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) +Copyright (C) 2013 AT&T Intellectual Property. All Rights Reserved. diff --git a/vtm/jni/tessellate/Makefile b/vtm/jni/tessellate/Makefile new file mode 100644 index 00000000..607753f6 --- /dev/null +++ b/vtm/jni/tessellate/Makefile @@ -0,0 +1,2 @@ +all: + gcc dict.c mesh.c render.c tess.c geom.c memalloc.c normal.c priorityq.c sweep.c tessmono.c tessellate.c main.c -o tessellate diff --git a/vtm/jni/tessellate/README.md b/vtm/jni/tessellate/README.md new file mode 100644 index 00000000..fef24f33 --- /dev/null +++ b/vtm/jni/tessellate/README.md @@ -0,0 +1,18 @@ +# A minimal, self-contained port of SGI's GLU libtess + +Polygon tessellation is a major pain in the neck. Have you ever tried +writing fast and robust code for it? libtess is, to my knowledge, the +only GPL-compatible, liberally-licensed, high-quality polygon +triangulator out there. + +This repository includes a self-contained function (tessellate, in +tessellate.c) that you can call to triangulate a polygon that is +potentially self-intersecting, with holes, or with duplicate +vertices. Simple examples of calling the tessellate function directly +are located in main.c. + +More interestingly, this repository also includes an +Emscripten-compiled module, _tessellate.js, and a Javascript-friendly +wrapper, in tessellate.js. Simple examples are available under +index.html. + diff --git a/vtm/jni/tessellate/dict-list.h b/vtm/jni/tessellate/dict-list.h new file mode 100644 index 00000000..11331a76 --- /dev/null +++ b/vtm/jni/tessellate/dict-list.h @@ -0,0 +1,100 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __dict_list_h_ +#define __dict_list_h_ + +/* Use #define's so that another heap implementation can use this one */ + +#define DictKey DictListKey +#define Dict DictList +#define DictNode DictListNode + +#define dictNewDict(frame,leq) __gl_dictListNewDict(frame,leq) +#define dictDeleteDict(dict) __gl_dictListDeleteDict(dict) + +#define dictSearch(dict,key) __gl_dictListSearch(dict,key) +#define dictInsert(dict,key) __gl_dictListInsert(dict,key) +#define dictInsertBefore(dict,node,key) __gl_dictListInsertBefore(dict,node,key) +#define dictDelete(dict,node) __gl_dictListDelete(dict,node) + +#define dictKey(n) __gl_dictListKey(n) +#define dictSucc(n) __gl_dictListSucc(n) +#define dictPred(n) __gl_dictListPred(n) +#define dictMin(d) __gl_dictListMin(d) +#define dictMax(d) __gl_dictListMax(d) + + + +typedef void *DictKey; +typedef struct Dict Dict; +typedef struct DictNode DictNode; + +Dict *dictNewDict( + void *frame, + int (*leq)(void *frame, DictKey key1, DictKey key2) ); + +void dictDeleteDict( Dict *dict ); + +/* Search returns the node with the smallest key greater than or equal + * to the given key. If there is no such key, returns a node whose + * key is NULL. Similarly, Succ(Max(d)) has a NULL key, etc. + */ +DictNode *dictSearch( Dict *dict, DictKey key ); +DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key ); +void dictDelete( Dict *dict, DictNode *node ); + +#define __gl_dictListKey(n) ((n)->key) +#define __gl_dictListSucc(n) ((n)->next) +#define __gl_dictListPred(n) ((n)->prev) +#define __gl_dictListMin(d) ((d)->head.next) +#define __gl_dictListMax(d) ((d)->head.prev) +#define __gl_dictListInsert(d,k) (dictInsertBefore((d),&(d)->head,(k))) + + +/*** Private data structures ***/ + +struct DictNode { + DictKey key; + DictNode *next; + DictNode *prev; +}; + +struct Dict { + DictNode head; + void *frame; + int (*leq)(void *frame, DictKey key1, DictKey key2); +}; + +#endif diff --git a/vtm/jni/tessellate/dict.c b/vtm/jni/tessellate/dict.c new file mode 100644 index 00000000..49d4f759 --- /dev/null +++ b/vtm/jni/tessellate/dict.c @@ -0,0 +1,111 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include +#include "dict-list.h" +#include "memalloc.h" + +/* really __gl_dictListNewDict */ +Dict *dictNewDict( void *frame, + int (*leq)(void *frame, DictKey key1, DictKey key2) ) +{ + Dict *dict = (Dict *) memAlloc( sizeof( Dict )); + DictNode *head; + + if (dict == NULL) return NULL; + + head = &dict->head; + + head->key = NULL; + head->next = head; + head->prev = head; + + dict->frame = frame; + dict->leq = leq; + + return dict; +} + +/* really __gl_dictListDeleteDict */ +void dictDeleteDict( Dict *dict ) +{ + DictNode *node, *next; + + for( node = dict->head.next; node != &dict->head; node = next ) { + next = node->next; + memFree( node ); + } + memFree( dict ); +} + +/* really __gl_dictListInsertBefore */ +DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key ) +{ + DictNode *newNode; + + do { + node = node->prev; + } while( node->key != NULL && ! (*dict->leq)(dict->frame, node->key, key)); + + newNode = (DictNode *) memAlloc( sizeof( DictNode )); + if (newNode == NULL) return NULL; + + newNode->key = key; + newNode->next = node->next; + node->next->prev = newNode; + newNode->prev = node; + node->next = newNode; + + return newNode; +} + +/* really __gl_dictListDelete */ +void dictDelete( Dict *dict, DictNode *node ) /*ARGSUSED*/ +{ + node->next->prev = node->prev; + node->prev->next = node->next; + memFree( node ); +} + +/* really __gl_dictListSearch */ +DictNode *dictSearch( Dict *dict, DictKey key ) +{ + DictNode *node = &dict->head; + + do { + node = node->next; + } while( node->key != NULL && ! (*dict->leq)(dict->frame, key, node->key)); + + return node; +} diff --git a/vtm/jni/tessellate/dict.h b/vtm/jni/tessellate/dict.h new file mode 100644 index 00000000..11331a76 --- /dev/null +++ b/vtm/jni/tessellate/dict.h @@ -0,0 +1,100 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __dict_list_h_ +#define __dict_list_h_ + +/* Use #define's so that another heap implementation can use this one */ + +#define DictKey DictListKey +#define Dict DictList +#define DictNode DictListNode + +#define dictNewDict(frame,leq) __gl_dictListNewDict(frame,leq) +#define dictDeleteDict(dict) __gl_dictListDeleteDict(dict) + +#define dictSearch(dict,key) __gl_dictListSearch(dict,key) +#define dictInsert(dict,key) __gl_dictListInsert(dict,key) +#define dictInsertBefore(dict,node,key) __gl_dictListInsertBefore(dict,node,key) +#define dictDelete(dict,node) __gl_dictListDelete(dict,node) + +#define dictKey(n) __gl_dictListKey(n) +#define dictSucc(n) __gl_dictListSucc(n) +#define dictPred(n) __gl_dictListPred(n) +#define dictMin(d) __gl_dictListMin(d) +#define dictMax(d) __gl_dictListMax(d) + + + +typedef void *DictKey; +typedef struct Dict Dict; +typedef struct DictNode DictNode; + +Dict *dictNewDict( + void *frame, + int (*leq)(void *frame, DictKey key1, DictKey key2) ); + +void dictDeleteDict( Dict *dict ); + +/* Search returns the node with the smallest key greater than or equal + * to the given key. If there is no such key, returns a node whose + * key is NULL. Similarly, Succ(Max(d)) has a NULL key, etc. + */ +DictNode *dictSearch( Dict *dict, DictKey key ); +DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key ); +void dictDelete( Dict *dict, DictNode *node ); + +#define __gl_dictListKey(n) ((n)->key) +#define __gl_dictListSucc(n) ((n)->next) +#define __gl_dictListPred(n) ((n)->prev) +#define __gl_dictListMin(d) ((d)->head.next) +#define __gl_dictListMax(d) ((d)->head.prev) +#define __gl_dictListInsert(d,k) (dictInsertBefore((d),&(d)->head,(k))) + + +/*** Private data structures ***/ + +struct DictNode { + DictKey key; + DictNode *next; + DictNode *prev; +}; + +struct Dict { + DictNode head; + void *frame; + int (*leq)(void *frame, DictKey key1, DictKey key2); +}; + +#endif diff --git a/vtm/jni/tessellate/geom.c b/vtm/jni/tessellate/geom.c new file mode 100644 index 00000000..35b36a39 --- /dev/null +++ b/vtm/jni/tessellate/geom.c @@ -0,0 +1,264 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include "gluos.h" +#include +#include "mesh.h" +#include "geom.h" + +int __gl_vertLeq( GLUvertex *u, GLUvertex *v ) +{ + /* Returns TRUE if u is lexicographically <= v. */ + + return VertLeq( u, v ); +} + +GLdouble __gl_edgeEval( GLUvertex *u, GLUvertex *v, GLUvertex *w ) +{ + /* Given three vertices u,v,w such that VertLeq(u,v) && VertLeq(v,w), + * evaluates the t-coord of the edge uw at the s-coord of the vertex v. + * Returns v->t - (uw)(v->s), ie. the signed distance from uw to v. + * If uw is vertical (and thus passes thru v), the result is zero. + * + * The calculation is extremely accurate and stable, even when v + * is very close to u or w. In particular if we set v->t = 0 and + * let r be the negated result (this evaluates (uw)(v->s)), then + * r is guaranteed to satisfy MIN(u->t,w->t) <= r <= MAX(u->t,w->t). + */ + GLdouble gapL, gapR; + + assert( VertLeq( u, v ) && VertLeq( v, w )); + + gapL = v->s - u->s; + gapR = w->s - v->s; + + if( gapL + gapR > 0 ) { + if( gapL < gapR ) { + return (v->t - u->t) + (u->t - w->t) * (gapL / (gapL + gapR)); + } else { + return (v->t - w->t) + (w->t - u->t) * (gapR / (gapL + gapR)); + } + } + /* vertical line */ + return 0; +} + +GLdouble __gl_edgeSign( GLUvertex *u, GLUvertex *v, GLUvertex *w ) +{ + /* Returns a number whose sign matches EdgeEval(u,v,w) but which + * is cheaper to evaluate. Returns > 0, == 0 , or < 0 + * as v is above, on, or below the edge uw. + */ + GLdouble gapL, gapR; + + assert( VertLeq( u, v ) && VertLeq( v, w )); + + gapL = v->s - u->s; + gapR = w->s - v->s; + + if( gapL + gapR > 0 ) { + return (v->t - w->t) * gapL + (v->t - u->t) * gapR; + } + /* vertical line */ + return 0; +} + + +/*********************************************************************** + * Define versions of EdgeSign, EdgeEval with s and t transposed. + */ + +GLdouble __gl_transEval( GLUvertex *u, GLUvertex *v, GLUvertex *w ) +{ + /* Given three vertices u,v,w such that TransLeq(u,v) && TransLeq(v,w), + * evaluates the t-coord of the edge uw at the s-coord of the vertex v. + * Returns v->s - (uw)(v->t), ie. the signed distance from uw to v. + * If uw is vertical (and thus passes thru v), the result is zero. + * + * The calculation is extremely accurate and stable, even when v + * is very close to u or w. In particular if we set v->s = 0 and + * let r be the negated result (this evaluates (uw)(v->t)), then + * r is guaranteed to satisfy MIN(u->s,w->s) <= r <= MAX(u->s,w->s). + */ + GLdouble gapL, gapR; + + assert( TransLeq( u, v ) && TransLeq( v, w )); + + gapL = v->t - u->t; + gapR = w->t - v->t; + + if( gapL + gapR > 0 ) { + if( gapL < gapR ) { + return (v->s - u->s) + (u->s - w->s) * (gapL / (gapL + gapR)); + } else { + return (v->s - w->s) + (w->s - u->s) * (gapR / (gapL + gapR)); + } + } + /* vertical line */ + return 0; +} + +GLdouble __gl_transSign( GLUvertex *u, GLUvertex *v, GLUvertex *w ) +{ + /* Returns a number whose sign matches TransEval(u,v,w) but which + * is cheaper to evaluate. Returns > 0, == 0 , or < 0 + * as v is above, on, or below the edge uw. + */ + GLdouble gapL, gapR; + + assert( TransLeq( u, v ) && TransLeq( v, w )); + + gapL = v->t - u->t; + gapR = w->t - v->t; + + if( gapL + gapR > 0 ) { + return (v->s - w->s) * gapL + (v->s - u->s) * gapR; + } + /* vertical line */ + return 0; +} + + +int __gl_vertCCW( GLUvertex *u, GLUvertex *v, GLUvertex *w ) +{ + /* For almost-degenerate situations, the results are not reliable. + * Unless the floating-point arithmetic can be performed without + * rounding errors, *any* implementation will give incorrect results + * on some degenerate inputs, so the client must have some way to + * handle this situation. + */ + return (u->s*(v->t - w->t) + v->s*(w->t - u->t) + w->s*(u->t - v->t)) >= 0; +} + +/* Given parameters a,x,b,y returns the value (b*x+a*y)/(a+b), + * or (x+y)/2 if a==b==0. It requires that a,b >= 0, and enforces + * this in the rare case that one argument is slightly negative. + * The implementation is extremely stable numerically. + * In particular it guarantees that the result r satisfies + * MIN(x,y) <= r <= MAX(x,y), and the results are very accurate + * even when a and b differ greatly in magnitude. + */ +#define RealInterpolate(a,x,b,y) \ + (a = (a < 0) ? 0 : a, b = (b < 0) ? 0 : b, \ + ((a <= b) ? ((b == 0) ? ((x+y) / 2) \ + : (x + (y-x) * (a/(a+b)))) \ + : (y + (x-y) * (b/(a+b))))) + +#ifndef FOR_TRITE_TEST_PROGRAM +#define Interpolate(a,x,b,y) RealInterpolate(a,x,b,y) +#else + +/* Claim: the ONLY property the sweep algorithm relies on is that + * MIN(x,y) <= r <= MAX(x,y). This is a nasty way to test that. + */ +#include +extern int RandomInterpolate; + +GLdouble Interpolate( GLdouble a, GLdouble x, GLdouble b, GLdouble y) +{ +printf("*********************%d\n",RandomInterpolate); + if( RandomInterpolate ) { + a = 1.2 * drand48() - 0.1; + a = (a < 0) ? 0 : ((a > 1) ? 1 : a); + b = 1.0 - a; + } + return RealInterpolate(a,x,b,y); +} + +#endif + +#define Swap(a,b) do { GLUvertex *t = a; a = b; b = t; } while (0) + +void __gl_edgeIntersect( GLUvertex *o1, GLUvertex *d1, + GLUvertex *o2, GLUvertex *d2, + GLUvertex *v ) +/* Given edges (o1,d1) and (o2,d2), compute their point of intersection. + * The computed point is guaranteed to lie in the intersection of the + * bounding rectangles defined by each edge. + */ +{ + GLdouble z1, z2; + + /* This is certainly not the most efficient way to find the intersection + * of two line segments, but it is very numerically stable. + * + * Strategy: find the two middle vertices in the VertLeq ordering, + * and interpolate the intersection s-value from these. Then repeat + * using the TransLeq ordering to find the intersection t-value. + */ + + if( ! VertLeq( o1, d1 )) { Swap( o1, d1 ); } + if( ! VertLeq( o2, d2 )) { Swap( o2, d2 ); } + if( ! VertLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); } + + if( ! VertLeq( o2, d1 )) { + /* Technically, no intersection -- do our best */ + v->s = (o2->s + d1->s) / 2; + } else if( VertLeq( d1, d2 )) { + /* Interpolate between o2 and d1 */ + z1 = EdgeEval( o1, o2, d1 ); + z2 = EdgeEval( o2, d1, d2 ); + if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; } + v->s = Interpolate( z1, o2->s, z2, d1->s ); + } else { + /* Interpolate between o2 and d2 */ + z1 = EdgeSign( o1, o2, d1 ); + z2 = -EdgeSign( o1, d2, d1 ); + if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; } + v->s = Interpolate( z1, o2->s, z2, d2->s ); + } + + /* Now repeat the process for t */ + + if( ! TransLeq( o1, d1 )) { Swap( o1, d1 ); } + if( ! TransLeq( o2, d2 )) { Swap( o2, d2 ); } + if( ! TransLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); } + + if( ! TransLeq( o2, d1 )) { + /* Technically, no intersection -- do our best */ + v->t = (o2->t + d1->t) / 2; + } else if( TransLeq( d1, d2 )) { + /* Interpolate between o2 and d1 */ + z1 = TransEval( o1, o2, d1 ); + z2 = TransEval( o2, d1, d2 ); + if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; } + v->t = Interpolate( z1, o2->t, z2, d1->t ); + } else { + /* Interpolate between o2 and d2 */ + z1 = TransSign( o1, o2, d1 ); + z2 = -TransSign( o1, d2, d1 ); + if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; } + v->t = Interpolate( z1, o2->t, z2, d2->t ); + } +} diff --git a/vtm/jni/tessellate/geom.h b/vtm/jni/tessellate/geom.h new file mode 100644 index 00000000..5cb76c7d --- /dev/null +++ b/vtm/jni/tessellate/geom.h @@ -0,0 +1,84 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __geom_h_ +#define __geom_h_ + +#include "mesh.h" + +#ifdef NO_BRANCH_CONDITIONS +/* MIPS architecture has special instructions to evaluate boolean + * conditions -- more efficient than branching, IF you can get the + * compiler to generate the right instructions (SGI compiler doesn't) + */ +#define VertEq(u,v) (((u)->s == (v)->s) & ((u)->t == (v)->t)) +#define VertLeq(u,v) (((u)->s < (v)->s) | \ + ((u)->s == (v)->s & (u)->t <= (v)->t)) +#else +#define VertEq(u,v) ((u)->s == (v)->s && (u)->t == (v)->t) +#define VertLeq(u,v) (((u)->s < (v)->s) || \ + ((u)->s == (v)->s && (u)->t <= (v)->t)) +#endif + +#define EdgeEval(u,v,w) __gl_edgeEval(u,v,w) +#define EdgeSign(u,v,w) __gl_edgeSign(u,v,w) + +/* Versions of VertLeq, EdgeSign, EdgeEval with s and t transposed. */ + +#define TransLeq(u,v) (((u)->t < (v)->t) || \ + ((u)->t == (v)->t && (u)->s <= (v)->s)) +#define TransEval(u,v,w) __gl_transEval(u,v,w) +#define TransSign(u,v,w) __gl_transSign(u,v,w) + + +#define EdgeGoesLeft(e) VertLeq( (e)->Dst, (e)->Org ) +#define EdgeGoesRight(e) VertLeq( (e)->Org, (e)->Dst ) + +#undef ABS +#define ABS(x) ((x) < 0 ? -(x) : (x)) +#define VertL1dist(u,v) (ABS(u->s - v->s) + ABS(u->t - v->t)) + +#define VertCCW(u,v,w) __gl_vertCCW(u,v,w) + +int __gl_vertLeq( GLUvertex *u, GLUvertex *v ); +GLdouble __gl_edgeEval( GLUvertex *u, GLUvertex *v, GLUvertex *w ); +GLdouble __gl_edgeSign( GLUvertex *u, GLUvertex *v, GLUvertex *w ); +GLdouble __gl_transEval( GLUvertex *u, GLUvertex *v, GLUvertex *w ); +GLdouble __gl_transSign( GLUvertex *u, GLUvertex *v, GLUvertex *w ); +int __gl_vertCCW( GLUvertex *u, GLUvertex *v, GLUvertex *w ); +void __gl_edgeIntersect( GLUvertex *o1, GLUvertex *d1, + GLUvertex *o2, GLUvertex *d2, + GLUvertex *v ); + +#endif diff --git a/vtm/jni/tessellate/glu.h b/vtm/jni/tessellate/glu.h new file mode 100644 index 00000000..fe88e7e1 --- /dev/null +++ b/vtm/jni/tessellate/glu.h @@ -0,0 +1,356 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ + +#ifndef __glu_h__ +#define __glu_h__ + +#define GLAPIENTRYP * +#define GLAPIENTRY +#define GLAPI + +typedef int GLint; +typedef unsigned int GLenum; +typedef unsigned int GLsizei; +typedef float GLfloat; +typedef double GLdouble; +typedef unsigned char GLubyte; +typedef int GLboolean; +typedef void GLvoid; + +#define GL_FALSE 0 +#define GL_TRUE 1 +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_FAN 0x0006 + +// #if (defined(_MSC_VER) || defined(__MINGW32__)) && defined(BUILD_GLU32) +// # undef GLAPI +// # define GLAPI __declspec(dllexport) +// #elif (defined(_MSC_VER) || defined(__MINGW32__)) && defined(_DLL) +// /* tag specifying we're building for DLL runtime support */ +// # undef GLAPI +// # define GLAPI __declspec(dllimport) +// #elif !defined(GLAPI) +// /* for use with static link lib build of Win32 edition only */ +// # define GLAPI extern +// #endif /* _STATIC_MESA support */ + +#ifdef __cplusplus +extern "C" { +#endif + +/*************************************************************/ + +/* Extensions */ +#define GLU_EXT_object_space_tess 1 +#define GLU_EXT_nurbs_tessellator 1 + +/* Boolean */ +#define GLU_FALSE 0 +#define GLU_TRUE 1 + +/* Version */ +#define GLU_VERSION_1_1 1 +#define GLU_VERSION_1_2 1 +#define GLU_VERSION_1_3 1 + +/* StringName */ +#define GLU_VERSION 100800 +#define GLU_EXTENSIONS 100801 + +/* ErrorCode */ +#define GLU_INVALID_ENUM 100900 +#define GLU_INVALID_VALUE 100901 +#define GLU_OUT_OF_MEMORY 100902 +#define GLU_INCOMPATIBLE_GL_VERSION 100903 +#define GLU_INVALID_OPERATION 100904 + +/* NurbsDisplay */ +/* GLU_FILL */ +#define GLU_OUTLINE_POLYGON 100240 +#define GLU_OUTLINE_PATCH 100241 + +/* NurbsCallback */ +#define GLU_NURBS_ERROR 100103 +#define GLU_ERROR 100103 +#define GLU_NURBS_BEGIN 100164 +#define GLU_NURBS_BEGIN_EXT 100164 +#define GLU_NURBS_VERTEX 100165 +#define GLU_NURBS_VERTEX_EXT 100165 +#define GLU_NURBS_NORMAL 100166 +#define GLU_NURBS_NORMAL_EXT 100166 +#define GLU_NURBS_COLOR 100167 +#define GLU_NURBS_COLOR_EXT 100167 +#define GLU_NURBS_TEXTURE_COORD 100168 +#define GLU_NURBS_TEX_COORD_EXT 100168 +#define GLU_NURBS_END 100169 +#define GLU_NURBS_END_EXT 100169 +#define GLU_NURBS_BEGIN_DATA 100170 +#define GLU_NURBS_BEGIN_DATA_EXT 100170 +#define GLU_NURBS_VERTEX_DATA 100171 +#define GLU_NURBS_VERTEX_DATA_EXT 100171 +#define GLU_NURBS_NORMAL_DATA 100172 +#define GLU_NURBS_NORMAL_DATA_EXT 100172 +#define GLU_NURBS_COLOR_DATA 100173 +#define GLU_NURBS_COLOR_DATA_EXT 100173 +#define GLU_NURBS_TEXTURE_COORD_DATA 100174 +#define GLU_NURBS_TEX_COORD_DATA_EXT 100174 +#define GLU_NURBS_END_DATA 100175 +#define GLU_NURBS_END_DATA_EXT 100175 + +/* NurbsError */ +#define GLU_NURBS_ERROR1 100251 +#define GLU_NURBS_ERROR2 100252 +#define GLU_NURBS_ERROR3 100253 +#define GLU_NURBS_ERROR4 100254 +#define GLU_NURBS_ERROR5 100255 +#define GLU_NURBS_ERROR6 100256 +#define GLU_NURBS_ERROR7 100257 +#define GLU_NURBS_ERROR8 100258 +#define GLU_NURBS_ERROR9 100259 +#define GLU_NURBS_ERROR10 100260 +#define GLU_NURBS_ERROR11 100261 +#define GLU_NURBS_ERROR12 100262 +#define GLU_NURBS_ERROR13 100263 +#define GLU_NURBS_ERROR14 100264 +#define GLU_NURBS_ERROR15 100265 +#define GLU_NURBS_ERROR16 100266 +#define GLU_NURBS_ERROR17 100267 +#define GLU_NURBS_ERROR18 100268 +#define GLU_NURBS_ERROR19 100269 +#define GLU_NURBS_ERROR20 100270 +#define GLU_NURBS_ERROR21 100271 +#define GLU_NURBS_ERROR22 100272 +#define GLU_NURBS_ERROR23 100273 +#define GLU_NURBS_ERROR24 100274 +#define GLU_NURBS_ERROR25 100275 +#define GLU_NURBS_ERROR26 100276 +#define GLU_NURBS_ERROR27 100277 +#define GLU_NURBS_ERROR28 100278 +#define GLU_NURBS_ERROR29 100279 +#define GLU_NURBS_ERROR30 100280 +#define GLU_NURBS_ERROR31 100281 +#define GLU_NURBS_ERROR32 100282 +#define GLU_NURBS_ERROR33 100283 +#define GLU_NURBS_ERROR34 100284 +#define GLU_NURBS_ERROR35 100285 +#define GLU_NURBS_ERROR36 100286 +#define GLU_NURBS_ERROR37 100287 + +/* NurbsProperty */ +#define GLU_AUTO_LOAD_MATRIX 100200 +#define GLU_CULLING 100201 +#define GLU_SAMPLING_TOLERANCE 100203 +#define GLU_DISPLAY_MODE 100204 +#define GLU_PARAMETRIC_TOLERANCE 100202 +#define GLU_SAMPLING_METHOD 100205 +#define GLU_U_STEP 100206 +#define GLU_V_STEP 100207 +#define GLU_NURBS_MODE 100160 +#define GLU_NURBS_MODE_EXT 100160 +#define GLU_NURBS_TESSELLATOR 100161 +#define GLU_NURBS_TESSELLATOR_EXT 100161 +#define GLU_NURBS_RENDERER 100162 +#define GLU_NURBS_RENDERER_EXT 100162 + +/* NurbsSampling */ +#define GLU_OBJECT_PARAMETRIC_ERROR 100208 +#define GLU_OBJECT_PARAMETRIC_ERROR_EXT 100208 +#define GLU_OBJECT_PATH_LENGTH 100209 +#define GLU_OBJECT_PATH_LENGTH_EXT 100209 +#define GLU_PATH_LENGTH 100215 +#define GLU_PARAMETRIC_ERROR 100216 +#define GLU_DOMAIN_DISTANCE 100217 + +/* NurbsTrim */ +#define GLU_MAP1_TRIM_2 100210 +#define GLU_MAP1_TRIM_3 100211 + +/* QuadricDrawStyle */ +#define GLU_POINT 100010 +#define GLU_LINE 100011 +#define GLU_FILL 100012 +#define GLU_SILHOUETTE 100013 + +/* QuadricCallback */ +/* GLU_ERROR */ + +/* QuadricNormal */ +#define GLU_SMOOTH 100000 +#define GLU_FLAT 100001 +#define GLU_NONE 100002 + +/* QuadricOrientation */ +#define GLU_OUTSIDE 100020 +#define GLU_INSIDE 100021 + +/* TessCallback */ +#define GLU_TESS_BEGIN 100100 +#define GLU_BEGIN 100100 +#define GLU_TESS_VERTEX 100101 +#define GLU_VERTEX 100101 +#define GLU_TESS_END 100102 +#define GLU_END 100102 +#define GLU_TESS_ERROR 100103 +#define GLU_TESS_EDGE_FLAG 100104 +#define GLU_EDGE_FLAG 100104 +#define GLU_TESS_COMBINE 100105 +#define GLU_TESS_BEGIN_DATA 100106 +#define GLU_TESS_VERTEX_DATA 100107 +#define GLU_TESS_END_DATA 100108 +#define GLU_TESS_ERROR_DATA 100109 +#define GLU_TESS_EDGE_FLAG_DATA 100110 +#define GLU_TESS_COMBINE_DATA 100111 + +/* TessContour */ +#define GLU_CW 100120 +#define GLU_CCW 100121 +#define GLU_INTERIOR 100122 +#define GLU_EXTERIOR 100123 +#define GLU_UNKNOWN 100124 + +/* TessProperty */ +#define GLU_TESS_WINDING_RULE 100140 +#define GLU_TESS_BOUNDARY_ONLY 100141 +#define GLU_TESS_TOLERANCE 100142 + +/* TessError */ +#define GLU_TESS_ERROR1 100151 +#define GLU_TESS_ERROR2 100152 +#define GLU_TESS_ERROR3 100153 +#define GLU_TESS_ERROR4 100154 +#define GLU_TESS_ERROR5 100155 +#define GLU_TESS_ERROR6 100156 +#define GLU_TESS_ERROR7 100157 +#define GLU_TESS_ERROR8 100158 +#define GLU_TESS_MISSING_BEGIN_POLYGON 100151 +#define GLU_TESS_MISSING_BEGIN_CONTOUR 100152 +#define GLU_TESS_MISSING_END_POLYGON 100153 +#define GLU_TESS_MISSING_END_CONTOUR 100154 +#define GLU_TESS_COORD_TOO_LARGE 100155 +#define GLU_TESS_NEED_COMBINE_CALLBACK 100156 + +/* TessWinding */ +#define GLU_TESS_WINDING_ODD 100130 +#define GLU_TESS_WINDING_NONZERO 100131 +#define GLU_TESS_WINDING_POSITIVE 100132 +#define GLU_TESS_WINDING_NEGATIVE 100133 +#define GLU_TESS_WINDING_ABS_GEQ_TWO 100134 + +/*************************************************************/ + + +#ifdef __cplusplus +class GLUnurbs; +class GLUquadric; +class GLUtesselator; +#else +typedef struct GLUnurbs GLUnurbs; +typedef struct GLUquadric GLUquadric; +typedef struct GLUtesselator GLUtesselator; +#endif + +typedef GLUnurbs GLUnurbsObj; +typedef GLUquadric GLUquadricObj; +typedef GLUtesselator GLUtesselatorObj; +typedef GLUtesselator GLUtriangulatorObj; + +#define GLU_TESS_MAX_COORD 1.0e150 + +/* Internal convenience typedefs */ +typedef void (GLAPIENTRYP _GLUfuncptr)(void); + +GLAPI void GLAPIENTRY gluBeginCurve (GLUnurbs* nurb); +GLAPI void GLAPIENTRY gluBeginPolygon (GLUtesselator* tess); +GLAPI void GLAPIENTRY gluBeginSurface (GLUnurbs* nurb); +GLAPI void GLAPIENTRY gluBeginTrim (GLUnurbs* nurb); +GLAPI GLint GLAPIENTRY gluBuild1DMipmapLevels (GLenum target, GLint internalFormat, GLsizei width, GLenum format, GLenum type, GLint level, GLint base, GLint max, const void *data); +GLAPI GLint GLAPIENTRY gluBuild1DMipmaps (GLenum target, GLint internalFormat, GLsizei width, GLenum format, GLenum type, const void *data); +GLAPI GLint GLAPIENTRY gluBuild2DMipmapLevels (GLenum target, GLint internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint level, GLint base, GLint max, const void *data); +GLAPI GLint GLAPIENTRY gluBuild2DMipmaps (GLenum target, GLint internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *data); +GLAPI GLint GLAPIENTRY gluBuild3DMipmapLevels (GLenum target, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLint level, GLint base, GLint max, const void *data); +GLAPI GLint GLAPIENTRY gluBuild3DMipmaps (GLenum target, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); +GLAPI GLboolean GLAPIENTRY gluCheckExtension (const GLubyte *extName, const GLubyte *extString); +GLAPI void GLAPIENTRY gluCylinder (GLUquadric* quad, GLdouble base, GLdouble top, GLdouble height, GLint slices, GLint stacks); +GLAPI void GLAPIENTRY gluDeleteNurbsRenderer (GLUnurbs* nurb); +GLAPI void GLAPIENTRY gluDeleteQuadric (GLUquadric* quad); +GLAPI void GLAPIENTRY gluDeleteTess (GLUtesselator* tess); +GLAPI void GLAPIENTRY gluDisk (GLUquadric* quad, GLdouble inner, GLdouble outer, GLint slices, GLint loops); +GLAPI void GLAPIENTRY gluEndCurve (GLUnurbs* nurb); +GLAPI void GLAPIENTRY gluEndPolygon (GLUtesselator* tess); +GLAPI void GLAPIENTRY gluEndSurface (GLUnurbs* nurb); +GLAPI void GLAPIENTRY gluEndTrim (GLUnurbs* nurb); +GLAPI const GLubyte * GLAPIENTRY gluErrorString (GLenum error); +GLAPI void GLAPIENTRY gluGetNurbsProperty (GLUnurbs* nurb, GLenum property, GLfloat* data); +GLAPI const GLubyte * GLAPIENTRY gluGetString (GLenum name); +GLAPI void GLAPIENTRY gluGetTessProperty (GLUtesselator* tess, GLenum which, GLdouble* data); +GLAPI void GLAPIENTRY gluLoadSamplingMatrices (GLUnurbs* nurb, const GLfloat *model, const GLfloat *perspective, const GLint *view); +GLAPI void GLAPIENTRY gluLookAt (GLdouble eyeX, GLdouble eyeY, GLdouble eyeZ, GLdouble centerX, GLdouble centerY, GLdouble centerZ, GLdouble upX, GLdouble upY, GLdouble upZ); +GLAPI GLUnurbs* GLAPIENTRY gluNewNurbsRenderer (void); +GLAPI GLUquadric* GLAPIENTRY gluNewQuadric (void); +GLAPI GLUtesselator* GLAPIENTRY gluNewTess (void); +GLAPI void GLAPIENTRY gluNextContour (GLUtesselator* tess, GLenum type); +GLAPI void GLAPIENTRY gluNurbsCallback (GLUnurbs* nurb, GLenum which, _GLUfuncptr CallBackFunc); +GLAPI void GLAPIENTRY gluNurbsCallbackData (GLUnurbs* nurb, GLvoid* userData); +GLAPI void GLAPIENTRY gluNurbsCallbackDataEXT (GLUnurbs* nurb, GLvoid* userData); +GLAPI void GLAPIENTRY gluNurbsCurve (GLUnurbs* nurb, GLint knotCount, GLfloat *knots, GLint stride, GLfloat *control, GLint order, GLenum type); +GLAPI void GLAPIENTRY gluNurbsProperty (GLUnurbs* nurb, GLenum property, GLfloat value); +GLAPI void GLAPIENTRY gluNurbsSurface (GLUnurbs* nurb, GLint sKnotCount, GLfloat* sKnots, GLint tKnotCount, GLfloat* tKnots, GLint sStride, GLint tStride, GLfloat* control, GLint sOrder, GLint tOrder, GLenum type); +GLAPI void GLAPIENTRY gluOrtho2D (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top); +GLAPI void GLAPIENTRY gluPartialDisk (GLUquadric* quad, GLdouble inner, GLdouble outer, GLint slices, GLint loops, GLdouble start, GLdouble sweep); +GLAPI void GLAPIENTRY gluPerspective (GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar); +GLAPI void GLAPIENTRY gluPickMatrix (GLdouble x, GLdouble y, GLdouble delX, GLdouble delY, GLint *viewport); +GLAPI GLint GLAPIENTRY gluProject (GLdouble objX, GLdouble objY, GLdouble objZ, const GLdouble *model, const GLdouble *proj, const GLint *view, GLdouble* winX, GLdouble* winY, GLdouble* winZ); +GLAPI void GLAPIENTRY gluPwlCurve (GLUnurbs* nurb, GLint count, GLfloat* data, GLint stride, GLenum type); +GLAPI void GLAPIENTRY gluQuadricCallback (GLUquadric* quad, GLenum which, _GLUfuncptr CallBackFunc); +GLAPI void GLAPIENTRY gluQuadricDrawStyle (GLUquadric* quad, GLenum draw); +GLAPI void GLAPIENTRY gluQuadricNormals (GLUquadric* quad, GLenum normal); +GLAPI void GLAPIENTRY gluQuadricOrientation (GLUquadric* quad, GLenum orientation); +GLAPI void GLAPIENTRY gluQuadricTexture (GLUquadric* quad, GLboolean texture); +GLAPI GLint GLAPIENTRY gluScaleImage (GLenum format, GLsizei wIn, GLsizei hIn, GLenum typeIn, const void *dataIn, GLsizei wOut, GLsizei hOut, GLenum typeOut, GLvoid* dataOut); +GLAPI void GLAPIENTRY gluSphere (GLUquadric* quad, GLdouble radius, GLint slices, GLint stacks); +GLAPI void GLAPIENTRY gluTessBeginContour (GLUtesselator* tess); +GLAPI void GLAPIENTRY gluTessBeginPolygon (GLUtesselator* tess, GLvoid* data); +GLAPI void GLAPIENTRY gluTessCallback (GLUtesselator* tess, GLenum which, _GLUfuncptr CallBackFunc); +GLAPI void GLAPIENTRY gluTessEndContour (GLUtesselator* tess); +GLAPI void GLAPIENTRY gluTessEndPolygon (GLUtesselator* tess); +GLAPI void GLAPIENTRY gluTessNormal (GLUtesselator* tess, GLdouble valueX, GLdouble valueY, GLdouble valueZ); +GLAPI void GLAPIENTRY gluTessProperty (GLUtesselator* tess, GLenum which, GLdouble data); +GLAPI void GLAPIENTRY gluTessVertex (GLUtesselator* tess, GLdouble *location, GLvoid* data); +GLAPI GLint GLAPIENTRY gluUnProject (GLdouble winX, GLdouble winY, GLdouble winZ, const GLdouble *model, const GLdouble *proj, const GLint *view, GLdouble* objX, GLdouble* objY, GLdouble* objZ); +GLAPI GLint GLAPIENTRY gluUnProject4 (GLdouble winX, GLdouble winY, GLdouble winZ, GLdouble clipW, const GLdouble *model, const GLdouble *proj, const GLint *view, GLdouble nearVal, GLdouble farVal, GLdouble* objX, GLdouble* objY, GLdouble* objZ, GLdouble* objW); + +#ifdef __cplusplus +} +#endif + +#endif /* __glu_h__ */ diff --git a/vtm/jni/tessellate/gluos.h b/vtm/jni/tessellate/gluos.h new file mode 100644 index 00000000..290a06f0 --- /dev/null +++ b/vtm/jni/tessellate/gluos.h @@ -0,0 +1,86 @@ +/* +** gluos.h - operating system dependencies for GLU +** +*/ +#ifdef __VMS +#ifdef __cplusplus +#pragma message disable nocordel +#pragma message disable codeunreachable +#pragma message disable codcauunr +#endif +#endif + +#ifdef __WATCOMC__ +/* Disable *lots* of warnings to get a clean build. I can't be bothered fixing the + * code at the moment, as it is pretty ugly. + */ +#pragma warning 7 10 +#pragma warning 13 10 +#pragma warning 14 10 +#pragma warning 367 10 +#pragma warning 379 10 +#pragma warning 726 10 +#pragma warning 836 10 +#endif + +#ifdef BUILD_FOR_SNAP + +#include +#include +#include + +#elif defined(_WIN32) + +#include /* For _MAX_PATH definition */ +#include +#include + +#define WIN32_LEAN_AND_MEAN +#define NOGDI +#define NOIME +#define NOMINMAX + +#ifdef __MINGW64_VERSION_MAJOR + #undef _WIN32_WINNT +#endif + +#ifndef _WIN32_WINNT + /* XXX: Workaround a bug in mingw-w64's headers when NOGDI is set and + * _WIN32_WINNT >= 0x0600 */ + #define _WIN32_WINNT 0x0400 +#endif +#ifndef STRICT + #define STRICT 1 +#endif + +#include + +/* Disable warnings */ +#if defined(_MSC_VER) +#pragma warning(disable : 4101) +#pragma warning(disable : 4244) +#pragma warning(disable : 4761) +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1300 +#pragma comment(linker, "/OPT:NOWIN98") +#endif + +#ifndef WINGDIAPI +#define WINGDIAPI +#endif + +#elif defined(__OS2__) + +#include +#include +#include +#define WINGDIAPI + +#else + +/* Disable Microsoft-specific keywords */ +#define GLAPIENTRY +#define WINGDIAPI + +#endif diff --git a/vtm/jni/tessellate/main.c b/vtm/jni/tessellate/main.c new file mode 100644 index 00000000..0ef62e2d --- /dev/null +++ b/vtm/jni/tessellate/main.c @@ -0,0 +1,54 @@ +#include "tessellate.h" +#include +#include + +void run_example(const double vertices_array[], + const double *contours_array[], + int contours_size) +{ + double *coordinates_out; + int *tris_out; + int nverts, ntris, i; + + const double *p = vertices_array; + /* const double **contours = contours_array; */ + + tessellate(&coordinates_out, &nverts, + &tris_out, &ntris, + contours_array, contours_array + contours_size); + + for (i=0; i<2 * nverts; ++i) { + fprintf(stdout, "%g ", coordinates_out[i]); + } + fprintf(stdout, "\n"); + for (i=0; i<3 * ntris; ++i) { + fprintf(stdout, "%d ", tris_out[i]); + } + fprintf(stdout, "\n"); + free(coordinates_out); + if (tris_out) + free(tris_out); +} + +int main() +{ + double a1[] = { 0, 0, 1, 5, 2, 0, -1, 3, 3, 3 }; + const double *c1[] = {a1, a1+10}; + int s1 = 2; + run_example(a1, c1, 2); + printf("\n"); + double a2[] = { 0, 0, 3, 0, 3, 3, 0, 3, + 1, 1, 2, 1, 2, 2, 1, 2 }; + const double *c2[] = {a2, a2+8, a2+16}; + int s2 = 3; + run_example(a2, c2, s2); + printf("\n"); + + double a3[] = { 441, 0, 326, 0, 326, 889, 12, 889, 12, 992, 755, 992, 755, 889, 441, 889 }; + const double *c3[] = { a3, a3+16 }; + int s3 = 2; + run_example(a3, c3, s3); + printf("\n"); + + return 0; +} diff --git a/vtm/jni/tessellate/memalloc.c b/vtm/jni/tessellate/memalloc.c new file mode 100644 index 00000000..81879ef7 --- /dev/null +++ b/vtm/jni/tessellate/memalloc.c @@ -0,0 +1,55 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include "memalloc.h" +#include "string.h" + +int __gl_memInit( size_t maxFast ) +{ +#ifndef NO_MALLOPT +/* mallopt( M_MXFAST, maxFast );*/ +#ifdef MEMORY_DEBUG + mallopt( M_DEBUG, 1 ); +#endif +#endif + return 1; +} + +#ifdef MEMORY_DEBUG +void *__gl_memAlloc( size_t n ) +{ + return memset( malloc( n ), 0xa5, n ); +} +#endif + diff --git a/vtm/jni/tessellate/memalloc.h b/vtm/jni/tessellate/memalloc.h new file mode 100644 index 00000000..c2f969b8 --- /dev/null +++ b/vtm/jni/tessellate/memalloc.h @@ -0,0 +1,54 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __memalloc_simple_h_ +#define __memalloc_simple_h_ + +#include + +#define memRealloc realloc +#define memFree free + +#define memInit __gl_memInit +/*extern void __gl_memInit( size_t );*/ +extern int __gl_memInit( size_t ); + +#ifndef MEMORY_DEBUG +#define memAlloc malloc +#else +#define memAlloc __gl_memAlloc +extern void * __gl_memAlloc( size_t ); +#endif + +#endif diff --git a/vtm/jni/tessellate/mesh.c b/vtm/jni/tessellate/mesh.c new file mode 100644 index 00000000..36cb3a7b --- /dev/null +++ b/vtm/jni/tessellate/mesh.c @@ -0,0 +1,798 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include "gluos.h" +#include +#include +#include "mesh.h" +#include "memalloc.h" + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +static GLUvertex *allocVertex() +{ + return (GLUvertex *)memAlloc( sizeof( GLUvertex )); +} + +static GLUface *allocFace() +{ + return (GLUface *)memAlloc( sizeof( GLUface )); +} + +/************************ Utility Routines ************************/ + +/* Allocate and free half-edges in pairs for efficiency. + * The *only* place that should use this fact is allocation/free. + */ +typedef struct { GLUhalfEdge e, eSym; } EdgePair; + +/* MakeEdge creates a new pair of half-edges which form their own loop. + * No vertex or face structures are allocated, but these must be assigned + * before the current edge operation is completed. + */ +static GLUhalfEdge *MakeEdge( GLUhalfEdge *eNext ) +{ + GLUhalfEdge *e; + GLUhalfEdge *eSym; + GLUhalfEdge *ePrev; + EdgePair *pair = (EdgePair *)memAlloc( sizeof( EdgePair )); + if (pair == NULL) return NULL; + + e = &pair->e; + eSym = &pair->eSym; + + /* Make sure eNext points to the first edge of the edge pair */ + if( eNext->Sym < eNext ) { eNext = eNext->Sym; } + + /* Insert in circular doubly-linked list before eNext. + * Note that the prev pointer is stored in Sym->next. + */ + ePrev = eNext->Sym->next; + eSym->next = ePrev; + ePrev->Sym->next = e; + e->next = eNext; + eNext->Sym->next = eSym; + + e->Sym = eSym; + e->Onext = e; + e->Lnext = eSym; + e->Org = NULL; + e->Lface = NULL; + e->winding = 0; + e->activeRegion = NULL; + + eSym->Sym = e; + eSym->Onext = eSym; + eSym->Lnext = e; + eSym->Org = NULL; + eSym->Lface = NULL; + eSym->winding = 0; + eSym->activeRegion = NULL; + + return e; +} + +/* Splice( a, b ) is best described by the Guibas/Stolfi paper or the + * CS348a notes (see mesh.h). Basically it modifies the mesh so that + * a->Onext and b->Onext are exchanged. This can have various effects + * depending on whether a and b belong to different face or vertex rings. + * For more explanation see __gl_meshSplice() below. + */ +static void Splice( GLUhalfEdge *a, GLUhalfEdge *b ) +{ + GLUhalfEdge *aOnext = a->Onext; + GLUhalfEdge *bOnext = b->Onext; + + aOnext->Sym->Lnext = b; + bOnext->Sym->Lnext = a; + a->Onext = bOnext; + b->Onext = aOnext; +} + +/* MakeVertex( newVertex, eOrig, vNext ) attaches a new vertex and makes it the + * origin of all edges in the vertex loop to which eOrig belongs. "vNext" gives + * a place to insert the new vertex in the global vertex list. We insert + * the new vertex *before* vNext so that algorithms which walk the vertex + * list will not see the newly created vertices. + */ +static void MakeVertex( GLUvertex *newVertex, + GLUhalfEdge *eOrig, GLUvertex *vNext ) +{ + GLUhalfEdge *e; + GLUvertex *vPrev; + GLUvertex *vNew = newVertex; + + assert(vNew != NULL); + + /* insert in circular doubly-linked list before vNext */ + vPrev = vNext->prev; + vNew->prev = vPrev; + vPrev->next = vNew; + vNew->next = vNext; + vNext->prev = vNew; + + vNew->anEdge = eOrig; + vNew->data = NULL; + /* leave coords, s, t undefined */ + + /* fix other edges on this vertex loop */ + e = eOrig; + do { + e->Org = vNew; + e = e->Onext; + } while( e != eOrig ); +} + +/* MakeFace( newFace, eOrig, fNext ) attaches a new face and makes it the left + * face of all edges in the face loop to which eOrig belongs. "fNext" gives + * a place to insert the new face in the global face list. We insert + * the new face *before* fNext so that algorithms which walk the face + * list will not see the newly created faces. + */ +static void MakeFace( GLUface *newFace, GLUhalfEdge *eOrig, GLUface *fNext ) +{ + GLUhalfEdge *e; + GLUface *fPrev; + GLUface *fNew = newFace; + + assert(fNew != NULL); + + /* insert in circular doubly-linked list before fNext */ + fPrev = fNext->prev; + fNew->prev = fPrev; + fPrev->next = fNew; + fNew->next = fNext; + fNext->prev = fNew; + + fNew->anEdge = eOrig; + fNew->data = NULL; + fNew->trail = NULL; + fNew->marked = FALSE; + + /* The new face is marked "inside" if the old one was. This is a + * convenience for the common case where a face has been split in two. + */ + fNew->inside = fNext->inside; + + /* fix other edges on this face loop */ + e = eOrig; + do { + e->Lface = fNew; + e = e->Lnext; + } while( e != eOrig ); +} + +/* KillEdge( eDel ) destroys an edge (the half-edges eDel and eDel->Sym), + * and removes from the global edge list. + */ +static void KillEdge( GLUhalfEdge *eDel ) +{ + GLUhalfEdge *ePrev, *eNext; + + /* Half-edges are allocated in pairs, see EdgePair above */ + if( eDel->Sym < eDel ) { eDel = eDel->Sym; } + + /* delete from circular doubly-linked list */ + eNext = eDel->next; + ePrev = eDel->Sym->next; + eNext->Sym->next = ePrev; + ePrev->Sym->next = eNext; + + memFree( eDel ); +} + + +/* KillVertex( vDel ) destroys a vertex and removes it from the global + * vertex list. It updates the vertex loop to point to a given new vertex. + */ +static void KillVertex( GLUvertex *vDel, GLUvertex *newOrg ) +{ + GLUhalfEdge *e, *eStart = vDel->anEdge; + GLUvertex *vPrev, *vNext; + + /* change the origin of all affected edges */ + e = eStart; + do { + e->Org = newOrg; + e = e->Onext; + } while( e != eStart ); + + /* delete from circular doubly-linked list */ + vPrev = vDel->prev; + vNext = vDel->next; + vNext->prev = vPrev; + vPrev->next = vNext; + + memFree( vDel ); +} + +/* KillFace( fDel ) destroys a face and removes it from the global face + * list. It updates the face loop to point to a given new face. + */ +static void KillFace( GLUface *fDel, GLUface *newLface ) +{ + GLUhalfEdge *e, *eStart = fDel->anEdge; + GLUface *fPrev, *fNext; + + /* change the left face of all affected edges */ + e = eStart; + do { + e->Lface = newLface; + e = e->Lnext; + } while( e != eStart ); + + /* delete from circular doubly-linked list */ + fPrev = fDel->prev; + fNext = fDel->next; + fNext->prev = fPrev; + fPrev->next = fNext; + + memFree( fDel ); +} + + +/****************** Basic Edge Operations **********************/ + +/* __gl_meshMakeEdge creates one edge, two vertices, and a loop (face). + * The loop consists of the two new half-edges. + */ +GLUhalfEdge *__gl_meshMakeEdge( GLUmesh *mesh ) +{ + GLUvertex *newVertex1= allocVertex(); + GLUvertex *newVertex2= allocVertex(); + GLUface *newFace= allocFace(); + GLUhalfEdge *e; + + /* if any one is null then all get freed */ + if (newVertex1 == NULL || newVertex2 == NULL || newFace == NULL) { + if (newVertex1 != NULL) memFree(newVertex1); + if (newVertex2 != NULL) memFree(newVertex2); + if (newFace != NULL) memFree(newFace); + return NULL; + } + + e = MakeEdge( &mesh->eHead ); + if (e == NULL) { + memFree(newVertex1); + memFree(newVertex2); + memFree(newFace); + return NULL; + } + + MakeVertex( newVertex1, e, &mesh->vHead ); + MakeVertex( newVertex2, e->Sym, &mesh->vHead ); + MakeFace( newFace, e, &mesh->fHead ); + return e; +} + + +/* __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the + * mesh connectivity and topology. It changes the mesh so that + * eOrg->Onext <- OLD( eDst->Onext ) + * eDst->Onext <- OLD( eOrg->Onext ) + * where OLD(...) means the value before the meshSplice operation. + * + * This can have two effects on the vertex structure: + * - if eOrg->Org != eDst->Org, the two vertices are merged together + * - if eOrg->Org == eDst->Org, the origin is split into two vertices + * In both cases, eDst->Org is changed and eOrg->Org is untouched. + * + * Similarly (and independently) for the face structure, + * - if eOrg->Lface == eDst->Lface, one loop is split into two + * - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one + * In both cases, eDst->Lface is changed and eOrg->Lface is unaffected. + * + * Some special cases: + * If eDst == eOrg, the operation has no effect. + * If eDst == eOrg->Lnext, the new face will have a single edge. + * If eDst == eOrg->Lprev, the old face will have a single edge. + * If eDst == eOrg->Onext, the new vertex will have a single edge. + * If eDst == eOrg->Oprev, the old vertex will have a single edge. + */ +int __gl_meshSplice( GLUhalfEdge *eOrg, GLUhalfEdge *eDst ) +{ + int joiningLoops = FALSE; + int joiningVertices = FALSE; + + if( eOrg == eDst ) return 1; + + if( eDst->Org != eOrg->Org ) { + /* We are merging two disjoint vertices -- destroy eDst->Org */ + joiningVertices = TRUE; + KillVertex( eDst->Org, eOrg->Org ); + } + if( eDst->Lface != eOrg->Lface ) { + /* We are connecting two disjoint loops -- destroy eDst->Lface */ + joiningLoops = TRUE; + KillFace( eDst->Lface, eOrg->Lface ); + } + + /* Change the edge structure */ + Splice( eDst, eOrg ); + + if( ! joiningVertices ) { + GLUvertex *newVertex= allocVertex(); + if (newVertex == NULL) return 0; + + /* We split one vertex into two -- the new vertex is eDst->Org. + * Make sure the old vertex points to a valid half-edge. + */ + MakeVertex( newVertex, eDst, eOrg->Org ); + eOrg->Org->anEdge = eOrg; + } + if( ! joiningLoops ) { + GLUface *newFace= allocFace(); + if (newFace == NULL) return 0; + + /* We split one loop into two -- the new loop is eDst->Lface. + * Make sure the old face points to a valid half-edge. + */ + MakeFace( newFace, eDst, eOrg->Lface ); + eOrg->Lface->anEdge = eOrg; + } + + return 1; +} + + +/* __gl_meshDelete( eDel ) removes the edge eDel. There are several cases: + * if (eDel->Lface != eDel->Rface), we join two loops into one; the loop + * eDel->Lface is deleted. Otherwise, we are splitting one loop into two; + * the newly created loop will contain eDel->Dst. If the deletion of eDel + * would create isolated vertices, those are deleted as well. + * + * This function could be implemented as two calls to __gl_meshSplice + * plus a few calls to memFree, but this would allocate and delete + * unnecessary vertices and faces. + */ +int __gl_meshDelete( GLUhalfEdge *eDel ) +{ + GLUhalfEdge *eDelSym = eDel->Sym; + int joiningLoops = FALSE; + + /* First step: disconnect the origin vertex eDel->Org. We make all + * changes to get a consistent mesh in this "intermediate" state. + */ + if( eDel->Lface != eDel->Rface ) { + /* We are joining two loops into one -- remove the left face */ + joiningLoops = TRUE; + KillFace( eDel->Lface, eDel->Rface ); + } + + if( eDel->Onext == eDel ) { + KillVertex( eDel->Org, NULL ); + } else { + /* Make sure that eDel->Org and eDel->Rface point to valid half-edges */ + eDel->Rface->anEdge = eDel->Oprev; + eDel->Org->anEdge = eDel->Onext; + + Splice( eDel, eDel->Oprev ); + if( ! joiningLoops ) { + GLUface *newFace= allocFace(); + if (newFace == NULL) return 0; + + /* We are splitting one loop into two -- create a new loop for eDel. */ + MakeFace( newFace, eDel, eDel->Lface ); + } + } + + /* Claim: the mesh is now in a consistent state, except that eDel->Org + * may have been deleted. Now we disconnect eDel->Dst. + */ + if( eDelSym->Onext == eDelSym ) { + KillVertex( eDelSym->Org, NULL ); + KillFace( eDelSym->Lface, NULL ); + } else { + /* Make sure that eDel->Dst and eDel->Lface point to valid half-edges */ + eDel->Lface->anEdge = eDelSym->Oprev; + eDelSym->Org->anEdge = eDelSym->Onext; + Splice( eDelSym, eDelSym->Oprev ); + } + + /* Any isolated vertices or faces have already been freed. */ + KillEdge( eDel ); + + return 1; +} + + +/******************** Other Edge Operations **********************/ + +/* All these routines can be implemented with the basic edge + * operations above. They are provided for convenience and efficiency. + */ + + +/* __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that + * eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex. + * eOrg and eNew will have the same left face. + */ +GLUhalfEdge *__gl_meshAddEdgeVertex( GLUhalfEdge *eOrg ) +{ + GLUhalfEdge *eNewSym; + GLUhalfEdge *eNew = MakeEdge( eOrg ); + if (eNew == NULL) return NULL; + + eNewSym = eNew->Sym; + + /* Connect the new edge appropriately */ + Splice( eNew, eOrg->Lnext ); + + /* Set the vertex and face information */ + eNew->Org = eOrg->Dst; + { + GLUvertex *newVertex= allocVertex(); + if (newVertex == NULL) return NULL; + + MakeVertex( newVertex, eNewSym, eNew->Org ); + } + eNew->Lface = eNewSym->Lface = eOrg->Lface; + + return eNew; +} + + +/* __gl_meshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew, + * such that eNew == eOrg->Lnext. The new vertex is eOrg->Dst == eNew->Org. + * eOrg and eNew will have the same left face. + */ +GLUhalfEdge *__gl_meshSplitEdge( GLUhalfEdge *eOrg ) +{ + GLUhalfEdge *eNew; + GLUhalfEdge *tempHalfEdge= __gl_meshAddEdgeVertex( eOrg ); + if (tempHalfEdge == NULL) return NULL; + + eNew = tempHalfEdge->Sym; + + /* Disconnect eOrg from eOrg->Dst and connect it to eNew->Org */ + Splice( eOrg->Sym, eOrg->Sym->Oprev ); + Splice( eOrg->Sym, eNew ); + + /* Set the vertex and face information */ + eOrg->Dst = eNew->Org; + eNew->Dst->anEdge = eNew->Sym; /* may have pointed to eOrg->Sym */ + eNew->Rface = eOrg->Rface; + eNew->winding = eOrg->winding; /* copy old winding information */ + eNew->Sym->winding = eOrg->Sym->winding; + + return eNew; +} + + +/* __gl_meshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst + * to eDst->Org, and returns the corresponding half-edge eNew. + * If eOrg->Lface == eDst->Lface, this splits one loop into two, + * and the newly created loop is eNew->Lface. Otherwise, two disjoint + * loops are merged into one, and the loop eDst->Lface is destroyed. + * + * If (eOrg == eDst), the new face will have only two edges. + * If (eOrg->Lnext == eDst), the old face is reduced to a single edge. + * If (eOrg->Lnext->Lnext == eDst), the old face is reduced to two edges. + */ +GLUhalfEdge *__gl_meshConnect( GLUhalfEdge *eOrg, GLUhalfEdge *eDst ) +{ + GLUhalfEdge *eNewSym; + int joiningLoops = FALSE; + GLUhalfEdge *eNew = MakeEdge( eOrg ); + if (eNew == NULL) return NULL; + + eNewSym = eNew->Sym; + + if( eDst->Lface != eOrg->Lface ) { + /* We are connecting two disjoint loops -- destroy eDst->Lface */ + joiningLoops = TRUE; + KillFace( eDst->Lface, eOrg->Lface ); + } + + /* Connect the new edge appropriately */ + Splice( eNew, eOrg->Lnext ); + Splice( eNewSym, eDst ); + + /* Set the vertex and face information */ + eNew->Org = eOrg->Dst; + eNewSym->Org = eDst->Org; + eNew->Lface = eNewSym->Lface = eOrg->Lface; + + /* Make sure the old face points to a valid half-edge */ + eOrg->Lface->anEdge = eNewSym; + + if( ! joiningLoops ) { + GLUface *newFace= allocFace(); + if (newFace == NULL) return NULL; + + /* We split one loop into two -- the new loop is eNew->Lface */ + MakeFace( newFace, eNew, eOrg->Lface ); + } + return eNew; +} + + +/******************** Other Operations **********************/ + +/* __gl_meshZapFace( fZap ) destroys a face and removes it from the + * global face list. All edges of fZap will have a NULL pointer as their + * left face. Any edges which also have a NULL pointer as their right face + * are deleted entirely (along with any isolated vertices this produces). + * An entire mesh can be deleted by zapping its faces, one at a time, + * in any order. Zapped faces cannot be used in further mesh operations! + */ +void __gl_meshZapFace( GLUface *fZap ) +{ + GLUhalfEdge *eStart = fZap->anEdge; + GLUhalfEdge *e, *eNext, *eSym; + GLUface *fPrev, *fNext; + + /* walk around face, deleting edges whose right face is also NULL */ + eNext = eStart->Lnext; + do { + e = eNext; + eNext = e->Lnext; + + e->Lface = NULL; + if( e->Rface == NULL ) { + /* delete the edge -- see __gl_MeshDelete above */ + + if( e->Onext == e ) { + KillVertex( e->Org, NULL ); + } else { + /* Make sure that e->Org points to a valid half-edge */ + e->Org->anEdge = e->Onext; + Splice( e, e->Oprev ); + } + eSym = e->Sym; + if( eSym->Onext == eSym ) { + KillVertex( eSym->Org, NULL ); + } else { + /* Make sure that eSym->Org points to a valid half-edge */ + eSym->Org->anEdge = eSym->Onext; + Splice( eSym, eSym->Oprev ); + } + KillEdge( e ); + } + } while( e != eStart ); + + /* delete from circular doubly-linked list */ + fPrev = fZap->prev; + fNext = fZap->next; + fNext->prev = fPrev; + fPrev->next = fNext; + + memFree( fZap ); +} + + +/* __gl_meshNewMesh() creates a new mesh with no edges, no vertices, + * and no loops (what we usually call a "face"). + */ +GLUmesh *__gl_meshNewMesh( void ) +{ + GLUvertex *v; + GLUface *f; + GLUhalfEdge *e; + GLUhalfEdge *eSym; + GLUmesh *mesh = (GLUmesh *)memAlloc( sizeof( GLUmesh )); + if (mesh == NULL) { + return NULL; + } + + v = &mesh->vHead; + f = &mesh->fHead; + e = &mesh->eHead; + eSym = &mesh->eHeadSym; + + v->next = v->prev = v; + v->anEdge = NULL; + v->data = NULL; + + f->next = f->prev = f; + f->anEdge = NULL; + f->data = NULL; + f->trail = NULL; + f->marked = FALSE; + f->inside = FALSE; + + e->next = e; + e->Sym = eSym; + e->Onext = NULL; + e->Lnext = NULL; + e->Org = NULL; + e->Lface = NULL; + e->winding = 0; + e->activeRegion = NULL; + + eSym->next = eSym; + eSym->Sym = e; + eSym->Onext = NULL; + eSym->Lnext = NULL; + eSym->Org = NULL; + eSym->Lface = NULL; + eSym->winding = 0; + eSym->activeRegion = NULL; + + return mesh; +} + + +/* __gl_meshUnion( mesh1, mesh2 ) forms the union of all structures in + * both meshes, and returns the new mesh (the old meshes are destroyed). + */ +GLUmesh *__gl_meshUnion( GLUmesh *mesh1, GLUmesh *mesh2 ) +{ + GLUface *f1 = &mesh1->fHead; + GLUvertex *v1 = &mesh1->vHead; + GLUhalfEdge *e1 = &mesh1->eHead; + GLUface *f2 = &mesh2->fHead; + GLUvertex *v2 = &mesh2->vHead; + GLUhalfEdge *e2 = &mesh2->eHead; + + /* Add the faces, vertices, and edges of mesh2 to those of mesh1 */ + if( f2->next != f2 ) { + f1->prev->next = f2->next; + f2->next->prev = f1->prev; + f2->prev->next = f1; + f1->prev = f2->prev; + } + + if( v2->next != v2 ) { + v1->prev->next = v2->next; + v2->next->prev = v1->prev; + v2->prev->next = v1; + v1->prev = v2->prev; + } + + if( e2->next != e2 ) { + e1->Sym->next->Sym->next = e2->next; + e2->next->Sym->next = e1->Sym->next; + e2->Sym->next->Sym->next = e1; + e1->Sym->next = e2->Sym->next; + } + + memFree( mesh2 ); + return mesh1; +} + + +#ifdef DELETE_BY_ZAPPING + +/* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh. + */ +void __gl_meshDeleteMesh( GLUmesh *mesh ) +{ + GLUface *fHead = &mesh->fHead; + + while( fHead->next != fHead ) { + __gl_meshZapFace( fHead->next ); + } + assert( mesh->vHead.next == &mesh->vHead ); + + memFree( mesh ); +} + +#else + +/* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh. + */ +void __gl_meshDeleteMesh( GLUmesh *mesh ) +{ + GLUface *f, *fNext; + GLUvertex *v, *vNext; + GLUhalfEdge *e, *eNext; + + for( f = mesh->fHead.next; f != &mesh->fHead; f = fNext ) { + fNext = f->next; + memFree( f ); + } + + for( v = mesh->vHead.next; v != &mesh->vHead; v = vNext ) { + vNext = v->next; + memFree( v ); + } + + for( e = mesh->eHead.next; e != &mesh->eHead; e = eNext ) { + /* One call frees both e and e->Sym (see EdgePair above) */ + eNext = e->next; + memFree( e ); + } + + memFree( mesh ); +} + +#endif + +#ifndef NDEBUG + +/* __gl_meshCheckMesh( mesh ) checks a mesh for self-consistency. + */ +void __gl_meshCheckMesh( GLUmesh *mesh ) +{ + GLUface *fHead = &mesh->fHead; + GLUvertex *vHead = &mesh->vHead; + GLUhalfEdge *eHead = &mesh->eHead; + GLUface *f, *fPrev; + GLUvertex *v, *vPrev; + GLUhalfEdge *e, *ePrev; + + fPrev = fHead; + for( fPrev = fHead ; (f = fPrev->next) != fHead; fPrev = f) { + assert( f->prev == fPrev ); + e = f->anEdge; + do { + assert( e->Sym != e ); + assert( e->Sym->Sym == e ); + assert( e->Lnext->Onext->Sym == e ); + assert( e->Onext->Sym->Lnext == e ); + assert( e->Lface == f ); + e = e->Lnext; + } while( e != f->anEdge ); + } + assert( f->prev == fPrev && f->anEdge == NULL && f->data == NULL ); + + vPrev = vHead; + for( vPrev = vHead ; (v = vPrev->next) != vHead; vPrev = v) { + assert( v->prev == vPrev ); + e = v->anEdge; + do { + assert( e->Sym != e ); + assert( e->Sym->Sym == e ); + assert( e->Lnext->Onext->Sym == e ); + assert( e->Onext->Sym->Lnext == e ); + assert( e->Org == v ); + e = e->Onext; + } while( e != v->anEdge ); + } + assert( v->prev == vPrev && v->anEdge == NULL && v->data == NULL ); + + ePrev = eHead; + for( ePrev = eHead ; (e = ePrev->next) != eHead; ePrev = e) { + assert( e->Sym->next == ePrev->Sym ); + assert( e->Sym != e ); + assert( e->Sym->Sym == e ); + assert( e->Org != NULL ); + assert( e->Dst != NULL ); + assert( e->Lnext->Onext->Sym == e ); + assert( e->Onext->Sym->Lnext == e ); + } + assert( e->Sym->next == ePrev->Sym + && e->Sym == &mesh->eHeadSym + && e->Sym->Sym == e + && e->Org == NULL && e->Dst == NULL + && e->Lface == NULL && e->Rface == NULL ); +} + +#endif diff --git a/vtm/jni/tessellate/mesh.h b/vtm/jni/tessellate/mesh.h new file mode 100644 index 00000000..20a4b2b9 --- /dev/null +++ b/vtm/jni/tessellate/mesh.h @@ -0,0 +1,266 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __mesh_h_ +#define __mesh_h_ + +#include "glu.h" + +typedef struct GLUmesh GLUmesh; + +typedef struct GLUvertex GLUvertex; +typedef struct GLUface GLUface; +typedef struct GLUhalfEdge GLUhalfEdge; + +typedef struct ActiveRegion ActiveRegion; /* Internal data */ + +/* The mesh structure is similar in spirit, notation, and operations + * to the "quad-edge" structure (see L. Guibas and J. Stolfi, Primitives + * for the manipulation of general subdivisions and the computation of + * Voronoi diagrams, ACM Transactions on Graphics, 4(2):74-123, April 1985). + * For a simplified description, see the course notes for CS348a, + * "Mathematical Foundations of Computer Graphics", available at the + * Stanford bookstore (and taught during the fall quarter). + * The implementation also borrows a tiny subset of the graph-based approach + * use in Mantyla's Geometric Work Bench (see M. Mantyla, An Introduction + * to Sold Modeling, Computer Science Press, Rockville, Maryland, 1988). + * + * The fundamental data structure is the "half-edge". Two half-edges + * go together to make an edge, but they point in opposite directions. + * Each half-edge has a pointer to its mate (the "symmetric" half-edge Sym), + * its origin vertex (Org), the face on its left side (Lface), and the + * adjacent half-edges in the CCW direction around the origin vertex + * (Onext) and around the left face (Lnext). There is also a "next" + * pointer for the global edge list (see below). + * + * The notation used for mesh navigation: + * Sym = the mate of a half-edge (same edge, but opposite direction) + * Onext = edge CCW around origin vertex (keep same origin) + * Dnext = edge CCW around destination vertex (keep same dest) + * Lnext = edge CCW around left face (dest becomes new origin) + * Rnext = edge CCW around right face (origin becomes new dest) + * + * "prev" means to substitute CW for CCW in the definitions above. + * + * The mesh keeps global lists of all vertices, faces, and edges, + * stored as doubly-linked circular lists with a dummy header node. + * The mesh stores pointers to these dummy headers (vHead, fHead, eHead). + * + * The circular edge list is special; since half-edges always occur + * in pairs (e and e->Sym), each half-edge stores a pointer in only + * one direction. Starting at eHead and following the e->next pointers + * will visit each *edge* once (ie. e or e->Sym, but not both). + * e->Sym stores a pointer in the opposite direction, thus it is + * always true that e->Sym->next->Sym->next == e. + * + * Each vertex has a pointer to next and previous vertices in the + * circular list, and a pointer to a half-edge with this vertex as + * the origin (NULL if this is the dummy header). There is also a + * field "data" for client data. + * + * Each face has a pointer to the next and previous faces in the + * circular list, and a pointer to a half-edge with this face as + * the left face (NULL if this is the dummy header). There is also + * a field "data" for client data. + * + * Note that what we call a "face" is really a loop; faces may consist + * of more than one loop (ie. not simply connected), but there is no + * record of this in the data structure. The mesh may consist of + * several disconnected regions, so it may not be possible to visit + * the entire mesh by starting at a half-edge and traversing the edge + * structure. + * + * The mesh does NOT support isolated vertices; a vertex is deleted along + * with its last edge. Similarly when two faces are merged, one of the + * faces is deleted (see __gl_meshDelete below). For mesh operations, + * all face (loop) and vertex pointers must not be NULL. However, once + * mesh manipulation is finished, __gl_MeshZapFace can be used to delete + * faces of the mesh, one at a time. All external faces can be "zapped" + * before the mesh is returned to the client; then a NULL face indicates + * a region which is not part of the output polygon. + */ + +struct GLUvertex { + GLUvertex *next; /* next vertex (never NULL) */ + GLUvertex *prev; /* previous vertex (never NULL) */ + GLUhalfEdge *anEdge; /* a half-edge with this origin */ + void *data; /* client's data */ + + /* Internal data (keep hidden) */ + GLdouble coords[3]; /* vertex location in 3D */ + GLdouble s, t; /* projection onto the sweep plane */ + long pqHandle; /* to allow deletion from priority queue */ +}; + +struct GLUface { + GLUface *next; /* next face (never NULL) */ + GLUface *prev; /* previous face (never NULL) */ + GLUhalfEdge *anEdge; /* a half edge with this left face */ + void *data; /* room for client's data */ + + /* Internal data (keep hidden) */ + GLUface *trail; /* "stack" for conversion to strips */ + GLboolean marked; /* flag for conversion to strips */ + GLboolean inside; /* this face is in the polygon interior */ +}; + +struct GLUhalfEdge { + GLUhalfEdge *next; /* doubly-linked list (prev==Sym->next) */ + GLUhalfEdge *Sym; /* same edge, opposite direction */ + GLUhalfEdge *Onext; /* next edge CCW around origin */ + GLUhalfEdge *Lnext; /* next edge CCW around left face */ + GLUvertex *Org; /* origin vertex (Overtex too long) */ + GLUface *Lface; /* left face */ + + /* Internal data (keep hidden) */ + ActiveRegion *activeRegion; /* a region with this upper edge (sweep.c) */ + int winding; /* change in winding number when crossing + from the right face to the left face */ +}; + +#define Rface Sym->Lface +#define Dst Sym->Org + +#define Oprev Sym->Lnext +#define Lprev Onext->Sym +#define Dprev Lnext->Sym +#define Rprev Sym->Onext +#define Dnext Rprev->Sym /* 3 pointers */ +#define Rnext Oprev->Sym /* 3 pointers */ + + +struct GLUmesh { + GLUvertex vHead; /* dummy header for vertex list */ + GLUface fHead; /* dummy header for face list */ + GLUhalfEdge eHead; /* dummy header for edge list */ + GLUhalfEdge eHeadSym; /* and its symmetric counterpart */ +}; + +/* The mesh operations below have three motivations: completeness, + * convenience, and efficiency. The basic mesh operations are MakeEdge, + * Splice, and Delete. All the other edge operations can be implemented + * in terms of these. The other operations are provided for convenience + * and/or efficiency. + * + * When a face is split or a vertex is added, they are inserted into the + * global list *before* the existing vertex or face (ie. e->Org or e->Lface). + * This makes it easier to process all vertices or faces in the global lists + * without worrying about processing the same data twice. As a convenience, + * when a face is split, the "inside" flag is copied from the old face. + * Other internal data (v->data, v->activeRegion, f->data, f->marked, + * f->trail, e->winding) is set to zero. + * + * ********************** Basic Edge Operations ************************** + * + * __gl_meshMakeEdge( mesh ) creates one edge, two vertices, and a loop. + * The loop (face) consists of the two new half-edges. + * + * __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the + * mesh connectivity and topology. It changes the mesh so that + * eOrg->Onext <- OLD( eDst->Onext ) + * eDst->Onext <- OLD( eOrg->Onext ) + * where OLD(...) means the value before the meshSplice operation. + * + * This can have two effects on the vertex structure: + * - if eOrg->Org != eDst->Org, the two vertices are merged together + * - if eOrg->Org == eDst->Org, the origin is split into two vertices + * In both cases, eDst->Org is changed and eOrg->Org is untouched. + * + * Similarly (and independently) for the face structure, + * - if eOrg->Lface == eDst->Lface, one loop is split into two + * - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one + * In both cases, eDst->Lface is changed and eOrg->Lface is unaffected. + * + * __gl_meshDelete( eDel ) removes the edge eDel. There are several cases: + * if (eDel->Lface != eDel->Rface), we join two loops into one; the loop + * eDel->Lface is deleted. Otherwise, we are splitting one loop into two; + * the newly created loop will contain eDel->Dst. If the deletion of eDel + * would create isolated vertices, those are deleted as well. + * + * ********************** Other Edge Operations ************************** + * + * __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that + * eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex. + * eOrg and eNew will have the same left face. + * + * __gl_meshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew, + * such that eNew == eOrg->Lnext. The new vertex is eOrg->Dst == eNew->Org. + * eOrg and eNew will have the same left face. + * + * __gl_meshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst + * to eDst->Org, and returns the corresponding half-edge eNew. + * If eOrg->Lface == eDst->Lface, this splits one loop into two, + * and the newly created loop is eNew->Lface. Otherwise, two disjoint + * loops are merged into one, and the loop eDst->Lface is destroyed. + * + * ************************ Other Operations ***************************** + * + * __gl_meshNewMesh() creates a new mesh with no edges, no vertices, + * and no loops (what we usually call a "face"). + * + * __gl_meshUnion( mesh1, mesh2 ) forms the union of all structures in + * both meshes, and returns the new mesh (the old meshes are destroyed). + * + * __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh. + * + * __gl_meshZapFace( fZap ) destroys a face and removes it from the + * global face list. All edges of fZap will have a NULL pointer as their + * left face. Any edges which also have a NULL pointer as their right face + * are deleted entirely (along with any isolated vertices this produces). + * An entire mesh can be deleted by zapping its faces, one at a time, + * in any order. Zapped faces cannot be used in further mesh operations! + * + * __gl_meshCheckMesh( mesh ) checks a mesh for self-consistency. + */ + +GLUhalfEdge *__gl_meshMakeEdge( GLUmesh *mesh ); +int __gl_meshSplice( GLUhalfEdge *eOrg, GLUhalfEdge *eDst ); +int __gl_meshDelete( GLUhalfEdge *eDel ); + +GLUhalfEdge *__gl_meshAddEdgeVertex( GLUhalfEdge *eOrg ); +GLUhalfEdge *__gl_meshSplitEdge( GLUhalfEdge *eOrg ); +GLUhalfEdge *__gl_meshConnect( GLUhalfEdge *eOrg, GLUhalfEdge *eDst ); + +GLUmesh *__gl_meshNewMesh( void ); +GLUmesh *__gl_meshUnion( GLUmesh *mesh1, GLUmesh *mesh2 ); +void __gl_meshDeleteMesh( GLUmesh *mesh ); +void __gl_meshZapFace( GLUface *fZap ); + +#ifdef NDEBUG +#define __gl_meshCheckMesh( mesh ) +#else +void __gl_meshCheckMesh( GLUmesh *mesh ); +#endif + +#endif diff --git a/vtm/jni/tessellate/normal.c b/vtm/jni/tessellate/normal.c new file mode 100644 index 00000000..9a3bd43d --- /dev/null +++ b/vtm/jni/tessellate/normal.c @@ -0,0 +1,257 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include "gluos.h" +#include "mesh.h" +#include "tess.h" +#include "normal.h" +#include +#include + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#define Dot(u,v) (u[0]*v[0] + u[1]*v[1] + u[2]*v[2]) + +#if 0 +static void Normalize( GLdouble v[3] ) +{ + GLdouble len = v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; + + assert( len > 0 ); + len = sqrt( len ); + v[0] /= len; + v[1] /= len; + v[2] /= len; +} +#endif + +#undef ABS +#define ABS(x) ((x) < 0 ? -(x) : (x)) + +static int LongAxis( GLdouble v[3] ) +{ + int i = 0; + + if( ABS(v[1]) > ABS(v[0]) ) { i = 1; } + if( ABS(v[2]) > ABS(v[i]) ) { i = 2; } + return i; +} + +static void ComputeNormal( GLUtesselator *tess, GLdouble norm[3] ) +{ + GLUvertex *v, *v1, *v2; + GLdouble c, tLen2, maxLen2; + GLdouble maxVal[3], minVal[3], d1[3], d2[3], tNorm[3]; + GLUvertex *maxVert[3], *minVert[3]; + GLUvertex *vHead = &tess->mesh->vHead; + int i; + + maxVal[0] = maxVal[1] = maxVal[2] = -2 * GLU_TESS_MAX_COORD; + minVal[0] = minVal[1] = minVal[2] = 2 * GLU_TESS_MAX_COORD; + + for( v = vHead->next; v != vHead; v = v->next ) { + for( i = 0; i < 3; ++i ) { + c = v->coords[i]; + if( c < minVal[i] ) { minVal[i] = c; minVert[i] = v; } + if( c > maxVal[i] ) { maxVal[i] = c; maxVert[i] = v; } + } + } + + /* Find two vertices separated by at least 1/sqrt(3) of the maximum + * distance between any two vertices + */ + i = 0; + if( maxVal[1] - minVal[1] > maxVal[0] - minVal[0] ) { i = 1; } + if( maxVal[2] - minVal[2] > maxVal[i] - minVal[i] ) { i = 2; } + if( minVal[i] >= maxVal[i] ) { + /* All vertices are the same -- normal doesn't matter */ + norm[0] = 0; norm[1] = 0; norm[2] = 1; + return; + } + + /* Look for a third vertex which forms the triangle with maximum area + * (Length of normal == twice the triangle area) + */ + maxLen2 = 0; + v1 = minVert[i]; + v2 = maxVert[i]; + d1[0] = v1->coords[0] - v2->coords[0]; + d1[1] = v1->coords[1] - v2->coords[1]; + d1[2] = v1->coords[2] - v2->coords[2]; + for( v = vHead->next; v != vHead; v = v->next ) { + d2[0] = v->coords[0] - v2->coords[0]; + d2[1] = v->coords[1] - v2->coords[1]; + d2[2] = v->coords[2] - v2->coords[2]; + tNorm[0] = d1[1]*d2[2] - d1[2]*d2[1]; + tNorm[1] = d1[2]*d2[0] - d1[0]*d2[2]; + tNorm[2] = d1[0]*d2[1] - d1[1]*d2[0]; + tLen2 = tNorm[0]*tNorm[0] + tNorm[1]*tNorm[1] + tNorm[2]*tNorm[2]; + if( tLen2 > maxLen2 ) { + maxLen2 = tLen2; + norm[0] = tNorm[0]; + norm[1] = tNorm[1]; + norm[2] = tNorm[2]; + } + } + + if( maxLen2 <= 0 ) { + /* All points lie on a single line -- any decent normal will do */ + norm[0] = norm[1] = norm[2] = 0; + norm[LongAxis(d1)] = 1; + } +} + + +static void CheckOrientation( GLUtesselator *tess ) +{ + GLdouble area; + GLUface *f, *fHead = &tess->mesh->fHead; + GLUvertex *v, *vHead = &tess->mesh->vHead; + GLUhalfEdge *e; + + /* When we compute the normal automatically, we choose the orientation + * so that the sum of the signed areas of all contours is non-negative. + */ + area = 0; + for( f = fHead->next; f != fHead; f = f->next ) { + e = f->anEdge; + if( e->winding <= 0 ) continue; + do { + area += (e->Org->s - e->Dst->s) * (e->Org->t + e->Dst->t); + e = e->Lnext; + } while( e != f->anEdge ); + } + if( area < 0 ) { + /* Reverse the orientation by flipping all the t-coordinates */ + for( v = vHead->next; v != vHead; v = v->next ) { + v->t = - v->t; + } + tess->tUnit[0] = - tess->tUnit[0]; + tess->tUnit[1] = - tess->tUnit[1]; + tess->tUnit[2] = - tess->tUnit[2]; + } +} + +#ifdef FOR_TRITE_TEST_PROGRAM +#include +extern int RandomSweep; +#define S_UNIT_X (RandomSweep ? (2*drand48()-1) : 1.0) +#define S_UNIT_Y (RandomSweep ? (2*drand48()-1) : 0.0) +#else +#if defined(SLANTED_SWEEP) +/* The "feature merging" is not intended to be complete. There are + * special cases where edges are nearly parallel to the sweep line + * which are not implemented. The algorithm should still behave + * robustly (ie. produce a reasonable tesselation) in the presence + * of such edges, however it may miss features which could have been + * merged. We could minimize this effect by choosing the sweep line + * direction to be something unusual (ie. not parallel to one of the + * coordinate axes). + */ +#define S_UNIT_X 0.50941539564955385 /* Pre-normalized */ +#define S_UNIT_Y 0.86052074622010633 +#else +#define S_UNIT_X 1.0 +#define S_UNIT_Y 0.0 +#endif +#endif + +/* Determine the polygon normal and project vertices onto the plane + * of the polygon. + */ +void __gl_projectPolygon( GLUtesselator *tess ) +{ + GLUvertex *v, *vHead = &tess->mesh->vHead; + GLdouble norm[3]; + GLdouble *sUnit, *tUnit; + int i, computedNormal = FALSE; + + norm[0] = tess->normal[0]; + norm[1] = tess->normal[1]; + norm[2] = tess->normal[2]; + if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) { + ComputeNormal( tess, norm ); + computedNormal = TRUE; + } + sUnit = tess->sUnit; + tUnit = tess->tUnit; + i = LongAxis( norm ); + +#if defined(FOR_TRITE_TEST_PROGRAM) || defined(TRUE_PROJECT) + /* Choose the initial sUnit vector to be approximately perpendicular + * to the normal. + */ + Normalize( norm ); + + sUnit[i] = 0; + sUnit[(i+1)%3] = S_UNIT_X; + sUnit[(i+2)%3] = S_UNIT_Y; + + /* Now make it exactly perpendicular */ + w = Dot( sUnit, norm ); + sUnit[0] -= w * norm[0]; + sUnit[1] -= w * norm[1]; + sUnit[2] -= w * norm[2]; + Normalize( sUnit ); + + /* Choose tUnit so that (sUnit,tUnit,norm) form a right-handed frame */ + tUnit[0] = norm[1]*sUnit[2] - norm[2]*sUnit[1]; + tUnit[1] = norm[2]*sUnit[0] - norm[0]*sUnit[2]; + tUnit[2] = norm[0]*sUnit[1] - norm[1]*sUnit[0]; + Normalize( tUnit ); +#else + /* Project perpendicular to a coordinate axis -- better numerically */ + sUnit[i] = 0; + sUnit[(i+1)%3] = S_UNIT_X; + sUnit[(i+2)%3] = S_UNIT_Y; + + tUnit[i] = 0; + tUnit[(i+1)%3] = (norm[i] > 0) ? -S_UNIT_Y : S_UNIT_Y; + tUnit[(i+2)%3] = (norm[i] > 0) ? S_UNIT_X : -S_UNIT_X; +#endif + + /* Project the vertices onto the sweep plane */ + for( v = vHead->next; v != vHead; v = v->next ) { + v->s = Dot( v->coords, sUnit ); + v->t = Dot( v->coords, tUnit ); + } + if( computedNormal ) { + CheckOrientation( tess ); + } +} diff --git a/vtm/jni/tessellate/normal.h b/vtm/jni/tessellate/normal.h new file mode 100644 index 00000000..c376ca44 --- /dev/null +++ b/vtm/jni/tessellate/normal.h @@ -0,0 +1,45 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __normal_h_ +#define __normal_h_ + +#include "tess.h" + +/* __gl_projectPolygon( tess ) determines the polygon normal + * and project vertices onto the plane of the polygon. + */ +void __gl_projectPolygon( GLUtesselator *tess ); + +#endif diff --git a/vtm/jni/tessellate/priorityq-heap.c b/vtm/jni/tessellate/priorityq-heap.c new file mode 100644 index 00000000..898c561b --- /dev/null +++ b/vtm/jni/tessellate/priorityq-heap.c @@ -0,0 +1,257 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include +#include +#include +#include "priorityq-heap.h" +#include "memalloc.h" + +#define INIT_SIZE 32 + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#ifdef FOR_TRITE_TEST_PROGRAM +#define LEQ(x,y) (*pq->leq)(x,y) +#else +/* Violates modularity, but a little faster */ +#include "geom.h" +#define LEQ(x,y) VertLeq((GLUvertex *)x, (GLUvertex *)y) +#endif + +/* really __gl_pqHeapNewPriorityQ */ +PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) ) +{ + PriorityQ *pq = (PriorityQ *)memAlloc( sizeof( PriorityQ )); + if (pq == NULL) return NULL; + + pq->size = 0; + pq->max = INIT_SIZE; + pq->nodes = (PQnode *)memAlloc( (INIT_SIZE + 1) * sizeof(pq->nodes[0]) ); + if (pq->nodes == NULL) { + memFree(pq); + return NULL; + } + + pq->handles = (PQhandleElem *)memAlloc( (INIT_SIZE + 1) * sizeof(pq->handles[0]) ); + if (pq->handles == NULL) { + memFree(pq->nodes); + memFree(pq); + return NULL; + } + + pq->initialized = FALSE; + pq->freeList = 0; + pq->leq = leq; + + pq->nodes[1].handle = 1; /* so that Minimum() returns NULL */ + pq->handles[1].key = NULL; + return pq; +} + +/* really __gl_pqHeapDeletePriorityQ */ +void pqDeletePriorityQ( PriorityQ *pq ) +{ + memFree( pq->handles ); + memFree( pq->nodes ); + memFree( pq ); +} + + +static void FloatDown( PriorityQ *pq, long curr ) +{ + PQnode *n = pq->nodes; + PQhandleElem *h = pq->handles; + PQhandle hCurr, hChild; + long child; + + hCurr = n[curr].handle; + for( ;; ) { + child = curr << 1; + if( child < pq->size && LEQ( h[n[child+1].handle].key, + h[n[child].handle].key )) { + ++child; + } + + assert(child <= pq->max); + + hChild = n[child].handle; + if( child > pq->size || LEQ( h[hCurr].key, h[hChild].key )) { + n[curr].handle = hCurr; + h[hCurr].node = curr; + break; + } + n[curr].handle = hChild; + h[hChild].node = curr; + curr = child; + } +} + + +static void FloatUp( PriorityQ *pq, long curr ) +{ + PQnode *n = pq->nodes; + PQhandleElem *h = pq->handles; + PQhandle hCurr, hParent; + long parent; + + hCurr = n[curr].handle; + for( ;; ) { + parent = curr >> 1; + hParent = n[parent].handle; + if( parent == 0 || LEQ( h[hParent].key, h[hCurr].key )) { + n[curr].handle = hCurr; + h[hCurr].node = curr; + break; + } + n[curr].handle = hParent; + h[hParent].node = curr; + curr = parent; + } +} + +/* really __gl_pqHeapInit */ +void pqInit( PriorityQ *pq ) +{ + long i; + + /* This method of building a heap is O(n), rather than O(n lg n). */ + + for( i = pq->size; i >= 1; --i ) { + FloatDown( pq, i ); + } + pq->initialized = TRUE; +} + +/* really __gl_pqHeapInsert */ +/* returns LONG_MAX iff out of memory */ +PQhandle pqInsert( PriorityQ *pq, PQkey keyNew ) +{ + long curr; + PQhandle free_handle; + + curr = ++ pq->size; + if( (curr*2) > pq->max ) { + PQnode *saveNodes= pq->nodes; + PQhandleElem *saveHandles= pq->handles; + + /* If the heap overflows, double its size. */ + pq->max <<= 1; + pq->nodes = (PQnode *)memRealloc( pq->nodes, + (size_t) + ((pq->max + 1) * sizeof( pq->nodes[0] ))); + if (pq->nodes == NULL) { + pq->nodes = saveNodes; /* restore ptr to free upon return */ + return LONG_MAX; + } + pq->handles = (PQhandleElem *)memRealloc( pq->handles, + (size_t) + ((pq->max + 1) * + sizeof( pq->handles[0] ))); + if (pq->handles == NULL) { + pq->handles = saveHandles; /* restore ptr to free upon return */ + return LONG_MAX; + } + } + + if( pq->freeList == 0 ) { + free_handle = curr; + } else { + free_handle = pq->freeList; + pq->freeList = pq->handles[free_handle].node; + } + + pq->nodes[curr].handle = free_handle; + pq->handles[free_handle].node = curr; + pq->handles[free_handle].key = keyNew; + + if( pq->initialized ) { + FloatUp( pq, curr ); + } + assert(free_handle != LONG_MAX); + return free_handle; +} + +/* really __gl_pqHeapExtractMin */ +PQkey pqExtractMin( PriorityQ *pq ) +{ + PQnode *n = pq->nodes; + PQhandleElem *h = pq->handles; + PQhandle hMin = n[1].handle; + PQkey min = h[hMin].key; + + if( pq->size > 0 ) { + n[1].handle = n[pq->size].handle; + h[n[1].handle].node = 1; + + h[hMin].key = NULL; + h[hMin].node = pq->freeList; + pq->freeList = hMin; + + if( -- pq->size > 0 ) { + FloatDown( pq, 1 ); + } + } + return min; +} + +/* really __gl_pqHeapDelete */ +void pqDelete( PriorityQ *pq, PQhandle hCurr ) +{ + PQnode *n = pq->nodes; + PQhandleElem *h = pq->handles; + long curr; + + assert( hCurr >= 1 && hCurr <= pq->max && h[hCurr].key != NULL ); + + curr = h[hCurr].node; + n[curr].handle = n[pq->size].handle; + h[n[curr].handle].node = curr; + + if( curr <= -- pq->size ) { + if( curr <= 1 || LEQ( h[n[curr>>1].handle].key, h[n[curr].handle].key )) { + FloatDown( pq, curr ); + } else { + FloatUp( pq, curr ); + } + } + h[hCurr].key = NULL; + h[hCurr].node = pq->freeList; + pq->freeList = hCurr; +} diff --git a/vtm/jni/tessellate/priorityq-heap.h b/vtm/jni/tessellate/priorityq-heap.h new file mode 100644 index 00000000..dc9aaef8 --- /dev/null +++ b/vtm/jni/tessellate/priorityq-heap.h @@ -0,0 +1,107 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __priorityq_heap_h_ +#define __priorityq_heap_h_ + +/* Use #define's so that another heap implementation can use this one */ + +#define PQkey PQHeapKey +#define PQhandle PQHeapHandle +#define PriorityQ PriorityQHeap + +#define pqNewPriorityQ(leq) __gl_pqHeapNewPriorityQ(leq) +#define pqDeletePriorityQ(pq) __gl_pqHeapDeletePriorityQ(pq) + +/* The basic operations are insertion of a new key (pqInsert), + * and examination/extraction of a key whose value is minimum + * (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete); + * for this purpose pqInsert returns a "handle" which is supplied + * as the argument. + * + * An initial heap may be created efficiently by calling pqInsert + * repeatedly, then calling pqInit. In any case pqInit must be called + * before any operations other than pqInsert are used. + * + * If the heap is empty, pqMinimum/pqExtractMin will return a NULL key. + * This may also be tested with pqIsEmpty. + */ +#define pqInit(pq) __gl_pqHeapInit(pq) +#define pqInsert(pq,key) __gl_pqHeapInsert(pq,key) +#define pqMinimum(pq) __gl_pqHeapMinimum(pq) +#define pqExtractMin(pq) __gl_pqHeapExtractMin(pq) +#define pqDelete(pq,handle) __gl_pqHeapDelete(pq,handle) +#define pqIsEmpty(pq) __gl_pqHeapIsEmpty(pq) + + +/* Since we support deletion the data structure is a little more + * complicated than an ordinary heap. "nodes" is the heap itself; + * active nodes are stored in the range 1..pq->size. When the + * heap exceeds its allocated size (pq->max), its size doubles. + * The children of node i are nodes 2i and 2i+1. + * + * Each node stores an index into an array "handles". Each handle + * stores a key, plus a pointer back to the node which currently + * represents that key (ie. nodes[handles[i].node].handle == i). + */ + +typedef void *PQkey; +typedef long PQhandle; +typedef struct PriorityQ PriorityQ; + +typedef struct { PQhandle handle; } PQnode; +typedef struct { PQkey key; PQhandle node; } PQhandleElem; + +struct PriorityQ { + PQnode *nodes; + PQhandleElem *handles; + long size, max; + PQhandle freeList; + int initialized; + int (*leq)(PQkey key1, PQkey key2); +}; + +PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) ); +void pqDeletePriorityQ( PriorityQ *pq ); + +void pqInit( PriorityQ *pq ); +PQhandle pqInsert( PriorityQ *pq, PQkey key ); +PQkey pqExtractMin( PriorityQ *pq ); +void pqDelete( PriorityQ *pq, PQhandle handle ); + + +#define __gl_pqHeapMinimum(pq) ((pq)->handles[(pq)->nodes[1].handle].key) +#define __gl_pqHeapIsEmpty(pq) ((pq)->size == 0) + +#endif diff --git a/vtm/jni/tessellate/priorityq-sort.h b/vtm/jni/tessellate/priorityq-sort.h new file mode 100644 index 00000000..746cf5fa --- /dev/null +++ b/vtm/jni/tessellate/priorityq-sort.h @@ -0,0 +1,117 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __priorityq_sort_h_ +#define __priorityq_sort_h_ + +#include "priorityq-heap.h" + +#undef PQkey +#undef PQhandle +#undef PriorityQ +#undef pqNewPriorityQ +#undef pqDeletePriorityQ +#undef pqInit +#undef pqInsert +#undef pqMinimum +#undef pqExtractMin +#undef pqDelete +#undef pqIsEmpty + +/* Use #define's so that another heap implementation can use this one */ + +#define PQkey PQSortKey +#define PQhandle PQSortHandle +#define PriorityQ PriorityQSort + +#define pqNewPriorityQ(leq) __gl_pqSortNewPriorityQ(leq) +#define pqDeletePriorityQ(pq) __gl_pqSortDeletePriorityQ(pq) + +/* The basic operations are insertion of a new key (pqInsert), + * and examination/extraction of a key whose value is minimum + * (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete); + * for this purpose pqInsert returns a "handle" which is supplied + * as the argument. + * + * An initial heap may be created efficiently by calling pqInsert + * repeatedly, then calling pqInit. In any case pqInit must be called + * before any operations other than pqInsert are used. + * + * If the heap is empty, pqMinimum/pqExtractMin will return a NULL key. + * This may also be tested with pqIsEmpty. + */ +#define pqInit(pq) __gl_pqSortInit(pq) +#define pqInsert(pq,key) __gl_pqSortInsert(pq,key) +#define pqMinimum(pq) __gl_pqSortMinimum(pq) +#define pqExtractMin(pq) __gl_pqSortExtractMin(pq) +#define pqDelete(pq,handle) __gl_pqSortDelete(pq,handle) +#define pqIsEmpty(pq) __gl_pqSortIsEmpty(pq) + + +/* Since we support deletion the data structure is a little more + * complicated than an ordinary heap. "nodes" is the heap itself; + * active nodes are stored in the range 1..pq->size. When the + * heap exceeds its allocated size (pq->max), its size doubles. + * The children of node i are nodes 2i and 2i+1. + * + * Each node stores an index into an array "handles". Each handle + * stores a key, plus a pointer back to the node which currently + * represents that key (ie. nodes[handles[i].node].handle == i). + */ + +typedef PQHeapKey PQkey; +typedef PQHeapHandle PQhandle; +typedef struct PriorityQ PriorityQ; + +struct PriorityQ { + PriorityQHeap *heap; + PQkey *keys; + PQkey **order; + PQhandle size, max; + int initialized; + int (*leq)(PQkey key1, PQkey key2); +}; + +PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) ); +void pqDeletePriorityQ( PriorityQ *pq ); + +int pqInit( PriorityQ *pq ); +PQhandle pqInsert( PriorityQ *pq, PQkey key ); +PQkey pqExtractMin( PriorityQ *pq ); +void pqDelete( PriorityQ *pq, PQhandle handle ); + +PQkey pqMinimum( PriorityQ *pq ); +int pqIsEmpty( PriorityQ *pq ); + +#endif diff --git a/vtm/jni/tessellate/priorityq.c b/vtm/jni/tessellate/priorityq.c new file mode 100644 index 00000000..c6b99cce --- /dev/null +++ b/vtm/jni/tessellate/priorityq.c @@ -0,0 +1,260 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include "gluos.h" +#include +#include +#include /* LONG_MAX */ +#include "memalloc.h" + +/* Include all the code for the regular heap-based queue here. */ + +#include "priorityq-heap.c" + +/* Now redefine all the function names to map to their "Sort" versions. */ + +#include "priorityq-sort.h" + +/* really __gl_pqSortNewPriorityQ */ +PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) ) +{ + PriorityQ *pq = (PriorityQ *)memAlloc( sizeof( PriorityQ )); + if (pq == NULL) return NULL; + + pq->heap = __gl_pqHeapNewPriorityQ( leq ); + if (pq->heap == NULL) { + memFree(pq); + return NULL; + } + + pq->keys = (PQHeapKey *)memAlloc( INIT_SIZE * sizeof(pq->keys[0]) ); + if (pq->keys == NULL) { + __gl_pqHeapDeletePriorityQ(pq->heap); + memFree(pq); + return NULL; + } + + pq->size = 0; + pq->max = INIT_SIZE; + pq->initialized = FALSE; + pq->leq = leq; + return pq; +} + +/* really __gl_pqSortDeletePriorityQ */ +void pqDeletePriorityQ( PriorityQ *pq ) +{ + assert(pq != NULL); + if (pq->heap != NULL) __gl_pqHeapDeletePriorityQ( pq->heap ); + if (pq->order != NULL) memFree( pq->order ); + if (pq->keys != NULL) memFree( pq->keys ); + memFree( pq ); +} + + +#define LT(x,y) (! LEQ(y,x)) +#define GT(x,y) (! LEQ(x,y)) +#define Swap(a,b) do{PQkey *tmp = *a; *a = *b; *b = tmp;}while(0) + +/* really __gl_pqSortInit */ +int pqInit( PriorityQ *pq ) +{ + PQkey **p, **r, **i, **j, *piv; + struct { PQkey **p, **r; } Stack[50], *top = Stack; + unsigned long seed = 2016473283; + + /* Create an array of indirect pointers to the keys, so that we + * the handles we have returned are still valid. + */ +/* + pq->order = (PQHeapKey **)memAlloc( (size_t) + (pq->size * sizeof(pq->order[0])) ); +*/ + pq->order = (PQHeapKey **)memAlloc( (size_t) + ((pq->size+1) * sizeof(pq->order[0])) ); +/* the previous line is a patch to compensate for the fact that IBM */ +/* machines return a null on a malloc of zero bytes (unlike SGI), */ +/* so we have to put in this defense to guard against a memory */ +/* fault four lines down. from fossum@austin.ibm.com. */ + if (pq->order == NULL) return 0; + + p = pq->order; + r = p + pq->size - 1; + for( piv = pq->keys, i = p; i <= r; ++piv, ++i ) { + *i = piv; + } + + /* Sort the indirect pointers in descending order, + * using randomized Quicksort + */ + top->p = p; top->r = r; ++top; + while( --top >= Stack ) { + p = top->p; + r = top->r; + while( r > p + 10 ) { + seed = seed * 1539415821 + 1; + i = p + seed % (r - p + 1); + piv = *i; + *i = *p; + *p = piv; + i = p - 1; + j = r + 1; + do { + do { ++i; } while( GT( **i, *piv )); + do { --j; } while( LT( **j, *piv )); + Swap( i, j ); + } while( i < j ); + Swap( i, j ); /* Undo last swap */ + if( i - p < r - j ) { + top->p = j+1; top->r = r; ++top; + r = i-1; + } else { + top->p = p; top->r = i-1; ++top; + p = j+1; + } + } + /* Insertion sort small lists */ + for( i = p+1; i <= r; ++i ) { + piv = *i; + for( j = i; j > p && LT( **(j-1), *piv ); --j ) { + *j = *(j-1); + } + *j = piv; + } + } + pq->max = pq->size; + pq->initialized = TRUE; + __gl_pqHeapInit( pq->heap ); /* always succeeds */ + +#ifndef NDEBUG + p = pq->order; + r = p + pq->size - 1; + for( i = p; i < r; ++i ) { + assert( LEQ( **(i+1), **i )); + } +#endif + + return 1; +} + +/* really __gl_pqSortInsert */ +/* returns LONG_MAX iff out of memory */ +PQhandle pqInsert( PriorityQ *pq, PQkey keyNew ) +{ + long curr; + + if( pq->initialized ) { + return __gl_pqHeapInsert( pq->heap, keyNew ); + } + curr = pq->size; + if( ++ pq->size >= pq->max ) { + PQkey *saveKey= pq->keys; + + /* If the heap overflows, double its size. */ + pq->max <<= 1; + pq->keys = (PQHeapKey *)memRealloc( pq->keys, + (size_t) + (pq->max * sizeof( pq->keys[0] ))); + if (pq->keys == NULL) { + pq->keys = saveKey; /* restore ptr to free upon return */ + return LONG_MAX; + } + } + assert(curr != LONG_MAX); + pq->keys[curr] = keyNew; + + /* Negative handles index the sorted array. */ + return -(curr+1); +} + +/* really __gl_pqSortExtractMin */ +PQkey pqExtractMin( PriorityQ *pq ) +{ + PQkey sortMin, heapMin; + + if( pq->size == 0 ) { + return __gl_pqHeapExtractMin( pq->heap ); + } + sortMin = *(pq->order[pq->size-1]); + if( ! __gl_pqHeapIsEmpty( pq->heap )) { + heapMin = __gl_pqHeapMinimum( pq->heap ); + if( LEQ( heapMin, sortMin )) { + return __gl_pqHeapExtractMin( pq->heap ); + } + } + do { + -- pq->size; + } while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL ); + return sortMin; +} + +/* really __gl_pqSortMinimum */ +PQkey pqMinimum( PriorityQ *pq ) +{ + PQkey sortMin, heapMin; + + if( pq->size == 0 ) { + return __gl_pqHeapMinimum( pq->heap ); + } + sortMin = *(pq->order[pq->size-1]); + if( ! __gl_pqHeapIsEmpty( pq->heap )) { + heapMin = __gl_pqHeapMinimum( pq->heap ); + if( LEQ( heapMin, sortMin )) { + return heapMin; + } + } + return sortMin; +} + +/* really __gl_pqSortIsEmpty */ +int pqIsEmpty( PriorityQ *pq ) +{ + return (pq->size == 0) && __gl_pqHeapIsEmpty( pq->heap ); +} + +/* really __gl_pqSortDelete */ +void pqDelete( PriorityQ *pq, PQhandle curr ) +{ + if( curr >= 0 ) { + __gl_pqHeapDelete( pq->heap, curr ); + return; + } + curr = -(curr+1); + assert( curr < pq->max && pq->keys[curr] != NULL ); + + pq->keys[curr] = NULL; + while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL ) { + -- pq->size; + } +} diff --git a/vtm/jni/tessellate/priorityq.h b/vtm/jni/tessellate/priorityq.h new file mode 100644 index 00000000..746cf5fa --- /dev/null +++ b/vtm/jni/tessellate/priorityq.h @@ -0,0 +1,117 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __priorityq_sort_h_ +#define __priorityq_sort_h_ + +#include "priorityq-heap.h" + +#undef PQkey +#undef PQhandle +#undef PriorityQ +#undef pqNewPriorityQ +#undef pqDeletePriorityQ +#undef pqInit +#undef pqInsert +#undef pqMinimum +#undef pqExtractMin +#undef pqDelete +#undef pqIsEmpty + +/* Use #define's so that another heap implementation can use this one */ + +#define PQkey PQSortKey +#define PQhandle PQSortHandle +#define PriorityQ PriorityQSort + +#define pqNewPriorityQ(leq) __gl_pqSortNewPriorityQ(leq) +#define pqDeletePriorityQ(pq) __gl_pqSortDeletePriorityQ(pq) + +/* The basic operations are insertion of a new key (pqInsert), + * and examination/extraction of a key whose value is minimum + * (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete); + * for this purpose pqInsert returns a "handle" which is supplied + * as the argument. + * + * An initial heap may be created efficiently by calling pqInsert + * repeatedly, then calling pqInit. In any case pqInit must be called + * before any operations other than pqInsert are used. + * + * If the heap is empty, pqMinimum/pqExtractMin will return a NULL key. + * This may also be tested with pqIsEmpty. + */ +#define pqInit(pq) __gl_pqSortInit(pq) +#define pqInsert(pq,key) __gl_pqSortInsert(pq,key) +#define pqMinimum(pq) __gl_pqSortMinimum(pq) +#define pqExtractMin(pq) __gl_pqSortExtractMin(pq) +#define pqDelete(pq,handle) __gl_pqSortDelete(pq,handle) +#define pqIsEmpty(pq) __gl_pqSortIsEmpty(pq) + + +/* Since we support deletion the data structure is a little more + * complicated than an ordinary heap. "nodes" is the heap itself; + * active nodes are stored in the range 1..pq->size. When the + * heap exceeds its allocated size (pq->max), its size doubles. + * The children of node i are nodes 2i and 2i+1. + * + * Each node stores an index into an array "handles". Each handle + * stores a key, plus a pointer back to the node which currently + * represents that key (ie. nodes[handles[i].node].handle == i). + */ + +typedef PQHeapKey PQkey; +typedef PQHeapHandle PQhandle; +typedef struct PriorityQ PriorityQ; + +struct PriorityQ { + PriorityQHeap *heap; + PQkey *keys; + PQkey **order; + PQhandle size, max; + int initialized; + int (*leq)(PQkey key1, PQkey key2); +}; + +PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) ); +void pqDeletePriorityQ( PriorityQ *pq ); + +int pqInit( PriorityQ *pq ); +PQhandle pqInsert( PriorityQ *pq, PQkey key ); +PQkey pqExtractMin( PriorityQ *pq ); +void pqDelete( PriorityQ *pq, PQhandle handle ); + +PQkey pqMinimum( PriorityQ *pq ); +int pqIsEmpty( PriorityQ *pq ); + +#endif diff --git a/vtm/jni/tessellate/render.c b/vtm/jni/tessellate/render.c new file mode 100644 index 00000000..bca836f0 --- /dev/null +++ b/vtm/jni/tessellate/render.c @@ -0,0 +1,502 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include "gluos.h" +#include +#include +#include "mesh.h" +#include "tess.h" +#include "render.h" + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +/* This structure remembers the information we need about a primitive + * to be able to render it later, once we have determined which + * primitive is able to use the most triangles. + */ +struct FaceCount { + long size; /* number of triangles used */ + GLUhalfEdge *eStart; /* edge where this primitive starts */ + void (*render)(GLUtesselator *, GLUhalfEdge *, long); + /* routine to render this primitive */ +}; + +static struct FaceCount MaximumFan( GLUhalfEdge *eOrig ); +static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig ); + +static void RenderFan( GLUtesselator *tess, GLUhalfEdge *eStart, long size ); +static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *eStart, long size ); +static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *eStart, + long size ); + +static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig ); +static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *head ); + + + +/************************ Strips and Fans decomposition ******************/ + +/* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle + * fans, strips, and separate triangles. A substantial effort is made + * to use as few rendering primitives as possible (ie. to make the fans + * and strips as large as possible). + * + * The rendering output is provided as callbacks (see the api). + */ +void __gl_renderMesh( GLUtesselator *tess, GLUmesh *mesh ) +{ + GLUface *f; + + /* Make a list of separate triangles so we can render them all at once */ + tess->lonelyTriList = NULL; + + for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) { + f->marked = FALSE; + } + for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) { + + /* We examine all faces in an arbitrary order. Whenever we find + * an unprocessed face F, we output a group of faces including F + * whose size is maximum. + */ + if( f->inside && ! f->marked ) { + RenderMaximumFaceGroup( tess, f ); + assert( f->marked ); + } + } + if( tess->lonelyTriList != NULL ) { + RenderLonelyTriangles( tess, tess->lonelyTriList ); + tess->lonelyTriList = NULL; + } +} + + +static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig ) +{ + /* We want to find the largest triangle fan or strip of unmarked faces + * which includes the given face fOrig. There are 3 possible fans + * passing through fOrig (one centered at each vertex), and 3 possible + * strips (one for each CCW permutation of the vertices). Our strategy + * is to try all of these, and take the primitive which uses the most + * triangles (a greedy approach). + */ + GLUhalfEdge *e = fOrig->anEdge; + struct FaceCount max, newFace; + + max.size = 1; + max.eStart = e; + max.render = &RenderTriangle; + + if( ! tess->flagBoundary ) { + newFace = MaximumFan( e ); if( newFace.size > max.size ) { max = newFace; } + newFace = MaximumFan( e->Lnext ); if( newFace.size > max.size ) { max = newFace; } + newFace = MaximumFan( e->Lprev ); if( newFace.size > max.size ) { max = newFace; } + + newFace = MaximumStrip( e ); if( newFace.size > max.size ) { max = newFace; } + newFace = MaximumStrip( e->Lnext ); if( newFace.size > max.size ) { max = newFace; } + newFace = MaximumStrip( e->Lprev ); if( newFace.size > max.size ) { max = newFace; } + } + (*(max.render))( tess, max.eStart, max.size ); +} + + +/* Macros which keep track of faces we have marked temporarily, and allow + * us to backtrack when necessary. With triangle fans, this is not + * really necessary, since the only awkward case is a loop of triangles + * around a single origin vertex. However with strips the situation is + * more complicated, and we need a general tracking method like the + * one here. + */ +#define Marked(f) (! (f)->inside || (f)->marked) + +#define AddToTrail(f,t) ((f)->trail = (t), (t) = (f), (f)->marked = TRUE) + +#define FreeTrail(t) do { \ + while( (t) != NULL ) { \ + (t)->marked = FALSE; t = (t)->trail; \ + } \ + } while(0) /* absorb trailing semicolon */ + + + +static struct FaceCount MaximumFan( GLUhalfEdge *eOrig ) +{ + /* eOrig->Lface is the face we want to render. We want to find the size + * of a maximal fan around eOrig->Org. To do this we just walk around + * the origin vertex as far as possible in both directions. + */ + struct FaceCount newFace = { 0, NULL, &RenderFan }; + GLUface *trail = NULL; + GLUhalfEdge *e; + + for( e = eOrig; ! Marked( e->Lface ); e = e->Onext ) { + AddToTrail( e->Lface, trail ); + ++newFace.size; + } + for( e = eOrig; ! Marked( e->Rface ); e = e->Oprev ) { + AddToTrail( e->Rface, trail ); + ++newFace.size; + } + newFace.eStart = e; + /*LINTED*/ + FreeTrail( trail ); + return newFace; +} + + +#define IsEven(n) (((n) & 1) == 0) + +static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig ) +{ + /* Here we are looking for a maximal strip that contains the vertices + * eOrig->Org, eOrig->Dst, eOrig->Lnext->Dst (in that order or the + * reverse, such that all triangles are oriented CCW). + * + * Again we walk forward and backward as far as possible. However for + * strips there is a twist: to get CCW orientations, there must be + * an *even* number of triangles in the strip on one side of eOrig. + * We walk the strip starting on a side with an even number of triangles; + * if both side have an odd number, we are forced to shorten one side. + */ + struct FaceCount newFace = { 0, NULL, &RenderStrip }; + long headSize = 0, tailSize = 0; + GLUface *trail = NULL; + GLUhalfEdge *e, *eTail, *eHead; + + for( e = eOrig; ! Marked( e->Lface ); ++tailSize, e = e->Onext ) { + AddToTrail( e->Lface, trail ); + ++tailSize; + e = e->Dprev; + if( Marked( e->Lface )) break; + AddToTrail( e->Lface, trail ); + } + eTail = e; + + for( e = eOrig; ! Marked( e->Rface ); ++headSize, e = e->Dnext ) { + AddToTrail( e->Rface, trail ); + ++headSize; + e = e->Oprev; + if( Marked( e->Rface )) break; + AddToTrail( e->Rface, trail ); + } + eHead = e; + + newFace.size = tailSize + headSize; + if( IsEven( tailSize )) { + newFace.eStart = eTail->Sym; + } else if( IsEven( headSize )) { + newFace.eStart = eHead; + } else { + /* Both sides have odd length, we must shorten one of them. In fact, + * we must start from eHead to guarantee inclusion of eOrig->Lface. + */ + --newFace.size; + newFace.eStart = eHead->Onext; + } + /*LINTED*/ + FreeTrail( trail ); + return newFace; +} + + +static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *e, long size ) +{ + /* Just add the triangle to a triangle list, so we can render all + * the separate triangles at once. + */ + assert( size == 1 ); + AddToTrail( e->Lface, tess->lonelyTriList ); +} + + +static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *f ) +{ + /* Now we render all the separate triangles which could not be + * grouped into a triangle fan or strip. + */ + GLUhalfEdge *e; + int newState; + int edgeState = -1; /* force edge state output for first vertex */ + + CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLES ); + + for( ; f != NULL; f = f->trail ) { + /* Loop once for each edge (there will always be 3 edges) */ + + e = f->anEdge; + do { + if( tess->flagBoundary ) { + /* Set the "edge state" to TRUE just before we output the + * first vertex of each edge on the polygon boundary. + */ + newState = ! e->Rface->inside; + if( edgeState != newState ) { + edgeState = newState; + CALL_EDGE_FLAG_OR_EDGE_FLAG_DATA( edgeState ); + } + } + CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); + + e = e->Lnext; + } while( e != f->anEdge ); + } + CALL_END_OR_END_DATA(); +} + + +static void RenderFan( GLUtesselator *tess, GLUhalfEdge *e, long size ) +{ + /* Render as many CCW triangles as possible in a fan starting from + * edge "e". The fan *should* contain exactly "size" triangles + * (otherwise we've goofed up somewhere). + */ + CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_FAN ); + CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); + CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); + + while( ! Marked( e->Lface )) { + e->Lface->marked = TRUE; + --size; + e = e->Onext; + CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); + } + + assert( size == 0 ); + CALL_END_OR_END_DATA(); +} + + +static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *e, long size ) +{ + /* Render as many CCW triangles as possible in a strip starting from + * edge "e". The strip *should* contain exactly "size" triangles + * (otherwise we've goofed up somewhere). + */ + CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_STRIP ); + CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); + CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); + + while( ! Marked( e->Lface )) { + e->Lface->marked = TRUE; + --size; + e = e->Dprev; + CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); + if( Marked( e->Lface )) break; + + e->Lface->marked = TRUE; + --size; + e = e->Onext; + CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); + } + + assert( size == 0 ); + CALL_END_OR_END_DATA(); +} + + +/************************ Boundary contour decomposition ******************/ + +/* __gl_renderBoundary( tess, mesh ) takes a mesh, and outputs one + * contour for each face marked "inside". The rendering output is + * provided as callbacks (see the api). + */ +void __gl_renderBoundary( GLUtesselator *tess, GLUmesh *mesh ) +{ + GLUface *f; + GLUhalfEdge *e; + + for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) { + if( f->inside ) { + CALL_BEGIN_OR_BEGIN_DATA( GL_LINE_LOOP ); + e = f->anEdge; + do { + CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); + e = e->Lnext; + } while( e != f->anEdge ); + CALL_END_OR_END_DATA(); + } + } +} + + +/************************ Quick-and-dirty decomposition ******************/ + +#define SIGN_INCONSISTENT 2 + +static int ComputeNormal( GLUtesselator *tess, GLdouble norm[3], int check ) +/* + * If check==FALSE, we compute the polygon normal and place it in norm[]. + * If check==TRUE, we check that each triangle in the fan from v0 has a + * consistent orientation with respect to norm[]. If triangles are + * consistently oriented CCW, return 1; if CW, return -1; if all triangles + * are degenerate return 0; otherwise (no consistent orientation) return + * SIGN_INCONSISTENT. + */ +{ + CachedVertex *v0 = tess->cache; + CachedVertex *vn = v0 + tess->cacheCount; + CachedVertex *vc; + GLdouble dot, xc, yc, zc, xp, yp, zp, n[3]; + int sign = 0; + + /* Find the polygon normal. It is important to get a reasonable + * normal even when the polygon is self-intersecting (eg. a bowtie). + * Otherwise, the computed normal could be very tiny, but perpendicular + * to the true plane of the polygon due to numerical noise. Then all + * the triangles would appear to be degenerate and we would incorrectly + * decompose the polygon as a fan (or simply not render it at all). + * + * We use a sum-of-triangles normal algorithm rather than the more + * efficient sum-of-trapezoids method (used in CheckOrientation() + * in normal.c). This lets us explicitly reverse the signed area + * of some triangles to get a reasonable normal in the self-intersecting + * case. + */ + if( ! check ) { + norm[0] = norm[1] = norm[2] = 0.0; + } + + vc = v0 + 1; + xc = vc->coords[0] - v0->coords[0]; + yc = vc->coords[1] - v0->coords[1]; + zc = vc->coords[2] - v0->coords[2]; + while( ++vc < vn ) { + xp = xc; yp = yc; zp = zc; + xc = vc->coords[0] - v0->coords[0]; + yc = vc->coords[1] - v0->coords[1]; + zc = vc->coords[2] - v0->coords[2]; + + /* Compute (vp - v0) cross (vc - v0) */ + n[0] = yp*zc - zp*yc; + n[1] = zp*xc - xp*zc; + n[2] = xp*yc - yp*xc; + + dot = n[0]*norm[0] + n[1]*norm[1] + n[2]*norm[2]; + if( ! check ) { + /* Reverse the contribution of back-facing triangles to get + * a reasonable normal for self-intersecting polygons (see above) + */ + if( dot >= 0 ) { + norm[0] += n[0]; norm[1] += n[1]; norm[2] += n[2]; + } else { + norm[0] -= n[0]; norm[1] -= n[1]; norm[2] -= n[2]; + } + } else if( dot != 0 ) { + /* Check the new orientation for consistency with previous triangles */ + if( dot > 0 ) { + if( sign < 0 ) return SIGN_INCONSISTENT; + sign = 1; + } else { + if( sign > 0 ) return SIGN_INCONSISTENT; + sign = -1; + } + } + } + return sign; +} + +/* __gl_renderCache( tess ) takes a single contour and tries to render it + * as a triangle fan. This handles convex polygons, as well as some + * non-convex polygons if we get lucky. + * + * Returns TRUE if the polygon was successfully rendered. The rendering + * output is provided as callbacks (see the api). + */ +GLboolean __gl_renderCache( GLUtesselator *tess ) +{ + CachedVertex *v0 = tess->cache; + CachedVertex *vn = v0 + tess->cacheCount; + CachedVertex *vc; + GLdouble norm[3]; + int sign; + + if( tess->cacheCount < 3 ) { + /* Degenerate contour -- no output */ + return TRUE; + } + + norm[0] = tess->normal[0]; + norm[1] = tess->normal[1]; + norm[2] = tess->normal[2]; + if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) { + ComputeNormal( tess, norm, FALSE ); + } + + sign = ComputeNormal( tess, norm, TRUE ); + if( sign == SIGN_INCONSISTENT ) { + /* Fan triangles did not have a consistent orientation */ + return FALSE; + } + if( sign == 0 ) { + /* All triangles were degenerate */ + return TRUE; + } + + /* Make sure we do the right thing for each winding rule */ + switch( tess->windingRule ) { + case GLU_TESS_WINDING_ODD: + case GLU_TESS_WINDING_NONZERO: + break; + case GLU_TESS_WINDING_POSITIVE: + if( sign < 0 ) return TRUE; + break; + case GLU_TESS_WINDING_NEGATIVE: + if( sign > 0 ) return TRUE; + break; + case GLU_TESS_WINDING_ABS_GEQ_TWO: + return TRUE; + } + + CALL_BEGIN_OR_BEGIN_DATA( tess->boundaryOnly ? GL_LINE_LOOP + : (tess->cacheCount > 3) ? GL_TRIANGLE_FAN + : GL_TRIANGLES ); + + CALL_VERTEX_OR_VERTEX_DATA( v0->data ); + if( sign > 0 ) { + for( vc = v0+1; vc < vn; ++vc ) { + CALL_VERTEX_OR_VERTEX_DATA( vc->data ); + } + } else { + for( vc = vn-1; vc > v0; --vc ) { + CALL_VERTEX_OR_VERTEX_DATA( vc->data ); + } + } + CALL_END_OR_END_DATA(); + return TRUE; +} diff --git a/vtm/jni/tessellate/render.h b/vtm/jni/tessellate/render.h new file mode 100644 index 00000000..a298c9a9 --- /dev/null +++ b/vtm/jni/tessellate/render.h @@ -0,0 +1,52 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __render_h_ +#define __render_h_ + +#include "mesh.h" + +/* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle + * fans, strips, and separate triangles. A substantial effort is made + * to use as few rendering primitives as possible (ie. to make the fans + * and strips as large as possible). + * + * The rendering output is provided as callbacks (see the api). + */ +void __gl_renderMesh( GLUtesselator *tess, GLUmesh *mesh ); +void __gl_renderBoundary( GLUtesselator *tess, GLUmesh *mesh ); + +GLboolean __gl_renderCache( GLUtesselator *tess ); + +#endif diff --git a/vtm/jni/tessellate/sweep.c b/vtm/jni/tessellate/sweep.c new file mode 100644 index 00000000..eca828ff --- /dev/null +++ b/vtm/jni/tessellate/sweep.c @@ -0,0 +1,1361 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include "gluos.h" +#include +#include +#include /* longjmp */ +#include /* LONG_MAX */ + +#include "mesh.h" +#include "geom.h" +#include "tess.h" +#include "dict.h" +#include "priorityq.h" +#include "memalloc.h" +#include "sweep.h" + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#ifdef FOR_TRITE_TEST_PROGRAM +extern void DebugEvent( GLUtesselator *tess ); +#else +#define DebugEvent( tess ) +#endif + +/* + * Invariants for the Edge Dictionary. + * - each pair of adjacent edges e2=Succ(e1) satisfies EdgeLeq(e1,e2) + * at any valid location of the sweep event + * - if EdgeLeq(e2,e1) as well (at any valid sweep event), then e1 and e2 + * share a common endpoint + * - for each e, e->Dst has been processed, but not e->Org + * - each edge e satisfies VertLeq(e->Dst,event) && VertLeq(event,e->Org) + * where "event" is the current sweep line event. + * - no edge e has zero length + * + * Invariants for the Mesh (the processed portion). + * - the portion of the mesh left of the sweep line is a planar graph, + * ie. there is *some* way to embed it in the plane + * - no processed edge has zero length + * - no two processed vertices have identical coordinates + * - each "inside" region is monotone, ie. can be broken into two chains + * of monotonically increasing vertices according to VertLeq(v1,v2) + * - a non-invariant: these chains may intersect (very slightly) + * + * Invariants for the Sweep. + * - if none of the edges incident to the event vertex have an activeRegion + * (ie. none of these edges are in the edge dictionary), then the vertex + * has only right-going edges. + * - if an edge is marked "fixUpperEdge" (it is a temporary edge introduced + * by ConnectRightVertex), then it is the only right-going edge from + * its associated vertex. (This says that these edges exist only + * when it is necessary.) + */ + +#undef MAX +#undef MIN +#define MAX(x,y) ((x) >= (y) ? (x) : (y)) +#define MIN(x,y) ((x) <= (y) ? (x) : (y)) + +/* When we merge two edges into one, we need to compute the combined + * winding of the new edge. + */ +#define AddWinding(eDst,eSrc) (eDst->winding += eSrc->winding, \ + eDst->Sym->winding += eSrc->Sym->winding) + +static void SweepEvent( GLUtesselator *tess, GLUvertex *vEvent ); +static void WalkDirtyRegions( GLUtesselator *tess, ActiveRegion *regUp ); +static int CheckForRightSplice( GLUtesselator *tess, ActiveRegion *regUp ); + +static int EdgeLeq( GLUtesselator *tess, ActiveRegion *reg1, + ActiveRegion *reg2 ) +/* + * Both edges must be directed from right to left (this is the canonical + * direction for the upper edge of each region). + * + * The strategy is to evaluate a "t" value for each edge at the + * current sweep line position, given by tess->event. The calculations + * are designed to be very stable, but of course they are not perfect. + * + * Special case: if both edge destinations are at the sweep event, + * we sort the edges by slope (they would otherwise compare equally). + */ +{ + GLUvertex *event = tess->event; + GLUhalfEdge *e1, *e2; + GLdouble t1, t2; + + e1 = reg1->eUp; + e2 = reg2->eUp; + + if( e1->Dst == event ) { + if( e2->Dst == event ) { + /* Two edges right of the sweep line which meet at the sweep event. + * Sort them by slope. + */ + if( VertLeq( e1->Org, e2->Org )) { + return EdgeSign( e2->Dst, e1->Org, e2->Org ) <= 0; + } + return EdgeSign( e1->Dst, e2->Org, e1->Org ) >= 0; + } + return EdgeSign( e2->Dst, event, e2->Org ) <= 0; + } + if( e2->Dst == event ) { + return EdgeSign( e1->Dst, event, e1->Org ) >= 0; + } + + /* General case - compute signed distance *from* e1, e2 to event */ + t1 = EdgeEval( e1->Dst, event, e1->Org ); + t2 = EdgeEval( e2->Dst, event, e2->Org ); + return (t1 >= t2); +} + + +static void DeleteRegion( GLUtesselator *tess, ActiveRegion *reg ) +{ + if( reg->fixUpperEdge ) { + /* It was created with zero winding number, so it better be + * deleted with zero winding number (ie. it better not get merged + * with a real edge). + */ + assert( reg->eUp->winding == 0 ); + } + reg->eUp->activeRegion = NULL; + dictDelete( tess->dict, reg->nodeUp ); /* __gl_dictListDelete */ + memFree( reg ); +} + + +static int FixUpperEdge( ActiveRegion *reg, GLUhalfEdge *newEdge ) +/* + * Replace an upper edge which needs fixing (see ConnectRightVertex). + */ +{ + assert( reg->fixUpperEdge ); + if ( !__gl_meshDelete( reg->eUp ) ) return 0; + reg->fixUpperEdge = FALSE; + reg->eUp = newEdge; + newEdge->activeRegion = reg; + + return 1; +} + +static ActiveRegion *TopLeftRegion( ActiveRegion *reg ) +{ + GLUvertex *org = reg->eUp->Org; + GLUhalfEdge *e; + + /* Find the region above the uppermost edge with the same origin */ + do { + reg = RegionAbove( reg ); + } while( reg->eUp->Org == org ); + + /* If the edge above was a temporary edge introduced by ConnectRightVertex, + * now is the time to fix it. + */ + if( reg->fixUpperEdge ) { + e = __gl_meshConnect( RegionBelow(reg)->eUp->Sym, reg->eUp->Lnext ); + if (e == NULL) return NULL; + if ( !FixUpperEdge( reg, e ) ) return NULL; + reg = RegionAbove( reg ); + } + return reg; +} + +static ActiveRegion *TopRightRegion( ActiveRegion *reg ) +{ + GLUvertex *dst = reg->eUp->Dst; + + /* Find the region above the uppermost edge with the same destination */ + do { + reg = RegionAbove( reg ); + } while( reg->eUp->Dst == dst ); + return reg; +} + +static ActiveRegion *AddRegionBelow( GLUtesselator *tess, + ActiveRegion *regAbove, + GLUhalfEdge *eNewUp ) +/* + * Add a new active region to the sweep line, *somewhere* below "regAbove" + * (according to where the new edge belongs in the sweep-line dictionary). + * The upper edge of the new region will be "eNewUp". + * Winding number and "inside" flag are not updated. + */ +{ + ActiveRegion *regNew = (ActiveRegion *)memAlloc( sizeof( ActiveRegion )); + if (regNew == NULL) longjmp(tess->env,1); + + regNew->eUp = eNewUp; + /* __gl_dictListInsertBefore */ + regNew->nodeUp = dictInsertBefore( tess->dict, regAbove->nodeUp, regNew ); + if (regNew->nodeUp == NULL) longjmp(tess->env,1); + regNew->fixUpperEdge = FALSE; + regNew->sentinel = FALSE; + regNew->dirty = FALSE; + + eNewUp->activeRegion = regNew; + return regNew; +} + +static GLboolean IsWindingInside( GLUtesselator *tess, int n ) +{ + switch( tess->windingRule ) { + case GLU_TESS_WINDING_ODD: + return (n & 1); + case GLU_TESS_WINDING_NONZERO: + return (n != 0); + case GLU_TESS_WINDING_POSITIVE: + return (n > 0); + case GLU_TESS_WINDING_NEGATIVE: + return (n < 0); + case GLU_TESS_WINDING_ABS_GEQ_TWO: + return (n >= 2) || (n <= -2); + } + /*LINTED*/ + assert( FALSE ); + /*NOTREACHED*/ + return GL_FALSE; /* avoid compiler complaints */ +} + + +static void ComputeWinding( GLUtesselator *tess, ActiveRegion *reg ) +{ + reg->windingNumber = RegionAbove(reg)->windingNumber + reg->eUp->winding; + reg->inside = IsWindingInside( tess, reg->windingNumber ); +} + + +static void FinishRegion( GLUtesselator *tess, ActiveRegion *reg ) +/* + * Delete a region from the sweep line. This happens when the upper + * and lower chains of a region meet (at a vertex on the sweep line). + * The "inside" flag is copied to the appropriate mesh face (we could + * not do this before -- since the structure of the mesh is always + * changing, this face may not have even existed until now). + */ +{ + GLUhalfEdge *e = reg->eUp; + GLUface *f = e->Lface; + + f->inside = reg->inside; + f->anEdge = e; /* optimization for __gl_meshTessellateMonoRegion() */ + DeleteRegion( tess, reg ); +} + + +static GLUhalfEdge *FinishLeftRegions( GLUtesselator *tess, + ActiveRegion *regFirst, ActiveRegion *regLast ) +/* + * We are given a vertex with one or more left-going edges. All affected + * edges should be in the edge dictionary. Starting at regFirst->eUp, + * we walk down deleting all regions where both edges have the same + * origin vOrg. At the same time we copy the "inside" flag from the + * active region to the face, since at this point each face will belong + * to at most one region (this was not necessarily true until this point + * in the sweep). The walk stops at the region above regLast; if regLast + * is NULL we walk as far as possible. At the same time we relink the + * mesh if necessary, so that the ordering of edges around vOrg is the + * same as in the dictionary. + */ +{ + ActiveRegion *reg, *regPrev; + GLUhalfEdge *e, *ePrev; + + regPrev = regFirst; + ePrev = regFirst->eUp; + while( regPrev != regLast ) { + regPrev->fixUpperEdge = FALSE; /* placement was OK */ + reg = RegionBelow( regPrev ); + e = reg->eUp; + if( e->Org != ePrev->Org ) { + if( ! reg->fixUpperEdge ) { + /* Remove the last left-going edge. Even though there are no further + * edges in the dictionary with this origin, there may be further + * such edges in the mesh (if we are adding left edges to a vertex + * that has already been processed). Thus it is important to call + * FinishRegion rather than just DeleteRegion. + */ + FinishRegion( tess, regPrev ); + break; + } + /* If the edge below was a temporary edge introduced by + * ConnectRightVertex, now is the time to fix it. + */ + e = __gl_meshConnect( ePrev->Lprev, e->Sym ); + if (e == NULL) longjmp(tess->env,1); + if ( !FixUpperEdge( reg, e ) ) longjmp(tess->env,1); + } + + /* Relink edges so that ePrev->Onext == e */ + if( ePrev->Onext != e ) { + if ( !__gl_meshSplice( e->Oprev, e ) ) longjmp(tess->env,1); + if ( !__gl_meshSplice( ePrev, e ) ) longjmp(tess->env,1); + } + FinishRegion( tess, regPrev ); /* may change reg->eUp */ + ePrev = reg->eUp; + regPrev = reg; + } + return ePrev; +} + + +static void AddRightEdges( GLUtesselator *tess, ActiveRegion *regUp, + GLUhalfEdge *eFirst, GLUhalfEdge *eLast, GLUhalfEdge *eTopLeft, + GLboolean cleanUp ) +/* + * Purpose: insert right-going edges into the edge dictionary, and update + * winding numbers and mesh connectivity appropriately. All right-going + * edges share a common origin vOrg. Edges are inserted CCW starting at + * eFirst; the last edge inserted is eLast->Oprev. If vOrg has any + * left-going edges already processed, then eTopLeft must be the edge + * such that an imaginary upward vertical segment from vOrg would be + * contained between eTopLeft->Oprev and eTopLeft; otherwise eTopLeft + * should be NULL. + */ +{ + ActiveRegion *reg, *regPrev; + GLUhalfEdge *e, *ePrev; + int firstTime = TRUE; + + /* Insert the new right-going edges in the dictionary */ + e = eFirst; + do { + assert( VertLeq( e->Org, e->Dst )); + AddRegionBelow( tess, regUp, e->Sym ); + e = e->Onext; + } while ( e != eLast ); + + /* Walk *all* right-going edges from e->Org, in the dictionary order, + * updating the winding numbers of each region, and re-linking the mesh + * edges to match the dictionary ordering (if necessary). + */ + if( eTopLeft == NULL ) { + eTopLeft = RegionBelow( regUp )->eUp->Rprev; + } + regPrev = regUp; + ePrev = eTopLeft; + for( ;; ) { + reg = RegionBelow( regPrev ); + e = reg->eUp->Sym; + if( e->Org != ePrev->Org ) break; + + if( e->Onext != ePrev ) { + /* Unlink e from its current position, and relink below ePrev */ + if ( !__gl_meshSplice( e->Oprev, e ) ) longjmp(tess->env,1); + if ( !__gl_meshSplice( ePrev->Oprev, e ) ) longjmp(tess->env,1); + } + /* Compute the winding number and "inside" flag for the new regions */ + reg->windingNumber = regPrev->windingNumber - e->winding; + reg->inside = IsWindingInside( tess, reg->windingNumber ); + + /* Check for two outgoing edges with same slope -- process these + * before any intersection tests (see example in __gl_computeInterior). + */ + regPrev->dirty = TRUE; + if( ! firstTime && CheckForRightSplice( tess, regPrev )) { + AddWinding( e, ePrev ); + DeleteRegion( tess, regPrev ); + if ( !__gl_meshDelete( ePrev ) ) longjmp(tess->env,1); + } + firstTime = FALSE; + regPrev = reg; + ePrev = e; + } + regPrev->dirty = TRUE; + assert( regPrev->windingNumber - e->winding == reg->windingNumber ); + + if( cleanUp ) { + /* Check for intersections between newly adjacent edges. */ + WalkDirtyRegions( tess, regPrev ); + } +} + + +static void CallCombine( GLUtesselator *tess, GLUvertex *isect, + void *data[4], GLfloat weights[4], int needed ) +{ + GLdouble coords[3]; + + /* Copy coord data in case the callback changes it. */ + coords[0] = isect->coords[0]; + coords[1] = isect->coords[1]; + coords[2] = isect->coords[2]; + + isect->data = NULL; + CALL_COMBINE_OR_COMBINE_DATA( coords, data, weights, &isect->data ); + if( isect->data == NULL ) { + if( ! needed ) { + isect->data = data[0]; + } else if( ! tess->fatalError ) { + /* The only way fatal error is when two edges are found to intersect, + * but the user has not provided the callback necessary to handle + * generated intersection points. + */ + CALL_ERROR_OR_ERROR_DATA( GLU_TESS_NEED_COMBINE_CALLBACK ); + tess->fatalError = TRUE; + } + } +} + +static void SpliceMergeVertices( GLUtesselator *tess, GLUhalfEdge *e1, + GLUhalfEdge *e2 ) +/* + * Two vertices with idential coordinates are combined into one. + * e1->Org is kept, while e2->Org is discarded. + */ +{ + void *data[4] = { NULL, NULL, NULL, NULL }; + GLfloat weights[4] = { 0.5, 0.5, 0.0, 0.0 }; + + data[0] = e1->Org->data; + data[1] = e2->Org->data; + CallCombine( tess, e1->Org, data, weights, FALSE ); + if ( !__gl_meshSplice( e1, e2 ) ) longjmp(tess->env,1); +} + +static void VertexWeights( GLUvertex *isect, GLUvertex *org, GLUvertex *dst, + GLfloat *weights ) +/* + * Find some weights which describe how the intersection vertex is + * a linear combination of "org" and "dest". Each of the two edges + * which generated "isect" is allocated 50% of the weight; each edge + * splits the weight between its org and dst according to the + * relative distance to "isect". + */ +{ + GLdouble t1 = VertL1dist( org, isect ); + GLdouble t2 = VertL1dist( dst, isect ); + + weights[0] = 0.5 * t2 / (t1 + t2); + weights[1] = 0.5 * t1 / (t1 + t2); + isect->coords[0] += weights[0]*org->coords[0] + weights[1]*dst->coords[0]; + isect->coords[1] += weights[0]*org->coords[1] + weights[1]*dst->coords[1]; + isect->coords[2] += weights[0]*org->coords[2] + weights[1]*dst->coords[2]; +} + + +static void GetIntersectData( GLUtesselator *tess, GLUvertex *isect, + GLUvertex *orgUp, GLUvertex *dstUp, + GLUvertex *orgLo, GLUvertex *dstLo ) +/* + * We've computed a new intersection point, now we need a "data" pointer + * from the user so that we can refer to this new vertex in the + * rendering callbacks. + */ +{ + void *data[4]; + GLfloat weights[4]; + + data[0] = orgUp->data; + data[1] = dstUp->data; + data[2] = orgLo->data; + data[3] = dstLo->data; + + isect->coords[0] = isect->coords[1] = isect->coords[2] = 0; + VertexWeights( isect, orgUp, dstUp, &weights[0] ); + VertexWeights( isect, orgLo, dstLo, &weights[2] ); + + CallCombine( tess, isect, data, weights, TRUE ); +} + +static int CheckForRightSplice( GLUtesselator *tess, ActiveRegion *regUp ) +/* + * Check the upper and lower edge of "regUp", to make sure that the + * eUp->Org is above eLo, or eLo->Org is below eUp (depending on which + * origin is leftmost). + * + * The main purpose is to splice right-going edges with the same + * dest vertex and nearly identical slopes (ie. we can't distinguish + * the slopes numerically). However the splicing can also help us + * to recover from numerical errors. For example, suppose at one + * point we checked eUp and eLo, and decided that eUp->Org is barely + * above eLo. Then later, we split eLo into two edges (eg. from + * a splice operation like this one). This can change the result of + * our test so that now eUp->Org is incident to eLo, or barely below it. + * We must correct this condition to maintain the dictionary invariants. + * + * One possibility is to check these edges for intersection again + * (ie. CheckForIntersect). This is what we do if possible. However + * CheckForIntersect requires that tess->event lies between eUp and eLo, + * so that it has something to fall back on when the intersection + * calculation gives us an unusable answer. So, for those cases where + * we can't check for intersection, this routine fixes the problem + * by just splicing the offending vertex into the other edge. + * This is a guaranteed solution, no matter how degenerate things get. + * Basically this is a combinatorial solution to a numerical problem. + */ +{ + ActiveRegion *regLo = RegionBelow(regUp); + GLUhalfEdge *eUp = regUp->eUp; + GLUhalfEdge *eLo = regLo->eUp; + + if( VertLeq( eUp->Org, eLo->Org )) { + if( EdgeSign( eLo->Dst, eUp->Org, eLo->Org ) > 0 ) return FALSE; + + /* eUp->Org appears to be below eLo */ + if( ! VertEq( eUp->Org, eLo->Org )) { + /* Splice eUp->Org into eLo */ + if ( __gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eUp, eLo->Oprev ) ) longjmp(tess->env,1); + regUp->dirty = regLo->dirty = TRUE; + + } else if( eUp->Org != eLo->Org ) { + /* merge the two vertices, discarding eUp->Org */ + pqDelete( tess->pq, eUp->Org->pqHandle ); /* __gl_pqSortDelete */ + SpliceMergeVertices( tess, eLo->Oprev, eUp ); + } + } else { + if( EdgeSign( eUp->Dst, eLo->Org, eUp->Org ) < 0 ) return FALSE; + + /* eLo->Org appears to be above eUp, so splice eLo->Org into eUp */ + RegionAbove(regUp)->dirty = regUp->dirty = TRUE; + if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eLo->Oprev, eUp ) ) longjmp(tess->env,1); + } + return TRUE; +} + +static int CheckForLeftSplice( GLUtesselator *tess, ActiveRegion *regUp ) +/* + * Check the upper and lower edge of "regUp", to make sure that the + * eUp->Dst is above eLo, or eLo->Dst is below eUp (depending on which + * destination is rightmost). + * + * Theoretically, this should always be true. However, splitting an edge + * into two pieces can change the results of previous tests. For example, + * suppose at one point we checked eUp and eLo, and decided that eUp->Dst + * is barely above eLo. Then later, we split eLo into two edges (eg. from + * a splice operation like this one). This can change the result of + * the test so that now eUp->Dst is incident to eLo, or barely below it. + * We must correct this condition to maintain the dictionary invariants + * (otherwise new edges might get inserted in the wrong place in the + * dictionary, and bad stuff will happen). + * + * We fix the problem by just splicing the offending vertex into the + * other edge. + */ +{ + ActiveRegion *regLo = RegionBelow(regUp); + GLUhalfEdge *eUp = regUp->eUp; + GLUhalfEdge *eLo = regLo->eUp; + GLUhalfEdge *e; + + assert( ! VertEq( eUp->Dst, eLo->Dst )); + + if( VertLeq( eUp->Dst, eLo->Dst )) { + if( EdgeSign( eUp->Dst, eLo->Dst, eUp->Org ) < 0 ) return FALSE; + + /* eLo->Dst is above eUp, so splice eLo->Dst into eUp */ + RegionAbove(regUp)->dirty = regUp->dirty = TRUE; + e = __gl_meshSplitEdge( eUp ); + if (e == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eLo->Sym, e ) ) longjmp(tess->env,1); + e->Lface->inside = regUp->inside; + } else { + if( EdgeSign( eLo->Dst, eUp->Dst, eLo->Org ) > 0 ) return FALSE; + + /* eUp->Dst is below eLo, so splice eUp->Dst into eLo */ + regUp->dirty = regLo->dirty = TRUE; + e = __gl_meshSplitEdge( eLo ); + if (e == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eUp->Lnext, eLo->Sym ) ) longjmp(tess->env,1); + e->Rface->inside = regUp->inside; + } + return TRUE; +} + + +static int CheckForIntersect( GLUtesselator *tess, ActiveRegion *regUp ) +/* + * Check the upper and lower edges of the given region to see if + * they intersect. If so, create the intersection and add it + * to the data structures. + * + * Returns TRUE if adding the new intersection resulted in a recursive + * call to AddRightEdges(); in this case all "dirty" regions have been + * checked for intersections, and possibly regUp has been deleted. + */ +{ + ActiveRegion *regLo = RegionBelow(regUp); + GLUhalfEdge *eUp = regUp->eUp; + GLUhalfEdge *eLo = regLo->eUp; + GLUvertex *orgUp = eUp->Org; + GLUvertex *orgLo = eLo->Org; + GLUvertex *dstUp = eUp->Dst; + GLUvertex *dstLo = eLo->Dst; + GLdouble tMinUp, tMaxLo; + GLUvertex isect, *orgMin; + GLUhalfEdge *e; + + assert( ! VertEq( dstLo, dstUp )); + assert( EdgeSign( dstUp, tess->event, orgUp ) <= 0 ); + assert( EdgeSign( dstLo, tess->event, orgLo ) >= 0 ); + assert( orgUp != tess->event && orgLo != tess->event ); + assert( ! regUp->fixUpperEdge && ! regLo->fixUpperEdge ); + + if( orgUp == orgLo ) return FALSE; /* right endpoints are the same */ + + tMinUp = MIN( orgUp->t, dstUp->t ); + tMaxLo = MAX( orgLo->t, dstLo->t ); + if( tMinUp > tMaxLo ) return FALSE; /* t ranges do not overlap */ + + if( VertLeq( orgUp, orgLo )) { + if( EdgeSign( dstLo, orgUp, orgLo ) > 0 ) return FALSE; + } else { + if( EdgeSign( dstUp, orgLo, orgUp ) < 0 ) return FALSE; + } + + /* At this point the edges intersect, at least marginally */ + DebugEvent( tess ); + + __gl_edgeIntersect( dstUp, orgUp, dstLo, orgLo, &isect ); + /* The following properties are guaranteed: */ + assert( MIN( orgUp->t, dstUp->t ) <= isect.t ); + assert( isect.t <= MAX( orgLo->t, dstLo->t )); + assert( MIN( dstLo->s, dstUp->s ) <= isect.s ); + assert( isect.s <= MAX( orgLo->s, orgUp->s )); + + if( VertLeq( &isect, tess->event )) { + /* The intersection point lies slightly to the left of the sweep line, + * so move it until it''s slightly to the right of the sweep line. + * (If we had perfect numerical precision, this would never happen + * in the first place). The easiest and safest thing to do is + * replace the intersection by tess->event. + */ + isect.s = tess->event->s; + isect.t = tess->event->t; + } + /* Similarly, if the computed intersection lies to the right of the + * rightmost origin (which should rarely happen), it can cause + * unbelievable inefficiency on sufficiently degenerate inputs. + * (If you have the test program, try running test54.d with the + * "X zoom" option turned on). + */ + orgMin = VertLeq( orgUp, orgLo ) ? orgUp : orgLo; + if( VertLeq( orgMin, &isect )) { + isect.s = orgMin->s; + isect.t = orgMin->t; + } + + if( VertEq( &isect, orgUp ) || VertEq( &isect, orgLo )) { + /* Easy case -- intersection at one of the right endpoints */ + (void) CheckForRightSplice( tess, regUp ); + return FALSE; + } + + if( (! VertEq( dstUp, tess->event ) + && EdgeSign( dstUp, tess->event, &isect ) >= 0) + || (! VertEq( dstLo, tess->event ) + && EdgeSign( dstLo, tess->event, &isect ) <= 0 )) + { + /* Very unusual -- the new upper or lower edge would pass on the + * wrong side of the sweep event, or through it. This can happen + * due to very small numerical errors in the intersection calculation. + */ + if( dstLo == tess->event ) { + /* Splice dstLo into eUp, and process the new region(s) */ + if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eLo->Sym, eUp ) ) longjmp(tess->env,1); + regUp = TopLeftRegion( regUp ); + if (regUp == NULL) longjmp(tess->env,1); + eUp = RegionBelow(regUp)->eUp; + FinishLeftRegions( tess, RegionBelow(regUp), regLo ); + AddRightEdges( tess, regUp, eUp->Oprev, eUp, eUp, TRUE ); + return TRUE; + } + if( dstUp == tess->event ) { + /* Splice dstUp into eLo, and process the new region(s) */ + if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eUp->Lnext, eLo->Oprev ) ) longjmp(tess->env,1); + regLo = regUp; + regUp = TopRightRegion( regUp ); + e = RegionBelow(regUp)->eUp->Rprev; + regLo->eUp = eLo->Oprev; + eLo = FinishLeftRegions( tess, regLo, NULL ); + AddRightEdges( tess, regUp, eLo->Onext, eUp->Rprev, e, TRUE ); + return TRUE; + } + /* Special case: called from ConnectRightVertex. If either + * edge passes on the wrong side of tess->event, split it + * (and wait for ConnectRightVertex to splice it appropriately). + */ + if( EdgeSign( dstUp, tess->event, &isect ) >= 0 ) { + RegionAbove(regUp)->dirty = regUp->dirty = TRUE; + if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1); + eUp->Org->s = tess->event->s; + eUp->Org->t = tess->event->t; + } + if( EdgeSign( dstLo, tess->event, &isect ) <= 0 ) { + regUp->dirty = regLo->dirty = TRUE; + if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1); + eLo->Org->s = tess->event->s; + eLo->Org->t = tess->event->t; + } + /* leave the rest for ConnectRightVertex */ + return FALSE; + } + + /* General case -- split both edges, splice into new vertex. + * When we do the splice operation, the order of the arguments is + * arbitrary as far as correctness goes. However, when the operation + * creates a new face, the work done is proportional to the size of + * the new face. We expect the faces in the processed part of + * the mesh (ie. eUp->Lface) to be smaller than the faces in the + * unprocessed original contours (which will be eLo->Oprev->Lface). + */ + if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1); + if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1); + if ( !__gl_meshSplice( eLo->Oprev, eUp ) ) longjmp(tess->env,1); + eUp->Org->s = isect.s; + eUp->Org->t = isect.t; + eUp->Org->pqHandle = pqInsert( tess->pq, eUp->Org ); /* __gl_pqSortInsert */ + if (eUp->Org->pqHandle == LONG_MAX) { + pqDeletePriorityQ(tess->pq); /* __gl_pqSortDeletePriorityQ */ + tess->pq = NULL; + longjmp(tess->env,1); + } + GetIntersectData( tess, eUp->Org, orgUp, dstUp, orgLo, dstLo ); + RegionAbove(regUp)->dirty = regUp->dirty = regLo->dirty = TRUE; + return FALSE; +} + +static void WalkDirtyRegions( GLUtesselator *tess, ActiveRegion *regUp ) +/* + * When the upper or lower edge of any region changes, the region is + * marked "dirty". This routine walks through all the dirty regions + * and makes sure that the dictionary invariants are satisfied + * (see the comments at the beginning of this file). Of course + * new dirty regions can be created as we make changes to restore + * the invariants. + */ +{ + ActiveRegion *regLo = RegionBelow(regUp); + GLUhalfEdge *eUp, *eLo; + + for( ;; ) { + /* Find the lowest dirty region (we walk from the bottom up). */ + while( regLo->dirty ) { + regUp = regLo; + regLo = RegionBelow(regLo); + } + if( ! regUp->dirty ) { + regLo = regUp; + regUp = RegionAbove( regUp ); + if( regUp == NULL || ! regUp->dirty ) { + /* We've walked all the dirty regions */ + return; + } + } + regUp->dirty = FALSE; + eUp = regUp->eUp; + eLo = regLo->eUp; + + if( eUp->Dst != eLo->Dst ) { + /* Check that the edge ordering is obeyed at the Dst vertices. */ + if( CheckForLeftSplice( tess, regUp )) { + + /* If the upper or lower edge was marked fixUpperEdge, then + * we no longer need it (since these edges are needed only for + * vertices which otherwise have no right-going edges). + */ + if( regLo->fixUpperEdge ) { + DeleteRegion( tess, regLo ); + if ( !__gl_meshDelete( eLo ) ) longjmp(tess->env,1); + regLo = RegionBelow( regUp ); + eLo = regLo->eUp; + } else if( regUp->fixUpperEdge ) { + DeleteRegion( tess, regUp ); + if ( !__gl_meshDelete( eUp ) ) longjmp(tess->env,1); + regUp = RegionAbove( regLo ); + eUp = regUp->eUp; + } + } + } + if( eUp->Org != eLo->Org ) { + if( eUp->Dst != eLo->Dst + && ! regUp->fixUpperEdge && ! regLo->fixUpperEdge + && (eUp->Dst == tess->event || eLo->Dst == tess->event) ) + { + /* When all else fails in CheckForIntersect(), it uses tess->event + * as the intersection location. To make this possible, it requires + * that tess->event lie between the upper and lower edges, and also + * that neither of these is marked fixUpperEdge (since in the worst + * case it might splice one of these edges into tess->event, and + * violate the invariant that fixable edges are the only right-going + * edge from their associated vertex). + */ + if( CheckForIntersect( tess, regUp )) { + /* WalkDirtyRegions() was called recursively; we're done */ + return; + } + } else { + /* Even though we can't use CheckForIntersect(), the Org vertices + * may violate the dictionary edge ordering. Check and correct this. + */ + (void) CheckForRightSplice( tess, regUp ); + } + } + if( eUp->Org == eLo->Org && eUp->Dst == eLo->Dst ) { + /* A degenerate loop consisting of only two edges -- delete it. */ + AddWinding( eLo, eUp ); + DeleteRegion( tess, regUp ); + if ( !__gl_meshDelete( eUp ) ) longjmp(tess->env,1); + regUp = RegionAbove( regLo ); + } + } +} + + +static void ConnectRightVertex( GLUtesselator *tess, ActiveRegion *regUp, + GLUhalfEdge *eBottomLeft ) +/* + * Purpose: connect a "right" vertex vEvent (one where all edges go left) + * to the unprocessed portion of the mesh. Since there are no right-going + * edges, two regions (one above vEvent and one below) are being merged + * into one. "regUp" is the upper of these two regions. + * + * There are two reasons for doing this (adding a right-going edge): + * - if the two regions being merged are "inside", we must add an edge + * to keep them separated (the combined region would not be monotone). + * - in any case, we must leave some record of vEvent in the dictionary, + * so that we can merge vEvent with features that we have not seen yet. + * For example, maybe there is a vertical edge which passes just to + * the right of vEvent; we would like to splice vEvent into this edge. + * + * However, we don't want to connect vEvent to just any vertex. We don''t + * want the new edge to cross any other edges; otherwise we will create + * intersection vertices even when the input data had no self-intersections. + * (This is a bad thing; if the user's input data has no intersections, + * we don't want to generate any false intersections ourselves.) + * + * Our eventual goal is to connect vEvent to the leftmost unprocessed + * vertex of the combined region (the union of regUp and regLo). + * But because of unseen vertices with all right-going edges, and also + * new vertices which may be created by edge intersections, we don''t + * know where that leftmost unprocessed vertex is. In the meantime, we + * connect vEvent to the closest vertex of either chain, and mark the region + * as "fixUpperEdge". This flag says to delete and reconnect this edge + * to the next processed vertex on the boundary of the combined region. + * Quite possibly the vertex we connected to will turn out to be the + * closest one, in which case we won''t need to make any changes. + */ +{ + GLUhalfEdge *eNew; + GLUhalfEdge *eTopLeft = eBottomLeft->Onext; + ActiveRegion *regLo = RegionBelow(regUp); + GLUhalfEdge *eUp = regUp->eUp; + GLUhalfEdge *eLo = regLo->eUp; + int degenerate = FALSE; + + if( eUp->Dst != eLo->Dst ) { + (void) CheckForIntersect( tess, regUp ); + } + + /* Possible new degeneracies: upper or lower edge of regUp may pass + * through vEvent, or may coincide with new intersection vertex + */ + if( VertEq( eUp->Org, tess->event )) { + if ( !__gl_meshSplice( eTopLeft->Oprev, eUp ) ) longjmp(tess->env,1); + regUp = TopLeftRegion( regUp ); + if (regUp == NULL) longjmp(tess->env,1); + eTopLeft = RegionBelow( regUp )->eUp; + FinishLeftRegions( tess, RegionBelow(regUp), regLo ); + degenerate = TRUE; + } + if( VertEq( eLo->Org, tess->event )) { + if ( !__gl_meshSplice( eBottomLeft, eLo->Oprev ) ) longjmp(tess->env,1); + eBottomLeft = FinishLeftRegions( tess, regLo, NULL ); + degenerate = TRUE; + } + if( degenerate ) { + AddRightEdges( tess, regUp, eBottomLeft->Onext, eTopLeft, eTopLeft, TRUE ); + return; + } + + /* Non-degenerate situation -- need to add a temporary, fixable edge. + * Connect to the closer of eLo->Org, eUp->Org. + */ + if( VertLeq( eLo->Org, eUp->Org )) { + eNew = eLo->Oprev; + } else { + eNew = eUp; + } + eNew = __gl_meshConnect( eBottomLeft->Lprev, eNew ); + if (eNew == NULL) longjmp(tess->env,1); + + /* Prevent cleanup, otherwise eNew might disappear before we've even + * had a chance to mark it as a temporary edge. + */ + AddRightEdges( tess, regUp, eNew, eNew->Onext, eNew->Onext, FALSE ); + eNew->Sym->activeRegion->fixUpperEdge = TRUE; + WalkDirtyRegions( tess, regUp ); +} + +/* Because vertices at exactly the same location are merged together + * before we process the sweep event, some degenerate cases can't occur. + * However if someone eventually makes the modifications required to + * merge features which are close together, the cases below marked + * TOLERANCE_NONZERO will be useful. They were debugged before the + * code to merge identical vertices in the main loop was added. + */ +#define TOLERANCE_NONZERO FALSE + +static void ConnectLeftDegenerate( GLUtesselator *tess, + ActiveRegion *regUp, GLUvertex *vEvent ) +/* + * The event vertex lies exacty on an already-processed edge or vertex. + * Adding the new vertex involves splicing it into the already-processed + * part of the mesh. + */ +{ + GLUhalfEdge *e, *eTopLeft, *eTopRight, *eLast; + ActiveRegion *reg; + + e = regUp->eUp; + if( VertEq( e->Org, vEvent )) { + /* e->Org is an unprocessed vertex - just combine them, and wait + * for e->Org to be pulled from the queue + */ + assert( TOLERANCE_NONZERO ); + SpliceMergeVertices( tess, e, vEvent->anEdge ); + return; + } + + if( ! VertEq( e->Dst, vEvent )) { + /* General case -- splice vEvent into edge e which passes through it */ + if (__gl_meshSplitEdge( e->Sym ) == NULL) longjmp(tess->env,1); + if( regUp->fixUpperEdge ) { + /* This edge was fixable -- delete unused portion of original edge */ + if ( !__gl_meshDelete( e->Onext ) ) longjmp(tess->env,1); + regUp->fixUpperEdge = FALSE; + } + if ( !__gl_meshSplice( vEvent->anEdge, e ) ) longjmp(tess->env,1); + SweepEvent( tess, vEvent ); /* recurse */ + return; + } + + /* vEvent coincides with e->Dst, which has already been processed. + * Splice in the additional right-going edges. + */ + assert( TOLERANCE_NONZERO ); + regUp = TopRightRegion( regUp ); + reg = RegionBelow( regUp ); + eTopRight = reg->eUp->Sym; + eTopLeft = eLast = eTopRight->Onext; + if( reg->fixUpperEdge ) { + /* Here e->Dst has only a single fixable edge going right. + * We can delete it since now we have some real right-going edges. + */ + assert( eTopLeft != eTopRight ); /* there are some left edges too */ + DeleteRegion( tess, reg ); + if ( !__gl_meshDelete( eTopRight ) ) longjmp(tess->env,1); + eTopRight = eTopLeft->Oprev; + } + if ( !__gl_meshSplice( vEvent->anEdge, eTopRight ) ) longjmp(tess->env,1); + if( ! EdgeGoesLeft( eTopLeft )) { + /* e->Dst had no left-going edges -- indicate this to AddRightEdges() */ + eTopLeft = NULL; + } + AddRightEdges( tess, regUp, eTopRight->Onext, eLast, eTopLeft, TRUE ); +} + + +static void ConnectLeftVertex( GLUtesselator *tess, GLUvertex *vEvent ) +/* + * Purpose: connect a "left" vertex (one where both edges go right) + * to the processed portion of the mesh. Let R be the active region + * containing vEvent, and let U and L be the upper and lower edge + * chains of R. There are two possibilities: + * + * - the normal case: split R into two regions, by connecting vEvent to + * the rightmost vertex of U or L lying to the left of the sweep line + * + * - the degenerate case: if vEvent is close enough to U or L, we + * merge vEvent into that edge chain. The subcases are: + * - merging with the rightmost vertex of U or L + * - merging with the active edge of U or L + * - merging with an already-processed portion of U or L + */ +{ + ActiveRegion *regUp, *regLo, *reg; + GLUhalfEdge *eUp, *eLo, *eNew; + ActiveRegion tmp; + + /* assert( vEvent->anEdge->Onext->Onext == vEvent->anEdge ); */ + + /* Get a pointer to the active region containing vEvent */ + tmp.eUp = vEvent->anEdge->Sym; + /* __GL_DICTLISTKEY */ /* __gl_dictListSearch */ + regUp = (ActiveRegion *)dictKey( dictSearch( tess->dict, &tmp )); + regLo = RegionBelow( regUp ); + eUp = regUp->eUp; + eLo = regLo->eUp; + + /* Try merging with U or L first */ + if( EdgeSign( eUp->Dst, vEvent, eUp->Org ) == 0 ) { + ConnectLeftDegenerate( tess, regUp, vEvent ); + return; + } + + /* Connect vEvent to rightmost processed vertex of either chain. + * e->Dst is the vertex that we will connect to vEvent. + */ + reg = VertLeq( eLo->Dst, eUp->Dst ) ? regUp : regLo; + + if( regUp->inside || reg->fixUpperEdge) { + if( reg == regUp ) { + eNew = __gl_meshConnect( vEvent->anEdge->Sym, eUp->Lnext ); + if (eNew == NULL) longjmp(tess->env,1); + } else { + GLUhalfEdge *tempHalfEdge= __gl_meshConnect( eLo->Dnext, vEvent->anEdge); + if (tempHalfEdge == NULL) longjmp(tess->env,1); + + eNew = tempHalfEdge->Sym; + } + if( reg->fixUpperEdge ) { + if ( !FixUpperEdge( reg, eNew ) ) longjmp(tess->env,1); + } else { + ComputeWinding( tess, AddRegionBelow( tess, regUp, eNew )); + } + SweepEvent( tess, vEvent ); + } else { + /* The new vertex is in a region which does not belong to the polygon. + * We don''t need to connect this vertex to the rest of the mesh. + */ + AddRightEdges( tess, regUp, vEvent->anEdge, vEvent->anEdge, NULL, TRUE ); + } +} + + +static void SweepEvent( GLUtesselator *tess, GLUvertex *vEvent ) +/* + * Does everything necessary when the sweep line crosses a vertex. + * Updates the mesh and the edge dictionary. + */ +{ + ActiveRegion *regUp, *reg; + GLUhalfEdge *e, *eTopLeft, *eBottomLeft; + + tess->event = vEvent; /* for access in EdgeLeq() */ + DebugEvent( tess ); + + /* Check if this vertex is the right endpoint of an edge that is + * already in the dictionary. In this case we don't need to waste + * time searching for the location to insert new edges. + */ + e = vEvent->anEdge; + while( e->activeRegion == NULL ) { + e = e->Onext; + if( e == vEvent->anEdge ) { + /* All edges go right -- not incident to any processed edges */ + ConnectLeftVertex( tess, vEvent ); + return; + } + } + + /* Processing consists of two phases: first we "finish" all the + * active regions where both the upper and lower edges terminate + * at vEvent (ie. vEvent is closing off these regions). + * We mark these faces "inside" or "outside" the polygon according + * to their winding number, and delete the edges from the dictionary. + * This takes care of all the left-going edges from vEvent. + */ + regUp = TopLeftRegion( e->activeRegion ); + if (regUp == NULL) longjmp(tess->env,1); + reg = RegionBelow( regUp ); + eTopLeft = reg->eUp; + eBottomLeft = FinishLeftRegions( tess, reg, NULL ); + + /* Next we process all the right-going edges from vEvent. This + * involves adding the edges to the dictionary, and creating the + * associated "active regions" which record information about the + * regions between adjacent dictionary edges. + */ + if( eBottomLeft->Onext == eTopLeft ) { + /* No right-going edges -- add a temporary "fixable" edge */ + ConnectRightVertex( tess, regUp, eBottomLeft ); + } else { + AddRightEdges( tess, regUp, eBottomLeft->Onext, eTopLeft, eTopLeft, TRUE ); + } +} + + +/* Make the sentinel coordinates big enough that they will never be + * merged with real input features. (Even with the largest possible + * input contour and the maximum tolerance of 1.0, no merging will be + * done with coordinates larger than 3 * GLU_TESS_MAX_COORD). + */ +#define SENTINEL_COORD (4 * GLU_TESS_MAX_COORD) + +static void AddSentinel( GLUtesselator *tess, GLdouble t ) +/* + * We add two sentinel edges above and below all other edges, + * to avoid special cases at the top and bottom. + */ +{ + GLUhalfEdge *e; + ActiveRegion *reg = (ActiveRegion *)memAlloc( sizeof( ActiveRegion )); + if (reg == NULL) longjmp(tess->env,1); + + e = __gl_meshMakeEdge( tess->mesh ); + if (e == NULL) longjmp(tess->env,1); + + e->Org->s = SENTINEL_COORD; + e->Org->t = t; + e->Dst->s = -SENTINEL_COORD; + e->Dst->t = t; + tess->event = e->Dst; /* initialize it */ + + reg->eUp = e; + reg->windingNumber = 0; + reg->inside = FALSE; + reg->fixUpperEdge = FALSE; + reg->sentinel = TRUE; + reg->dirty = FALSE; + reg->nodeUp = dictInsert( tess->dict, reg ); /* __gl_dictListInsertBefore */ + if (reg->nodeUp == NULL) longjmp(tess->env,1); +} + + +static void InitEdgeDict( GLUtesselator *tess ) +/* + * We maintain an ordering of edge intersections with the sweep line. + * This order is maintained in a dynamic dictionary. + */ +{ + /* __gl_dictListNewDict */ + tess->dict = dictNewDict( tess, (int (*)(void *, DictKey, DictKey)) EdgeLeq ); + if (tess->dict == NULL) longjmp(tess->env,1); + + AddSentinel( tess, -SENTINEL_COORD ); + AddSentinel( tess, SENTINEL_COORD ); +} + + +static void DoneEdgeDict( GLUtesselator *tess ) +{ + ActiveRegion *reg; +#ifndef NDEBUG + int fixedEdges = 0; +#endif + + /* __GL_DICTLISTKEY */ /* __GL_DICTLISTMIN */ + while( (reg = (ActiveRegion *)dictKey( dictMin( tess->dict ))) != NULL ) { + /* + * At the end of all processing, the dictionary should contain + * only the two sentinel edges, plus at most one "fixable" edge + * created by ConnectRightVertex(). + */ + if( ! reg->sentinel ) { + assert( reg->fixUpperEdge ); + assert( ++fixedEdges == 1 ); + } + assert( reg->windingNumber == 0 ); + DeleteRegion( tess, reg ); +/* __gl_meshDelete( reg->eUp );*/ + } + dictDeleteDict( tess->dict ); /* __gl_dictListDeleteDict */ +} + + +static void RemoveDegenerateEdges( GLUtesselator *tess ) +/* + * Remove zero-length edges, and contours with fewer than 3 vertices. + */ +{ + GLUhalfEdge *e, *eNext, *eLnext; + GLUhalfEdge *eHead = &tess->mesh->eHead; + + /*LINTED*/ + for( e = eHead->next; e != eHead; e = eNext ) { + eNext = e->next; + eLnext = e->Lnext; + + if( VertEq( e->Org, e->Dst ) && e->Lnext->Lnext != e ) { + /* Zero-length edge, contour has at least 3 edges */ + + SpliceMergeVertices( tess, eLnext, e ); /* deletes e->Org */ + if ( !__gl_meshDelete( e ) ) longjmp(tess->env,1); /* e is a self-loop */ + e = eLnext; + eLnext = e->Lnext; + } + if( eLnext->Lnext == e ) { + /* Degenerate contour (one or two edges) */ + + if( eLnext != e ) { + if( eLnext == eNext || eLnext == eNext->Sym ) { eNext = eNext->next; } + if ( !__gl_meshDelete( eLnext ) ) longjmp(tess->env,1); + } + if( e == eNext || e == eNext->Sym ) { eNext = eNext->next; } + if ( !__gl_meshDelete( e ) ) longjmp(tess->env,1); + } + } +} + +static int InitPriorityQ( GLUtesselator *tess ) +/* + * Insert all vertices into the priority queue which determines the + * order in which vertices cross the sweep line. + */ +{ + PriorityQ *pq; + GLUvertex *v, *vHead; + + /* __gl_pqSortNewPriorityQ */ + pq = tess->pq = pqNewPriorityQ( (int (*)(PQkey, PQkey)) __gl_vertLeq ); + if (pq == NULL) return 0; + + vHead = &tess->mesh->vHead; + for( v = vHead->next; v != vHead; v = v->next ) { + v->pqHandle = pqInsert( pq, v ); /* __gl_pqSortInsert */ + if (v->pqHandle == LONG_MAX) break; + } + if (v != vHead || !pqInit( pq ) ) { /* __gl_pqSortInit */ + pqDeletePriorityQ(tess->pq); /* __gl_pqSortDeletePriorityQ */ + tess->pq = NULL; + return 0; + } + + return 1; +} + + +static void DonePriorityQ( GLUtesselator *tess ) +{ + pqDeletePriorityQ( tess->pq ); /* __gl_pqSortDeletePriorityQ */ +} + + +static int RemoveDegenerateFaces( GLUmesh *mesh ) +/* + * Delete any degenerate faces with only two edges. WalkDirtyRegions() + * will catch almost all of these, but it won't catch degenerate faces + * produced by splice operations on already-processed edges. + * The two places this can happen are in FinishLeftRegions(), when + * we splice in a "temporary" edge produced by ConnectRightVertex(), + * and in CheckForLeftSplice(), where we splice already-processed + * edges to ensure that our dictionary invariants are not violated + * by numerical errors. + * + * In both these cases it is *very* dangerous to delete the offending + * edge at the time, since one of the routines further up the stack + * will sometimes be keeping a pointer to that edge. + */ +{ + GLUface *f, *fNext; + GLUhalfEdge *e; + + /*LINTED*/ + for( f = mesh->fHead.next; f != &mesh->fHead; f = fNext ) { + fNext = f->next; + e = f->anEdge; + assert( e->Lnext != e ); + + if( e->Lnext->Lnext == e ) { + /* A face with only two edges */ + AddWinding( e->Onext, e ); + if ( !__gl_meshDelete( e ) ) return 0; + } + } + return 1; +} + +int __gl_computeInterior( GLUtesselator *tess ) +/* + * __gl_computeInterior( tess ) computes the planar arrangement specified + * by the given contours, and further subdivides this arrangement + * into regions. Each region is marked "inside" if it belongs + * to the polygon, according to the rule given by tess->windingRule. + * Each interior region is guaranteed be monotone. + */ +{ + GLUvertex *v, *vNext; + + tess->fatalError = FALSE; + + /* Each vertex defines an event for our sweep line. Start by inserting + * all the vertices in a priority queue. Events are processed in + * lexicographic order, ie. + * + * e1 < e2 iff e1.x < e2.x || (e1.x == e2.x && e1.y < e2.y) + */ + RemoveDegenerateEdges( tess ); + if ( !InitPriorityQ( tess ) ) return 0; /* if error */ + InitEdgeDict( tess ); + + /* __gl_pqSortExtractMin */ + while( (v = (GLUvertex *)pqExtractMin( tess->pq )) != NULL ) { + for( ;; ) { + vNext = (GLUvertex *)pqMinimum( tess->pq ); /* __gl_pqSortMinimum */ + if( vNext == NULL || ! VertEq( vNext, v )) break; + + /* Merge together all vertices at exactly the same location. + * This is more efficient than processing them one at a time, + * simplifies the code (see ConnectLeftDegenerate), and is also + * important for correct handling of certain degenerate cases. + * For example, suppose there are two identical edges A and B + * that belong to different contours (so without this code they would + * be processed by separate sweep events). Suppose another edge C + * crosses A and B from above. When A is processed, we split it + * at its intersection point with C. However this also splits C, + * so when we insert B we may compute a slightly different + * intersection point. This might leave two edges with a small + * gap between them. This kind of error is especially obvious + * when using boundary extraction (GLU_TESS_BOUNDARY_ONLY). + */ + vNext = (GLUvertex *)pqExtractMin( tess->pq ); /* __gl_pqSortExtractMin*/ + SpliceMergeVertices( tess, v->anEdge, vNext->anEdge ); + } + SweepEvent( tess, v ); + } + + /* Set tess->event for debugging purposes */ + /* __GL_DICTLISTKEY */ /* __GL_DICTLISTMIN */ + tess->event = ((ActiveRegion *) dictKey( dictMin( tess->dict )))->eUp->Org; + DebugEvent( tess ); + DoneEdgeDict( tess ); + DonePriorityQ( tess ); + + if ( !RemoveDegenerateFaces( tess->mesh ) ) return 0; + __gl_meshCheckMesh( tess->mesh ); + + return 1; +} diff --git a/vtm/jni/tessellate/sweep.h b/vtm/jni/tessellate/sweep.h new file mode 100644 index 00000000..feb68b0f --- /dev/null +++ b/vtm/jni/tessellate/sweep.h @@ -0,0 +1,77 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __sweep_h_ +#define __sweep_h_ + +#include "mesh.h" + +/* __gl_computeInterior( tess ) computes the planar arrangement specified + * by the given contours, and further subdivides this arrangement + * into regions. Each region is marked "inside" if it belongs + * to the polygon, according to the rule given by tess->windingRule. + * Each interior region is guaranteed be monotone. + */ +int __gl_computeInterior( GLUtesselator *tess ); + + +/* The following is here *only* for access by debugging routines */ + +#include "dict.h" + +/* For each pair of adjacent edges crossing the sweep line, there is + * an ActiveRegion to represent the region between them. The active + * regions are kept in sorted order in a dynamic dictionary. As the + * sweep line crosses each vertex, we update the affected regions. + */ + +struct ActiveRegion { + GLUhalfEdge *eUp; /* upper edge, directed right to left */ + DictNode *nodeUp; /* dictionary node corresponding to eUp */ + int windingNumber; /* used to determine which regions are + * inside the polygon */ + GLboolean inside; /* is this region inside the polygon? */ + GLboolean sentinel; /* marks fake edges at t = +/-infinity */ + GLboolean dirty; /* marks regions where the upper or lower + * edge has changed, but we haven't checked + * whether they intersect yet */ + GLboolean fixUpperEdge; /* marks temporary edges introduced when + * we process a "right vertex" (one without + * any edges leaving to the right) */ +}; + +#define RegionBelow(r) ((ActiveRegion *) dictKey(dictPred((r)->nodeUp))) +#define RegionAbove(r) ((ActiveRegion *) dictKey(dictSucc((r)->nodeUp))) + +#endif diff --git a/vtm/jni/tessellate/tess.c b/vtm/jni/tessellate/tess.c new file mode 100644 index 00000000..4a0e8dea --- /dev/null +++ b/vtm/jni/tessellate/tess.c @@ -0,0 +1,632 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include "gluos.h" +#include +#include +#include +#include "memalloc.h" +#include "tess.h" +#include "mesh.h" +#include "normal.h" +#include "sweep.h" +#include "tessmono.h" +#include "render.h" + +#define GLU_TESS_DEFAULT_TOLERANCE 0.0 +#define GLU_TESS_MESH 100112 /* void (*)(GLUmesh *mesh) */ + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +/*ARGSUSED*/ static void GLAPIENTRY noBegin( GLenum type ) {} +/*ARGSUSED*/ static void GLAPIENTRY noEdgeFlag( GLboolean boundaryEdge ) {} +/*ARGSUSED*/ static void GLAPIENTRY noVertex( void *data ) {} +/*ARGSUSED*/ static void GLAPIENTRY noEnd( void ) {} +/*ARGSUSED*/ static void GLAPIENTRY noError( GLenum errnum ) {} +/*ARGSUSED*/ static void GLAPIENTRY noCombine( GLdouble coords[3], void *data[4], + GLfloat weight[4], void **dataOut ) {} +/*ARGSUSED*/ static void GLAPIENTRY noMesh( GLUmesh *mesh ) {} + + +/*ARGSUSED*/ void GLAPIENTRY __gl_noBeginData( GLenum type, + void *polygonData ) {} +/*ARGSUSED*/ void GLAPIENTRY __gl_noEdgeFlagData( GLboolean boundaryEdge, + void *polygonData ) {} +/*ARGSUSED*/ void GLAPIENTRY __gl_noVertexData( void *data, + void *polygonData ) {} +/*ARGSUSED*/ void GLAPIENTRY __gl_noEndData( void *polygonData ) {} +/*ARGSUSED*/ void GLAPIENTRY __gl_noErrorData( GLenum errnum, + void *polygonData ) {} +/*ARGSUSED*/ void GLAPIENTRY __gl_noCombineData( GLdouble coords[3], + void *data[4], + GLfloat weight[4], + void **outData, + void *polygonData ) {} + +/* Half-edges are allocated in pairs (see mesh.c) */ +typedef struct { GLUhalfEdge e, eSym; } EdgePair; + +#undef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define MAX_FAST_ALLOC (MAX(sizeof(EdgePair), \ + MAX(sizeof(GLUvertex),sizeof(GLUface)))) + + +GLUtesselator * GLAPIENTRY +gluNewTess( void ) +{ + GLUtesselator *tess; + + /* Only initialize fields which can be changed by the api. Other fields + * are initialized where they are used. + */ + + if (memInit( MAX_FAST_ALLOC ) == 0) { + return 0; /* out of memory */ + } + tess = (GLUtesselator *)memAlloc( sizeof( GLUtesselator )); + if (tess == NULL) { + return 0; /* out of memory */ + } + + tess->state = T_DORMANT; + + tess->normal[0] = 0; + tess->normal[1] = 0; + tess->normal[2] = 0; + + tess->relTolerance = GLU_TESS_DEFAULT_TOLERANCE; + tess->windingRule = GLU_TESS_WINDING_ODD; + tess->flagBoundary = FALSE; + tess->boundaryOnly = FALSE; + + tess->callBegin = &noBegin; + tess->callEdgeFlag = &noEdgeFlag; + tess->callVertex = &noVertex; + tess->callEnd = &noEnd; + + tess->callError = &noError; + tess->callCombine = &noCombine; + tess->callMesh = &noMesh; + + tess->callBeginData= &__gl_noBeginData; + tess->callEdgeFlagData= &__gl_noEdgeFlagData; + tess->callVertexData= &__gl_noVertexData; + tess->callEndData= &__gl_noEndData; + tess->callErrorData= &__gl_noErrorData; + tess->callCombineData= &__gl_noCombineData; + + tess->polygonData= NULL; + + return tess; +} + +static void MakeDormant( GLUtesselator *tess ) +{ + /* Return the tessellator to its original dormant state. */ + + if( tess->mesh != NULL ) { + __gl_meshDeleteMesh( tess->mesh ); + } + tess->state = T_DORMANT; + tess->lastEdge = NULL; + tess->mesh = NULL; +} + +#define RequireState( tess, s ) if( tess->state != s ) GotoState(tess,s) + +static void GotoState( GLUtesselator *tess, enum TessState newState ) +{ + while( tess->state != newState ) { + /* We change the current state one level at a time, to get to + * the desired state. + */ + if( tess->state < newState ) { + switch( tess->state ) { + case T_DORMANT: + CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_BEGIN_POLYGON ); + gluTessBeginPolygon( tess, NULL ); + break; + case T_IN_POLYGON: + CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_BEGIN_CONTOUR ); + gluTessBeginContour( tess ); + break; + default: + ; + } + } else { + switch( tess->state ) { + case T_IN_CONTOUR: + CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_END_CONTOUR ); + gluTessEndContour( tess ); + break; + case T_IN_POLYGON: + CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_END_POLYGON ); + /* gluTessEndPolygon( tess ) is too much work! */ + MakeDormant( tess ); + break; + default: + ; + } + } + } +} + + +void GLAPIENTRY +gluDeleteTess( GLUtesselator *tess ) +{ + RequireState( tess, T_DORMANT ); + memFree( tess ); +} + + +void GLAPIENTRY +gluTessProperty( GLUtesselator *tess, GLenum which, GLdouble value ) +{ + GLenum windingRule; + + switch( which ) { + case GLU_TESS_TOLERANCE: + if( value < 0.0 || value > 1.0 ) break; + tess->relTolerance = value; + return; + + case GLU_TESS_WINDING_RULE: + windingRule = (GLenum) value; + if( windingRule != value ) break; /* not an integer */ + + switch( windingRule ) { + case GLU_TESS_WINDING_ODD: + case GLU_TESS_WINDING_NONZERO: + case GLU_TESS_WINDING_POSITIVE: + case GLU_TESS_WINDING_NEGATIVE: + case GLU_TESS_WINDING_ABS_GEQ_TWO: + tess->windingRule = windingRule; + return; + default: + break; + } + + case GLU_TESS_BOUNDARY_ONLY: + tess->boundaryOnly = (value != 0); + return; + + default: + CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM ); + return; + } + CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_VALUE ); +} + +/* Returns tessellator property */ +void GLAPIENTRY +gluGetTessProperty( GLUtesselator *tess, GLenum which, GLdouble *value ) +{ + switch (which) { + case GLU_TESS_TOLERANCE: + /* tolerance should be in range [0..1] */ + assert(0.0 <= tess->relTolerance && tess->relTolerance <= 1.0); + *value= tess->relTolerance; + break; + case GLU_TESS_WINDING_RULE: + assert(tess->windingRule == GLU_TESS_WINDING_ODD || + tess->windingRule == GLU_TESS_WINDING_NONZERO || + tess->windingRule == GLU_TESS_WINDING_POSITIVE || + tess->windingRule == GLU_TESS_WINDING_NEGATIVE || + tess->windingRule == GLU_TESS_WINDING_ABS_GEQ_TWO); + *value= tess->windingRule; + break; + case GLU_TESS_BOUNDARY_ONLY: + assert(tess->boundaryOnly == TRUE || tess->boundaryOnly == FALSE); + *value= tess->boundaryOnly; + break; + default: + *value= 0.0; + CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM ); + break; + } +} /* gluGetTessProperty() */ + +void GLAPIENTRY +gluTessNormal( GLUtesselator *tess, GLdouble x, GLdouble y, GLdouble z ) +{ + tess->normal[0] = x; + tess->normal[1] = y; + tess->normal[2] = z; +} + +void GLAPIENTRY +gluTessCallback( GLUtesselator *tess, GLenum which, _GLUfuncptr fn) +{ + switch( which ) { + case GLU_TESS_BEGIN: + tess->callBegin = (fn == NULL) ? &noBegin : (void (GLAPIENTRY *)(GLenum)) fn; + return; + case GLU_TESS_BEGIN_DATA: + tess->callBeginData = (fn == NULL) ? + &__gl_noBeginData : (void (GLAPIENTRY *)(GLenum, void *)) fn; + return; + case GLU_TESS_EDGE_FLAG: + tess->callEdgeFlag = (fn == NULL) ? &noEdgeFlag : + (void (GLAPIENTRY *)(GLboolean)) fn; + /* If the client wants boundary edges to be flagged, + * we render everything as separate triangles (no strips or fans). + */ + tess->flagBoundary = (fn != NULL); + return; + case GLU_TESS_EDGE_FLAG_DATA: + tess->callEdgeFlagData= (fn == NULL) ? + &__gl_noEdgeFlagData : (void (GLAPIENTRY *)(GLboolean, void *)) fn; + /* If the client wants boundary edges to be flagged, + * we render everything as separate triangles (no strips or fans). + */ + tess->flagBoundary = (fn != NULL); + return; + case GLU_TESS_VERTEX: + tess->callVertex = (fn == NULL) ? &noVertex : + (void (GLAPIENTRY *)(void *)) fn; + return; + case GLU_TESS_VERTEX_DATA: + tess->callVertexData = (fn == NULL) ? + &__gl_noVertexData : (void (GLAPIENTRY *)(void *, void *)) fn; + return; + case GLU_TESS_END: + tess->callEnd = (fn == NULL) ? &noEnd : (void (GLAPIENTRY *)(void)) fn; + return; + case GLU_TESS_END_DATA: + tess->callEndData = (fn == NULL) ? &__gl_noEndData : + (void (GLAPIENTRY *)(void *)) fn; + return; + case GLU_TESS_ERROR: + tess->callError = (fn == NULL) ? &noError : (void (GLAPIENTRY *)(GLenum)) fn; + return; + case GLU_TESS_ERROR_DATA: + tess->callErrorData = (fn == NULL) ? + &__gl_noErrorData : (void (GLAPIENTRY *)(GLenum, void *)) fn; + return; + case GLU_TESS_COMBINE: + tess->callCombine = (fn == NULL) ? &noCombine : + (void (GLAPIENTRY *)(GLdouble [3],void *[4], GLfloat [4], void ** )) fn; + return; + case GLU_TESS_COMBINE_DATA: + tess->callCombineData = (fn == NULL) ? &__gl_noCombineData : + (void (GLAPIENTRY *)(GLdouble [3], + void *[4], + GLfloat [4], + void **, + void *)) fn; + return; + case GLU_TESS_MESH: + tess->callMesh = (fn == NULL) ? &noMesh : (void (GLAPIENTRY *)(GLUmesh *)) fn; + return; + default: + CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM ); + return; + } +} + +static int AddVertex( GLUtesselator *tess, GLdouble coords[3], void *data ) +{ + GLUhalfEdge *e; + + e = tess->lastEdge; + if( e == NULL ) { + /* Make a self-loop (one vertex, one edge). */ + + e = __gl_meshMakeEdge( tess->mesh ); + if (e == NULL) return 0; + if ( !__gl_meshSplice( e, e->Sym ) ) return 0; + } else { + /* Create a new vertex and edge which immediately follow e + * in the ordering around the left face. + */ + if (__gl_meshSplitEdge( e ) == NULL) return 0; + e = e->Lnext; + } + + /* The new vertex is now e->Org. */ + e->Org->data = data; + e->Org->coords[0] = coords[0]; + e->Org->coords[1] = coords[1]; + e->Org->coords[2] = coords[2]; + + /* The winding of an edge says how the winding number changes as we + * cross from the edge''s right face to its left face. We add the + * vertices in such an order that a CCW contour will add +1 to + * the winding number of the region inside the contour. + */ + e->winding = 1; + e->Sym->winding = -1; + + tess->lastEdge = e; + + return 1; +} + + +static void CacheVertex( GLUtesselator *tess, GLdouble coords[3], void *data ) +{ + CachedVertex *v = &tess->cache[tess->cacheCount]; + + v->data = data; + v->coords[0] = coords[0]; + v->coords[1] = coords[1]; + v->coords[2] = coords[2]; + ++tess->cacheCount; +} + + +static int EmptyCache( GLUtesselator *tess ) +{ + CachedVertex *v = tess->cache; + CachedVertex *vLast; + + tess->mesh = __gl_meshNewMesh(); + if (tess->mesh == NULL) return 0; + + for( vLast = v + tess->cacheCount; v < vLast; ++v ) { + if ( !AddVertex( tess, v->coords, v->data ) ) return 0; + } + tess->cacheCount = 0; + tess->emptyCache = FALSE; + + return 1; +} + + +void GLAPIENTRY +gluTessVertex( GLUtesselator *tess, GLdouble coords[3], void *data ) +{ + int i, tooLarge = FALSE; + GLdouble x, clamped[3]; + + RequireState( tess, T_IN_CONTOUR ); + + if( tess->emptyCache ) { + if ( !EmptyCache( tess ) ) { + CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY ); + return; + } + tess->lastEdge = NULL; + } + for( i = 0; i < 3; ++i ) { + x = coords[i]; + if( x < - GLU_TESS_MAX_COORD ) { + x = - GLU_TESS_MAX_COORD; + tooLarge = TRUE; + } + if( x > GLU_TESS_MAX_COORD ) { + x = GLU_TESS_MAX_COORD; + tooLarge = TRUE; + } + clamped[i] = x; + } + if( tooLarge ) { + CALL_ERROR_OR_ERROR_DATA( GLU_TESS_COORD_TOO_LARGE ); + } + + if( tess->mesh == NULL ) { + if( tess->cacheCount < TESS_MAX_CACHE ) { + CacheVertex( tess, clamped, data ); + return; + } + if ( !EmptyCache( tess ) ) { + CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY ); + return; + } + } + if ( !AddVertex( tess, clamped, data ) ) { + CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY ); + } +} + + +void GLAPIENTRY +gluTessBeginPolygon( GLUtesselator *tess, void *data ) +{ + RequireState( tess, T_DORMANT ); + + tess->state = T_IN_POLYGON; + tess->cacheCount = 0; + tess->emptyCache = FALSE; + tess->mesh = NULL; + + tess->polygonData= data; +} + + +void GLAPIENTRY +gluTessBeginContour( GLUtesselator *tess ) +{ + RequireState( tess, T_IN_POLYGON ); + + tess->state = T_IN_CONTOUR; + tess->lastEdge = NULL; + if( tess->cacheCount > 0 ) { + /* Just set a flag so we don't get confused by empty contours + * -- these can be generated accidentally with the obsolete + * NextContour() interface. + */ + tess->emptyCache = TRUE; + } +} + + +void GLAPIENTRY +gluTessEndContour( GLUtesselator *tess ) +{ + RequireState( tess, T_IN_CONTOUR ); + tess->state = T_IN_POLYGON; +} + +void GLAPIENTRY +gluTessEndPolygon( GLUtesselator *tess ) +{ + GLUmesh *mesh; + + if (setjmp(tess->env) != 0) { + /* come back here if out of memory */ + CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY ); + return; + } + + RequireState( tess, T_IN_POLYGON ); + tess->state = T_DORMANT; + + if( tess->mesh == NULL ) { + if( ! tess->flagBoundary && tess->callMesh == &noMesh ) { + + /* Try some special code to make the easy cases go quickly + * (eg. convex polygons). This code does NOT handle multiple contours, + * intersections, edge flags, and of course it does not generate + * an explicit mesh either. + */ + if( __gl_renderCache( tess )) { + tess->polygonData= NULL; + return; + } + } + if ( !EmptyCache( tess ) ) longjmp(tess->env,1); /* could've used a label*/ + } + + /* Determine the polygon normal and project vertices onto the plane + * of the polygon. + */ + __gl_projectPolygon( tess ); + + /* __gl_computeInterior( tess ) computes the planar arrangement specified + * by the given contours, and further subdivides this arrangement + * into regions. Each region is marked "inside" if it belongs + * to the polygon, according to the rule given by tess->windingRule. + * Each interior region is guaranteed be monotone. + */ + if ( !__gl_computeInterior( tess ) ) { + longjmp(tess->env,1); /* could've used a label */ + } + + mesh = tess->mesh; + if( ! tess->fatalError ) { + int rc = 1; + + /* If the user wants only the boundary contours, we throw away all edges + * except those which separate the interior from the exterior. + * Otherwise we tessellate all the regions marked "inside". + */ + if( tess->boundaryOnly ) { + rc = __gl_meshSetWindingNumber( mesh, 1, TRUE ); + } else { + rc = __gl_meshTessellateInterior( mesh ); + } + if (rc == 0) longjmp(tess->env,1); /* could've used a label */ + + __gl_meshCheckMesh( mesh ); + + if( tess->callBegin != &noBegin || tess->callEnd != &noEnd + || tess->callVertex != &noVertex || tess->callEdgeFlag != &noEdgeFlag + || tess->callBeginData != &__gl_noBeginData + || tess->callEndData != &__gl_noEndData + || tess->callVertexData != &__gl_noVertexData + || tess->callEdgeFlagData != &__gl_noEdgeFlagData ) + { + if( tess->boundaryOnly ) { + __gl_renderBoundary( tess, mesh ); /* output boundary contours */ + } else { + __gl_renderMesh( tess, mesh ); /* output strips and fans */ + } + } + if( tess->callMesh != &noMesh ) { + + /* Throw away the exterior faces, so that all faces are interior. + * This way the user doesn't have to check the "inside" flag, + * and we don't need to even reveal its existence. It also leaves + * the freedom for an implementation to not generate the exterior + * faces in the first place. + */ + __gl_meshDiscardExterior( mesh ); + (*tess->callMesh)( mesh ); /* user wants the mesh itself */ + tess->mesh = NULL; + tess->polygonData= NULL; + return; + } + } + __gl_meshDeleteMesh( mesh ); + tess->polygonData= NULL; + tess->mesh = NULL; +} + + +/*XXXblythe unused function*/ +#if 0 +void GLAPIENTRY +gluDeleteMesh( GLUmesh *mesh ) +{ + __gl_meshDeleteMesh( mesh ); +} +#endif + + + +/*******************************************************/ + +/* Obsolete calls -- for backward compatibility */ + +void GLAPIENTRY +gluBeginPolygon( GLUtesselator *tess ) +{ + gluTessBeginPolygon( tess, NULL ); + gluTessBeginContour( tess ); +} + + +/*ARGSUSED*/ +void GLAPIENTRY +gluNextContour( GLUtesselator *tess, GLenum type ) +{ + gluTessEndContour( tess ); + gluTessBeginContour( tess ); +} + + +void GLAPIENTRY +gluEndPolygon( GLUtesselator *tess ) +{ + gluTessEndContour( tess ); + gluTessEndPolygon( tess ); +} diff --git a/vtm/jni/tessellate/tess.h b/vtm/jni/tessellate/tess.h new file mode 100644 index 00000000..5c885d7a --- /dev/null +++ b/vtm/jni/tessellate/tess.h @@ -0,0 +1,165 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __tess_h_ +#define __tess_h_ + +#include "glu.h" +#include +#include "mesh.h" +#include "dict.h" +#include "priorityq.h" + +/* The begin/end calls must be properly nested. We keep track of + * the current state to enforce the ordering. + */ +enum TessState { T_DORMANT, T_IN_POLYGON, T_IN_CONTOUR }; + +/* We cache vertex data for single-contour polygons so that we can + * try a quick-and-dirty decomposition first. + */ +#define TESS_MAX_CACHE 100 + +typedef struct CachedVertex { + GLdouble coords[3]; + void *data; +} CachedVertex; + +struct GLUtesselator { + + /*** state needed for collecting the input data ***/ + + enum TessState state; /* what begin/end calls have we seen? */ + + GLUhalfEdge *lastEdge; /* lastEdge->Org is the most recent vertex */ + GLUmesh *mesh; /* stores the input contours, and eventually + the tessellation itself */ + + void (GLAPIENTRY *callError)( GLenum errnum ); + + /*** state needed for projecting onto the sweep plane ***/ + + GLdouble normal[3]; /* user-specified normal (if provided) */ + GLdouble sUnit[3]; /* unit vector in s-direction (debugging) */ + GLdouble tUnit[3]; /* unit vector in t-direction (debugging) */ + + /*** state needed for the line sweep ***/ + + GLdouble relTolerance; /* tolerance for merging features */ + GLenum windingRule; /* rule for determining polygon interior */ + GLboolean fatalError; /* fatal error: needed combine callback */ + + Dict *dict; /* edge dictionary for sweep line */ + PriorityQ *pq; /* priority queue of vertex events */ + GLUvertex *event; /* current sweep event being processed */ + + void (GLAPIENTRY *callCombine)( GLdouble coords[3], void *data[4], + GLfloat weight[4], void **outData ); + + /*** state needed for rendering callbacks (see render.c) ***/ + + GLboolean flagBoundary; /* mark boundary edges (use EdgeFlag) */ + GLboolean boundaryOnly; /* Extract contours, not triangles */ + GLUface *lonelyTriList; + /* list of triangles which could not be rendered as strips or fans */ + + void (GLAPIENTRY *callBegin)( GLenum type ); + void (GLAPIENTRY *callEdgeFlag)( GLboolean boundaryEdge ); + void (GLAPIENTRY *callVertex)( void *data ); + void (GLAPIENTRY *callEnd)( void ); + void (GLAPIENTRY *callMesh)( GLUmesh *mesh ); + + + /*** state needed to cache single-contour polygons for renderCache() */ + + GLboolean emptyCache; /* empty cache on next vertex() call */ + int cacheCount; /* number of cached vertices */ + CachedVertex cache[TESS_MAX_CACHE]; /* the vertex data */ + + /*** rendering callbacks that also pass polygon data ***/ + void (GLAPIENTRY *callBeginData)( GLenum type, void *polygonData ); + void (GLAPIENTRY *callEdgeFlagData)( GLboolean boundaryEdge, + void *polygonData ); + void (GLAPIENTRY *callVertexData)( void *data, void *polygonData ); + void (GLAPIENTRY *callEndData)( void *polygonData ); + void (GLAPIENTRY *callErrorData)( GLenum errnum, void *polygonData ); + void (GLAPIENTRY *callCombineData)( GLdouble coords[3], void *data[4], + GLfloat weight[4], void **outData, + void *polygonData ); + + jmp_buf env; /* place to jump to when memAllocs fail */ + + void *polygonData; /* client data for current polygon */ +}; + +void GLAPIENTRY __gl_noBeginData( GLenum type, void *polygonData ); +void GLAPIENTRY __gl_noEdgeFlagData( GLboolean boundaryEdge, void *polygonData ); +void GLAPIENTRY __gl_noVertexData( void *data, void *polygonData ); +void GLAPIENTRY __gl_noEndData( void *polygonData ); +void GLAPIENTRY __gl_noErrorData( GLenum errnum, void *polygonData ); +void GLAPIENTRY __gl_noCombineData( GLdouble coords[3], void *data[4], + GLfloat weight[4], void **outData, + void *polygonData ); + +#define CALL_BEGIN_OR_BEGIN_DATA(a) \ + if (tess->callBeginData != &__gl_noBeginData) \ + (*tess->callBeginData)((a),tess->polygonData); \ + else (*tess->callBegin)((a)); + +#define CALL_VERTEX_OR_VERTEX_DATA(a) \ + if (tess->callVertexData != &__gl_noVertexData) \ + (*tess->callVertexData)((a),tess->polygonData); \ + else (*tess->callVertex)((a)); + +#define CALL_EDGE_FLAG_OR_EDGE_FLAG_DATA(a) \ + if (tess->callEdgeFlagData != &__gl_noEdgeFlagData) \ + (*tess->callEdgeFlagData)((a),tess->polygonData); \ + else (*tess->callEdgeFlag)((a)); + +#define CALL_END_OR_END_DATA() \ + if (tess->callEndData != &__gl_noEndData) \ + (*tess->callEndData)(tess->polygonData); \ + else (*tess->callEnd)(); + +#define CALL_COMBINE_OR_COMBINE_DATA(a,b,c,d) \ + if (tess->callCombineData != &__gl_noCombineData) \ + (*tess->callCombineData)((a),(b),(c),(d),tess->polygonData); \ + else (*tess->callCombine)((a),(b),(c),(d)); + +#define CALL_ERROR_OR_ERROR_DATA(a) \ + if (tess->callErrorData != &__gl_noErrorData) \ + (*tess->callErrorData)((a),tess->polygonData); \ + else (*tess->callError)((a)); + +#endif diff --git a/vtm/jni/tessellate/tessellate.c b/vtm/jni/tessellate/tessellate.c new file mode 100644 index 00000000..d0e2d649 --- /dev/null +++ b/vtm/jni/tessellate/tessellate.c @@ -0,0 +1,382 @@ +#include +#include +#include "glu.h" +#include "tess.h" +#include +#include + +#ifdef __ANDROID__ +#include +#endif + +#ifndef uintptr_t +typedef unsigned long uintptr_t; +#endif +/******************************************************************************/ + +typedef struct Triangle { + int v[3]; + struct Triangle *prev; +} Triangle; + +typedef struct Vertex { + double pt[3]; + int index; + struct Vertex *prev; +} Vertex; + +typedef struct TessContext { + Triangle *latest_t; + int n_tris; + + Vertex *v_prev; + Vertex *v_prevprev; + Vertex *latest_v; + GLenum current_mode; + int odd_even_strip; + + void (*vertex_cb)(Vertex *, struct TessContext *); +} TessContext; + +void skip_vertex(Vertex *v, TessContext *ctx); + +/******************************************************************************/ + +TessContext *new_tess_context() +{ + TessContext *result = (TessContext *) malloc(sizeof(struct TessContext)); + result->latest_t = NULL; + result->latest_v = NULL; + result->n_tris = 0; + result->v_prev = NULL; + result->v_prevprev = NULL; + result->v_prev = NULL; + result->v_prev = NULL; + result->vertex_cb = &skip_vertex; + result->odd_even_strip = 0; + return result; +} + +void destroy_tess_context(TessContext *ctx) +{ + free(ctx); +} + +Vertex *new_vertex(TessContext *ctx, double x, double y) +{ + Vertex *result = (Vertex *) malloc(sizeof(Vertex)); + result->prev = ctx->latest_v; + result->pt[0] = x; + result->pt[1] = y; + result->pt[2] = 0; + + if (ctx->latest_v == NULL) { + result->index = 0; + } + else { + result->index = ctx->latest_v->index + 1; + } + return ctx->latest_v = result; +} + +Triangle *new_triangle(TessContext *ctx, int v1, int v2, int v3) +{ + Triangle *result = (Triangle *) malloc(sizeof(Triangle)); + result->prev = ctx->latest_t; + result->v[0] = v1; + result->v[1] = v2; + result->v[2] = v3; + ctx->n_tris++; + return ctx->latest_t = result; +} + +/******************************************************************************/ + +void skip_vertex(Vertex *v, TessContext *ctx) { +} +; + +void fan_vertex(Vertex *v, TessContext *ctx) { + if (ctx->v_prevprev == NULL) { + ctx->v_prevprev = v; + return; + } + if (ctx->v_prev == NULL) { + ctx->v_prev = v; + return; + } + new_triangle(ctx, ctx->v_prevprev->index, ctx->v_prev->index, v->index); + ctx->v_prev = v; +} + +void strip_vertex(Vertex *v, TessContext *ctx) +{ + if (ctx->v_prev == NULL) { + ctx->v_prev = v; + return; + } + if (ctx->v_prevprev == NULL) { + ctx->v_prevprev = v; + return; + } + if (ctx->odd_even_strip) { + new_triangle(ctx, ctx->v_prevprev->index, ctx->v_prev->index, v->index); + } + else { + new_triangle(ctx, ctx->v_prev->index, ctx->v_prevprev->index, v->index); + } + ctx->odd_even_strip = !ctx->odd_even_strip; + + ctx->v_prev = ctx->v_prevprev; + ctx->v_prevprev = v; +} + +void triangle_vertex(Vertex *v, TessContext *ctx) { + if (ctx->v_prevprev == NULL) { + ctx->v_prevprev = v; + return; + } + if (ctx->v_prev == NULL) { + ctx->v_prev = v; + return; + } + new_triangle(ctx, ctx->v_prevprev->index, ctx->v_prev->index, v->index); + ctx->v_prev = ctx->v_prevprev = NULL; +} + +void vertex(void *vertex_data, void *poly_data) +{ + Vertex *ptr = (Vertex *) vertex_data; + TessContext *ctx = (TessContext *) poly_data; + ctx->vertex_cb(ptr, ctx); +} + +void begin(GLenum which, void *poly_data) +{ + TessContext *ctx = (TessContext *) poly_data; + ctx->v_prev = ctx->v_prevprev = NULL; + ctx->odd_even_strip = 0; + switch (which) { + case GL_TRIANGLES: + ctx->vertex_cb = &triangle_vertex; + break; + case GL_TRIANGLE_STRIP: + ctx->vertex_cb = &strip_vertex; + break; + case GL_TRIANGLE_FAN: + ctx->vertex_cb = &fan_vertex; + break; + default: + printf(stderr, "ERROR, can't handle %d\n", (int) which); + ctx->vertex_cb = &skip_vertex; + break; + } +} + +void combine(const GLdouble newVertex[3], + const void *neighborVertex[4], + const GLfloat neighborWeight[4], void **outData, void *polyData) +{ + TessContext *ctx = (TessContext *) polyData; + Vertex *result = new_vertex(ctx, newVertex[0], newVertex[1]); + *outData = result; +} + +void write_output(TessContext *ctx, float **coordinates_out, int **tris_out, int *vc, int *tc) +{ + int n_verts = 1 + ctx->latest_v->index; + *vc = n_verts; + int n_tris_copy = ctx->n_tris; + *tc = ctx->n_tris; + *coordinates_out = malloc(n_verts * sizeof(float) * 2); + *tris_out = (ctx->n_tris ? malloc(ctx->n_tris * sizeof(int) * 3) : NULL); + + while (ctx->latest_v) { + (*coordinates_out)[2 * ctx->latest_v->index] = ctx->latest_v->pt[0]; + (*coordinates_out)[2 * ctx->latest_v->index + 1] = ctx->latest_v->pt[1]; + Vertex *prev = ctx->latest_v->prev; + free(ctx->latest_v); + ctx->latest_v = prev; + } + + while (ctx->latest_t) { + (*tris_out)[3 * (n_tris_copy - 1)] = ctx->latest_t->v[0]; + (*tris_out)[3 * (n_tris_copy - 1) + 1] = ctx->latest_t->v[1]; + (*tris_out)[3 * (n_tris_copy - 1) + 2] = ctx->latest_t->v[2]; + Triangle *prev = ctx->latest_t->prev; + free(ctx->latest_t); + ctx->latest_t = prev; + n_tris_copy--; + } +} + +TessContext *tessellate( + int *nverts, + int *ntris, + const float **contoursbegin, + const float **contoursend) +{ + const float *contourbegin, *contourend; + Vertex *current_vertex; + GLUtesselator *tess; + TessContext *ctx; + + tess = gluNewTess(); + ctx = new_tess_context(); + + gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (GLvoid (*)()) &vertex); + gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (GLvoid (*)()) &begin); + gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (GLvoid (*)()) &combine); + + gluTessBeginPolygon(tess, ctx); + do { + contourbegin = *contoursbegin++; + contourend = *contoursbegin; + gluTessBeginContour(tess); + while (contourbegin != contourend) { + current_vertex = new_vertex(ctx, contourbegin[0], contourbegin[1]); + contourbegin += 2; + gluTessVertex(tess, current_vertex->pt, current_vertex); + } + gluTessEndContour(tess); + } while (contoursbegin != (contoursend - 1)); + gluTessEndPolygon(tess); + + //write_output(ctx, verts, tris, nverts, ntris); + //destroy_tess_context(ctx); + + gluDeleteTess(tess); + + return ctx; +} +#ifdef __ANDROID__ +#define printf(...) __android_log_print(ANDROID_LOG_DEBUG, "Tesselate", __VA_ARGS__) +#endif + +#define CAST_CTX(x) (TessContext *)(uintptr_t) x + +void Java_org_oscim_renderer_sublayers_MeshLayer_tessFinish(JNIEnv *env, jclass c, + jlong ptr_context) { + + TessContext *ctx = CAST_CTX(ptr_context); + + while (ctx->latest_v) { + Vertex *prev = ctx->latest_v->prev; + free(ctx->latest_v); + ctx->latest_v = prev; + } + + while (ctx->latest_t) { + Triangle *prev = ctx->latest_t->prev; + free(ctx->latest_t); + ctx->latest_t = prev; + } + + destroy_tess_context(ctx); +} + +jint Java_org_oscim_renderer_sublayers_MeshLayer_tessGetCoordinates(JNIEnv *env, jclass c, + jlong ptr_context, jshortArray obj_coords, jfloat scale) { + + TessContext *ctx = CAST_CTX(ptr_context); + + int length = (*env)->GetArrayLength(env, obj_coords); + + jshort* coords = (jshort*) (*env)->GetPrimitiveArrayCritical(env, obj_coords, 0); + if (coords == NULL) { + return 0; + } + + int n_verts = 1 + ctx->latest_v->index; + int n_tris_copy = ctx->n_tris; + + int cnt = 0; + for (; ctx->latest_v && cnt < length; cnt += 2) { + coords[cnt + 0] = (ctx->latest_v->pt[0] * scale) + 0.5f; + coords[cnt + 1] = (ctx->latest_v->pt[1] * scale) + 0.5f; + Vertex *prev = ctx->latest_v->prev; + free(ctx->latest_v); + ctx->latest_v = prev; + } + (*env)->ReleasePrimitiveArrayCritical(env, obj_coords, coords, JNI_ABORT); + + return cnt; +} + +jint Java_org_oscim_renderer_sublayers_MeshLayer_tessGetIndices(JNIEnv *env, jclass c, + jlong ptr_context, jshortArray obj_indices) { + + TessContext *ctx = CAST_CTX(ptr_context); + + int length = (*env)->GetArrayLength(env, obj_indices); + + jshort* tris = (jshort*) (*env)->GetPrimitiveArrayCritical(env, obj_indices, 0); + if (tris == NULL) { + return 0; + } + + int n_tris_copy = ctx->n_tris; + + int cnt = 0; + + for (; ctx->latest_t && cnt < length; cnt += 3) { + tris[cnt + 0] = ctx->latest_t->v[0]; + tris[cnt + 1] = ctx->latest_t->v[1]; + tris[cnt + 2] = ctx->latest_t->v[2]; + Triangle *prev = ctx->latest_t->prev; + + free(ctx->latest_t); + ctx->latest_t = prev; + n_tris_copy--; + } + + ctx->n_tris = n_tris_copy; + + (*env)->ReleasePrimitiveArrayCritical(env, obj_indices, tris, JNI_ABORT); + + return cnt; +} + +jlong Java_org_oscim_renderer_sublayers_MeshLayer_tessellate(JNIEnv *env, jclass c, + jfloatArray obj_points, jint pos, + jshortArray obj_index, jint ipos, + jint num_rings) { //, jintArray obj_out) { + + jboolean isCopy; + + printf("add %d %d %d\n", pos, ipos, num_rings); + + float* orig_points = (float*) (*env)->GetPrimitiveArrayCritical(env, obj_points, &isCopy); + if (orig_points == NULL) + return 0; + + const float *points = orig_points + pos; + + jshort* orig_indices = (jshort*) (*env)->GetPrimitiveArrayCritical(env, obj_index, &isCopy); + if (orig_indices == NULL) { + (*env)->ReleasePrimitiveArrayCritical(env, obj_points, orig_points, JNI_ABORT); + return 0; + } + + jshort* indices = orig_indices + ipos; + + const float **rings = malloc(sizeof(float*) * (num_rings + 1)); + int offset = 0; + for (int i = 0; i < num_rings; i++) { + rings[i] = points + offset; + offset += indices[i]; + } + rings[num_rings] = points + offset; + + int nverts, ntris; + + TessContext *ctx = tessellate(&nverts, &ntris, + rings, rings + (num_rings + 1)); + + free(rings); + + (*env)->ReleasePrimitiveArrayCritical(env, obj_index, orig_indices, JNI_ABORT); + (*env)->ReleasePrimitiveArrayCritical(env, obj_points, orig_points, JNI_ABORT); + + return (long) ctx; +} diff --git a/vtm/jni/tessellate/tessellate.h b/vtm/jni/tessellate/tessellate.h new file mode 100644 index 00000000..e1ca013d --- /dev/null +++ b/vtm/jni/tessellate/tessellate.h @@ -0,0 +1,13 @@ +typedef struct Vertex { + double pt[3]; + int index; + struct Vertex *prev; +} Vertex; + +//void tessellate +// (double **verts, +// int *nverts, +// int **tris, +// int *ntris, +// const float **contoursbegin, +// const float **contoursend); diff --git a/vtm/jni/tessellate/tessmono.c b/vtm/jni/tessellate/tessmono.c new file mode 100644 index 00000000..4d084400 --- /dev/null +++ b/vtm/jni/tessellate/tessmono.c @@ -0,0 +1,201 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#include "gluos.h" +#include +#include "geom.h" +#include "mesh.h" +#include "tessmono.h" +#include + +#define AddWinding(eDst,eSrc) (eDst->winding += eSrc->winding, \ + eDst->Sym->winding += eSrc->Sym->winding) + +/* __gl_meshTessellateMonoRegion( face ) tessellates a monotone region + * (what else would it do??) The region must consist of a single + * loop of half-edges (see mesh.h) oriented CCW. "Monotone" in this + * case means that any vertical line intersects the interior of the + * region in a single interval. + * + * Tessellation consists of adding interior edges (actually pairs of + * half-edges), to split the region into non-overlapping triangles. + * + * The basic idea is explained in Preparata and Shamos (which I don''t + * have handy right now), although their implementation is more + * complicated than this one. The are two edge chains, an upper chain + * and a lower chain. We process all vertices from both chains in order, + * from right to left. + * + * The algorithm ensures that the following invariant holds after each + * vertex is processed: the untessellated region consists of two + * chains, where one chain (say the upper) is a single edge, and + * the other chain is concave. The left vertex of the single edge + * is always to the left of all vertices in the concave chain. + * + * Each step consists of adding the rightmost unprocessed vertex to one + * of the two chains, and forming a fan of triangles from the rightmost + * of two chain endpoints. Determining whether we can add each triangle + * to the fan is a simple orientation test. By making the fan as large + * as possible, we restore the invariant (check it yourself). + */ +int __gl_meshTessellateMonoRegion( GLUface *face ) +{ + GLUhalfEdge *up, *lo; + + /* All edges are oriented CCW around the boundary of the region. + * First, find the half-edge whose origin vertex is rightmost. + * Since the sweep goes from left to right, face->anEdge should + * be close to the edge we want. + */ + up = face->anEdge; + assert( up->Lnext != up && up->Lnext->Lnext != up ); + + for( ; VertLeq( up->Dst, up->Org ); up = up->Lprev ) + ; + for( ; VertLeq( up->Org, up->Dst ); up = up->Lnext ) + ; + lo = up->Lprev; + + while( up->Lnext != lo ) { + if( VertLeq( up->Dst, lo->Org )) { + /* up->Dst is on the left. It is safe to form triangles from lo->Org. + * The EdgeGoesLeft test guarantees progress even when some triangles + * are CW, given that the upper and lower chains are truly monotone. + */ + while( lo->Lnext != up && (EdgeGoesLeft( lo->Lnext ) + || EdgeSign( lo->Org, lo->Dst, lo->Lnext->Dst ) <= 0 )) { + GLUhalfEdge *tempHalfEdge= __gl_meshConnect( lo->Lnext, lo ); + if (tempHalfEdge == NULL) return 0; + lo = tempHalfEdge->Sym; + } + lo = lo->Lprev; + } else { + /* lo->Org is on the left. We can make CCW triangles from up->Dst. */ + while( lo->Lnext != up && (EdgeGoesRight( up->Lprev ) + || EdgeSign( up->Dst, up->Org, up->Lprev->Org ) >= 0 )) { + GLUhalfEdge *tempHalfEdge= __gl_meshConnect( up, up->Lprev ); + if (tempHalfEdge == NULL) return 0; + up = tempHalfEdge->Sym; + } + up = up->Lnext; + } + } + + /* Now lo->Org == up->Dst == the leftmost vertex. The remaining region + * can be tessellated in a fan from this leftmost vertex. + */ + assert( lo->Lnext != up ); + while( lo->Lnext->Lnext != up ) { + GLUhalfEdge *tempHalfEdge= __gl_meshConnect( lo->Lnext, lo ); + if (tempHalfEdge == NULL) return 0; + lo = tempHalfEdge->Sym; + } + + return 1; +} + + +/* __gl_meshTessellateInterior( mesh ) tessellates each region of + * the mesh which is marked "inside" the polygon. Each such region + * must be monotone. + */ +int __gl_meshTessellateInterior( GLUmesh *mesh ) +{ + GLUface *f, *next; + + /*LINTED*/ + for( f = mesh->fHead.next; f != &mesh->fHead; f = next ) { + /* Make sure we don''t try to tessellate the new triangles. */ + next = f->next; + if( f->inside ) { + if ( !__gl_meshTessellateMonoRegion( f ) ) return 0; + } + } + + return 1; +} + + +/* __gl_meshDiscardExterior( mesh ) zaps (ie. sets to NULL) all faces + * which are not marked "inside" the polygon. Since further mesh operations + * on NULL faces are not allowed, the main purpose is to clean up the + * mesh so that exterior loops are not represented in the data structure. + */ +void __gl_meshDiscardExterior( GLUmesh *mesh ) +{ + GLUface *f, *next; + + /*LINTED*/ + for( f = mesh->fHead.next; f != &mesh->fHead; f = next ) { + /* Since f will be destroyed, save its next pointer. */ + next = f->next; + if( ! f->inside ) { + __gl_meshZapFace( f ); + } + } +} + +#define MARKED_FOR_DELETION 0x7fffffff + +/* __gl_meshSetWindingNumber( mesh, value, keepOnlyBoundary ) resets the + * winding numbers on all edges so that regions marked "inside" the + * polygon have a winding number of "value", and regions outside + * have a winding number of 0. + * + * If keepOnlyBoundary is TRUE, it also deletes all edges which do not + * separate an interior region from an exterior one. + */ +int __gl_meshSetWindingNumber( GLUmesh *mesh, int value, + GLboolean keepOnlyBoundary ) +{ + GLUhalfEdge *e, *eNext; + + for( e = mesh->eHead.next; e != &mesh->eHead; e = eNext ) { + eNext = e->next; + if( e->Rface->inside != e->Lface->inside ) { + + /* This is a boundary edge (one side is interior, one is exterior). */ + e->winding = (e->Lface->inside) ? value : -value; + } else { + + /* Both regions are interior, or both are exterior. */ + if( ! keepOnlyBoundary ) { + e->winding = 0; + } else { + if ( !__gl_meshDelete( e ) ) return 0; + } + } + } + return 1; +} diff --git a/vtm/jni/tessellate/tessmono.h b/vtm/jni/tessellate/tessmono.h new file mode 100644 index 00000000..8ee1b2fe --- /dev/null +++ b/vtm/jni/tessellate/tessmono.h @@ -0,0 +1,71 @@ +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ +/* +** Author: Eric Veach, July 1994. +** +*/ + +#ifndef __tessmono_h_ +#define __tessmono_h_ + +/* __gl_meshTessellateMonoRegion( face ) tessellates a monotone region + * (what else would it do??) The region must consist of a single + * loop of half-edges (see mesh.h) oriented CCW. "Monotone" in this + * case means that any vertical line intersects the interior of the + * region in a single interval. + * + * Tessellation consists of adding interior edges (actually pairs of + * half-edges), to split the region into non-overlapping triangles. + * + * __gl_meshTessellateInterior( mesh ) tessellates each region of + * the mesh which is marked "inside" the polygon. Each such region + * must be monotone. + * + * __gl_meshDiscardExterior( mesh ) zaps (ie. sets to NULL) all faces + * which are not marked "inside" the polygon. Since further mesh operations + * on NULL faces are not allowed, the main purpose is to clean up the + * mesh so that exterior loops are not represented in the data structure. + * + * __gl_meshSetWindingNumber( mesh, value, keepOnlyBoundary ) resets the + * winding numbers on all edges so that regions marked "inside" the + * polygon have a winding number of "value", and regions outside + * have a winding number of 0. + * + * If keepOnlyBoundary is TRUE, it also deletes all edges which do not + * separate an interior region from an exterior one. + */ + +int __gl_meshTessellateMonoRegion( GLUface *face ); +int __gl_meshTessellateInterior( GLUmesh *mesh ); +void __gl_meshDiscardExterior( GLUmesh *mesh ); +int __gl_meshSetWindingNumber( GLUmesh *mesh, int value, + GLboolean keepOnlyBoundary ); + +#endif diff --git a/vtm/jni/triangle/README b/vtm/jni/triangle/README new file mode 100644 index 00000000..b33ea009 --- /dev/null +++ b/vtm/jni/triangle/README @@ -0,0 +1,198 @@ +Triangle +A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator. +Version 1.6 + +Show Me +A Display Program for Meshes and More. +Version 1.6 + +Copyright 1993, 1995, 1997, 1998, 2002, 2005 Jonathan Richard Shewchuk +2360 Woolsey #H +Berkeley, California 94705-1927 +Please send bugs and comments to jrs@cs.berkeley.edu + +Created as part of the Quake project (tools for earthquake simulation). +Supported in part by NSF Grant CMS-9318163 and an NSERC 1967 Scholarship. +There is no warranty whatsoever. Use at your own risk. + + +Triangle generates exact Delaunay triangulations, constrained Delaunay +triangulations, conforming Delaunay triangulations, Voronoi diagrams, and +high-quality triangular meshes. The latter can be generated with no small +or large angles, and are thus suitable for finite element analysis. +Show Me graphically displays the contents of the geometric files used by +Triangle. Show Me can also write images in PostScript form. + +Information on the algorithms used by Triangle, including complete +references, can be found in the comments at the beginning of the triangle.c +source file. Another listing of these references, with PostScript copies +of some of the papers, is available from the Web page + + http://www.cs.cmu.edu/~quake/triangle.research.html + +------------------------------------------------------------------------------ + +These programs may be freely redistributed under the condition that the +copyright notices (including the copy of this notice in the code comments +and the copyright notice printed when the `-h' switch is selected) are +not removed, and no compensation is received. Private, research, and +institutional use is free. You may distribute modified versions of this +code UNDER THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT +IN THE SAME FILE REMAIN UNDER COPYRIGHT OF THE ORIGINAL AUTHOR, BOTH +SOURCE AND OBJECT CODE ARE MADE FREELY AVAILABLE WITHOUT CHARGE, AND +CLEAR NOTICE IS GIVEN OF THE MODIFICATIONS. Distribution of this code as +part of a commercial system is permissible ONLY BY DIRECT ARRANGEMENT +WITH THE AUTHOR. (If you are not directly supplying this code to a +customer, and you are instead telling them how they can obtain it for +free, then you are not required to make any arrangement with me.) + +------------------------------------------------------------------------------ + +The files included in this distribution are: + + README The file you're reading now. + triangle.c Complete C source code for Triangle. + showme.c Complete C source code for Show Me. + triangle.h Include file for calling Triangle from another program. + tricall.c Sample program that calls Triangle. + makefile Makefile for compiling Triangle and Show Me. + A.poly A sample input file. + +Each of Triangle and Show Me is a single portable C file. The easiest way +to compile them is to edit and use the included makefile. Before +compiling, read the makefile, which describes your options, and edit it +accordingly. You should specify: + + The source and binary directories. + + The C compiler and level of optimization. + + The "correct" directories for include files (especially X include files), + if necessary. + + Do you want single precision or double? (The default is double.) Do you + want to leave out some of Triangle's features to reduce the size of the + executable file? Investigate the SINGLE, REDUCED, and CDT_ONLY symbols. + + If yours is not a Unix system, define the NO_TIMER symbol to remove the + Unix-specific timing code. Also, don't try to compile Show Me; it only + works with X Windows. + + If you are compiling on an Intel x86 CPU and using gcc w/Linux or + Microsoft C, be sure to define the LINUX or CPU86 (for Microsoft) symbol + during compilation so that the exact arithmetic works right. + +Once you've done this, type "make" to compile the programs. Alternatively, +the files are usually easy to compile without a makefile: + + cc -O -o triangle triangle.c -lm + cc -O -o showme showme.c -lX11 + +On some systems, the C compiler won't be able to find the X include files +or libraries, and you'll need to specify an include path or library path: + + cc -O -I/usr/local/include -o showme showme.c -L/usr/local/lib -lX11 + +Some processors, including Intel x86 family and possibly Motorola 68xxx +family chips, are IEEE conformant but have extended length internal +floating-point registers that may defeat Triangle's exact arithmetic +routines by failing to cause enough roundoff error! Typically, there is a +way to set these internal registers so that they are rounded off to IEEE +single or double precision format. I believe (but I'm not certain) that +Triangle has the right incantations for x86 chips, if you have gcc running +under Linux (define the LINUX compiler symbol) or Microsoft C (define the +CPU86 compiler symbol). + +If you have a different processor or operating system, or if I got the +incantations wrong, you should check your C compiler or system manuals to +find out how to configure these internal registers to the precision you are +using. Otherwise, the exact arithmetic routines won't be exact at all. +See http://www.cs.cmu.edu/~quake/robust.pc.html for details. Triangle's +exact arithmetic hasn't a hope of working on machines like the Cray C90 or +Y-MP, which are not IEEE conformant and have inaccurate rounding. + +Triangle and Show Me have both text and HTML documentation. The latter is +illustrated. Find it on the Web at + + http://www.cs.cmu.edu/~quake/triangle.html + http://www.cs.cmu.edu/~quake/showme.html + +Complete text instructions are printed by invoking each program with the +`-h' switch: + + triangle -h + showme -h + +The instructions are long; you'll probably want to pipe the output to +`more' or `lpr' or redirect it to a file. + +Both programs give a short list of command line options if they are invoked +without arguments (that is, just type `triangle' or `showme'). + +Try out Triangle on the enclosed sample file, A.poly: + + triangle -p A + showme A.poly & + +Triangle will read the Planar Straight Line Graph defined by A.poly, and +write its constrained Delaunay triangulation to A.1.node and A.1.ele. +Show Me will display the figure defined by A.poly. There are two buttons +marked "ele" in the Show Me window; click on the top one. This will cause +Show Me to load and display the triangulation. + +For contrast, try running + + triangle -pq A + +Now, click on the same "ele" button. A new triangulation will be loaded; +this one having no angles smaller than 20 degrees. + +To see a Voronoi diagram, try this: + + cp A.poly A.node + triangle -v A + +Click the "ele" button again. You will see the Delaunay triangulation of +the points in A.poly, without the segments. Now click the top "voro" button. +You will see the Voronoi diagram corresponding to that Delaunay triangulation. +Click the "Reset" button to see the full extent of the diagram. + +------------------------------------------------------------------------------ + +If you wish to call Triangle from another program, instructions for doing +so are contained in the file `triangle.h' (but read Triangle's regular +instructions first!). Also look at `tricall.c', which provides an example +of how to call Triangle. + +Type "make trilibrary" to create triangle.o, a callable object file. +Alternatively, the object file is usually easy to compile without a +makefile: + + cc -DTRILIBRARY -O -c triangle.c + +Type "make distclean" to remove all the object and executable files created +by make. + +------------------------------------------------------------------------------ + +If you use Triangle, and especially if you use it to accomplish real work, +I would like very much to hear from you. A short letter or email (to +jrs@cs.berkeley.edu) describing how you use Triangle will mean a lot to me. +The more people I know are using this program, the more easily I can +justify spending time on improvements and on the three-dimensional +successor to Triangle, which in turn will benefit you. Also, I can put you +on a list to receive email whenever a new version of Triangle is available. + +If you use a mesh generated by Triangle or plotted by Show Me in a +publication, please include an acknowledgment as well. And please spell +Triangle with a capital `T'! If you want to include a citation, use +`Jonathan Richard Shewchuk, ``Triangle: Engineering a 2D Quality Mesh +Generator and Delaunay Triangulator,'' in Applied Computational Geometry: +Towards Geometric Engineering (Ming C. Lin and Dinesh Manocha, editors), +volume 1148 of Lecture Notes in Computer Science, pages 203-222, +Springer-Verlag, Berlin, May 1996. (From the First ACM Workshop on Applied +Computational Geometry.)' + + +Jonathan Richard Shewchuk +July 27, 2005 diff --git a/vtm/jni/triangle/TriangleJni.c b/vtm/jni/triangle/TriangleJni.c new file mode 100644 index 00000000..56721f3a --- /dev/null +++ b/vtm/jni/triangle/TriangleJni.c @@ -0,0 +1,308 @@ +#include +#include +#include +#include +#include +#include "triangle.h" + +#ifdef __ANDROID__ +#include +#define printf(...) __android_log_print(ANDROID_LOG_DEBUG, "Triangle", __VA_ARGS__) +#endif + +// from www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html +#if 0 +int pnpoly(int nvert, float *vert, float testx, float testy) +{ + int i, j, c = 0; + for (i = 0, j = (nvert-1)*2; i < nvert * 2; j = i++) + { + if ( ((vert[i*2+1] > testy) != (vert[j*j+1] > testy)) && + (testx < (vert[j*2]-vert[i*2]) + * (testy - vert[i*2+1]) + / (vert[j*2+1]-vert[i*2+1]) + vert[i*2]) ) + c = !c; + } + return c; +} + +int compare_dups(const void *a, const void *b) { + int da = *((const long*) a); + int db = *((const long*) b); + return (da > db) - (da < db); +} + +void shiftSegment(TriangleIO *in, int *seg, int pos) { + int size = (in->numberofsegments - pos - 1) * sizeof(int) * 2; + printf("shift %d - %d %d\n", size, in->numberofsegments, pos); + if (size > 0) + memmove(seg, seg + 2, size); + + in->numberofsegments -= 1; +} +struct { + int p1; + int p2; +} segment; + +#endif + +static void printPoly(TriangleIO *in) { + // print poly format to check with triangle/showme + printf("%d 2 0 0\n", in->numberofpoints); + for (int j = 0; j < in->numberofpoints; j++) + printf("%d %f %f\n", j, in->pointlist[j*2], in->pointlist[j*2+1]); + + int *seg = in->segmentlist; + printf("%d 0\n", in->numberofsegments); + for (int j = 0; j < in->numberofsegments; j++, seg += 2) + printf("%d %d %d\n", j, *seg, *(seg+1)); + + printf("%d 0\n", in->numberofholes); + for (int j = 0; j < in->numberofholes; j++) { + printf("%d %f %f\n", j, in->holelist[j*2], in->holelist[j*2+1]); + } +} + +jint Java_org_oscim_renderer_sublayers_ExtrusionLayer_triangulate(JNIEnv *env, jclass c, + jfloatArray obj_points, jint pos, jint len, jint num_rings, jobject indice_buf, jint offset) { + + jshort* indices = (jshort*) (*env)->GetDirectBufferAddress(env, indice_buf); + jboolean isCopy; + + float* orig_points = (float*) (*env)->GetPrimitiveArrayCritical(env, obj_points, &isCopy); + if (orig_points == NULL) + return 0; + + float *points = orig_points + pos; + + TriangleIO in, out; + + memset(&in, 0, sizeof(TriangleIO)); + + in.numberofpoints = len >> 1; + in.pointlist = (float *) points; + + // check if explicitly closed + if (in.pointlist[0] == in.pointlist[indices[0] - 2] + && in.pointlist[1] == in.pointlist[indices[0] - 1]) { + int point = 0; + for (int i = 0; i < num_rings; i++) { + // remove last point in ring + indices[i] -= 2; + int last = point + (indices[i] >> 1); + + if (in.numberofpoints - last > 1) + memmove(in.pointlist + (last * 2), in.pointlist + ((last + 1) * 2), + (in.numberofpoints - last - 1) * 2 * sizeof(float)); + + in.numberofpoints--; + point = last; + } + } + + int dups = 0; + + float *i_points = points; + int *skip_list = NULL; + + // check for duplicate vertices and keep a list + // of dups and the first occurence + for (int i = 0; i < in.numberofpoints - 1; i++) { + float x = *i_points++; + float y = *i_points++; + float *j_points = i_points; + + for (int j = i + 1; j < in.numberofpoints; j++, j_points += 2) { + if ((*j_points == x) && (*(j_points + 1) == y)) { + skip_list = realloc(skip_list, (dups + 2) * 2 * sizeof(int)); + skip_list[dups * 2 + 0] = j; + skip_list[dups * 2 + 1] = i; + dups++; + } + } + } + + in.segmentlist = (int *) malloc(in.numberofpoints * 2 * sizeof(int)); + in.numberofsegments = in.numberofpoints; + in.numberofholes = num_rings - 1; + + int *rings = NULL; + if (in.numberofholes > 0) { + in.holelist = (float *) malloc(in.numberofholes * 2 * sizeof(float)); + rings = (int*) malloc(num_rings * sizeof(int)); + } + + int *seg = in.segmentlist; + float *hole = in.holelist; + + // counter going through all points + int point; + // counter going through all rings + int ring; + + // assign all points to segments for each ring + for (ring = 0, point = 0; ring < num_rings; ring++, point++) { + int len; + int num_points = indices[ring] >> 1; + + if (rings) + rings[ring] = num_points; + + // add holes: we need a point inside the hole... + // this is just a heuristic, assuming that two + // 'parallel' lines have a distance of at least + // 1 unit. you'll notice when things went wrong + // when the hole is rendered instead of the poly + if (ring > 0) { + int k = point * 2; + + float nx = in.pointlist[k++]; + float ny = in.pointlist[k++]; + + float cx = 0, cy = 0, vx = 0, vy = 0; + + // try to find a large enough segment + for (len = (point + num_points) * 2; k < len;) { + cx = nx; + cy = ny; + + nx = in.pointlist[k++]; + ny = in.pointlist[k++]; + + vx = nx - cx; + vy = ny - cy; + + if (vx > 4 || vx < -4 || vy > 4 || vy < -4) + break; + } + + float a = sqrt(vx * vx + vy * vy); + + float ux = -vy / a; + float uy = vx / a; + + float centerx = cx + vx / 2.0 - (ux * 0.1); + float centery = cy + vy / 2.0 - (uy * 0.1); + + *hole++ = centerx; + *hole++ = centery; + } + + // close ring + int last = point + (num_points - 1); + *seg++ = last; + *seg++ = point; + + for (len = point + num_points - 1; point < len; point++) { + *seg++ = point; + *seg++ = point + 1; + } + } + + if (dups) { + for (int i = 0; i < dups; i++) { + printf("duplicate points at %d, %d: %f,%f\n", + skip_list[i*2], skip_list[i*2+1], + in.pointlist[skip_list[i*2+1]*2], + in.pointlist[skip_list[i*2+1]*2+1]); + } + printPoly(&in); + + // replace duplicate positions with first occurence + for (int i = 0; i < dups; i++) { + // position of the duplicate vertex + int pos = skip_list[i * 2] - i; + // first vertex + int replacement = skip_list[i * 2 + 1]; + + seg = in.segmentlist; + for (int j = 0; j < in.numberofsegments * 2; j++, seg++) { + if (*seg == pos) { + printf("%d: %d <- %d", j, pos, replacement); + *seg = replacement; + } + } + } + } + + memset(&out, 0, sizeof(TriangleIO)); + out.trianglelist = (INDICE*) indices; + + // p - use polygon input, for CDT + // z - zero offset array offsets... + // P - no poly output + // N - no node output + // B - no bound output + // Q - be quiet! + + TriangleOptions opt; + memset(&opt, 0, sizeof(TriangleOptions)); + + opt.dwyer = 1; + opt.steiner = -1; + opt.order = 1; + opt.maxarea = -1.0; + + opt.poly = 1; + opt.usesegments = 1; + opt.nopolywritten = 1; + opt.nonodewritten = 1; + opt.nobound = 1; + opt.quiet = 1; + + triangulate(&opt, &in, &out, (TriangleIO *) NULL); + + if (in.numberofpoints < out.numberofpoints) { + // TODO rerun with 'nonodewritten = 0' + printf( "polygon input is bad! points in:%d out%d\n", in.numberofpoints, out.numberofpoints); + out.numberoftriangles = 0; + } + else if (out.trianglelist) + { + // scale to stride and add offset + short stride = 2; + + if (offset < 0) + offset = 0; + + INDICE *tri = out.trianglelist; + + for (int n = out.numberoftriangles * 3; n > 0; n--, tri++) + *tri = *tri * stride + offset; + + // when a ring has an odd number of points one (or rather two) + // additional vertices will be added. so the following rings + // needs extra offset... + int start = offset; + for (int j = 0, m = in.numberofholes; j < m; j++) { + start += rings[j] * stride; + + // even number of points? + if (!(rings[j] & 1)) + continue; + + tri = out.trianglelist; + int n = out.numberoftriangles * 3; + + for (; n-- > 0; tri++) + if (*tri >= start) + *tri += stride; + + start += stride; + } + } + else + { + printf( "triangle failed %d\n", out.numberofpoints); + } + + (*env)->ReleasePrimitiveArrayCritical(env, obj_points, orig_points, JNI_ABORT); + + free(in.segmentlist); + free(in.holelist); + free(rings); + free(skip_list); + + return out.numberoftriangles; +} diff --git a/vtm/jni/triangle/triangle.c b/vtm/jni/triangle/triangle.c new file mode 100644 index 00000000..c4e1a3ca --- /dev/null +++ b/vtm/jni/triangle/triangle.c @@ -0,0 +1,7390 @@ +/*****************************************************************************/ +/* */ +/* 888888888 ,o, / 888 */ +/* 888 88o88o " o8888o 88o8888o o88888o 888 o88888o */ +/* 888 888 888 88b 888 888 888 888 888 d888 88b */ +/* 888 888 888 o88^o888 888 888 "88888" 888 8888oo888 */ +/* 888 888 888 C888 888 888 888 / 888 q888 */ +/* 888 888 888 "88o^888 888 888 Cb 888 "88oooo" */ +/* "8oo8D */ +/* */ +/* A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator. */ +/* (triangle.c) */ +/* */ +/* Version 1.6 */ +/* July 28, 2005 */ +/* */ +/* Copyright 1993, 1995, 1997, 1998, 2002, 2005 */ +/* Jonathan Richard Shewchuk */ +/* 2360 Woolsey #H */ +/* Berkeley, California 94705-1927 */ +/* jrs@cs.berkeley.edu */ +/* */ +/* This program may be freely redistributed under the condition that the */ +/* copyright notices (including this entire header and the copyright */ +/* notice printed when the `-h' switch is selected) are not removed, and */ +/* no compensation is received. Private, research, and institutional */ +/* use is free. You may distribute modified versions of this code UNDER */ +/* THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT IN THE */ +/* SAME FILE REMAIN UNDER COPYRIGHT OF THE ORIGINAL AUTHOR, BOTH SOURCE */ +/* AND OBJECT CODE ARE MADE FREELY AVAILABLE WITHOUT CHARGE, AND CLEAR */ +/* NOTICE IS GIVEN OF THE MODIFICATIONS. Distribution of this code as */ +/* part of a commercial system is permissible ONLY BY DIRECT ARRANGEMENT */ +/* WITH THE AUTHOR. (If you are not directly supplying this code to a */ +/* customer, and you are instead telling them how they can obtain it for */ +/* free, then you are not required to make any arrangement with me.) */ +/* */ +/* Hypertext instructions for Triangle are available on the Web at */ +/* */ +/* http://www.cs.cmu.edu/~quake/triangle.html */ +/* */ +/* Disclaimer: Neither I nor Carnegie Mellon warrant this code in any way */ +/* whatsoever. This code is provided "as-is". Use at your own risk. */ +/* */ +/* Some of the references listed below are marked with an asterisk. [*] */ +/* These references are available for downloading from the Web page */ +/* */ +/* http://www.cs.cmu.edu/~quake/triangle.research.html */ +/* */ +/* Three papers discussing aspects of Triangle are available. A short */ +/* overview appears in "Triangle: Engineering a 2D Quality Mesh */ +/* Generator and Delaunay Triangulator," in Applied Computational */ +/* Geometry: Towards Geometric Engineering, Ming C. Lin and Dinesh */ +/* Manocha, editors, Lecture Notes in Computer Science volume 1148, */ +/* pages 203-222, Springer-Verlag, Berlin, May 1996 (from the First ACM */ +/* Workshop on Applied Computational Geometry). [*] */ +/* */ +/* The algorithms are discussed in the greatest detail in "Delaunay */ +/* Refinement Algorithms for Triangular Mesh Generation," Computational */ +/* Geometry: Theory and Applications 22(1-3):21-74, May 2002. [*] */ +/* */ +/* More detail about the data structures may be found in my dissertation: */ +/* "Delaunay Refinement Mesh Generation," Ph.D. thesis, Technical Report */ +/* CMU-CS-97-137, School of Computer Science, Carnegie Mellon University, */ +/* Pittsburgh, Pennsylvania, 18 May 1997. [*] */ +/* */ +/* Triangle was created as part of the Quake Project in the School of */ +/* Computer Science at Carnegie Mellon University. For further */ +/* information, see Hesheng Bao, Jacobo Bielak, Omar Ghattas, Loukas F. */ +/* Kallivokas, David R. O'Hallaron, Jonathan R. Shewchuk, and Jifeng Xu, */ +/* "Large-scale Simulation of Elastic Wave Propagation in Heterogeneous */ +/* Media on Parallel Computers," Computer Methods in Applied Mechanics */ +/* and Engineering 152(1-2):85-102, 22 January 1998. */ +/* */ +/* Triangle's Delaunay refinement algorithm for quality mesh generation is */ +/* a hybrid of one due to Jim Ruppert, "A Delaunay Refinement Algorithm */ +/* for Quality 2-Dimensional Mesh Generation," Journal of Algorithms */ +/* 18(3):548-585, May 1995 [*], and one due to L. Paul Chew, "Guaranteed- */ +/* Quality Mesh Generation for Curved Surfaces," Proceedings of the Ninth */ +/* Annual Symposium on Computational Geometry (San Diego, California), */ +/* pages 274-280, Association for Computing Machinery, May 1993, */ +/* http://portal.acm.org/citation.cfm?id=161150 . */ +/* */ +/* The Delaunay refinement algorithm has been modified so that it meshes */ +/* domains with small input angles well, as described in Gary L. Miller, */ +/* Steven E. Pav, and Noel J. Walkington, "When and Why Ruppert's */ +/* Algorithm Works," Twelfth International Meshing Roundtable, pages */ +/* 91-102, Sandia National Laboratories, September 2003. [*] */ +/* */ +/* My implementation of the divide-and-conquer and incremental Delaunay */ +/* triangulation algorithms follows closely the presentation of Guibas */ +/* and Stolfi, even though I use a triangle-based data structure instead */ +/* of their quad-edge data structure. (In fact, I originally implemented */ +/* Triangle using the quad-edge data structure, but the switch to a */ +/* triangle-based data structure sped Triangle by a factor of two.) The */ +/* mesh manipulation primitives and the two aforementioned Delaunay */ +/* triangulation algorithms are described by Leonidas J. Guibas and Jorge */ +/* Stolfi, "Primitives for the Manipulation of General Subdivisions and */ +/* the Computation of Voronoi Diagrams," ACM Transactions on Graphics */ +/* 4(2):74-123, April 1985, http://portal.acm.org/citation.cfm?id=282923 .*/ +/* */ +/* Their O(n log n) divide-and-conquer algorithm is adapted from Der-Tsai */ +/* Lee and Bruce J. Schachter, "Two Algorithms for Constructing the */ +/* Delaunay Triangulation," International Journal of Computer and */ +/* Information Science 9(3):219-242, 1980. Triangle's improvement of the */ +/* divide-and-conquer algorithm by alternating between vertical and */ +/* horizontal cuts was introduced by Rex A. Dwyer, "A Faster Divide-and- */ +/* Conquer Algorithm for Constructing Delaunay Triangulations," */ +/* Algorithmica 2(2):137-151, 1987. */ +/* */ +/* The incremental insertion algorithm was first proposed by C. L. Lawson, */ +/* "Software for C1 Surface Interpolation," in Mathematical Software III, */ +/* John R. Rice, editor, Academic Press, New York, pp. 161-194, 1977. */ +/* For point location, I use the algorithm of Ernst P. Mucke, Isaac */ +/* Saias, and Binhai Zhu, "Fast Randomized Point Location Without */ +/* Preprocessing in Two- and Three-Dimensional Delaunay Triangulations," */ +/* Proceedings of the Twelfth Annual Symposium on Computational Geometry, */ +/* ACM, May 1996. [*] If I were to randomize the order of vertex */ +/* insertion (I currently don't bother), their result combined with the */ +/* result of Kenneth L. Clarkson and Peter W. Shor, "Applications of */ +/* Random Sampling in Computational Geometry II," Discrete & */ +/* Computational Geometry 4(1):387-421, 1989, would yield an expected */ +/* O(n^{4/3}) bound on running time. */ +/* */ +/* The O(n log n) sweepline Delaunay triangulation algorithm is taken from */ +/* Steven Fortune, "A Sweepline Algorithm for Voronoi Diagrams", */ +/* Algorithmica 2(2):153-174, 1987. A random sample of edges on the */ +/* boundary of the triangulation are maintained in a splay tree for the */ +/* purpose of point location. Splay trees are described by Daniel */ +/* Dominic Sleator and Robert Endre Tarjan, "Self-Adjusting Binary Search */ +/* Trees," Journal of the ACM 32(3):652-686, July 1985, */ +/* http://portal.acm.org/citation.cfm?id=3835 . */ +/* */ +/* The algorithms for exact computation of the signs of determinants are */ +/* described in Jonathan Richard Shewchuk, "Adaptive Precision Floating- */ +/* Point Arithmetic and Fast Robust Geometric Predicates," Discrete & */ +/* Computational Geometry 18(3):305-363, October 1997. (Also available */ +/* as Technical Report CMU-CS-96-140, School of Computer Science, */ +/* Carnegie Mellon University, Pittsburgh, Pennsylvania, May 1996.) [*] */ +/* An abbreviated version appears as Jonathan Richard Shewchuk, "Robust */ +/* Adaptive Floating-Point Geometric Predicates," Proceedings of the */ +/* Twelfth Annual Symposium on Computational Geometry, ACM, May 1996. [*] */ +/* Many of the ideas for my exact arithmetic routines originate with */ +/* Douglas M. Priest, "Algorithms for Arbitrary Precision Floating Point */ +/* Arithmetic," Tenth Symposium on Computer Arithmetic, pp. 132-143, IEEE */ +/* Computer Society Press, 1991. [*] Many of the ideas for the correct */ +/* evaluation of the signs of determinants are taken from Steven Fortune */ +/* and Christopher J. Van Wyk, "Efficient Exact Arithmetic for Computa- */ +/* tional Geometry," Proceedings of the Ninth Annual Symposium on */ +/* Computational Geometry, ACM, pp. 163-172, May 1993, and from Steven */ +/* Fortune, "Numerical Stability of Algorithms for 2D Delaunay Triangu- */ +/* lations," International Journal of Computational Geometry & Applica- */ +/* tions 5(1-2):193-213, March-June 1995. */ +/* */ +/* The method of inserting new vertices off-center (not precisely at the */ +/* circumcenter of every poor-quality triangle) is from Alper Ungor, */ +/* "Off-centers: A New Type of Steiner Points for Computing Size-Optimal */ +/* Quality-Guaranteed Delaunay Triangulations," Proceedings of LATIN */ +/* 2004 (Buenos Aires, Argentina), April 2004. */ +/* */ +/* For definitions of and results involving Delaunay triangulations, */ +/* constrained and conforming versions thereof, and other aspects of */ +/* triangular mesh generation, see the excellent survey by Marshall Bern */ +/* and David Eppstein, "Mesh Generation and Optimal Triangulation," in */ +/* Computing and Euclidean Geometry, Ding-Zhu Du and Frank Hwang, */ +/* editors, World Scientific, Singapore, pp. 23-90, 1992. [*] */ +/* */ +/* The time for incrementally adding PSLG (planar straight line graph) */ +/* segments to create a constrained Delaunay triangulation is probably */ +/* O(t^2) per segment in the worst case and O(t) per segment in the */ +/* common case, where t is the number of triangles that intersect the */ +/* segment before it is inserted. This doesn't count point location, */ +/* which can be much more expensive. I could improve this to O(d log d) */ +/* time, but d is usually quite small, so it's not worth the bother. */ +/* (This note does not apply when the -s switch is used, invoking a */ +/* different method is used to insert segments.) */ +/* */ +/* The time for deleting a vertex from a Delaunay triangulation is O(d^2) */ +/* in the worst case and O(d) in the common case, where d is the degree */ +/* of the vertex being deleted. I could improve this to O(d log d) time, */ +/* but d is usually quite small, so it's not worth the bother. */ +/* */ +/* Ruppert's Delaunay refinement algorithm typically generates triangles */ +/* at a linear rate (constant time per triangle) after the initial */ +/* triangulation is formed. There may be pathological cases where */ +/* quadratic time is required, but these never arise in practice. */ +/* */ +/* The geometric predicates (circumcenter calculations, segment */ +/* intersection formulae, etc.) appear in my "Lecture Notes on Geometric */ +/* Robustness" at http://www.cs.berkeley.edu/~jrs/mesh . */ +/* */ +/* If you make any improvements to this code, please please please let me */ +/* know, so that I may obtain the improvements. Even if you don't change */ +/* the code, I'd still love to hear what it's being used for. */ +/* */ +/*****************************************************************************/ + +#include "triangle_private.h" + +/* Fast lookup arrays to speed some of the mesh manipulation primitives. */ + +int plus1mod3[3] = { 1, 2, 0 }; +int minus1mod3[3] = { 2, 0, 1 }; + +/********* User-defined triangle evaluation routine begins here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* triunsuitable() Determine if a triangle is unsuitable, and thus must */ +/* be further refined. */ +/* */ +/* You may write your own procedure that decides whether or not a selected */ +/* triangle is too big (and needs to be refined). There are two ways to do */ +/* this. */ +/* */ +/* (1) Modify the procedure `triunsuitable' below, then recompile */ +/* Triangle. */ +/* */ +/* (2) Define the symbol EXTERNAL_TEST (either by adding the definition */ +/* to this file, or by using the appropriate compiler switch). This way, */ +/* you can compile triangle.c separately from your test. Write your own */ +/* `triunsuitable' procedure in a separate C file (using the same prototype */ +/* as below). Compile it and link the object code with triangle.o. */ +/* */ +/* This procedure returns 1 if the triangle is too large and should be */ +/* refined; 0 otherwise. */ +/* */ +/*****************************************************************************/ + +#ifdef EXTERNAL_TEST + +int triunsuitable(); + +#else /* not EXTERNAL_TEST */ + +int triunsuitable(vertex triorg, vertex tridest, vertex triapex, REAL area) { + REAL dxoa, dxda, dxod; + REAL dyoa, dyda, dyod; + REAL oalen, dalen, odlen; + REAL maxlen; + + dxoa = triorg[0] - triapex[0]; + dyoa = triorg[1] - triapex[1]; + dxda = tridest[0] - triapex[0]; + dyda = tridest[1] - triapex[1]; + dxod = triorg[0] - tridest[0]; + dyod = triorg[1] - tridest[1]; + /* Find the squares of the lengths of the triangle's three edges. */ + oalen = dxoa * dxoa + dyoa * dyoa; + dalen = dxda * dxda + dyda * dyda; + odlen = dxod * dxod + dyod * dyod; + /* Find the square of the length of the longest edge. */ + maxlen = (dalen > oalen) ? dalen : oalen; + maxlen = (odlen > maxlen) ? odlen : maxlen; + + if (maxlen > 0.05 * (triorg[0] * triorg[0] + triorg[1] * triorg[1]) + 0.02) { + return 1; + } + else { + return 0; + } +} + +#endif /* not EXTERNAL_TEST */ + +/** **/ +/** **/ +/********* User-defined triangle evaluation routine ends here *********/ + +/********* Memory allocation and program exit wrappers begin here *********/ +/** **/ +/** **/ + +void triexit(int status) { + printf("Exit %d.\n", status); + + exit(status); +} + +VOID *trimalloc(int size) { + VOID *memptr; + + memptr = (VOID *) malloc((unsigned int) size); + if (memptr == (VOID *) NULL) { + printf("Error: Out of memory.\n"); + triexit(1); + } + return (memptr); +} + +void trifree(VOID *memptr) { + free(memptr); +} + +/** **/ +/** **/ +/********* Memory allocation and program exit wrappers end here *********/ + +/********* User interaction routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* internalerror() Ask the user to send me the defective product. Exit. */ +/* */ +/*****************************************************************************/ + +int error_set = 0; +void internalerror() { + error_set = 1; + printf("Triangle is going to quit its job now\n"); + //printf(" Please report this bug to jrs@cs.berkeley.edu\n"); + ///printf(" Include the message above, your input data set, and the exact\n"); + //printf(" command line you used to run Triangle.\n"); + //triexit(1); +} + +/*****************************************************************************/ +/* */ +/* parsecommandline() Read the command line, identify switches, and set */ +/* up options and file names. */ +/* */ +/*****************************************************************************/ + +void parsecommandline(int argc, char **argv, struct behavior *b) { + error_set = 0; + +#define STARTINDEX 0 + + int i, j; + + b->poly = b->refine = b->quality = 0; + b->vararea = b->fixedarea = b->usertest = 0; + b->regionattrib = b->convex = b->weighted = b->jettison = 0; + b->firstnumber = 1; + b->edgesout = b->voronoi = b->neighbors = b->geomview = 0; + b->nobound = b->nopolywritten = b->nonodewritten = b->noelewritten = 0; + b->noiterationnum = 0; + b->noholes = b->noexact = 0; + b->incremental = b->sweepline = 0; + b->dwyer = 1; + b->splitseg = 0; + b->docheck = 0; + b->nobisect = 0; + b->conformdel = 0; + b->steiner = -1; + b->order = 1; + b->minangle = 0.0; + b->maxarea = -1.0; + b->quiet = b->verbose = 0; + + for (i = STARTINDEX; i < argc; i++) { + for (j = STARTINDEX; argv[i][j] != '\0'; j++) { + if (argv[i][j] == 'p') { + b->poly = 1; + } +#ifndef CDT_ONLY + if (argv[i][j] == 'r') + { + b->refine = 1; + } + if (argv[i][j] == 'q') + { + b->quality = 1; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) + { + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) + { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + b->minangle = (REAL) strtod(workstring, (char **) NULL); + } + else + { + b->minangle = 20.0; + } + } + if (argv[i][j] == 'a') + { + b->quality = 1; + if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) + { + b->fixedarea = 1; + k = 0; + while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) || + (argv[i][j + 1] == '.')) + { + j++; + workstring[k] = argv[i][j]; + k++; + } + workstring[k] = '\0'; + b->maxarea = (REAL) strtod(workstring, (char **) NULL); + if (b->maxarea <= 0.0) + { + printf("Error: Maximum area must be greater than zero.\n"); + triexit(1); + } + } + else + { + b->vararea = 1; + } + } + if (argv[i][j] == 'u') + { + b->quality = 1; + b->usertest = 1; + } +#endif /* not CDT_ONLY */ + if (argv[i][j] == 'A') { + b->regionattrib = 1; + } + if (argv[i][j] == 'c') { + b->convex = 1; + } + if (argv[i][j] == 'w') { + b->weighted = 1; + } + if (argv[i][j] == 'W') { + b->weighted = 2; + } + if (argv[i][j] == 'j') { + b->jettison = 1; + } + if (argv[i][j] == 'z') { + b->firstnumber = 0; + } + if (argv[i][j] == 'e') { + b->edgesout = 1; + } + if (argv[i][j] == 'v') { + b->voronoi = 1; + } + if (argv[i][j] == 'n') { + b->neighbors = 1; + } + if (argv[i][j] == 'g') { + b->geomview = 1; + } + if (argv[i][j] == 'B') { + b->nobound = 1; + } + if (argv[i][j] == 'P') { + b->nopolywritten = 1; + } + if (argv[i][j] == 'N') { + b->nonodewritten = 1; + } + if (argv[i][j] == 'E') { + b->noelewritten = 1; + } + if (argv[i][j] == 'O') { + b->noholes = 1; + } + if (argv[i][j] == 'X') { + b->noexact = 1; + } + if (argv[i][j] == 'o') { + if (argv[i][j + 1] == '2') { + j++; + b->order = 2; + } + } +#ifndef CDT_ONLY + if (argv[i][j] == 'Y') + { + b->nobisect++; + } + if (argv[i][j] == 'S') + { + b->steiner = 0; + while ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) + { + j++; + b->steiner = b->steiner * 10 + (int) (argv[i][j] - '0'); + } + } +#endif /* not CDT_ONLY */ +#ifndef REDUCED + if (argv[i][j] == 'i') + { + b->incremental = 1; + } + if (argv[i][j] == 'F') + { + b->sweepline = 1; + } +#endif /* not REDUCED */ + if (argv[i][j] == 'l') { + b->dwyer = 0; + } +#ifndef REDUCED +#ifndef CDT_ONLY + if (argv[i][j] == 's') + { + b->splitseg = 1; + } + if ((argv[i][j] == 'D') || (argv[i][j] == 'L')) + { + b->quality = 1; + b->conformdel = 1; + } +#endif /* not CDT_ONLY */ + if (argv[i][j] == 'C') + { + b->docheck = 1; + } +#endif /* not REDUCED */ + if (argv[i][j] == 'Q') { + b->quiet = 1; + } + if (argv[i][j] == 'V') { + b->verbose++; + } + } + } + b->usesegments = b->poly || b->refine || b->quality || b->convex; + b->goodangle = cos(b->minangle * PI / 180.0); + if (b->goodangle == 1.0) { + b->offconstant = 0.0; + } + else { + b->offconstant = 0.475 * sqrt((1.0 + b->goodangle) / (1.0 - b->goodangle)); + } + b->goodangle *= b->goodangle; + if (b->refine && b->noiterationnum) { + printf( "Error: You cannot use the -I switch when refining a triangulation.\n"); + triexit(1); + } + /* Be careful not to allocate space for element area constraints that */ + /* will never be assigned any value (other than the default -1.0). */ + if (!b->refine && !b->poly) { + b->vararea = 0; + } + /* Be careful not to add an extra attribute to each element unless the */ + /* input supports it (PSLG in, but not refining a preexisting mesh). */ + if (b->refine || !b->poly) { + b->regionattrib = 0; + } + /* Regular/weighted triangulations are incompatible with PSLGs */ + /* and meshing. */ + if (b->weighted && (b->poly || b->quality)) { + b->weighted = 0; + if (!b->quiet) { + printf("Warning: weighted triangulations (-w, -W) are incompatible\n"); + printf(" with PSLGs (-p) and meshing (-q, -a, -u). Weights ignored.\n"); + } + } + if (b->jettison && b->nonodewritten && !b->quiet) { + printf("Warning: -j and -N switches are somewhat incompatible.\n"); + printf(" If any vertices are jettisoned, you will need the output\n"); + printf(" .node file to reconstruct the new node indices."); + } +} + +/** **/ +/** **/ +/********* User interaction routines begin here *********/ + +/********* Memory management routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* poolzero() Set all of a pool's fields to zero. */ +/* */ +/* This procedure should never be called on a pool that has any memory */ +/* allocated to it, as that memory would leak. */ +/* */ +/*****************************************************************************/ + +void poolzero(struct memorypool *pool) { + pool->firstblock = (VOID **) NULL; + pool->nowblock = (VOID **) NULL; + pool->nextitem = (VOID *) NULL; + pool->deaditemstack = (VOID *) NULL; + pool->pathblock = (VOID **) NULL; + pool->pathitem = (VOID *) NULL; + pool->alignbytes = 0; + pool->itembytes = 0; + pool->itemsperblock = 0; + pool->itemsfirstblock = 0; + pool->items = 0; + pool->maxitems = 0; + pool->unallocateditems = 0; + pool->pathitemsleft = 0; +} + +/*****************************************************************************/ +/* */ +/* poolrestart() Deallocate all items in a pool. */ +/* */ +/* The pool is returned to its starting state, except that no memory is */ +/* freed to the operating system. Rather, the previously allocated blocks */ +/* are ready to be reused. */ +/* */ +/*****************************************************************************/ + +void poolrestart(struct memorypool *pool) { + unsigned long alignptr; + + pool->items = 0; + pool->maxitems = 0; + + /* Set the currently active block. */ + pool->nowblock = pool->firstblock; + /* Find the first item in the pool. Increment by the size of (VOID *). */ + alignptr = (unsigned long) (pool->nowblock + 1); + /* Align the item on an `alignbytes'-byte boundary. */ + pool->nextitem = (VOID *) (alignptr + (unsigned long) pool->alignbytes + - (alignptr % (unsigned long) pool->alignbytes)); + /* There are lots of unallocated items left in this block. */ + pool->unallocateditems = pool->itemsfirstblock; + /* The stack of deallocated items is empty. */ + pool->deaditemstack = (VOID *) NULL; +} + +/*****************************************************************************/ +/* */ +/* poolinit() Initialize a pool of memory for allocation of items. */ +/* */ +/* This routine initializes the machinery for allocating items. A `pool' */ +/* is created whose records have size at least `bytecount'. Items will be */ +/* allocated in `itemcount'-item blocks. Each item is assumed to be a */ +/* collection of words, and either pointers or floating-point values are */ +/* assumed to be the "primary" word type. (The "primary" word type is used */ +/* to determine alignment of items.) If `alignment' isn't zero, all items */ +/* will be `alignment'-byte aligned in memory. `alignment' must be either */ +/* a multiple or a factor of the primary word size; powers of two are safe. */ +/* `alignment' is normally used to create a few unused bits at the bottom */ +/* of each item's pointer, in which information may be stored. */ +/* */ +/* Don't change this routine unless you understand it. */ +/* */ +/*****************************************************************************/ + +void poolinit(struct memorypool *pool, int bytecount, int itemcount, int firstitemcount, + int alignment) { + /* Find the proper alignment, which must be at least as large as: */ + /* - The parameter `alignment'. */ + /* - sizeof(VOID *), so the stack of dead items can be maintained */ + /* without unaligned accesses. */ + if (alignment > sizeof(VOID *)) { + pool->alignbytes = alignment; + } + else { + pool->alignbytes = sizeof(VOID *); + } + pool->itembytes = ((bytecount - 1) / pool->alignbytes + 1) * pool->alignbytes; + pool->itemsperblock = itemcount; + if (firstitemcount == 0) { + pool->itemsfirstblock = itemcount; + } + else { + pool->itemsfirstblock = firstitemcount; + } + + /* Allocate a block of items. Space for `itemsfirstblock' items and one */ + /* pointer (to point to the next block) are allocated, as well as space */ + /* to ensure alignment of the items. */ + pool->firstblock = (VOID **) trimalloc( + pool->itemsfirstblock * pool->itembytes + (int) sizeof(VOID *) + pool->alignbytes); + /* Set the next block pointer to NULL. */ + *(pool->firstblock) = (VOID *) NULL; + poolrestart(pool); +} + +/*****************************************************************************/ +/* */ +/* pooldeinit() Free to the operating system all memory taken by a pool. */ +/* */ +/*****************************************************************************/ + +void pooldeinit(struct memorypool *pool) { + while (pool->firstblock != (VOID **) NULL) { + pool->nowblock = (VOID **) *(pool->firstblock); + trifree((VOID *) pool->firstblock); + pool->firstblock = pool->nowblock; + } +} + +/*****************************************************************************/ +/* */ +/* poolalloc() Allocate space for an item. */ +/* */ +/*****************************************************************************/ + +VOID *poolalloc(struct memorypool *pool) { + VOID *newitem; + VOID **newblock; + unsigned long alignptr; + + /* First check the linked list of dead items. If the list is not */ + /* empty, allocate an item from the list rather than a fresh one. */ + if (pool->deaditemstack != (VOID *) NULL) { + newitem = pool->deaditemstack; /* Take first item in list. */ + pool->deaditemstack = *(VOID **) pool->deaditemstack; + } + else { + /* Check if there are any free items left in the current block. */ + if (pool->unallocateditems == 0) { + /* Check if another block must be allocated. */ + if (*(pool->nowblock) == (VOID *) NULL) { + /* Allocate a new block of items, pointed to by the previous block. */ + newblock = (VOID **) trimalloc( + pool->itemsperblock * pool->itembytes + (int) sizeof(VOID *) + pool->alignbytes); + *(pool->nowblock) = (VOID *) newblock; + /* The next block pointer is NULL. */ + *newblock = (VOID *) NULL; + } + + /* Move to the new block. */ + pool->nowblock = (VOID **) *(pool->nowblock); + /* Find the first item in the block. */ + /* Increment by the size of (VOID *). */ + alignptr = (unsigned long) (pool->nowblock + 1); + /* Align the item on an `alignbytes'-byte boundary. */ + pool->nextitem = (VOID *) (alignptr + (unsigned long) pool->alignbytes + - (alignptr % (unsigned long) pool->alignbytes)); + /* There are lots of unallocated items left in this block. */ + pool->unallocateditems = pool->itemsperblock; + } + + /* Allocate a new item. */ + newitem = pool->nextitem; + /* Advance `nextitem' pointer to next free item in block. */ + pool->nextitem = (VOID *) ((char *) pool->nextitem + pool->itembytes); + pool->unallocateditems--; + pool->maxitems++; + } + pool->items++; + return newitem; +} + +/*****************************************************************************/ +/* */ +/* pooldealloc() Deallocate space for an item. */ +/* */ +/* The deallocated space is stored in a queue for later reuse. */ +/* */ +/*****************************************************************************/ + +void pooldealloc(struct memorypool *pool, VOID *dyingitem) { + /* Push freshly killed item onto stack. */ + *((VOID **) dyingitem) = pool->deaditemstack; + pool->deaditemstack = dyingitem; + pool->items--; +} + +/*****************************************************************************/ +/* */ +/* traversalinit() Prepare to traverse the entire list of items. */ +/* */ +/* This routine is used in conjunction with traverse(). */ +/* */ +/*****************************************************************************/ + +void traversalinit(struct memorypool *pool) { + unsigned long alignptr; + + /* Begin the traversal in the first block. */ + pool->pathblock = pool->firstblock; + /* Find the first item in the block. Increment by the size of (VOID *). */ + alignptr = (unsigned long) (pool->pathblock + 1); + /* Align with item on an `alignbytes'-byte boundary. */ + pool->pathitem = (VOID *) (alignptr + (unsigned long) pool->alignbytes + - (alignptr % (unsigned long) pool->alignbytes)); + /* Set the number of items left in the current block. */ + pool->pathitemsleft = pool->itemsfirstblock; +} + +/*****************************************************************************/ +/* */ +/* traverse() Find the next item in the list. */ +/* */ +/* This routine is used in conjunction with traversalinit(). Be forewarned */ +/* that this routine successively returns all items in the list, including */ +/* deallocated ones on the deaditemqueue. It's up to you to figure out */ +/* which ones are actually dead. Why? I don't want to allocate extra */ +/* space just to demarcate dead items. It can usually be done more */ +/* space-efficiently by a routine that knows something about the structure */ +/* of the item. */ +/* */ +/*****************************************************************************/ + +VOID *traverse(struct memorypool *pool) { + VOID *newitem; + unsigned long alignptr; + + /* Stop upon exhausting the list of items. */ + if (pool->pathitem == pool->nextitem) { + return (VOID *) NULL; + } + + /* Check whether any untraversed items remain in the current block. */ + if (pool->pathitemsleft == 0) { + /* Find the next block. */ + pool->pathblock = (VOID **) *(pool->pathblock); + /* Find the first item in the block. Increment by the size of (VOID *). */ + alignptr = (unsigned long) (pool->pathblock + 1); + /* Align with item on an `alignbytes'-byte boundary. */ + pool->pathitem = (VOID *) (alignptr + (unsigned long) pool->alignbytes + - (alignptr % (unsigned long) pool->alignbytes)); + /* Set the number of items left in the current block. */ + pool->pathitemsleft = pool->itemsperblock; + } + + newitem = pool->pathitem; + /* Find the next item in the block. */ + pool->pathitem = (VOID *) ((char *) pool->pathitem + pool->itembytes); + pool->pathitemsleft--; + return newitem; +} + +/*****************************************************************************/ +/* */ +/* dummyinit() Initialize the triangle that fills "outer space" and the */ +/* omnipresent subsegment. */ +/* */ +/* The triangle that fills "outer space," called `dummytri', is pointed to */ +/* by every triangle and subsegment on a boundary (be it outer or inner) of */ +/* the triangulation. Also, `dummytri' points to one of the triangles on */ +/* the convex hull (until the holes and concavities are carved), making it */ +/* possible to find a starting triangle for point location. */ +/* */ +/* The omnipresent subsegment, `dummysub', is pointed to by every triangle */ +/* or subsegment that doesn't have a full complement of real subsegments */ +/* to point to. */ +/* */ +/* `dummytri' and `dummysub' are generally required to fulfill only a few */ +/* invariants: their vertices must remain NULL and `dummytri' must always */ +/* be bonded (at offset zero) to some triangle on the convex hull of the */ +/* mesh, via a boundary edge. Otherwise, the connections of `dummytri' and */ +/* `dummysub' may change willy-nilly. This makes it possible to avoid */ +/* writing a good deal of special-case code (in the edge flip, for example) */ +/* for dealing with the boundary of the mesh, places where no subsegment is */ +/* present, and so forth. Other entities are frequently bonded to */ +/* `dummytri' and `dummysub' as if they were real mesh entities, with no */ +/* harm done. */ +/* */ +/*****************************************************************************/ + +void dummyinit(struct mesh *m, struct behavior *b, int trianglebytes, int subsegbytes) { + unsigned long alignptr; + + /* Set up `dummytri', the `triangle' that occupies "outer space." */ + m->dummytribase = (triangle *) trimalloc(trianglebytes + m->triangles.alignbytes); + /* Align `dummytri' on a `triangles.alignbytes'-byte boundary. */ + alignptr = (unsigned long) m->dummytribase; + m->dummytri = (triangle *) (alignptr + (unsigned long) m->triangles.alignbytes + - (alignptr % (unsigned long) m->triangles.alignbytes)); + /* Initialize the three adjoining triangles to be "outer space." These */ + /* will eventually be changed by various bonding operations, but their */ + /* values don't really matter, as long as they can legally be */ + /* dereferenced. */ + m->dummytri[0] = (triangle) m->dummytri; + m->dummytri[1] = (triangle) m->dummytri; + m->dummytri[2] = (triangle) m->dummytri; + /* Three NULL vertices. */ + m->dummytri[3] = (triangle) NULL; + m->dummytri[4] = (triangle) NULL; + m->dummytri[5] = (triangle) NULL; + + if (b->usesegments) { + /* Set up `dummysub', the omnipresent subsegment pointed to by any */ + /* triangle side or subsegment end that isn't attached to a real */ + /* subsegment. */ + m->dummysubbase = (subseg *) trimalloc(subsegbytes + m->subsegs.alignbytes); + /* Align `dummysub' on a `subsegs.alignbytes'-byte boundary. */ + alignptr = (unsigned long) m->dummysubbase; + m->dummysub = (subseg *) (alignptr + (unsigned long) m->subsegs.alignbytes + - (alignptr % (unsigned long) m->subsegs.alignbytes)); + /* Initialize the two adjoining subsegments to be the omnipresent */ + /* subsegment. These will eventually be changed by various bonding */ + /* operations, but their values don't really matter, as long as they */ + /* can legally be dereferenced. */ + m->dummysub[0] = (subseg) m->dummysub; + m->dummysub[1] = (subseg) m->dummysub; + /* Four NULL vertices. */ + m->dummysub[2] = (subseg) NULL; + m->dummysub[3] = (subseg) NULL; + m->dummysub[4] = (subseg) NULL; + m->dummysub[5] = (subseg) NULL; + /* Initialize the two adjoining triangles to be "outer space." */ + m->dummysub[6] = (subseg) m->dummytri; + m->dummysub[7] = (subseg) m->dummytri; + /* Set the boundary marker to zero. */ + *(int *) (m->dummysub + 8) = 0; + + /* Initialize the three adjoining subsegments of `dummytri' to be */ + /* the omnipresent subsegment. */ + m->dummytri[6] = (triangle) m->dummysub; + m->dummytri[7] = (triangle) m->dummysub; + m->dummytri[8] = (triangle) m->dummysub; + } +} + +/*****************************************************************************/ +/* */ +/* initializevertexpool() Calculate the size of the vertex data structure */ +/* and initialize its memory pool. */ +/* */ +/* This routine also computes the `vertexmarkindex' and `vertex2triindex' */ +/* indices used to find values within each vertex. */ +/* */ +/*****************************************************************************/ + +void initializevertexpool(struct mesh *m, struct behavior *b) { + int vertexsize; + + /* The index within each vertex at which the boundary marker is found, */ + /* followed by the vertex type. Ensure the vertex marker is aligned to */ + /* a sizeof(int)-byte address. */ + m->vertexmarkindex = ((m->mesh_dim + m->nextras) * sizeof(REAL) + sizeof(int) - 1) / sizeof(int); + vertexsize = (m->vertexmarkindex + 2) * sizeof(int); + if (b->poly) { + /* The index within each vertex at which a triangle pointer is found. */ + /* Ensure the pointer is aligned to a sizeof(triangle)-byte address. */ + m->vertex2triindex = (vertexsize + sizeof(triangle) - 1) / sizeof(triangle); + vertexsize = (m->vertex2triindex + 1) * sizeof(triangle); + } + + /* Initialize the pool of vertices. */ + poolinit(&m->vertices, vertexsize, VERTEXPERBLOCK, + m->invertices > VERTEXPERBLOCK ? m->invertices : VERTEXPERBLOCK, + sizeof(REAL)); +} + +/*****************************************************************************/ +/* */ +/* initializetrisubpools() Calculate the sizes of the triangle and */ +/* subsegment data structures and initialize */ +/* their memory pools. */ +/* */ +/* This routine also computes the `highorderindex', `elemattribindex', and */ +/* `areaboundindex' indices used to find values within each triangle. */ +/* */ +/*****************************************************************************/ + +void initializetrisubpools(struct mesh *m, struct behavior *b) { + int trisize; + + /* The index within each triangle at which the extra nodes (above three) */ + /* associated with high order elements are found. There are three */ + /* pointers to other triangles, three pointers to corners, and possibly */ + /* three pointers to subsegments before the extra nodes. */ + m->highorderindex = 6 + (b->usesegments * 3); + /* The number of bytes occupied by a triangle. */ + trisize = ((b->order + 1) * (b->order + 2) / 2 + (m->highorderindex - 3)) * sizeof(triangle); + /* The index within each triangle at which its attributes are found, */ + /* where the index is measured in REALs. */ + m->elemattribindex = (trisize + sizeof(REAL) - 1) / sizeof(REAL); + /* The index within each triangle at which the maximum area constraint */ + /* is found, where the index is measured in REALs. Note that if the */ + /* `regionattrib' flag is set, an additional attribute will be added. */ + m->areaboundindex = m->elemattribindex + m->eextras + b->regionattrib; + /* If triangle attributes or an area bound are needed, increase the number */ + /* of bytes occupied by a triangle. */ + if (b->vararea) { + trisize = (m->areaboundindex + 1) * sizeof(REAL); + } + else if (m->eextras + b->regionattrib > 0) { + trisize = m->areaboundindex * sizeof(REAL); + } + /* If a Voronoi diagram or triangle neighbor graph is requested, make */ + /* sure there's room to store an integer index in each triangle. This */ + /* integer index can occupy the same space as the subsegment pointers */ + /* or attributes or area constraint or extra nodes. */ + if ((b->voronoi || b->neighbors) && (trisize < 6 * sizeof(triangle) + sizeof(int))) { + trisize = 6 * sizeof(triangle) + sizeof(int); + } + + /* Having determined the memory size of a triangle, initialize the pool. */ + poolinit(&m->triangles, trisize, TRIPERBLOCK, + (2 * m->invertices - 2) > TRIPERBLOCK ? (2 * m->invertices - 2) : TRIPERBLOCK, 4); + + if (b->usesegments) { + /* Initialize the pool of subsegments. Take into account all eight */ + /* pointers and one boundary marker. */ + poolinit(&m->subsegs, 8 * sizeof(triangle) + sizeof(int), SUBSEGPERBLOCK, SUBSEGPERBLOCK, 4); + + /* Initialize the "outer space" triangle and omnipresent subsegment. */ + dummyinit(m, b, m->triangles.itembytes, m->subsegs.itembytes); + } + else { + /* Initialize the "outer space" triangle. */ + dummyinit(m, b, m->triangles.itembytes, 0); + } +} + +/*****************************************************************************/ +/* */ +/* triangledealloc() Deallocate space for a triangle, marking it dead. */ +/* */ +/*****************************************************************************/ + +void triangledealloc(struct mesh *m, triangle *dyingtriangle) { + /* Mark the triangle as dead. This makes it possible to detect dead */ + /* triangles when traversing the list of all triangles. */ + killtri(dyingtriangle); + pooldealloc(&m->triangles, (VOID *) dyingtriangle); +} + +/*****************************************************************************/ +/* */ +/* triangletraverse() Traverse the triangles, skipping dead ones. */ +/* */ +/*****************************************************************************/ + +triangle *triangletraverse(struct mesh *m) { + triangle *newtriangle; + + do { + newtriangle = (triangle *) traverse(&m->triangles); + if (newtriangle == (triangle *) NULL) { + return (triangle *) NULL; + } + } while (deadtri(newtriangle)); /* Skip dead ones. */ + return newtriangle; +} + +/*****************************************************************************/ +/* */ +/* subsegdealloc() Deallocate space for a subsegment, marking it dead. */ +/* */ +/*****************************************************************************/ + +void subsegdealloc(struct mesh *m, subseg *dyingsubseg) { + /* Mark the subsegment as dead. This makes it possible to detect dead */ + /* subsegments when traversing the list of all subsegments. */ + killsubseg(dyingsubseg); + pooldealloc(&m->subsegs, (VOID *) dyingsubseg); +} + +/*****************************************************************************/ +/* */ +/* subsegtraverse() Traverse the subsegments, skipping dead ones. */ +/* */ +/*****************************************************************************/ + +subseg *subsegtraverse(struct mesh *m) { + subseg *newsubseg; + + do { + newsubseg = (subseg *) traverse(&m->subsegs); + if (newsubseg == (subseg *) NULL) { + return (subseg *) NULL; + } + } while (deadsubseg(newsubseg)); /* Skip dead ones. */ + return newsubseg; +} + +/*****************************************************************************/ +/* */ +/* vertexdealloc() Deallocate space for a vertex, marking it dead. */ +/* */ +/*****************************************************************************/ + +void vertexdealloc(struct mesh *m, vertex dyingvertex) { + /* Mark the vertex as dead. This makes it possible to detect dead */ + /* vertices when traversing the list of all vertices. */ + setvertextype(dyingvertex, DEADVERTEX); + pooldealloc(&m->vertices, (VOID *) dyingvertex); +} + +/*****************************************************************************/ +/* */ +/* vertextraverse() Traverse the vertices, skipping dead ones. */ +/* */ +/*****************************************************************************/ + +vertex vertextraverse(struct mesh *m) { + vertex newvertex; + + do { + newvertex = (vertex) traverse(&m->vertices); + if (newvertex == (vertex) NULL) { + return (vertex) NULL; + } + } while (vertextype(newvertex) == DEADVERTEX); /* Skip dead ones. */ + return newvertex; +} + +/*****************************************************************************/ +/* */ +/* getvertex() Get a specific vertex, by number, from the list. */ +/* */ +/* The first vertex is number 'firstnumber'. */ +/* */ +/* Note that this takes O(n) time (with a small constant, if VERTEXPERBLOCK */ +/* is large). I don't care to take the trouble to make it work in constant */ +/* time. */ +/* */ +/*****************************************************************************/ + +vertex getvertex(struct mesh *m, struct behavior *b, int number) { + VOID **getblock; + char *foundvertex; + unsigned long alignptr; + int current; + + getblock = m->vertices.firstblock; + current = b->firstnumber; + + /* Find the right block. */ + if (current + m->vertices.itemsfirstblock <= number) { + getblock = (VOID **) *getblock; + current += m->vertices.itemsfirstblock; + while (current + m->vertices.itemsperblock <= number) { + getblock = (VOID **) *getblock; + current += m->vertices.itemsperblock; + } + } + + /* Now find the right vertex. */ + alignptr = (unsigned long) (getblock + 1); + foundvertex = (char *) (alignptr + (unsigned long) m->vertices.alignbytes + - (alignptr % (unsigned long) m->vertices.alignbytes)); + return (vertex) (foundvertex + m->vertices.itembytes * (number - current)); +} + +/*****************************************************************************/ +/* */ +/* triangledeinit() Free all remaining allocated memory. */ +/* */ +/*****************************************************************************/ + +void triangledeinit(struct mesh *m, struct behavior *b) { + pooldeinit(&m->triangles); + trifree((VOID *) m->dummytribase); + if (b->usesegments) { + pooldeinit(&m->subsegs); + trifree((VOID *) m->dummysubbase); + } + pooldeinit(&m->vertices); +#ifndef CDT_ONLY + if (b->quality) + { + pooldeinit(&m->badsubsegs); + if ((b->minangle > 0.0) || b->vararea || b->fixedarea || b->usertest) + { + pooldeinit(&m->badtriangles); + pooldeinit(&m->flipstackers); + } + } +#endif /* not CDT_ONLY */ +} + +/** **/ +/** **/ +/********* Memory management routines end here *********/ + +/********* Constructors begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* maketriangle() Create a new triangle with orientation zero. */ +/* */ +/*****************************************************************************/ + +void maketriangle(struct mesh *m, struct behavior *b, struct otri *newotri) { + int i; + + newotri->tri = (triangle *) poolalloc(&m->triangles); + /* Initialize the three adjoining triangles to be "outer space". */ + newotri->tri[0] = (triangle) m->dummytri; + newotri->tri[1] = (triangle) m->dummytri; + newotri->tri[2] = (triangle) m->dummytri; + /* Three NULL vertices. */ + newotri->tri[3] = (triangle) NULL; + newotri->tri[4] = (triangle) NULL; + newotri->tri[5] = (triangle) NULL; + if (b->usesegments) { + /* Initialize the three adjoining subsegments to be the omnipresent */ + /* subsegment. */ + newotri->tri[6] = (triangle) m->dummysub; + newotri->tri[7] = (triangle) m->dummysub; + newotri->tri[8] = (triangle) m->dummysub; + } + for (i = 0; i < m->eextras; i++) { + setelemattribute(*newotri, i, 0.0); + } + if (b->vararea) { + setareabound(*newotri, -1.0); + } + + newotri->orient = 0; +} + +/*****************************************************************************/ +/* */ +/* makesubseg() Create a new subsegment with orientation zero. */ +/* */ +/*****************************************************************************/ + +void makesubseg(struct mesh *m, struct osub *newsubseg) { + newsubseg->ss = (subseg *) poolalloc(&m->subsegs); + /* Initialize the two adjoining subsegments to be the omnipresent */ + /* subsegment. */ + newsubseg->ss[0] = (subseg) m->dummysub; + newsubseg->ss[1] = (subseg) m->dummysub; + /* Four NULL vertices. */ + newsubseg->ss[2] = (subseg) NULL; + newsubseg->ss[3] = (subseg) NULL; + newsubseg->ss[4] = (subseg) NULL; + newsubseg->ss[5] = (subseg) NULL; + /* Initialize the two adjoining triangles to be "outer space." */ + newsubseg->ss[6] = (subseg) m->dummytri; + newsubseg->ss[7] = (subseg) m->dummytri; + /* Set the boundary marker to zero. */ + setmark(*newsubseg, 0); + + newsubseg->ssorient = 0; +} + +/** **/ +/** **/ +/********* Constructors end here *********/ + +/********* Geometric primitives begin here *********/ +/** **/ +/** **/ + +/* The adaptive exact arithmetic geometric predicates implemented herein are */ +/* described in detail in my paper, "Adaptive Precision Floating-Point */ +/* Arithmetic and Fast Robust Geometric Predicates." See the header for a */ +/* full citation. */ + +/* Which of the following two methods of finding the absolute values is */ +/* fastest is compiler-dependent. A few compilers can inline and optimize */ +/* the fabs() call; but most will incur the overhead of a function call, */ +/* which is disastrously slow. A faster way on IEEE machines might be to */ +/* mask the appropriate bit, but that's difficult to do in C without */ +/* forcing the value to be stored to memory (rather than be kept in the */ +/* register to which the optimizer assigned it). */ + +#define Absolute(a) ((a) >= 0.0 ? (a) : -(a)) +/* #define Absolute(a) fabs(a) */ + +/* Many of the operations are broken up into two pieces, a main part that */ +/* performs an approximate operation, and a "tail" that computes the */ +/* roundoff error of that operation. */ +/* */ +/* The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(), */ +/* Split(), and Two_Product() are all implemented as described in the */ +/* reference. Each of these macros requires certain variables to be */ +/* defined in the calling routine. The variables `bvirt', `c', `abig', */ +/* `_i', `_j', `_k', `_l', `_m', and `_n' are declared `' because */ +/* they store the result of an operation that may incur roundoff error. */ +/* The input parameter `x' (or the highest numbered `x_' parameter) must */ +/* also be declared `'. */ + +#define Fast_Two_Sum_Tail(a, b, x, y) \ + bvirt = x - a; \ + y = b - bvirt + +#define Fast_Two_Sum(a, b, x, y) \ + x = (REAL) (a + b); \ + Fast_Two_Sum_Tail(a, b, x, y) + +#define Two_Sum_Tail(a, b, x, y) \ + bvirt = (REAL) (x - a); \ + avirt = x - bvirt; \ + bround = b - bvirt; \ + around = a - avirt; \ + y = around + bround + +#define Two_Sum(a, b, x, y) \ + x = (REAL) (a + b); \ + Two_Sum_Tail(a, b, x, y) + +#define Two_Diff_Tail(a, b, x, y) \ + bvirt = (REAL) (a - x); \ + avirt = x + bvirt; \ + bround = bvirt - b; \ + around = a - avirt; \ + y = around + bround + +#define Two_Diff(a, b, x, y) \ + x = (REAL) (a - b); \ + Two_Diff_Tail(a, b, x, y) + +#define Split(a, ahi, alo) \ + c = (REAL) (splitter * a); \ + abig = (REAL) (c - a); \ + ahi = c - abig; \ + alo = a - ahi + +#define Two_Product_Tail(a, b, x, y) \ + Split(a, ahi, alo); \ + Split(b, bhi, blo); \ + err1 = x - (ahi * bhi); \ + err2 = err1 - (alo * bhi); \ + err3 = err2 - (ahi * blo); \ + y = (alo * blo) - err3 + +#define Two_Product(a, b, x, y) \ + x = (REAL) (a * b); \ + Two_Product_Tail(a, b, x, y) + +/* Two_Product_Presplit() is Two_Product() where one of the inputs has */ +/* already been split. Avoids redundant splitting. */ + +#define Two_Product_Presplit(a, b, bhi, blo, x, y) \ + x = (REAL) (a * b); \ + Split(a, ahi, alo); \ + err1 = x - (ahi * bhi); \ + err2 = err1 - (alo * bhi); \ + err3 = err2 - (ahi * blo); \ + y = (alo * blo) - err3 + +/* Square() can be done more quickly than Two_Product(). */ + +#define Square_Tail(a, x, y) \ + Split(a, ahi, alo); \ + err1 = x - (ahi * ahi); \ + err3 = err1 - ((ahi + ahi) * alo); \ + y = (alo * alo) - err3 + +#define Square(a, x, y) \ + x = (REAL) (a * a); \ + Square_Tail(a, x, y) + +/* Macros for summing expansions of various fixed lengths. These are all */ +/* unrolled versions of Expansion_Sum(). */ + +#define Two_One_Sum(a1, a0, b, x2, x1, x0) \ + Two_Sum(a0, b , _i, x0); \ + Two_Sum(a1, _i, x2, x1) + +#define Two_One_Diff(a1, a0, b, x2, x1, x0) \ + Two_Diff(a0, b , _i, x0); \ + Two_Sum( a1, _i, x2, x1) + +#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \ + Two_One_Sum(a1, a0, b0, _j, _0, x0); \ + Two_One_Sum(_j, _0, b1, x3, x2, x1) + +#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \ + Two_One_Diff(a1, a0, b0, _j, _0, x0); \ + Two_One_Diff(_j, _0, b1, x3, x2, x1) + +/* Macro for multiplying a two-component expansion by a single component. */ + +#define Two_One_Product(a1, a0, b, x3, x2, x1, x0) \ + Split(b, bhi, blo); \ + Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \ + Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x1); \ + Fast_Two_Sum(_j, _k, x3, x2) + +/*****************************************************************************/ +/* */ +/* exactinit() Initialize the variables used for exact arithmetic. */ +/* */ +/* `epsilon' is the largest power of two such that 1.0 + epsilon = 1.0 in */ +/* floating-point arithmetic. `epsilon' bounds the relative roundoff */ +/* error. It is used for floating-point error analysis. */ +/* */ +/* `splitter' is used to split floating-point numbers into two half- */ +/* length significands for exact multiplication. */ +/* */ +/* I imagine that a highly optimizing compiler might be too smart for its */ +/* own good, and somehow cause this routine to fail, if it pretends that */ +/* floating-point arithmetic is too much like real arithmetic. */ +/* */ +/* Don't change this routine unless you fully understand it. */ +/* */ +/*****************************************************************************/ + +void exactinit() { + REAL half; + REAL check, lastcheck; + int every_other; +#ifdef LINUX + int cword; +#endif /* LINUX */ + +#ifdef CPU86 +#ifdef SINGLE + _control87(_PC_24, _MCW_PC); /* Set FPU control word for single precision. */ +#else /* not SINGLE */ + _control87(_PC_53, _MCW_PC); /* Set FPU control word for double precision. */ +#endif /* not SINGLE */ +#endif /* CPU86 */ +#ifdef LINUX +#ifdef SINGLE + /* cword = 4223; */ + cword = 4210; /* set FPU control word for single precision */ +#else /* not SINGLE */ + /* cword = 4735; */ + cword = 4722; /* set FPU control word for double precision */ +#endif /* not SINGLE */ + _FPU_SETCW(cword); +#endif /* LINUX */ + + every_other = 1; + half = 0.5; + epsilon = 1.0; + splitter = 1.0; + check = 1.0; + /* Repeatedly divide `epsilon' by two until it is too small to add to */ + /* one without causing roundoff. (Also check if the sum is equal to */ + /* the previous sum, for machines that round up instead of using exact */ + /* rounding. Not that these routines will work on such machines.) */ + do { + lastcheck = check; + epsilon *= half; + if (every_other) { + splitter *= 2.0; + } + every_other = !every_other; + check = 1.0 + epsilon; + } while ((check != 1.0) && (check != lastcheck)); + splitter += 1.0; + /* Error bounds for orientation and incircle tests. */ + resulterrbound = (3.0 + 8.0 * epsilon) * epsilon; + ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon; + ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon; + ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon; + iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon; + iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon; + iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon; + o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon; + o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon; + o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon; +} + +/*****************************************************************************/ +/* */ +/* fast_expansion_sum_zeroelim() Sum two expansions, eliminating zero */ +/* components from the output expansion. */ +/* */ +/* Sets h = e + f. See my Robust Predicates paper for details. */ +/* */ +/* If round-to-even is used (as with IEEE 754), maintains the strongly */ +/* nonoverlapping property. (That is, if e is strongly nonoverlapping, h */ +/* will be also.) Does NOT maintain the nonoverlapping or nonadjacent */ +/* properties. */ +/* */ +/*****************************************************************************/ + +int fast_expansion_sum_zeroelim(int elen, REAL *e, int flen, REAL *f, REAL *h) { + REAL Q; + REAL Qnew; + REAL hh; + REAL bvirt; + REAL avirt, bround, around; + int eindex, findex, hindex; + REAL enow, fnow; + + enow = e[0]; + fnow = f[0]; + eindex = findex = 0; + if ((fnow > enow) == (fnow > -enow)) { + Q = enow; + enow = e[++eindex]; + } + else { + Q = fnow; + fnow = f[++findex]; + } + hindex = 0; + if ((eindex < elen) && (findex < flen)) { + if ((fnow > enow) == (fnow > -enow)) { + Fast_Two_Sum(enow, Q, Qnew, hh); + enow = e[++eindex]; + } + else { + Fast_Two_Sum(fnow, Q, Qnew, hh); + fnow = f[++findex]; + } + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + while ((eindex < elen) && (findex < flen)) { + if ((fnow > enow) == (fnow > -enow)) { + Two_Sum(Q, enow, Qnew, hh); + enow = e[++eindex]; + } + else { + Two_Sum(Q, fnow, Qnew, hh); + fnow = f[++findex]; + } + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + } + while (eindex < elen) { + Two_Sum(Q, enow, Qnew, hh); + enow = e[++eindex]; + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + while (findex < flen) { + Two_Sum(Q, fnow, Qnew, hh); + fnow = f[++findex]; + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + if ((Q != 0.0) || (hindex == 0)) { + h[hindex++] = Q; + } + return hindex; +} + +/*****************************************************************************/ +/* */ +/* scale_expansion_zeroelim() Multiply an expansion by a scalar, */ +/* eliminating zero components from the */ +/* output expansion. */ +/* */ +/* Sets h = be. See my Robust Predicates paper for details. */ +/* */ +/* Maintains the nonoverlapping property. If round-to-even is used (as */ +/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */ +/* properties as well. (That is, if e has one of these properties, so */ +/* will h.) */ +/* */ +/*****************************************************************************/ + +int scale_expansion_zeroelim(int elen, REAL *e, REAL b, REAL *h) { + REAL Q, sum; + REAL hh; + REAL product1; + REAL product0; + int eindex, hindex; + REAL enow; + REAL bvirt; + REAL avirt, bround, around; + REAL c; + REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + + Split(b, bhi, blo); + Two_Product_Presplit(e[0], b, bhi, blo, Q, hh); + hindex = 0; + if (hh != 0) { + h[hindex++] = hh; + } + for (eindex = 1; eindex < elen; eindex++) { + enow = e[eindex]; + Two_Product_Presplit(enow, b, bhi, blo, product1, product0); + Two_Sum(Q, product0, sum, hh); + if (hh != 0) { + h[hindex++] = hh; + } + Fast_Two_Sum(product1, sum, Q, hh); + if (hh != 0) { + h[hindex++] = hh; + } + } + if ((Q != 0.0) || (hindex == 0)) { + h[hindex++] = Q; + } + return hindex; +} + +/*****************************************************************************/ +/* */ +/* estimate() Produce a one-word estimate of an expansion's value. */ +/* */ +/* See my Robust Predicates paper for details. */ +/* */ +/*****************************************************************************/ + +REAL estimate(int elen, REAL *e) { + REAL Q; + int eindex; + + Q = e[0]; + for (eindex = 1; eindex < elen; eindex++) { + Q += e[eindex]; + } + return Q; +} + +/*****************************************************************************/ +/* */ +/* counterclockwise() Return a positive value if the points pa, pb, and */ +/* pc occur in counterclockwise order; a negative */ +/* value if they occur in clockwise order; and zero */ +/* if they are collinear. The result is also a rough */ +/* approximation of twice the signed area of the */ +/* triangle defined by the three points. */ +/* */ +/* Uses exact arithmetic if necessary to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. This determinant is */ +/* computed adaptively, in the sense that exact arithmetic is used only to */ +/* the degree it is needed to ensure that the returned value has the */ +/* correct sign. Hence, this function is usually quite fast, but will run */ +/* more slowly when the input points are collinear or nearly so. */ +/* */ +/* See my Robust Predicates paper for details. */ +/* */ +/*****************************************************************************/ + +REAL counterclockwiseadapt(vertex pa, vertex pb, vertex pc, REAL detsum) { + REAL acx, acy, bcx, bcy; + REAL acxtail, acytail, bcxtail, bcytail; + REAL detleft, detright; + REAL detlefttail, detrighttail; + REAL det, errbound; + REAL B[4], C1[8], C2[12], D[16]; + REAL B3; + int C1length, C2length, Dlength; + REAL u[4]; + REAL u3; + REAL s1, t1; + REAL s0, t0; + + REAL bvirt; + REAL avirt, bround, around; + REAL c; + REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + REAL _i, _j; + REAL _0; + + acx = (REAL) (pa[0] - pc[0]); + bcx = (REAL) (pb[0] - pc[0]); + acy = (REAL) (pa[1] - pc[1]); + bcy = (REAL) (pb[1] - pc[1]); + + Two_Product(acx, bcy, detleft, detlefttail); + Two_Product(acy, bcx, detright, detrighttail); + + Two_Two_Diff(detleft, detlefttail, detright, detrighttail, B3, B[2], B[1], B[0]); + B[3] = B3; + + det = estimate(4, B); + errbound = ccwerrboundB * detsum; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pc[0], acx, acxtail); + Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail); + Two_Diff_Tail(pa[1], pc[1], acy, acytail); + Two_Diff_Tail(pb[1], pc[1], bcy, bcytail); + + if ((acxtail == 0.0) && (acytail == 0.0) && (bcxtail == 0.0) && (bcytail == 0.0)) { + return det; + } + + errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det); + det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Product(acxtail, bcy, s1, s0); + Two_Product(acytail, bcx, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1); + + Two_Product(acx, bcytail, s1, s0); + Two_Product(acy, bcxtail, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2); + + Two_Product(acxtail, bcytail, s1, s0); + Two_Product(acytail, bcxtail, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D); + + return (D[Dlength - 1]); +} + +REAL counterclockwise(struct mesh *m, struct behavior *b, vertex pa, vertex pb, vertex pc) { + REAL detleft, detright, det; + REAL detsum, errbound; + + m->counterclockcount++; + + detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]); + detright = (pa[1] - pc[1]) * (pb[0] - pc[0]); + det = detleft - detright; + + if (b->noexact) { + return det; + } + + if (detleft > 0.0) { + if (detright <= 0.0) { + return det; + } + else { + detsum = detleft + detright; + } + } + else if (detleft < 0.0) { + if (detright >= 0.0) { + return det; + } + else { + detsum = -detleft - detright; + } + } + else { + return det; + } + + errbound = ccwerrboundA * detsum; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + return counterclockwiseadapt(pa, pb, pc, detsum); +} + +/*****************************************************************************/ +/* */ +/* incircle() Return a positive value if the point pd lies inside the */ +/* circle passing through pa, pb, and pc; a negative value if */ +/* it lies outside; and zero if the four points are cocircular.*/ +/* The points pa, pb, and pc must be in counterclockwise */ +/* order, or the sign of the result will be reversed. */ +/* */ +/* Uses exact arithmetic if necessary to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. This determinant is */ +/* computed adaptively, in the sense that exact arithmetic is used only to */ +/* the degree it is needed to ensure that the returned value has the */ +/* correct sign. Hence, this function is usually quite fast, but will run */ +/* more slowly when the input points are cocircular or nearly so. */ +/* */ +/* See my Robust Predicates paper for details. */ +/* */ +/*****************************************************************************/ + +REAL incircleadapt(vertex pa, vertex pb, vertex pc, vertex pd, REAL permanent) { + REAL adx, bdx, cdx, ady, bdy, cdy; + REAL det, errbound; + + REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; + REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0; + REAL bc[4], ca[4], ab[4]; + REAL bc3, ca3, ab3; + REAL axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32]; + int axbclen, axxbclen, aybclen, ayybclen, alen; + REAL bxca[8], bxxca[16], byca[8], byyca[16], bdet[32]; + int bxcalen, bxxcalen, bycalen, byycalen, blen; + REAL cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32]; + int cxablen, cxxablen, cyablen, cyyablen, clen; + REAL abdet[64]; + int ablen; + REAL fin1[1152], fin2[1152]; + REAL *finnow, *finother, *finswap; + int finlength; + + REAL adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail; + REAL adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1; + REAL adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0; + REAL aa[4], bb[4], cc[4]; + REAL aa3, bb3, cc3; + REAL ti1, tj1; + REAL ti0, tj0; + REAL u[4], v[4]; + REAL u3, v3; + REAL temp8[8], temp16a[16], temp16b[16], temp16c[16]; + REAL temp32a[32], temp32b[32], temp48[48], temp64[64]; + int temp8len, temp16alen, temp16blen, temp16clen; + int temp32alen, temp32blen, temp48len, temp64len; + REAL axtbb[8], axtcc[8], aytbb[8], aytcc[8]; + int axtbblen, axtcclen, aytbblen, aytcclen; + REAL bxtaa[8], bxtcc[8], bytaa[8], bytcc[8]; + int bxtaalen, bxtcclen, bytaalen, bytcclen; + REAL cxtaa[8], cxtbb[8], cytaa[8], cytbb[8]; + int cxtaalen, cxtbblen, cytaalen, cytbblen; + REAL axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8]; + int axtbclen, aytbclen, bxtcalen, bytcalen, cxtablen, cytablen; + REAL axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16]; + int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen; + REAL axtbctt[8], aytbctt[8], bxtcatt[8]; + REAL bytcatt[8], cxtabtt[8], cytabtt[8]; + int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen; + REAL abt[8], bct[8], cat[8]; + int abtlen, bctlen, catlen; + REAL abtt[4], bctt[4], catt[4]; + int abttlen, bcttlen, cattlen; + REAL abtt3, bctt3, catt3; + REAL negate; + + REAL bvirt; + REAL avirt, bround, around; + REAL c; + REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + REAL _i, _j; + REAL _0; + + adx = (REAL) (pa[0] - pd[0]); + bdx = (REAL) (pb[0] - pd[0]); + cdx = (REAL) (pc[0] - pd[0]); + ady = (REAL) (pa[1] - pd[1]); + bdy = (REAL) (pb[1] - pd[1]); + cdy = (REAL) (pc[1] - pd[1]); + + Two_Product(bdx, cdy, bdxcdy1, bdxcdy0); + Two_Product(cdx, bdy, cdxbdy1, cdxbdy0); + Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + axbclen = scale_expansion_zeroelim(4, bc, adx, axbc); + axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc); + aybclen = scale_expansion_zeroelim(4, bc, ady, aybc); + ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc); + alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet); + + Two_Product(cdx, ady, cdxady1, cdxady0); + Two_Product(adx, cdy, adxcdy1, adxcdy0); + Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); + ca[3] = ca3; + bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca); + bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca); + bycalen = scale_expansion_zeroelim(4, ca, bdy, byca); + byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca); + blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet); + + Two_Product(adx, bdy, adxbdy1, adxbdy0); + Two_Product(bdx, ady, bdxady1, bdxady0); + Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab); + cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab); + cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab); + cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab); + clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1); + + det = estimate(finlength, fin1); + errbound = iccerrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pd[0], adx, adxtail); + Two_Diff_Tail(pa[1], pd[1], ady, adytail); + Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); + Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); + Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); + Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); + if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) && (adytail == 0.0) + && (bdytail == 0.0) && (cdytail == 0.0)) { + return det; + } + + errbound = iccerrboundC * permanent + resulterrbound * Absolute(det); + det += ((adx * adx + ady * ady) + * ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail)) + + 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx)) + + ((bdx * bdx + bdy * bdy) + * ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail)) + + 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx)) + + ((cdx * cdx + cdy * cdy) + * ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail)) + + 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx)); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + finnow = fin1; + finother = fin2; + + if ((bdxtail != 0.0) || (bdytail != 0.0) || (cdxtail != 0.0) || (cdytail != 0.0)) { + Square(adx, adxadx1, adxadx0); + Square(ady, adyady1, adyady0); + Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]); + aa[3] = aa3; + } + if ((cdxtail != 0.0) || (cdytail != 0.0) || (adxtail != 0.0) || (adytail != 0.0)) { + Square(bdx, bdxbdx1, bdxbdx0); + Square(bdy, bdybdy1, bdybdy0); + Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]); + bb[3] = bb3; + } + if ((adxtail != 0.0) || (adytail != 0.0) || (bdxtail != 0.0) || (bdytail != 0.0)) { + Square(cdx, cdxcdx1, cdxcdx0); + Square(cdy, cdycdy1, cdycdy0); + Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]); + cc[3] = cc3; + } + + if (adxtail != 0.0) { + axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc); + temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx, temp16a); + + axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc); + temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b); + + axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb); + temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (adytail != 0.0) { + aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc); + temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady, temp16a); + + aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb); + temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b); + + aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc); + temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdxtail != 0.0) { + bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca); + temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx, temp16a); + + bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa); + temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b); + + bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc); + temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdytail != 0.0) { + bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca); + temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy, temp16a); + + bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc); + temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b); + + bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa); + temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdxtail != 0.0) { + cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab); + temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx, temp16a); + + cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb); + temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b); + + cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa); + temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdytail != 0.0) { + cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab); + temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy, temp16a); + + cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa); + temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b); + + cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb); + temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + if ((adxtail != 0.0) || (adytail != 0.0)) { + if ((bdxtail != 0.0) || (bdytail != 0.0) || (cdxtail != 0.0) || (cdytail != 0.0)) { + Two_Product(bdxtail, cdy, ti1, ti0); + Two_Product(bdx, cdytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -bdy; + Two_Product(cdxtail, negate, ti1, ti0); + negate = -bdytail; + Two_Product(cdx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct); + + Two_Product(bdxtail, cdytail, ti1, ti0); + Two_Product(cdxtail, bdytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]); + bctt[3] = bctt3; + bcttlen = 4; + } + else { + bct[0] = 0.0; + bctlen = 1; + bctt[0] = 0.0; + bcttlen = 1; + } + + if (adxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a); + axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct); + temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (bdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, + finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, + finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail, temp32a); + axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt); + temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx, temp16a); + temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, + temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (adytail != 0.0) { + temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a); + aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct); + temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail, temp32a); + aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt); + temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady, temp16a); + temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, + temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + if ((bdxtail != 0.0) || (bdytail != 0.0)) { + if ((cdxtail != 0.0) || (cdytail != 0.0) || (adxtail != 0.0) || (adytail != 0.0)) { + Two_Product(cdxtail, ady, ti1, ti0); + Two_Product(cdx, adytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -cdy; + Two_Product(adxtail, negate, ti1, ti0); + negate = -cdytail; + Two_Product(adx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat); + + Two_Product(cdxtail, adytail, ti1, ti0); + Two_Product(adxtail, cdytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]); + catt[3] = catt3; + cattlen = 4; + } + else { + cat[0] = 0.0; + catlen = 1; + catt[0] = 0.0; + cattlen = 1; + } + + if (bdxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a); + bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat); + temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (cdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, + finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (adytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, + finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail, temp32a); + bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt); + temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx, temp16a); + temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, + temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdytail != 0.0) { + temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a); + bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat); + temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail, temp32a); + bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt); + temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy, temp16a); + temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, + temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + if ((cdxtail != 0.0) || (cdytail != 0.0)) { + if ((adxtail != 0.0) || (adytail != 0.0) || (bdxtail != 0.0) || (bdytail != 0.0)) { + Two_Product(adxtail, bdy, ti1, ti0); + Two_Product(adx, bdytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -ady; + Two_Product(bdxtail, negate, ti1, ti0); + negate = -adytail; + Two_Product(bdx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt); + + Two_Product(adxtail, bdytail, ti1, ti0); + Two_Product(bdxtail, adytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]); + abtt[3] = abtt3; + abttlen = 4; + } + else { + abt[0] = 0.0; + abtlen = 1; + abtt[0] = 0.0; + abttlen = 1; + } + + if (cdxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a); + cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt); + temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (adytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, + finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, + finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail, temp32a); + cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt); + temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx, temp16a); + temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, + temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdytail != 0.0) { + temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a); + cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt); + temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail, temp32a); + cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt); + temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy, temp16a); + temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, + temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + + return finnow[finlength - 1]; +} + +REAL incircle(struct mesh *m, struct behavior *b, vertex pa, vertex pb, vertex pc, vertex pd) { + REAL adx, bdx, cdx, ady, bdy, cdy; + REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; + REAL alift, blift, clift; + REAL det; + REAL permanent, errbound; + + m->incirclecount++; + + adx = pa[0] - pd[0]; + bdx = pb[0] - pd[0]; + cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; + cdy = pc[1] - pd[1]; + + bdxcdy = bdx * cdy; + cdxbdy = cdx * bdy; + alift = adx * adx + ady * ady; + + cdxady = cdx * ady; + adxcdy = adx * cdy; + blift = bdx * bdx + bdy * bdy; + + adxbdy = adx * bdy; + bdxady = bdx * ady; + clift = cdx * cdx + cdy * cdy; + + det = alift * (bdxcdy - cdxbdy) + blift * (cdxady - adxcdy) + clift * (adxbdy - bdxady); + + if (b->noexact) { + return det; + } + + permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift + + (Absolute(cdxady) + Absolute(adxcdy)) * blift + + (Absolute(adxbdy) + Absolute(bdxady)) * clift; + errbound = iccerrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + return det; + } + + return incircleadapt(pa, pb, pc, pd, permanent); +} + +/*****************************************************************************/ +/* */ +/* orient3d() Return a positive value if the point pd lies below the */ +/* plane passing through pa, pb, and pc; "below" is defined so */ +/* that pa, pb, and pc appear in counterclockwise order when */ +/* viewed from above the plane. Returns a negative value if */ +/* pd lies above the plane. Returns zero if the points are */ +/* coplanar. The result is also a rough approximation of six */ +/* times the signed volume of the tetrahedron defined by the */ +/* four points. */ +/* */ +/* Uses exact arithmetic if necessary to ensure a correct answer. The */ +/* result returned is the determinant of a matrix. This determinant is */ +/* computed adaptively, in the sense that exact arithmetic is used only to */ +/* the degree it is needed to ensure that the returned value has the */ +/* correct sign. Hence, this function is usually quite fast, but will run */ +/* more slowly when the input points are coplanar or nearly so. */ +/* */ +/* See my Robust Predicates paper for details. */ +/* */ +/*****************************************************************************/ + +REAL orient3dadapt(vertex pa, vertex pb, vertex pc, vertex pd, REAL aheight, REAL bheight, + REAL cheight, REAL dheight, REAL permanent) { + REAL adx, bdx, cdx, ady, bdy, cdy, adheight, bdheight, cdheight; + REAL det, errbound; + + REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; + REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0; + REAL bc[4], ca[4], ab[4]; + REAL bc3, ca3, ab3; + REAL adet[8], bdet[8], cdet[8]; + int alen, blen, clen; + REAL abdet[16]; + int ablen; + REAL *finnow, *finother, *finswap; + REAL fin1[192], fin2[192]; + int finlength; + + REAL adxtail, bdxtail, cdxtail; + REAL adytail, bdytail, cdytail; + REAL adheighttail, bdheighttail, cdheighttail; + REAL at_blarge, at_clarge; + REAL bt_clarge, bt_alarge; + REAL ct_alarge, ct_blarge; + REAL at_b[4], at_c[4], bt_c[4], bt_a[4], ct_a[4], ct_b[4]; + int at_blen, at_clen, bt_clen, bt_alen, ct_alen, ct_blen; + REAL bdxt_cdy1, cdxt_bdy1, cdxt_ady1; + REAL adxt_cdy1, adxt_bdy1, bdxt_ady1; + REAL bdxt_cdy0, cdxt_bdy0, cdxt_ady0; + REAL adxt_cdy0, adxt_bdy0, bdxt_ady0; + REAL bdyt_cdx1, cdyt_bdx1, cdyt_adx1; + REAL adyt_cdx1, adyt_bdx1, bdyt_adx1; + REAL bdyt_cdx0, cdyt_bdx0, cdyt_adx0; + REAL adyt_cdx0, adyt_bdx0, bdyt_adx0; + REAL bct[8], cat[8], abt[8]; + int bctlen, catlen, abtlen; + REAL bdxt_cdyt1, cdxt_bdyt1, cdxt_adyt1; + REAL adxt_cdyt1, adxt_bdyt1, bdxt_adyt1; + REAL bdxt_cdyt0, cdxt_bdyt0, cdxt_adyt0; + REAL adxt_cdyt0, adxt_bdyt0, bdxt_adyt0; + REAL u[4], v[12], w[16]; + REAL u3; + int vlength, wlength; + REAL negate; + + REAL bvirt; + REAL avirt, bround, around; + REAL c; + REAL abig; + REAL ahi, alo, bhi, blo; + REAL err1, err2, err3; + REAL _i, _j, _k; + REAL _0; + + adx = (REAL) (pa[0] - pd[0]); + bdx = (REAL) (pb[0] - pd[0]); + cdx = (REAL) (pc[0] - pd[0]); + ady = (REAL) (pa[1] - pd[1]); + bdy = (REAL) (pb[1] - pd[1]); + cdy = (REAL) (pc[1] - pd[1]); + adheight = (REAL) (aheight - dheight); + bdheight = (REAL) (bheight - dheight); + cdheight = (REAL) (cheight - dheight); + + Two_Product(bdx, cdy, bdxcdy1, bdxcdy0); + Two_Product(cdx, bdy, cdxbdy1, cdxbdy0); + Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + alen = scale_expansion_zeroelim(4, bc, adheight, adet); + + Two_Product(cdx, ady, cdxady1, cdxady0); + Two_Product(adx, cdy, adxcdy1, adxcdy0); + Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); + ca[3] = ca3; + blen = scale_expansion_zeroelim(4, ca, bdheight, bdet); + + Two_Product(adx, bdy, adxbdy1, adxbdy0); + Two_Product(bdx, ady, bdxady1, bdxady0); + Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + clen = scale_expansion_zeroelim(4, ab, cdheight, cdet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1); + + det = estimate(finlength, fin1); + errbound = o3derrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pd[0], adx, adxtail); + Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); + Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); + Two_Diff_Tail(pa[1], pd[1], ady, adytail); + Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); + Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); + Two_Diff_Tail(aheight, dheight, adheight, adheighttail); + Two_Diff_Tail(bheight, dheight, bdheight, bdheighttail); + Two_Diff_Tail(cheight, dheight, cdheight, cdheighttail); + + if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) && (adytail == 0.0) + && (bdytail == 0.0) && (cdytail == 0.0) && (adheighttail == 0.0) && (bdheighttail == 0.0) + && (cdheighttail == 0.0)) { + return det; + } + + errbound = o3derrboundC * permanent + resulterrbound * Absolute(det); + det += (adheight * ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail)) + + adheighttail * (bdx * cdy - bdy * cdx)) + + (bdheight * ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail)) + + bdheighttail * (cdx * ady - cdy * adx)) + + (cdheight * ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail)) + + cdheighttail * (adx * bdy - ady * bdx)); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + finnow = fin1; + finother = fin2; + + if (adxtail == 0.0) { + if (adytail == 0.0) { + at_b[0] = 0.0; + at_blen = 1; + at_c[0] = 0.0; + at_clen = 1; + } + else { + negate = -adytail; + Two_Product(negate, bdx, at_blarge, at_b[0]); + at_b[1] = at_blarge; + at_blen = 2; + Two_Product(adytail, cdx, at_clarge, at_c[0]); + at_c[1] = at_clarge; + at_clen = 2; + } + } + else { + if (adytail == 0.0) { + Two_Product(adxtail, bdy, at_blarge, at_b[0]); + at_b[1] = at_blarge; + at_blen = 2; + negate = -adxtail; + Two_Product(negate, cdy, at_clarge, at_c[0]); + at_c[1] = at_clarge; + at_clen = 2; + } + else { + Two_Product(adxtail, bdy, adxt_bdy1, adxt_bdy0); + Two_Product(adytail, bdx, adyt_bdx1, adyt_bdx0); + Two_Two_Diff(adxt_bdy1, adxt_bdy0, adyt_bdx1, adyt_bdx0, at_blarge, at_b[2], at_b[1], + at_b[0]); + at_b[3] = at_blarge; + at_blen = 4; + Two_Product(adytail, cdx, adyt_cdx1, adyt_cdx0); + Two_Product(adxtail, cdy, adxt_cdy1, adxt_cdy0); + Two_Two_Diff(adyt_cdx1, adyt_cdx0, adxt_cdy1, adxt_cdy0, at_clarge, at_c[2], at_c[1], + at_c[0]); + at_c[3] = at_clarge; + at_clen = 4; + } + } + if (bdxtail == 0.0) { + if (bdytail == 0.0) { + bt_c[0] = 0.0; + bt_clen = 1; + bt_a[0] = 0.0; + bt_alen = 1; + } + else { + negate = -bdytail; + Two_Product(negate, cdx, bt_clarge, bt_c[0]); + bt_c[1] = bt_clarge; + bt_clen = 2; + Two_Product(bdytail, adx, bt_alarge, bt_a[0]); + bt_a[1] = bt_alarge; + bt_alen = 2; + } + } + else { + if (bdytail == 0.0) { + Two_Product(bdxtail, cdy, bt_clarge, bt_c[0]); + bt_c[1] = bt_clarge; + bt_clen = 2; + negate = -bdxtail; + Two_Product(negate, ady, bt_alarge, bt_a[0]); + bt_a[1] = bt_alarge; + bt_alen = 2; + } + else { + Two_Product(bdxtail, cdy, bdxt_cdy1, bdxt_cdy0); + Two_Product(bdytail, cdx, bdyt_cdx1, bdyt_cdx0); + Two_Two_Diff(bdxt_cdy1, bdxt_cdy0, bdyt_cdx1, bdyt_cdx0, bt_clarge, bt_c[2], bt_c[1], + bt_c[0]); + bt_c[3] = bt_clarge; + bt_clen = 4; + Two_Product(bdytail, adx, bdyt_adx1, bdyt_adx0); + Two_Product(bdxtail, ady, bdxt_ady1, bdxt_ady0); + Two_Two_Diff(bdyt_adx1, bdyt_adx0, bdxt_ady1, bdxt_ady0, bt_alarge, bt_a[2], bt_a[1], + bt_a[0]); + bt_a[3] = bt_alarge; + bt_alen = 4; + } + } + if (cdxtail == 0.0) { + if (cdytail == 0.0) { + ct_a[0] = 0.0; + ct_alen = 1; + ct_b[0] = 0.0; + ct_blen = 1; + } + else { + negate = -cdytail; + Two_Product(negate, adx, ct_alarge, ct_a[0]); + ct_a[1] = ct_alarge; + ct_alen = 2; + Two_Product(cdytail, bdx, ct_blarge, ct_b[0]); + ct_b[1] = ct_blarge; + ct_blen = 2; + } + } + else { + if (cdytail == 0.0) { + Two_Product(cdxtail, ady, ct_alarge, ct_a[0]); + ct_a[1] = ct_alarge; + ct_alen = 2; + negate = -cdxtail; + Two_Product(negate, bdy, ct_blarge, ct_b[0]); + ct_b[1] = ct_blarge; + ct_blen = 2; + } + else { + Two_Product(cdxtail, ady, cdxt_ady1, cdxt_ady0); + Two_Product(cdytail, adx, cdyt_adx1, cdyt_adx0); + Two_Two_Diff(cdxt_ady1, cdxt_ady0, cdyt_adx1, cdyt_adx0, ct_alarge, ct_a[2], ct_a[1], + ct_a[0]); + ct_a[3] = ct_alarge; + ct_alen = 4; + Two_Product(cdytail, bdx, cdyt_bdx1, cdyt_bdx0); + Two_Product(cdxtail, bdy, cdxt_bdy1, cdxt_bdy0); + Two_Two_Diff(cdyt_bdx1, cdyt_bdx0, cdxt_bdy1, cdxt_bdy0, ct_blarge, ct_b[2], ct_b[1], + ct_b[0]); + ct_b[3] = ct_blarge; + ct_blen = 4; + } + } + + bctlen = fast_expansion_sum_zeroelim(bt_clen, bt_c, ct_blen, ct_b, bct); + wlength = scale_expansion_zeroelim(bctlen, bct, adheight, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + catlen = fast_expansion_sum_zeroelim(ct_alen, ct_a, at_clen, at_c, cat); + wlength = scale_expansion_zeroelim(catlen, cat, bdheight, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + abtlen = fast_expansion_sum_zeroelim(at_blen, at_b, bt_alen, bt_a, abt); + wlength = scale_expansion_zeroelim(abtlen, abt, cdheight, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + if (adheighttail != 0.0) { + vlength = scale_expansion_zeroelim(4, bc, adheighttail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdheighttail != 0.0) { + vlength = scale_expansion_zeroelim(4, ca, bdheighttail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdheighttail != 0.0) { + vlength = scale_expansion_zeroelim(4, ab, cdheighttail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + if (adxtail != 0.0) { + if (bdytail != 0.0) { + Two_Product(adxtail, bdytail, adxt_bdyt1, adxt_bdyt0); + Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdheight, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (cdheighttail != 0.0) { + Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdheighttail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + if (cdytail != 0.0) { + negate = -adxtail; + Two_Product(negate, cdytail, adxt_cdyt1, adxt_cdyt0); + Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdheight, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (bdheighttail != 0.0) { + Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdheighttail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + } + if (bdxtail != 0.0) { + if (cdytail != 0.0) { + Two_Product(bdxtail, cdytail, bdxt_cdyt1, bdxt_cdyt0); + Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adheight, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (adheighttail != 0.0) { + Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adheighttail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + if (adytail != 0.0) { + negate = -bdxtail; + Two_Product(negate, adytail, bdxt_adyt1, bdxt_adyt0); + Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdheight, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (cdheighttail != 0.0) { + Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdheighttail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + } + if (cdxtail != 0.0) { + if (adytail != 0.0) { + Two_Product(cdxtail, adytail, cdxt_adyt1, cdxt_adyt0); + Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdheight, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (bdheighttail != 0.0) { + Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdheighttail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + if (bdytail != 0.0) { + negate = -cdxtail; + Two_Product(negate, bdytail, cdxt_bdyt1, cdxt_bdyt0); + Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adheight, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (adheighttail != 0.0) { + Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adheighttail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + } + + if (adheighttail != 0.0) { + wlength = scale_expansion_zeroelim(bctlen, bct, adheighttail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdheighttail != 0.0) { + wlength = scale_expansion_zeroelim(catlen, cat, bdheighttail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdheighttail != 0.0) { + wlength = scale_expansion_zeroelim(abtlen, abt, cdheighttail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + return finnow[finlength - 1]; +} + +REAL orient3d(struct mesh *m, struct behavior *b, vertex pa, vertex pb, vertex pc, vertex pd, + REAL aheight, REAL bheight, REAL cheight, REAL dheight) { + REAL adx, bdx, cdx, ady, bdy, cdy, adheight, bdheight, cdheight; + REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; + REAL det; + REAL permanent, errbound; + + m->orient3dcount++; + + adx = pa[0] - pd[0]; + bdx = pb[0] - pd[0]; + cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; + cdy = pc[1] - pd[1]; + adheight = aheight - dheight; + bdheight = bheight - dheight; + cdheight = cheight - dheight; + + bdxcdy = bdx * cdy; + cdxbdy = cdx * bdy; + + cdxady = cdx * ady; + adxcdy = adx * cdy; + + adxbdy = adx * bdy; + bdxady = bdx * ady; + + det = adheight * (bdxcdy - cdxbdy) + bdheight * (cdxady - adxcdy) + cdheight * (adxbdy - bdxady); + + if (b->noexact) { + return det; + } + + permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adheight) + + (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdheight) + + (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdheight); + errbound = o3derrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + return det; + } + + return orient3dadapt(pa, pb, pc, pd, aheight, bheight, cheight, dheight, permanent); +} + +/*****************************************************************************/ +/* */ +/* nonregular() Return a positive value if the point pd is incompatible */ +/* with the circle or plane passing through pa, pb, and pc */ +/* (meaning that pd is inside the circle or below the */ +/* plane); a negative value if it is compatible; and zero if */ +/* the four points are cocircular/coplanar. The points pa, */ +/* pb, and pc must be in counterclockwise order, or the sign */ +/* of the result will be reversed. */ +/* */ +/* If the -w switch is used, the points are lifted onto the parabolic */ +/* lifting map, then they are dropped according to their weights, then the */ +/* 3D orientation test is applied. If the -W switch is used, the points' */ +/* heights are already provided, so the 3D orientation test is applied */ +/* directly. If neither switch is used, the incircle test is applied. */ +/* */ +/*****************************************************************************/ + +REAL nonregular(struct mesh *m, struct behavior *b, vertex pa, vertex pb, vertex pc, vertex pd) { + if (b->weighted == 0) { + return incircle(m, b, pa, pb, pc, pd); + } + else if (b->weighted == 1) { + return orient3d(m, b, pa, pb, pc, pd, pa[0] * pa[0] + pa[1] * pa[1] - pa[2], + pb[0] * pb[0] + pb[1] * pb[1] - pb[2], pc[0] * pc[0] + pc[1] * pc[1] - pc[2], + pd[0] * pd[0] + pd[1] * pd[1] - pd[2]); + } + else { + return orient3d(m, b, pa, pb, pc, pd, pa[2], pb[2], pc[2], pd[2]); + } +} + +/*****************************************************************************/ +/* */ +/* findcircumcenter() Find the circumcenter of a triangle. */ +/* */ +/* The result is returned both in terms of x-y coordinates and xi-eta */ +/* (barycentric) coordinates. The xi-eta coordinate system is defined in */ +/* terms of the triangle: the origin of the triangle is the origin of the */ +/* coordinate system; the destination of the triangle is one unit along the */ +/* xi axis; and the apex of the triangle is one unit along the eta axis. */ +/* This procedure also returns the square of the length of the triangle's */ +/* shortest edge. */ +/* */ +/*****************************************************************************/ + +void findcircumcenter(struct mesh *m, struct behavior *b, vertex torg, vertex tdest, vertex tapex, + vertex circumcenter, REAL *xi, REAL *eta, int offcenter) { + REAL xdo, ydo, xao, yao; + REAL dodist, aodist, dadist; + REAL denominator; + REAL dx, dy, dxoff, dyoff; + + m->circumcentercount++; + + /* Compute the circumcenter of the triangle. */ + xdo = tdest[0] - torg[0]; + ydo = tdest[1] - torg[1]; + xao = tapex[0] - torg[0]; + yao = tapex[1] - torg[1]; + dodist = xdo * xdo + ydo * ydo; + aodist = xao * xao + yao * yao; + dadist = (tdest[0] - tapex[0]) * (tdest[0] - tapex[0]) + + (tdest[1] - tapex[1]) * (tdest[1] - tapex[1]); + if (b->noexact) { + denominator = 0.5 / (xdo * yao - xao * ydo); + } + else { + /* Use the counterclockwise() routine to ensure a positive (and */ + /* reasonably accurate) result, avoiding any possibility of */ + /* division by zero. */ + denominator = 0.5 / counterclockwise(m, b, tdest, tapex, torg); + /* Don't count the above as an orientation test. */ + m->counterclockcount--; + } + dx = (yao * dodist - ydo * aodist) * denominator; + dy = (xdo * aodist - xao * dodist) * denominator; + + /* Find the (squared) length of the triangle's shortest edge. This */ + /* serves as a conservative estimate of the insertion radius of the */ + /* circumcenter's parent. The estimate is used to ensure that */ + /* the algorithm terminates even if very small angles appear in */ + /* the input PSLG. */ + if ((dodist < aodist) && (dodist < dadist)) { + if (offcenter && (b->offconstant > 0.0)) { + /* Find the position of the off-center, as described by Alper Ungor. */ + dxoff = 0.5 * xdo - b->offconstant * ydo; + dyoff = 0.5 * ydo + b->offconstant * xdo; + /* If the off-center is closer to the origin than the */ + /* circumcenter, use the off-center instead. */ + if (dxoff * dxoff + dyoff * dyoff < dx * dx + dy * dy) { + dx = dxoff; + dy = dyoff; + } + } + } + else if (aodist < dadist) { + if (offcenter && (b->offconstant > 0.0)) { + dxoff = 0.5 * xao + b->offconstant * yao; + dyoff = 0.5 * yao - b->offconstant * xao; + /* If the off-center is closer to the origin than the */ + /* circumcenter, use the off-center instead. */ + if (dxoff * dxoff + dyoff * dyoff < dx * dx + dy * dy) { + dx = dxoff; + dy = dyoff; + } + } + } + else { + if (offcenter && (b->offconstant > 0.0)) { + dxoff = 0.5 * (tapex[0] - tdest[0]) - b->offconstant * (tapex[1] - tdest[1]); + dyoff = 0.5 * (tapex[1] - tdest[1]) + b->offconstant * (tapex[0] - tdest[0]); + /* If the off-center is closer to the destination than the */ + /* circumcenter, use the off-center instead. */ + if (dxoff * dxoff + dyoff * dyoff < (dx - xdo) * (dx - xdo) + (dy - ydo) * (dy - ydo)) { + dx = xdo + dxoff; + dy = ydo + dyoff; + } + } + } + + circumcenter[0] = torg[0] + dx; + circumcenter[1] = torg[1] + dy; + + /* To interpolate vertex attributes for the new vertex inserted at */ + /* the circumcenter, define a coordinate system with a xi-axis, */ + /* directed from the triangle's origin to its destination, and */ + /* an eta-axis, directed from its origin to its apex. */ + /* Calculate the xi and eta coordinates of the circumcenter. */ + *xi = (yao * dx - xao * dy) * (2.0 * denominator); + *eta = (xdo * dy - ydo * dx) * (2.0 * denominator); +} + +/** **/ +/** **/ +/********* Geometric primitives end here *********/ + +/*****************************************************************************/ +/* */ +/* triangleinit() Initialize some variables. */ +/* */ +/*****************************************************************************/ + +void triangleinit(struct mesh *m) { + poolzero(&m->vertices); + poolzero(&m->triangles); + poolzero(&m->subsegs); + poolzero(&m->viri); + poolzero(&m->badsubsegs); + poolzero(&m->badtriangles); + poolzero(&m->flipstackers); + poolzero(&m->splaynodes); + + m->recenttri.tri = (triangle *) NULL; /* No triangle has been visited yet. */ + m->undeads = 0; /* No eliminated input vertices yet. */ + m->samples = 1; /* Point location should take at least one sample. */ + m->checksegments = 0; /* There are no segments in the triangulation yet. */ + m->checkquality = 0; /* The quality triangulation stage has not begun. */ + m->incirclecount = m->counterclockcount = m->orient3dcount = 0; + m->hyperbolacount = m->circletopcount = m->circumcentercount = 0; + randomseed = 1; + + exactinit(); /* Initialize exact arithmetic constants. */ +} + +/*****************************************************************************/ +/* */ +/* randomnation() Generate a random number between 0 and `choices' - 1. */ +/* */ +/* This is a simple linear congruential random number generator. Hence, it */ +/* is a bad random number generator, but good enough for most randomized */ +/* geometric algorithms. */ +/* */ +/*****************************************************************************/ + +unsigned long randomnation(unsigned int choices) { + randomseed = (randomseed * 1366l + 150889l) % 714025l; + return randomseed / (714025l / choices + 1); +} + +/********* Point location routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* makevertexmap() Construct a mapping from vertices to triangles to */ +/* improve the speed of point location for segment */ +/* insertion. */ +/* */ +/* Traverses all the triangles, and provides each corner of each triangle */ +/* with a pointer to that triangle. Of course, pointers will be */ +/* overwritten by other pointers because (almost) each vertex is a corner */ +/* of several triangles, but in the end every vertex will point to some */ +/* triangle that contains it. */ +/* */ +/*****************************************************************************/ + +void makevertexmap(struct mesh *m, struct behavior *b) { + struct otri triangleloop; + vertex triorg; + + if (b->verbose) { + printf(" Constructing mapping from vertices to triangles.\n"); + } + traversalinit(&m->triangles); + triangleloop.tri = triangletraverse(m); + while (triangleloop.tri != (triangle *) NULL) { + /* Check all three vertices of the triangle. */ + for (triangleloop.orient = 0; triangleloop.orient < 3; triangleloop.orient++) { + org(triangleloop, triorg); + setvertex2tri(triorg, encode(triangleloop)); + } + triangleloop.tri = triangletraverse(m); + } +} + +/*****************************************************************************/ +/* */ +/* preciselocate() Find a triangle or edge containing a given point. */ +/* */ +/* Begins its search from `searchtri'. It is important that `searchtri' */ +/* be a handle with the property that `searchpoint' is strictly to the left */ +/* of the edge denoted by `searchtri', or is collinear with that edge and */ +/* does not intersect that edge. (In particular, `searchpoint' should not */ +/* be the origin or destination of that edge.) */ +/* */ +/* These conditions are imposed because preciselocate() is normally used in */ +/* one of two situations: */ +/* */ +/* (1) To try to find the location to insert a new point. Normally, we */ +/* know an edge that the point is strictly to the left of. In the */ +/* incremental Delaunay algorithm, that edge is a bounding box edge. */ +/* In Ruppert's Delaunay refinement algorithm for quality meshing, */ +/* that edge is the shortest edge of the triangle whose circumcenter */ +/* is being inserted. */ +/* */ +/* (2) To try to find an existing point. In this case, any edge on the */ +/* convex hull is a good starting edge. You must screen out the */ +/* possibility that the vertex sought is an endpoint of the starting */ +/* edge before you call preciselocate(). */ +/* */ +/* On completion, `searchtri' is a triangle that contains `searchpoint'. */ +/* */ +/* This implementation differs from that given by Guibas and Stolfi. It */ +/* walks from triangle to triangle, crossing an edge only if `searchpoint' */ +/* is on the other side of the line containing that edge. After entering */ +/* a triangle, there are two edges by which one can leave that triangle. */ +/* If both edges are valid (`searchpoint' is on the other side of both */ +/* edges), one of the two is chosen by drawing a line perpendicular to */ +/* the entry edge (whose endpoints are `forg' and `fdest') passing through */ +/* `fapex'. Depending on which side of this perpendicular `searchpoint' */ +/* falls on, an exit edge is chosen. */ +/* */ +/* This implementation is empirically faster than the Guibas and Stolfi */ +/* point location routine (which I originally used), which tends to spiral */ +/* in toward its target. */ +/* */ +/* Returns ONVERTEX if the point lies on an existing vertex. `searchtri' */ +/* is a handle whose origin is the existing vertex. */ +/* */ +/* Returns ONEDGE if the point lies on a mesh edge. `searchtri' is a */ +/* handle whose primary edge is the edge on which the point lies. */ +/* */ +/* Returns INTRIANGLE if the point lies strictly within a triangle. */ +/* `searchtri' is a handle on the triangle that contains the point. */ +/* */ +/* Returns OUTSIDE if the point lies outside the mesh. `searchtri' is a */ +/* handle whose primary edge the point is to the right of. This might */ +/* occur when the circumcenter of a triangle falls just slightly outside */ +/* the mesh due to floating-point roundoff error. It also occurs when */ +/* seeking a hole or region point that a foolish user has placed outside */ +/* the mesh. */ +/* */ +/* If `stopatsubsegment' is nonzero, the search will stop if it tries to */ +/* walk through a subsegment, and will return OUTSIDE. */ +/* */ +/* WARNING: This routine is designed for convex triangulations, and will */ +/* not generally work after the holes and concavities have been carved. */ +/* However, it can still be used to find the circumcenter of a triangle, as */ +/* long as the search is begun from the triangle in question. */ +/* */ +/*****************************************************************************/ + +enum locateresult preciselocate(struct mesh *m, struct behavior *b, vertex searchpoint, + struct otri *searchtri, int stopatsubsegment) { + struct otri backtracktri; + struct osub checkedge; + vertex forg, fdest, fapex; + REAL orgorient, destorient; + int moveleft; + triangle ptr; /* Temporary variable used by sym(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + if (b->verbose > 2) { + printf(" Searching for point (%.12g, %.12g).\n", searchpoint[0], searchpoint[1]); + } + /* Where are we? */ + org(*searchtri, forg); + dest(*searchtri, fdest); + apex(*searchtri, fapex); + while (1) { + if (b->verbose > 2) { + printf( + " At (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", forg[0], forg[1], fdest[0], fdest[1], fapex[0], fapex[1]); + } + /* Check whether the apex is the point we seek. */ + if ((fapex[0] == searchpoint[0]) && (fapex[1] == searchpoint[1])) { + lprevself(*searchtri); + return ONVERTEX; + } + /* Does the point lie on the other side of the line defined by the */ + /* triangle edge opposite the triangle's destination? */ + destorient = counterclockwise(m, b, forg, fapex, searchpoint); + /* Does the point lie on the other side of the line defined by the */ + /* triangle edge opposite the triangle's origin? */ + orgorient = counterclockwise(m, b, fapex, fdest, searchpoint); + if (destorient > 0.0) { + if (orgorient > 0.0) { + /* Move left if the inner product of (fapex - searchpoint) and */ + /* (fdest - forg) is positive. This is equivalent to drawing */ + /* a line perpendicular to the line (forg, fdest) and passing */ + /* through `fapex', and determining which side of this line */ + /* `searchpoint' falls on. */ + moveleft = (fapex[0] - searchpoint[0]) * (fdest[0] - forg[0]) + + (fapex[1] - searchpoint[1]) * (fdest[1] - forg[1]) > 0.0; + } + else { + moveleft = 1; + } + } + else { + if (orgorient > 0.0) { + moveleft = 0; + } + else { + /* The point we seek must be on the boundary of or inside this */ + /* triangle. */ + if (destorient == 0.0) { + lprevself(*searchtri); + return ONEDGE; + } + if (orgorient == 0.0) { + lnextself(*searchtri); + return ONEDGE; + } + return INTRIANGLE; + } + } + + /* Move to another triangle. Leave a trace `backtracktri' in case */ + /* floating-point roundoff or some such bogey causes us to walk */ + /* off a boundary of the triangulation. */ + if (moveleft) { + lprev(*searchtri, backtracktri); + fdest = fapex; + } + else { + lnext(*searchtri, backtracktri); + forg = fapex; + } + sym(backtracktri, *searchtri); + + if (m->checksegments && stopatsubsegment) { + /* Check for walking through a subsegment. */ + tspivot(backtracktri, checkedge); + if (checkedge.ss != m->dummysub) { + /* Go back to the last triangle. */ + otricopy(backtracktri, *searchtri); + return OUTSIDE; + } + } + /* Check for walking right out of the triangulation. */ + if (searchtri->tri == m->dummytri) { + /* Go back to the last triangle. */ + otricopy(backtracktri, *searchtri); + return OUTSIDE; + } + + apex(*searchtri, fapex); + } +} + +/*****************************************************************************/ +/* */ +/* locate() Find a triangle or edge containing a given point. */ +/* */ +/* Searching begins from one of: the input `searchtri', a recently */ +/* encountered triangle `recenttri', or from a triangle chosen from a */ +/* random sample. The choice is made by determining which triangle's */ +/* origin is closest to the point we are searching for. Normally, */ +/* `searchtri' should be a handle on the convex hull of the triangulation. */ +/* */ +/* Details on the random sampling method can be found in the Mucke, Saias, */ +/* and Zhu paper cited in the header of this code. */ +/* */ +/* On completion, `searchtri' is a triangle that contains `searchpoint'. */ +/* */ +/* Returns ONVERTEX if the point lies on an existing vertex. `searchtri' */ +/* is a handle whose origin is the existing vertex. */ +/* */ +/* Returns ONEDGE if the point lies on a mesh edge. `searchtri' is a */ +/* handle whose primary edge is the edge on which the point lies. */ +/* */ +/* Returns INTRIANGLE if the point lies strictly within a triangle. */ +/* `searchtri' is a handle on the triangle that contains the point. */ +/* */ +/* Returns OUTSIDE if the point lies outside the mesh. `searchtri' is a */ +/* handle whose primary edge the point is to the right of. This might */ +/* occur when the circumcenter of a triangle falls just slightly outside */ +/* the mesh due to floating-point roundoff error. It also occurs when */ +/* seeking a hole or region point that a foolish user has placed outside */ +/* the mesh. */ +/* */ +/* WARNING: This routine is designed for convex triangulations, and will */ +/* not generally work after the holes and concavities have been carved. */ +/* */ +/*****************************************************************************/ + +enum locateresult locate(struct mesh *m, struct behavior *b, vertex searchpoint, + struct otri *searchtri) { + VOID **sampleblock; + char *firsttri; + struct otri sampletri; + vertex torg, tdest; + unsigned long alignptr; + REAL searchdist, dist; + REAL ahead; + long samplesperblock, totalsamplesleft, samplesleft; + long population, totalpopulation; + triangle ptr; /* Temporary variable used by sym(). */ + + if (b->verbose > 2) { + printf( + " Randomly sampling for a triangle near point (%.12g, %.12g).\n", searchpoint[0], searchpoint[1]); + } + /* Record the distance from the suggested starting triangle to the */ + /* point we seek. */ + org(*searchtri, torg); + searchdist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) + + (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]); + if (b->verbose > 2) { + printf(" Boundary triangle has origin (%.12g, %.12g).\n", torg[0], torg[1]); + } + + /* If a recently encountered triangle has been recorded and has not been */ + /* deallocated, test it as a good starting point. */ + if (m->recenttri.tri != (triangle *) NULL) { + if (!deadtri(m->recenttri.tri)) { + org(m->recenttri, torg); + if ((torg[0] == searchpoint[0]) && (torg[1] == searchpoint[1])) { + otricopy(m->recenttri, *searchtri); + return ONVERTEX; + } + dist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) + + (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]); + if (dist < searchdist) { + otricopy(m->recenttri, *searchtri); + searchdist = dist; + if (b->verbose > 2) { + printf( + " Choosing recent triangle with origin (%.12g, %.12g).\n", torg[0], torg[1]); + } + } + } + } + + /* The number of random samples taken is proportional to the cube root of */ + /* the number of triangles in the mesh. The next bit of code assumes */ + /* that the number of triangles increases monotonically (or at least */ + /* doesn't decrease enough to matter). */ + while (SAMPLEFACTOR * m->samples * m->samples * m->samples < m->triangles.items) { + m->samples++; + } + + /* We'll draw ceiling(samples * TRIPERBLOCK / maxitems) random samples */ + /* from each block of triangles (except the first)--until we meet the */ + /* sample quota. The ceiling means that blocks at the end might be */ + /* neglected, but I don't care. */ + samplesperblock = (m->samples * TRIPERBLOCK - 1) / m->triangles.maxitems + 1; + /* We'll draw ceiling(samples * itemsfirstblock / maxitems) random samples */ + /* from the first block of triangles. */ + samplesleft = (m->samples * m->triangles.itemsfirstblock - 1) / m->triangles.maxitems + 1; + totalsamplesleft = m->samples; + population = m->triangles.itemsfirstblock; + totalpopulation = m->triangles.maxitems; + sampleblock = m->triangles.firstblock; + sampletri.orient = 0; + while (totalsamplesleft > 0) { + /* If we're in the last block, `population' needs to be corrected. */ + if (population > totalpopulation) { + population = totalpopulation; + } + /* Find a pointer to the first triangle in the block. */ + alignptr = (unsigned long) (sampleblock + 1); + firsttri = (char *) (alignptr + (unsigned long) m->triangles.alignbytes + - (alignptr % (unsigned long) m->triangles.alignbytes)); + + /* Choose `samplesleft' randomly sampled triangles in this block. */ + do { + sampletri.tri = (triangle *) (firsttri + + (randomnation((unsigned int) population) * m->triangles.itembytes)); + if (!deadtri(sampletri.tri)) { + org(sampletri, torg); + dist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) + + (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]); + if (dist < searchdist) { + otricopy(sampletri, *searchtri); + searchdist = dist; + if (b->verbose > 2) { + printf(" Choosing triangle with origin (%.12g, %.12g).\n", torg[0], torg[1]); + } + } + } + + samplesleft--; + totalsamplesleft--; + } while ((samplesleft > 0) && (totalsamplesleft > 0)); + + if (totalsamplesleft > 0) { + sampleblock = (VOID **) *sampleblock; + samplesleft = samplesperblock; + totalpopulation -= population; + population = TRIPERBLOCK; + } + } + + /* Where are we? */ + org(*searchtri, torg); + dest(*searchtri, tdest); + /* Check the starting triangle's vertices. */ + if ((torg[0] == searchpoint[0]) && (torg[1] == searchpoint[1])) { + return ONVERTEX; + } + if ((tdest[0] == searchpoint[0]) && (tdest[1] == searchpoint[1])) { + lnextself(*searchtri); + return ONVERTEX; + } + /* Orient `searchtri' to fit the preconditions of calling preciselocate(). */ + ahead = counterclockwise(m, b, torg, tdest, searchpoint); + if (ahead < 0.0) { + /* Turn around so that `searchpoint' is to the left of the */ + /* edge specified by `searchtri'. */ + symself(*searchtri); + } + else if (ahead == 0.0) { + /* Check if `searchpoint' is between `torg' and `tdest'. */ + if (((torg[0] < searchpoint[0]) == (searchpoint[0] < tdest[0])) + && ((torg[1] < searchpoint[1]) == (searchpoint[1] < tdest[1]))) { + return ONEDGE; + } + } + return preciselocate(m, b, searchpoint, searchtri, 0); +} + +/** **/ +/** **/ +/********* Point location routines end here *********/ + +/********* Mesh transformation routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* insertsubseg() Create a new subsegment and insert it between two */ +/* triangles. */ +/* */ +/* The new subsegment is inserted at the edge described by the handle */ +/* `tri'. Its vertices are properly initialized. The marker `subsegmark' */ +/* is applied to the subsegment and, if appropriate, its vertices. */ +/* */ +/*****************************************************************************/ + +void insertsubseg(struct mesh *m, struct behavior *b, struct otri *tri, int subsegmark) { + struct otri oppotri; + struct osub newsubseg; + vertex triorg, tridest; + triangle ptr; /* Temporary variable used by sym(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + org(*tri, triorg); + dest(*tri, tridest); + /* Mark vertices if possible. */ + if (vertexmark(triorg) == 0) { + setvertexmark(triorg, subsegmark); + } + if (vertexmark(tridest) == 0) { + setvertexmark(tridest, subsegmark); + } + /* Check if there's already a subsegment here. */ + tspivot(*tri, newsubseg); + if (newsubseg.ss == m->dummysub) { + /* Make new subsegment and initialize its vertices. */ + makesubseg(m, &newsubseg); + setsorg(newsubseg, tridest); + setsdest(newsubseg, triorg); + setsegorg(newsubseg, tridest); + setsegdest(newsubseg, triorg); + /* Bond new subsegment to the two triangles it is sandwiched between. */ + /* Note that the facing triangle `oppotri' might be equal to */ + /* `dummytri' (outer space), but the new subsegment is bonded to it */ + /* all the same. */ + tsbond(*tri, newsubseg); + sym(*tri, oppotri); + ssymself(newsubseg); + tsbond(oppotri, newsubseg); + setmark(newsubseg, subsegmark); + if (b->verbose > 2) { + printf(" Inserting new "); + printsubseg(m, b, &newsubseg); + } + } + else { + if (mark(newsubseg) == 0) { + setmark(newsubseg, subsegmark); + } + } +} + +/*****************************************************************************/ +/* */ +/* Terminology */ +/* */ +/* A "local transformation" replaces a small set of triangles with another */ +/* set of triangles. This may or may not involve inserting or deleting a */ +/* vertex. */ +/* */ +/* The term "casing" is used to describe the set of triangles that are */ +/* attached to the triangles being transformed, but are not transformed */ +/* themselves. Think of the casing as a fixed hollow structure inside */ +/* which all the action happens. A "casing" is only defined relative to */ +/* a single transformation; each occurrence of a transformation will */ +/* involve a different casing. */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* */ +/* flip() Transform two triangles to two different triangles by flipping */ +/* an edge counterclockwise within a quadrilateral. */ +/* */ +/* Imagine the original triangles, abc and bad, oriented so that the */ +/* shared edge ab lies in a horizontal plane, with the vertex b on the left */ +/* and the vertex a on the right. The vertex c lies below the edge, and */ +/* the vertex d lies above the edge. The `flipedge' handle holds the edge */ +/* ab of triangle abc, and is directed left, from vertex a to vertex b. */ +/* */ +/* The triangles abc and bad are deleted and replaced by the triangles cdb */ +/* and dca. The triangles that represent abc and bad are NOT deallocated; */ +/* they are reused for dca and cdb, respectively. Hence, any handles that */ +/* may have held the original triangles are still valid, although not */ +/* directed as they were before. */ +/* */ +/* Upon completion of this routine, the `flipedge' handle holds the edge */ +/* dc of triangle dca, and is directed down, from vertex d to vertex c. */ +/* (Hence, the two triangles have rotated counterclockwise.) */ +/* */ +/* WARNING: This transformation is geometrically valid only if the */ +/* quadrilateral adbc is convex. Furthermore, this transformation is */ +/* valid only if there is not a subsegment between the triangles abc and */ +/* bad. This routine does not check either of these preconditions, and */ +/* it is the responsibility of the calling routine to ensure that they are */ +/* met. If they are not, the streets shall be filled with wailing and */ +/* gnashing of teeth. */ +/* */ +/*****************************************************************************/ + +void flip(struct mesh *m, struct behavior *b, struct otri *flipedge) { + struct otri botleft, botright; + struct otri topleft, topright; + struct otri top; + struct otri botlcasing, botrcasing; + struct otri toplcasing, toprcasing; + struct osub botlsubseg, botrsubseg; + struct osub toplsubseg, toprsubseg; + vertex leftvertex, rightvertex, botvertex; + vertex farvertex; + triangle ptr; /* Temporary variable used by sym(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + /* Identify the vertices of the quadrilateral. */ + org(*flipedge, rightvertex); + dest(*flipedge, leftvertex); + apex(*flipedge, botvertex); + sym(*flipedge, top); +#ifdef SELF_CHECK + if (top.tri == m->dummytri) + { + printf("Internal error in flip(): Attempt to flip on boundary.\n"); + lnextself(*flipedge); + return; + } + if (m->checksegments) + { + tspivot(*flipedge, toplsubseg); + if (toplsubseg.ss != m->dummysub) + { + printf("Internal error in flip(): Attempt to flip a segment.\n"); + lnextself(*flipedge); + return; + } + } +#endif /* SELF_CHECK */ + apex(top, farvertex); + + /* Identify the casing of the quadrilateral. */ + lprev(top, topleft); + sym(topleft, toplcasing); + lnext(top, topright); + sym(topright, toprcasing); + lnext(*flipedge, botleft); + sym(botleft, botlcasing); + lprev(*flipedge, botright); + sym(botright, botrcasing); + /* Rotate the quadrilateral one-quarter turn counterclockwise. */ + bond(topleft, botlcasing); + bond(botleft, botrcasing); + bond(botright, toprcasing); + bond(topright, toplcasing); + + if (m->checksegments) { + /* Check for subsegments and rebond them to the quadrilateral. */ + tspivot(topleft, toplsubseg); + tspivot(botleft, botlsubseg); + tspivot(botright, botrsubseg); + tspivot(topright, toprsubseg); + if (toplsubseg.ss == m->dummysub) { + tsdissolve(topright); + } + else { + tsbond(topright, toplsubseg); + } + if (botlsubseg.ss == m->dummysub) { + tsdissolve(topleft); + } + else { + tsbond(topleft, botlsubseg); + } + if (botrsubseg.ss == m->dummysub) { + tsdissolve(botleft); + } + else { + tsbond(botleft, botrsubseg); + } + if (toprsubseg.ss == m->dummysub) { + tsdissolve(botright); + } + else { + tsbond(botright, toprsubseg); + } + } + + /* New vertex assignments for the rotated quadrilateral. */ + setorg(*flipedge, farvertex); + setdest(*flipedge, botvertex); + setapex(*flipedge, rightvertex); + setorg(top, botvertex); + setdest(top, farvertex); + setapex(top, leftvertex); + if (b->verbose > 2) { + printf(" Edge flip results in left "); + printtriangle(m, b, &top); + printf(" and right "); + printtriangle(m, b, flipedge); + } +} + +/*****************************************************************************/ +/* */ +/* unflip() Transform two triangles to two different triangles by */ +/* flipping an edge clockwise within a quadrilateral. Reverses */ +/* the flip() operation so that the data structures representing */ +/* the triangles are back where they were before the flip(). */ +/* */ +/* Imagine the original triangles, abc and bad, oriented so that the */ +/* shared edge ab lies in a horizontal plane, with the vertex b on the left */ +/* and the vertex a on the right. The vertex c lies below the edge, and */ +/* the vertex d lies above the edge. The `flipedge' handle holds the edge */ +/* ab of triangle abc, and is directed left, from vertex a to vertex b. */ +/* */ +/* The triangles abc and bad are deleted and replaced by the triangles cdb */ +/* and dca. The triangles that represent abc and bad are NOT deallocated; */ +/* they are reused for cdb and dca, respectively. Hence, any handles that */ +/* may have held the original triangles are still valid, although not */ +/* directed as they were before. */ +/* */ +/* Upon completion of this routine, the `flipedge' handle holds the edge */ +/* cd of triangle cdb, and is directed up, from vertex c to vertex d. */ +/* (Hence, the two triangles have rotated clockwise.) */ +/* */ +/* WARNING: This transformation is geometrically valid only if the */ +/* quadrilateral adbc is convex. Furthermore, this transformation is */ +/* valid only if there is not a subsegment between the triangles abc and */ +/* bad. This routine does not check either of these preconditions, and */ +/* it is the responsibility of the calling routine to ensure that they are */ +/* met. If they are not, the streets shall be filled with wailing and */ +/* gnashing of teeth. */ +/* */ +/*****************************************************************************/ + +void unflip(struct mesh *m, struct behavior *b, struct otri *flipedge) { + struct otri botleft, botright; + struct otri topleft, topright; + struct otri top; + struct otri botlcasing, botrcasing; + struct otri toplcasing, toprcasing; + struct osub botlsubseg, botrsubseg; + struct osub toplsubseg, toprsubseg; + vertex leftvertex, rightvertex, botvertex; + vertex farvertex; + triangle ptr; /* Temporary variable used by sym(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + /* Identify the vertices of the quadrilateral. */ + org(*flipedge, rightvertex); + dest(*flipedge, leftvertex); + apex(*flipedge, botvertex); + sym(*flipedge, top); +#ifdef SELF_CHECK + if (top.tri == m->dummytri) + { + printf("Internal error in unflip(): Attempt to flip on boundary.\n"); + lnextself(*flipedge); + return; + } + if (m->checksegments) + { + tspivot(*flipedge, toplsubseg); + if (toplsubseg.ss != m->dummysub) + { + printf("Internal error in unflip(): Attempt to flip a subsegment.\n"); + lnextself(*flipedge); + return; + } + } +#endif /* SELF_CHECK */ + apex(top, farvertex); + + /* Identify the casing of the quadrilateral. */ + lprev(top, topleft); + sym(topleft, toplcasing); + lnext(top, topright); + sym(topright, toprcasing); + lnext(*flipedge, botleft); + sym(botleft, botlcasing); + lprev(*flipedge, botright); + sym(botright, botrcasing); + /* Rotate the quadrilateral one-quarter turn clockwise. */ + bond(topleft, toprcasing); + bond(botleft, toplcasing); + bond(botright, botlcasing); + bond(topright, botrcasing); + + if (m->checksegments) { + /* Check for subsegments and rebond them to the quadrilateral. */ + tspivot(topleft, toplsubseg); + tspivot(botleft, botlsubseg); + tspivot(botright, botrsubseg); + tspivot(topright, toprsubseg); + if (toplsubseg.ss == m->dummysub) { + tsdissolve(botleft); + } + else { + tsbond(botleft, toplsubseg); + } + if (botlsubseg.ss == m->dummysub) { + tsdissolve(botright); + } + else { + tsbond(botright, botlsubseg); + } + if (botrsubseg.ss == m->dummysub) { + tsdissolve(topright); + } + else { + tsbond(topright, botrsubseg); + } + if (toprsubseg.ss == m->dummysub) { + tsdissolve(topleft); + } + else { + tsbond(topleft, toprsubseg); + } + } + + /* New vertex assignments for the rotated quadrilateral. */ + setorg(*flipedge, botvertex); + setdest(*flipedge, farvertex); + setapex(*flipedge, leftvertex); + setorg(top, farvertex); + setdest(top, botvertex); + setapex(top, rightvertex); + if (b->verbose > 2) { + printf(" Edge unflip results in left "); + printtriangle(m, b, flipedge); + printf(" and right "); + printtriangle(m, b, &top); + } +} + +/*****************************************************************************/ +/* */ +/* insertvertex() Insert a vertex into a Delaunay triangulation, */ +/* performing flips as necessary to maintain the Delaunay */ +/* property. */ +/* */ +/* The point `insertvertex' is located. If `searchtri.tri' is not NULL, */ +/* the search for the containing triangle begins from `searchtri'. If */ +/* `searchtri.tri' is NULL, a full point location procedure is called. */ +/* If `insertvertex' is found inside a triangle, the triangle is split into */ +/* three; if `insertvertex' lies on an edge, the edge is split in two, */ +/* thereby splitting the two adjacent triangles into four. Edge flips are */ +/* used to restore the Delaunay property. If `insertvertex' lies on an */ +/* existing vertex, no action is taken, and the value DUPLICATEVERTEX is */ +/* returned. On return, `searchtri' is set to a handle whose origin is the */ +/* existing vertex. */ +/* */ +/* Normally, the parameter `splitseg' is set to NULL, implying that no */ +/* subsegment should be split. In this case, if `insertvertex' is found to */ +/* lie on a segment, no action is taken, and the value VIOLATINGVERTEX is */ +/* returned. On return, `searchtri' is set to a handle whose primary edge */ +/* is the violated subsegment. */ +/* */ +/* If the calling routine wishes to split a subsegment by inserting a */ +/* vertex in it, the parameter `splitseg' should be that subsegment. In */ +/* this case, `searchtri' MUST be the triangle handle reached by pivoting */ +/* from that subsegment; no point location is done. */ +/* */ +/* `segmentflaws' and `triflaws' are flags that indicate whether or not */ +/* there should be checks for the creation of encroached subsegments or bad */ +/* quality triangles. If a newly inserted vertex encroaches upon */ +/* subsegments, these subsegments are added to the list of subsegments to */ +/* be split if `segmentflaws' is set. If bad triangles are created, these */ +/* are added to the queue if `triflaws' is set. */ +/* */ +/* If a duplicate vertex or violated segment does not prevent the vertex */ +/* from being inserted, the return value will be ENCROACHINGVERTEX if the */ +/* vertex encroaches upon a subsegment (and checking is enabled), or */ +/* SUCCESSFULVERTEX otherwise. In either case, `searchtri' is set to a */ +/* handle whose origin is the newly inserted vertex. */ +/* */ +/* insertvertex() does not use flip() for reasons of speed; some */ +/* information can be reused from edge flip to edge flip, like the */ +/* locations of subsegments. */ +/* */ +/*****************************************************************************/ + +enum insertvertexresult insertvertex(struct mesh *m, struct behavior *b, vertex newvertex, + struct otri *searchtri, struct osub *splitseg, int segmentflaws, int triflaws) { + struct otri horiz; + struct otri top; + struct otri botleft, botright; + struct otri topleft, topright; + struct otri newbotleft, newbotright; + struct otri newtopright; + struct otri botlcasing, botrcasing; + struct otri toplcasing, toprcasing; + struct otri testtri; + struct osub botlsubseg, botrsubseg; + struct osub toplsubseg, toprsubseg; + struct osub brokensubseg; + struct osub checksubseg; + struct osub rightsubseg; + struct osub newsubseg; + struct badsubseg *encroached; + struct flipstacker *newflip; + vertex first; + vertex leftvertex, rightvertex, botvertex, topvertex, farvertex; + vertex segmentorg, segmentdest; + REAL attrib; + REAL area; + enum insertvertexresult success; + enum locateresult intersect; + int doflip; + int mirrorflag; + int enq; + int i; + triangle ptr; /* Temporary variable used by sym(). */ + subseg sptr; /* Temporary variable used by spivot() and tspivot(). */ + + if (b->verbose > 1) { + printf(" Inserting (%.12g, %.12g).\n", newvertex[0], newvertex[1]); + } + + if (splitseg == (struct osub *) NULL) { + /* Find the location of the vertex to be inserted. Check if a good */ + /* starting triangle has already been provided by the caller. */ + if (searchtri->tri == m->dummytri) { + /* Find a boundary triangle. */ + horiz.tri = m->dummytri; + horiz.orient = 0; + symself(horiz); + /* Search for a triangle containing `newvertex'. */ + intersect = locate(m, b, newvertex, &horiz); + } + else { + /* Start searching from the triangle provided by the caller. */ + otricopy(*searchtri, horiz); + intersect = preciselocate(m, b, newvertex, &horiz, 1); + } + } + else { + /* The calling routine provides the subsegment in which */ + /* the vertex is inserted. */ + otricopy(*searchtri, horiz); + intersect = ONEDGE; + } + + if (intersect == ONVERTEX) { + /* There's already a vertex there. Return in `searchtri' a triangle */ + /* whose origin is the existing vertex. */ + otricopy(horiz, *searchtri); + otricopy(horiz, m->recenttri); + return DUPLICATEVERTEX; + } + if ((intersect == ONEDGE) || (intersect == OUTSIDE)) { + /* The vertex falls on an edge or boundary. */ + if (m->checksegments && (splitseg == (struct osub *) NULL)) { + /* Check whether the vertex falls on a subsegment. */ + tspivot(horiz, brokensubseg); + if (brokensubseg.ss != m->dummysub) { + /* The vertex falls on a subsegment, and hence will not be inserted. */ + if (segmentflaws) { + enq = b->nobisect != 2; + if (enq && (b->nobisect == 1)) { + /* This subsegment may be split only if it is an */ + /* internal boundary. */ + sym(horiz, testtri); + enq = testtri.tri != m->dummytri; + } + if (enq) { + /* Add the subsegment to the list of encroached subsegments. */ + encroached = (struct badsubseg *) poolalloc(&m->badsubsegs); + encroached->encsubseg = sencode(brokensubseg); + sorg(brokensubseg, encroached->subsegorg); + sdest(brokensubseg, encroached->subsegdest); + if (b->verbose > 2) { + printf( + " Queueing encroached subsegment (%.12g, %.12g) (%.12g, %.12g).\n", encroached->subsegorg[0], encroached->subsegorg[1], encroached->subsegdest[0], encroached->subsegdest[1]); + } + } + } + /* Return a handle whose primary edge contains the vertex, */ + /* which has not been inserted. */ + otricopy(horiz, *searchtri); + otricopy(horiz, m->recenttri); + return VIOLATINGVERTEX; + } + } + + /* Insert the vertex on an edge, dividing one triangle into two (if */ + /* the edge lies on a boundary) or two triangles into four. */ + lprev(horiz, botright); + sym(botright, botrcasing); + sym(horiz, topright); + /* Is there a second triangle? (Or does this edge lie on a boundary?) */ + mirrorflag = topright.tri != m->dummytri; + if (mirrorflag) { + lnextself(topright); + sym(topright, toprcasing); + maketriangle(m, b, &newtopright); + } + else { + /* Splitting a boundary edge increases the number of boundary edges. */ + m->hullsize++; + } + maketriangle(m, b, &newbotright); + + /* Set the vertices of changed and new triangles. */ + org(horiz, rightvertex); + dest(horiz, leftvertex); + apex(horiz, botvertex); + setorg(newbotright, botvertex); + setdest(newbotright, rightvertex); + setapex(newbotright, newvertex); + setorg(horiz, newvertex); + for (i = 0; i < m->eextras; i++) { + /* Set the element attributes of a new triangle. */ + setelemattribute(newbotright, i, elemattribute(botright, i)); + } + if (b->vararea) { + /* Set the area constraint of a new triangle. */ + setareabound(newbotright, areabound(botright)); + } + if (mirrorflag) { + dest(topright, topvertex); + setorg(newtopright, rightvertex); + setdest(newtopright, topvertex); + setapex(newtopright, newvertex); + setorg(topright, newvertex); + for (i = 0; i < m->eextras; i++) { + /* Set the element attributes of another new triangle. */ + setelemattribute(newtopright, i, elemattribute(topright, i)); + } + if (b->vararea) { + /* Set the area constraint of another new triangle. */ + setareabound(newtopright, areabound(topright)); + } + } + + /* There may be subsegments that need to be bonded */ + /* to the new triangle(s). */ + if (m->checksegments) { + tspivot(botright, botrsubseg); + if (botrsubseg.ss != m->dummysub) { + tsdissolve(botright); + tsbond(newbotright, botrsubseg); + } + if (mirrorflag) { + tspivot(topright, toprsubseg); + if (toprsubseg.ss != m->dummysub) { + tsdissolve(topright); + tsbond(newtopright, toprsubseg); + } + } + } + + /* Bond the new triangle(s) to the surrounding triangles. */ + bond(newbotright, botrcasing); + lprevself(newbotright); + bond(newbotright, botright); + lprevself(newbotright); + if (mirrorflag) { + bond(newtopright, toprcasing); + lnextself(newtopright); + bond(newtopright, topright); + lnextself(newtopright); + bond(newtopright, newbotright); + } + + if (splitseg != (struct osub *) NULL) { + /* Split the subsegment into two. */ + setsdest(*splitseg, newvertex); + segorg(*splitseg, segmentorg); + segdest(*splitseg, segmentdest); + ssymself(*splitseg); + spivot(*splitseg, rightsubseg); + insertsubseg(m, b, &newbotright, mark(*splitseg)); + tspivot(newbotright, newsubseg); + setsegorg(newsubseg, segmentorg); + setsegdest(newsubseg, segmentdest); + sbond(*splitseg, newsubseg); + ssymself(newsubseg); + sbond(newsubseg, rightsubseg); + ssymself(*splitseg); + /* Transfer the subsegment's boundary marker to the vertex */ + /* if required. */ + if (vertexmark(newvertex) == 0) { + setvertexmark(newvertex, mark(*splitseg)); + } + } + + if (m->checkquality) { + poolrestart(&m->flipstackers); + m->lastflip = (struct flipstacker *) poolalloc(&m->flipstackers); + m->lastflip->flippedtri = encode(horiz); + m->lastflip->prevflip = (struct flipstacker *) &insertvertex; + } + +#ifdef SELF_CHECK + if (counterclockwise(m, b, rightvertex, leftvertex, botvertex) < 0.0) + { + printf("Internal error in insertvertex():\n"); + printf( + " Clockwise triangle prior to edge vertex insertion (bottom).\n"); + } + if (mirrorflag) + { + if (counterclockwise(m, b, leftvertex, rightvertex, topvertex) < 0.0) + { + printf("Internal error in insertvertex():\n"); + printf(" Clockwise triangle prior to edge vertex insertion (top).\n"); + } + if (counterclockwise(m, b, rightvertex, topvertex, newvertex) < 0.0) + { + printf("Internal error in insertvertex():\n"); + printf( + " Clockwise triangle after edge vertex insertion (top right).\n"); + } + if (counterclockwise(m, b, topvertex, leftvertex, newvertex) < 0.0) + { + printf("Internal error in insertvertex():\n"); + printf( + " Clockwise triangle after edge vertex insertion (top left).\n"); + } + } + if (counterclockwise(m, b, leftvertex, botvertex, newvertex) < 0.0) + { + printf("Internal error in insertvertex():\n"); + printf( + " Clockwise triangle after edge vertex insertion (bottom left).\n"); + } + if (counterclockwise(m, b, botvertex, rightvertex, newvertex) < 0.0) + { + printf("Internal error in insertvertex():\n"); + printf( + " Clockwise triangle after edge vertex insertion (bottom right).\n"); + } +#endif /* SELF_CHECK */ + if (b->verbose > 2) { + printf(" Updating bottom left "); + printtriangle(m, b, &botright); + if (mirrorflag) { + printf(" Updating top left "); + printtriangle(m, b, &topright); + printf(" Creating top right "); + printtriangle(m, b, &newtopright); + } + printf(" Creating bottom right "); + printtriangle(m, b, &newbotright); + } + + /* Position `horiz' on the first edge to check for */ + /* the Delaunay property. */ + lnextself(horiz); + } + else { + /* Insert the vertex in a triangle, splitting it into three. */ + lnext(horiz, botleft); + lprev(horiz, botright); + sym(botleft, botlcasing); + sym(botright, botrcasing); + maketriangle(m, b, &newbotleft); + maketriangle(m, b, &newbotright); + + /* Set the vertices of changed and new triangles. */ + org(horiz, rightvertex); + dest(horiz, leftvertex); + apex(horiz, botvertex); + setorg(newbotleft, leftvertex); + setdest(newbotleft, botvertex); + setapex(newbotleft, newvertex); + setorg(newbotright, botvertex); + setdest(newbotright, rightvertex); + setapex(newbotright, newvertex); + setapex(horiz, newvertex); + for (i = 0; i < m->eextras; i++) { + /* Set the element attributes of the new triangles. */ + attrib = elemattribute(horiz, i); + setelemattribute(newbotleft, i, attrib); + setelemattribute(newbotright, i, attrib); + } + if (b->vararea) { + /* Set the area constraint of the new triangles. */ + area = areabound(horiz); + setareabound(newbotleft, area); + setareabound(newbotright, area); + } + + /* There may be subsegments that need to be bonded */ + /* to the new triangles. */ + if (m->checksegments) { + tspivot(botleft, botlsubseg); + if (botlsubseg.ss != m->dummysub) { + tsdissolve(botleft); + tsbond(newbotleft, botlsubseg); + } + tspivot(botright, botrsubseg); + if (botrsubseg.ss != m->dummysub) { + tsdissolve(botright); + tsbond(newbotright, botrsubseg); + } + } + + /* Bond the new triangles to the surrounding triangles. */ + bond(newbotleft, botlcasing); + bond(newbotright, botrcasing); + lnextself(newbotleft); + lprevself(newbotright); + bond(newbotleft, newbotright); + lnextself(newbotleft); + bond(botleft, newbotleft); + lprevself(newbotright); + bond(botright, newbotright); + + if (m->checkquality) { + poolrestart(&m->flipstackers); + m->lastflip = (struct flipstacker *) poolalloc(&m->flipstackers); + m->lastflip->flippedtri = encode(horiz); + m->lastflip->prevflip = (struct flipstacker *) NULL; + } + +#ifdef SELF_CHECK + if (counterclockwise(m, b, rightvertex, leftvertex, botvertex) < 0.0) + { + printf("Internal error in insertvertex():\n"); + printf(" Clockwise triangle prior to vertex insertion.\n"); + } + if (counterclockwise(m, b, rightvertex, leftvertex, newvertex) < 0.0) + { + printf("Internal error in insertvertex():\n"); + printf(" Clockwise triangle after vertex insertion (top).\n"); + } + if (counterclockwise(m, b, leftvertex, botvertex, newvertex) < 0.0) + { + printf("Internal error in insertvertex():\n"); + printf(" Clockwise triangle after vertex insertion (left).\n"); + } + if (counterclockwise(m, b, botvertex, rightvertex, newvertex) < 0.0) + { + printf("Internal error in insertvertex():\n"); + printf(" Clockwise triangle after vertex insertion (right).\n"); + } +#endif /* SELF_CHECK */ + if (b->verbose > 2) { + printf(" Updating top "); + printtriangle(m, b, &horiz); + printf(" Creating left "); + printtriangle(m, b, &newbotleft); + printf(" Creating right "); + printtriangle(m, b, &newbotright); + } + } + + /* The insertion is successful by default, unless an encroached */ + /* subsegment is found. */ + success = SUCCESSFULVERTEX; + /* Circle around the newly inserted vertex, checking each edge opposite */ + /* it for the Delaunay property. Non-Delaunay edges are flipped. */ + /* `horiz' is always the edge being checked. `first' marks where to */ + /* stop circling. */ + org(horiz, first); + rightvertex = first; + dest(horiz, leftvertex); + /* Circle until finished. */ + while (1) { + /* By default, the edge will be flipped. */ + doflip = 1; + + if (m->checksegments) { + /* Check for a subsegment, which cannot be flipped. */ + tspivot(horiz, checksubseg); + if (checksubseg.ss != m->dummysub) { + /* The edge is a subsegment and cannot be flipped. */ + doflip = 0; +#ifndef CDT_ONLY + if (segmentflaws) + { + /* Does the new vertex encroach upon this subsegment? */ + if (checkseg4encroach(m, b, &checksubseg)) + { + success = ENCROACHINGVERTEX; + } + } +#endif /* not CDT_ONLY */ + } + } + + if (doflip) { + /* Check if the edge is a boundary edge. */ + sym(horiz, top); + if (top.tri == m->dummytri) { + /* The edge is a boundary edge and cannot be flipped. */ + doflip = 0; + } + else { + /* Find the vertex on the other side of the edge. */ + apex(top, farvertex); + /* In the incremental Delaunay triangulation algorithm, any of */ + /* `leftvertex', `rightvertex', and `farvertex' could be vertices */ + /* of the triangular bounding box. These vertices must be */ + /* treated as if they are infinitely distant, even though their */ + /* "coordinates" are not. */ + if ((leftvertex == m->infvertex1) || (leftvertex == m->infvertex2) + || (leftvertex == m->infvertex3)) { + /* `leftvertex' is infinitely distant. Check the convexity of */ + /* the boundary of the triangulation. 'farvertex' might be */ + /* infinite as well, but trust me, this same condition should */ + /* be applied. */ + doflip = counterclockwise(m, b, newvertex, rightvertex, farvertex) > 0.0; + } + else if ((rightvertex == m->infvertex1) || (rightvertex == m->infvertex2) + || (rightvertex == m->infvertex3)) { + /* `rightvertex' is infinitely distant. Check the convexity of */ + /* the boundary of the triangulation. 'farvertex' might be */ + /* infinite as well, but trust me, this same condition should */ + /* be applied. */ + doflip = counterclockwise(m, b, farvertex, leftvertex, newvertex) > 0.0; + } + else if ((farvertex == m->infvertex1) || (farvertex == m->infvertex2) + || (farvertex == m->infvertex3)) { + /* `farvertex' is infinitely distant and cannot be inside */ + /* the circumcircle of the triangle `horiz'. */ + doflip = 0; + } + else { + /* Test whether the edge is locally Delaunay. */ + doflip = incircle(m, b, leftvertex, newvertex, rightvertex, farvertex) > 0.0; + } + if (doflip) { + /* We made it! Flip the edge `horiz' by rotating its containing */ + /* quadrilateral (the two triangles adjacent to `horiz'). */ + /* Identify the casing of the quadrilateral. */ + lprev(top, topleft); + sym(topleft, toplcasing); + lnext(top, topright); + sym(topright, toprcasing); + lnext(horiz, botleft); + sym(botleft, botlcasing); + lprev(horiz, botright); + sym(botright, botrcasing); + /* Rotate the quadrilateral one-quarter turn counterclockwise. */ + bond(topleft, botlcasing); + bond(botleft, botrcasing); + bond(botright, toprcasing); + bond(topright, toplcasing); + if (m->checksegments) { + /* Check for subsegments and rebond them to the quadrilateral. */ + tspivot(topleft, toplsubseg); + tspivot(botleft, botlsubseg); + tspivot(botright, botrsubseg); + tspivot(topright, toprsubseg); + if (toplsubseg.ss == m->dummysub) { + tsdissolve(topright); + } + else { + tsbond(topright, toplsubseg); + } + if (botlsubseg.ss == m->dummysub) { + tsdissolve(topleft); + } + else { + tsbond(topleft, botlsubseg); + } + if (botrsubseg.ss == m->dummysub) { + tsdissolve(botleft); + } + else { + tsbond(botleft, botrsubseg); + } + if (toprsubseg.ss == m->dummysub) { + tsdissolve(botright); + } + else { + tsbond(botright, toprsubseg); + } + } + /* New vertex assignments for the rotated quadrilateral. */ + setorg(horiz, farvertex); + setdest(horiz, newvertex); + setapex(horiz, rightvertex); + setorg(top, newvertex); + setdest(top, farvertex); + setapex(top, leftvertex); + for (i = 0; i < m->eextras; i++) { + /* Take the average of the two triangles' attributes. */ + attrib = 0.5 * (elemattribute(top, i) + elemattribute(horiz, i)); + setelemattribute(top, i, attrib); + setelemattribute(horiz, i, attrib); + } + if (b->vararea) { + if ((areabound(top) <= 0.0) || (areabound(horiz) <= 0.0)) { + area = -1.0; + } + else { + /* Take the average of the two triangles' area constraints. */ + /* This prevents small area constraints from migrating a */ + /* long, long way from their original location due to flips. */ + area = 0.5 * (areabound(top) + areabound(horiz)); + } + setareabound(top, area); + setareabound(horiz, area); + } + + if (m->checkquality) { + newflip = (struct flipstacker *) poolalloc(&m->flipstackers); + newflip->flippedtri = encode(horiz); + newflip->prevflip = m->lastflip; + m->lastflip = newflip; + } + +#ifdef SELF_CHECK + if (newvertex != (vertex) NULL) + { + if (counterclockwise(m, b, leftvertex, newvertex, rightvertex) < + 0.0) + { + printf("Internal error in insertvertex():\n"); + printf(" Clockwise triangle prior to edge flip (bottom).\n"); + } + /* The following test has been removed because constrainededge() */ + /* sometimes generates inverted triangles that insertvertex() */ + /* removes. */ + /* + if (counterclockwise(m, b, rightvertex, farvertex, leftvertex) < + 0.0) { + printf("Internal error in insertvertex():\n"); + printf(" Clockwise triangle prior to edge flip (top).\n"); + } + */ + if (counterclockwise(m, b, farvertex, leftvertex, newvertex) < + 0.0) + { + printf("Internal error in insertvertex():\n"); + printf(" Clockwise triangle after edge flip (left).\n"); + } + if (counterclockwise(m, b, newvertex, rightvertex, farvertex) < + 0.0) + { + printf("Internal error in insertvertex():\n"); + printf(" Clockwise triangle after edge flip (right).\n"); + } + } +#endif /* SELF_CHECK */ + if (b->verbose > 2) { + printf(" Edge flip results in left "); + lnextself(topleft); + printtriangle(m, b, &topleft); + printf(" and right "); + printtriangle(m, b, &horiz); + } + /* On the next iterations, consider the two edges that were */ + /* exposed (this is, are now visible to the newly inserted */ + /* vertex) by the edge flip. */ + lprevself(horiz); + leftvertex = farvertex; + } + } + } + if (!doflip) { + /* The handle `horiz' is accepted as locally Delaunay. */ +#ifndef CDT_ONLY + if (triflaws) + { + /* Check the triangle `horiz' for quality. */ + testtriangle(m, b, &horiz); + } +#endif /* not CDT_ONLY */ + /* Look for the next edge around the newly inserted vertex. */ + lnextself(horiz); + sym(horiz, testtri); + /* Check for finishing a complete revolution about the new vertex, or */ + /* falling outside of the triangulation. The latter will happen */ + /* when a vertex is inserted at a boundary. */ + if ((leftvertex == first) || (testtri.tri == m->dummytri)) { + /* We're done. Return a triangle whose origin is the new vertex. */ + lnext(horiz, *searchtri); + lnext(horiz, m->recenttri); + return success; + } + /* Finish finding the next edge around the newly inserted vertex. */ + lnext(testtri, horiz); + rightvertex = leftvertex; + dest(horiz, leftvertex); + } + } +} + +/********* Divide-and-conquer Delaunay triangulation begins here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* The divide-and-conquer bounding box */ +/* */ +/* I originally implemented the divide-and-conquer and incremental Delaunay */ +/* triangulations using the edge-based data structure presented by Guibas */ +/* and Stolfi. Switching to a triangle-based data structure doubled the */ +/* speed. However, I had to think of a few extra tricks to maintain the */ +/* elegance of the original algorithms. */ +/* */ +/* The "bounding box" used by my variant of the divide-and-conquer */ +/* algorithm uses one triangle for each edge of the convex hull of the */ +/* triangulation. These bounding triangles all share a common apical */ +/* vertex, which is represented by NULL and which represents nothing. */ +/* The bounding triangles are linked in a circular fan about this NULL */ +/* vertex, and the edges on the convex hull of the triangulation appear */ +/* opposite the NULL vertex. You might find it easiest to imagine that */ +/* the NULL vertex is a point in 3D space behind the center of the */ +/* triangulation, and that the bounding triangles form a sort of cone. */ +/* */ +/* This bounding box makes it easy to represent degenerate cases. For */ +/* instance, the triangulation of two vertices is a single edge. This edge */ +/* is represented by two bounding box triangles, one on each "side" of the */ +/* edge. These triangles are also linked together in a fan about the NULL */ +/* vertex. */ +/* */ +/* The bounding box also makes it easy to traverse the convex hull, as the */ +/* divide-and-conquer algorithm needs to do. */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* */ +/* vertexsort() Sort an array of vertices by x-coordinate, using the */ +/* y-coordinate as a secondary key. */ +/* */ +/* Uses quicksort. Randomized O(n log n) time. No, I did not make any of */ +/* the usual quicksort mistakes. */ +/* */ +/*****************************************************************************/ + +void vertexsort(vertex *sortarray, int arraysize) { + int left, right; + int pivot; + REAL pivotx, pivoty; + vertex temp; + + if (arraysize == 2) { + /* Recursive base case. */ + if ((sortarray[0][0] > sortarray[1][0]) + || ((sortarray[0][0] == sortarray[1][0]) && (sortarray[0][1] > sortarray[1][1]))) { + temp = sortarray[1]; + sortarray[1] = sortarray[0]; + sortarray[0] = temp; + } + return; + } + /* Choose a random pivot to split the array. */ + pivot = (int) randomnation((unsigned int) arraysize); + pivotx = sortarray[pivot][0]; + pivoty = sortarray[pivot][1]; + /* Split the array. */ + left = -1; + right = arraysize; + while (left < right) { + /* Search for a vertex whose x-coordinate is too large for the left. */ + do { + left++; + } while ((left <= right) + && ((sortarray[left][0] < pivotx) + || ((sortarray[left][0] == pivotx) && (sortarray[left][1] < pivoty)))); + /* Search for a vertex whose x-coordinate is too small for the right. */ + do { + right--; + } while ((left <= right) + && ((sortarray[right][0] > pivotx) + || ((sortarray[right][0] == pivotx) && (sortarray[right][1] > pivoty)))); + if (left < right) { + /* Swap the left and right vertices. */ + temp = sortarray[left]; + sortarray[left] = sortarray[right]; + sortarray[right] = temp; + } + } + if (left > 1) { + /* Recursively sort the left subset. */ + vertexsort(sortarray, left); + } + if (right < arraysize - 2) { + /* Recursively sort the right subset. */ + vertexsort(&sortarray[right + 1], arraysize - right - 1); + } +} + +/*****************************************************************************/ +/* */ +/* vertexmedian() An order statistic algorithm, almost. Shuffles an */ +/* array of vertices so that the first `median' vertices */ +/* occur lexicographically before the remaining vertices. */ +/* */ +/* Uses the x-coordinate as the primary key if axis == 0; the y-coordinate */ +/* if axis == 1. Very similar to the vertexsort() procedure, but runs in */ +/* randomized linear time. */ +/* */ +/*****************************************************************************/ + +void vertexmedian(vertex *sortarray, int arraysize, int median, int axis) { + int left, right; + int pivot; + REAL pivot1, pivot2; + vertex temp; + + if (arraysize == 2) { + /* Recursive base case. */ + if ((sortarray[0][axis] > sortarray[1][axis]) + || ((sortarray[0][axis] == sortarray[1][axis]) + && (sortarray[0][1 - axis] > sortarray[1][1 - axis]))) { + temp = sortarray[1]; + sortarray[1] = sortarray[0]; + sortarray[0] = temp; + } + return; + } + /* Choose a random pivot to split the array. */ + pivot = (int) randomnation((unsigned int) arraysize); + pivot1 = sortarray[pivot][axis]; + pivot2 = sortarray[pivot][1 - axis]; + /* Split the array. */ + left = -1; + right = arraysize; + while (left < right) { + /* Search for a vertex whose x-coordinate is too large for the left. */ + do { + left++; + } while ((left <= right) + && ((sortarray[left][axis] < pivot1) + || ((sortarray[left][axis] == pivot1) && (sortarray[left][1 - axis] < pivot2)))); + /* Search for a vertex whose x-coordinate is too small for the right. */ + do { + right--; + } while ((left <= right) + && ((sortarray[right][axis] > pivot1) + || ((sortarray[right][axis] == pivot1) && (sortarray[right][1 - axis] > pivot2)))); + if (left < right) { + /* Swap the left and right vertices. */ + temp = sortarray[left]; + sortarray[left] = sortarray[right]; + sortarray[right] = temp; + } + } + /* Unlike in vertexsort(), at most one of the following */ + /* conditionals is true. */ + if (left > median) { + /* Recursively shuffle the left subset. */ + vertexmedian(sortarray, left, median, axis); + } + if (right < median - 1) { + /* Recursively shuffle the right subset. */ + vertexmedian(&sortarray[right + 1], arraysize - right - 1, median - right - 1, axis); + } +} + +/*****************************************************************************/ +/* */ +/* alternateaxes() Sorts the vertices as appropriate for the divide-and- */ +/* conquer algorithm with alternating cuts. */ +/* */ +/* Partitions by x-coordinate if axis == 0; by y-coordinate if axis == 1. */ +/* For the base case, subsets containing only two or three vertices are */ +/* always sorted by x-coordinate. */ +/* */ +/*****************************************************************************/ + +void alternateaxes(vertex *sortarray, int arraysize, int axis) { + int divider; + + divider = arraysize >> 1; + if (arraysize <= 3) { + /* Recursive base case: subsets of two or three vertices will be */ + /* handled specially, and should always be sorted by x-coordinate. */ + axis = 0; + } + /* Partition with a horizontal or vertical cut. */ + vertexmedian(sortarray, arraysize, divider, axis); + /* Recursively partition the subsets with a cross cut. */ + if (arraysize - divider >= 2) { + if (divider >= 2) { + alternateaxes(sortarray, divider, 1 - axis); + } + alternateaxes(&sortarray[divider], arraysize - divider, 1 - axis); + } +} + +/*****************************************************************************/ +/* */ +/* mergehulls() Merge two adjacent Delaunay triangulations into a */ +/* single Delaunay triangulation. */ +/* */ +/* This is similar to the algorithm given by Guibas and Stolfi, but uses */ +/* a triangle-based, rather than edge-based, data structure. */ +/* */ +/* The algorithm walks up the gap between the two triangulations, knitting */ +/* them together. As they are merged, some of their bounding triangles */ +/* are converted into real triangles of the triangulation. The procedure */ +/* pulls each hull's bounding triangles apart, then knits them together */ +/* like the teeth of two gears. The Delaunay property determines, at each */ +/* step, whether the next "tooth" is a bounding triangle of the left hull */ +/* or the right. When a bounding triangle becomes real, its apex is */ +/* changed from NULL to a real vertex. */ +/* */ +/* Only two new triangles need to be allocated. These become new bounding */ +/* triangles at the top and bottom of the seam. They are used to connect */ +/* the remaining bounding triangles (those that have not been converted */ +/* into real triangles) into a single fan. */ +/* */ +/* On entry, `farleft' and `innerleft' are bounding triangles of the left */ +/* triangulation. The origin of `farleft' is the leftmost vertex, and */ +/* the destination of `innerleft' is the rightmost vertex of the */ +/* triangulation. Similarly, `innerright' and `farright' are bounding */ +/* triangles of the right triangulation. The origin of `innerright' and */ +/* destination of `farright' are the leftmost and rightmost vertices. */ +/* */ +/* On completion, the origin of `farleft' is the leftmost vertex of the */ +/* merged triangulation, and the destination of `farright' is the rightmost */ +/* vertex. */ +/* */ +/*****************************************************************************/ + +void mergehulls(struct mesh *m, struct behavior *b, struct otri *farleft, struct otri *innerleft, + struct otri *innerright, struct otri *farright, int axis) { + struct otri leftcand, rightcand; + struct otri baseedge; + struct otri nextedge; + struct otri sidecasing, topcasing, outercasing; + struct otri checkedge; + vertex innerleftdest; + vertex innerrightorg; + vertex innerleftapex, innerrightapex; + vertex farleftpt, farrightpt; + vertex farleftapex, farrightapex; + vertex lowerleft, lowerright; + vertex upperleft, upperright; + vertex nextapex; + vertex checkvertex; + int changemade; + int badedge; + int leftfinished, rightfinished; + triangle ptr; /* Temporary variable used by sym(). */ + + dest(*innerleft, innerleftdest); + apex(*innerleft, innerleftapex); + org(*innerright, innerrightorg); + apex(*innerright, innerrightapex); + /* Special treatment for horizontal cuts. */ + if (b->dwyer && (axis == 1)) { + org(*farleft, farleftpt); + apex(*farleft, farleftapex); + dest(*farright, farrightpt); + apex(*farright, farrightapex); + /* The pointers to the extremal vertices are shifted to point to the */ + /* topmost and bottommost vertex of each hull, rather than the */ + /* leftmost and rightmost vertices. */ + while (farleftapex[1] < farleftpt[1]) { + lnextself(*farleft); + symself(*farleft); + farleftpt = farleftapex; + apex(*farleft, farleftapex); + } + sym(*innerleft, checkedge); + apex(checkedge, checkvertex); + while (checkvertex[1] > innerleftdest[1]) { + lnext(checkedge, *innerleft); + innerleftapex = innerleftdest; + innerleftdest = checkvertex; + sym(*innerleft, checkedge); + apex(checkedge, checkvertex); + } + while (innerrightapex[1] < innerrightorg[1]) { + lnextself(*innerright); + symself(*innerright); + innerrightorg = innerrightapex; + apex(*innerright, innerrightapex); + } + sym(*farright, checkedge); + apex(checkedge, checkvertex); + while (checkvertex[1] > farrightpt[1]) { + lnext(checkedge, *farright); + farrightapex = farrightpt; + farrightpt = checkvertex; + sym(*farright, checkedge); + apex(checkedge, checkvertex); + } + } + /* Find a line tangent to and below both hulls. */ + do { + changemade = 0; + /* Make innerleftdest the "bottommost" vertex of the left hull. */ + if (counterclockwise(m, b, innerleftdest, innerleftapex, innerrightorg) > 0.0) { + lprevself(*innerleft); + symself(*innerleft); + innerleftdest = innerleftapex; + apex(*innerleft, innerleftapex); + changemade = 1; + } + /* Make innerrightorg the "bottommost" vertex of the right hull. */ + if (counterclockwise(m, b, innerrightapex, innerrightorg, innerleftdest) > 0.0) { + lnextself(*innerright); + symself(*innerright); + innerrightorg = innerrightapex; + apex(*innerright, innerrightapex); + changemade = 1; + } + } while (changemade); + /* Find the two candidates to be the next "gear tooth." */ + sym(*innerleft, leftcand); + sym(*innerright, rightcand); + /* Create the bottom new bounding triangle. */ + maketriangle(m, b, &baseedge); + /* Connect it to the bounding boxes of the left and right triangulations. */ + bond(baseedge, *innerleft); + lnextself(baseedge); + bond(baseedge, *innerright); + lnextself(baseedge); + setorg(baseedge, innerrightorg); + setdest(baseedge, innerleftdest); + /* Apex is intentionally left NULL. */ + if (b->verbose > 2) { + printf(" Creating base bounding "); + printtriangle(m, b, &baseedge); + } + /* Fix the extreme triangles if necessary. */ + org(*farleft, farleftpt); + if (innerleftdest == farleftpt) { + lnext(baseedge, *farleft); + } + dest(*farright, farrightpt); + if (innerrightorg == farrightpt) { + lprev(baseedge, *farright); + } + /* The vertices of the current knitting edge. */ + lowerleft = innerleftdest; + lowerright = innerrightorg; + /* The candidate vertices for knitting. */ + apex(leftcand, upperleft); + apex(rightcand, upperright); + /* Walk up the gap between the two triangulations, knitting them together. */ + while (1) { + /* Have we reached the top? (This isn't quite the right question, */ + /* because even though the left triangulation might seem finished now, */ + /* moving up on the right triangulation might reveal a new vertex of */ + /* the left triangulation. And vice-versa.) */ + leftfinished = counterclockwise(m, b, upperleft, lowerleft, lowerright) <= 0.0; + rightfinished = counterclockwise(m, b, upperright, lowerleft, lowerright) <= 0.0; + if (leftfinished && rightfinished) { + /* Create the top new bounding triangle. */ + maketriangle(m, b, &nextedge); + setorg(nextedge, lowerleft); + setdest(nextedge, lowerright); + /* Apex is intentionally left NULL. */ + /* Connect it to the bounding boxes of the two triangulations. */ + bond(nextedge, baseedge); + lnextself(nextedge); + bond(nextedge, rightcand); + lnextself(nextedge); + bond(nextedge, leftcand); + if (b->verbose > 2) { + printf(" Creating top bounding "); + printtriangle(m, b, &nextedge); + } + /* Special treatment for horizontal cuts. */ + if (b->dwyer && (axis == 1)) { + org(*farleft, farleftpt); + apex(*farleft, farleftapex); + dest(*farright, farrightpt); + apex(*farright, farrightapex); + sym(*farleft, checkedge); + apex(checkedge, checkvertex); + /* The pointers to the extremal vertices are restored to the */ + /* leftmost and rightmost vertices (rather than topmost and */ + /* bottommost). */ + while (checkvertex[0] < farleftpt[0]) { + lprev(checkedge, *farleft); + farleftapex = farleftpt; + farleftpt = checkvertex; + sym(*farleft, checkedge); + apex(checkedge, checkvertex); + } + while (farrightapex[0] > farrightpt[0]) { + lprevself(*farright); + symself(*farright); + farrightpt = farrightapex; + apex(*farright, farrightapex); + } + } + return; + } + /* Consider eliminating edges from the left triangulation. */ + if (!leftfinished) { + /* What vertex would be exposed if an edge were deleted? */ + lprev(leftcand, nextedge); + symself(nextedge); + apex(nextedge, nextapex); + /* If nextapex is NULL, then no vertex would be exposed; the */ + /* triangulation would have been eaten right through. */ + if (nextapex != (vertex) NULL) { + /* Check whether the edge is Delaunay. */ + badedge = incircle(m, b, lowerleft, lowerright, upperleft, nextapex) > 0.0; + while (badedge) { + /* Eliminate the edge with an edge flip. As a result, the */ + /* left triangulation will have one more boundary triangle. */ + lnextself(nextedge); + sym(nextedge, topcasing); + lnextself(nextedge); + sym(nextedge, sidecasing); + bond(nextedge, topcasing); + bond(leftcand, sidecasing); + lnextself(leftcand); + sym(leftcand, outercasing); + lprevself(nextedge); + bond(nextedge, outercasing); + /* Correct the vertices to reflect the edge flip. */ + setorg(leftcand, lowerleft); + setdest(leftcand, NULL); + setapex(leftcand, nextapex); + setorg(nextedge, NULL); + setdest(nextedge, upperleft); + setapex(nextedge, nextapex); + /* Consider the newly exposed vertex. */ + upperleft = nextapex; + /* What vertex would be exposed if another edge were deleted? */ + otricopy(sidecasing, nextedge); + apex(nextedge, nextapex); + if (nextapex != (vertex) NULL) { + /* Check whether the edge is Delaunay. */ + badedge = incircle(m, b, lowerleft, lowerright, upperleft, nextapex) > 0.0; + } + else { + /* Avoid eating right through the triangulation. */ + badedge = 0; + } + } + } + } + /* Consider eliminating edges from the right triangulation. */ + if (!rightfinished) { + /* What vertex would be exposed if an edge were deleted? */ + lnext(rightcand, nextedge); + symself(nextedge); + apex(nextedge, nextapex); + /* If nextapex is NULL, then no vertex would be exposed; the */ + /* triangulation would have been eaten right through. */ + if (nextapex != (vertex) NULL) { + /* Check whether the edge is Delaunay. */ + badedge = incircle(m, b, lowerleft, lowerright, upperright, nextapex) > 0.0; + while (badedge) { + /* Eliminate the edge with an edge flip. As a result, the */ + /* right triangulation will have one more boundary triangle. */ + lprevself(nextedge); + sym(nextedge, topcasing); + lprevself(nextedge); + sym(nextedge, sidecasing); + bond(nextedge, topcasing); + bond(rightcand, sidecasing); + lprevself(rightcand); + sym(rightcand, outercasing); + lnextself(nextedge); + bond(nextedge, outercasing); + /* Correct the vertices to reflect the edge flip. */ + setorg(rightcand, NULL); + setdest(rightcand, lowerright); + setapex(rightcand, nextapex); + setorg(nextedge, upperright); + setdest(nextedge, NULL); + setapex(nextedge, nextapex); + /* Consider the newly exposed vertex. */ + upperright = nextapex; + /* What vertex would be exposed if another edge were deleted? */ + otricopy(sidecasing, nextedge); + apex(nextedge, nextapex); + if (nextapex != (vertex) NULL) { + /* Check whether the edge is Delaunay. */ + badedge = incircle(m, b, lowerleft, lowerright, upperright, nextapex) > 0.0; + } + else { + /* Avoid eating right through the triangulation. */ + badedge = 0; + } + } + } + } + if (leftfinished + || (!rightfinished + && (incircle(m, b, upperleft, lowerleft, lowerright, upperright) > 0.0))) { + /* Knit the triangulations, adding an edge from `lowerleft' */ + /* to `upperright'. */ + bond(baseedge, rightcand); + lprev(rightcand, baseedge); + setdest(baseedge, lowerleft); + lowerright = upperright; + sym(baseedge, rightcand); + apex(rightcand, upperright); + } + else { + /* Knit the triangulations, adding an edge from `upperleft' */ + /* to `lowerright'. */ + bond(baseedge, leftcand); + lnext(leftcand, baseedge); + setorg(baseedge, lowerright); + lowerleft = upperleft; + sym(baseedge, leftcand); + apex(leftcand, upperleft); + } + if (b->verbose > 2) { + printf(" Connecting "); + printtriangle(m, b, &baseedge); + } + } +} + +/*****************************************************************************/ +/* */ +/* divconqrecurse() Recursively form a Delaunay triangulation by the */ +/* divide-and-conquer method. */ +/* */ +/* Recursively breaks down the problem into smaller pieces, which are */ +/* knitted together by mergehulls(). The base cases (problems of two or */ +/* three vertices) are handled specially here. */ +/* */ +/* On completion, `farleft' and `farright' are bounding triangles such that */ +/* the origin of `farleft' is the leftmost vertex (breaking ties by */ +/* choosing the highest leftmost vertex), and the destination of */ +/* `farright' is the rightmost vertex (breaking ties by choosing the */ +/* lowest rightmost vertex). */ +/* */ +/*****************************************************************************/ + +void divconqrecurse(struct mesh *m, struct behavior *b, vertex *sortarray, int vertices, int axis, + struct otri *farleft, struct otri *farright) { + struct otri midtri, tri1, tri2, tri3; + struct otri innerleft, innerright; + REAL area; + int divider; + + if (b->verbose > 2) { + printf(" Triangulating %d vertices.\n", vertices); + } + if (vertices == 2) { + /* The triangulation of two vertices is an edge. An edge is */ + /* represented by two bounding triangles. */ + maketriangle(m, b, farleft); + setorg(*farleft, sortarray[0]); + setdest(*farleft, sortarray[1]); + /* The apex is intentionally left NULL. */ + maketriangle(m, b, farright); + setorg(*farright, sortarray[1]); + setdest(*farright, sortarray[0]); + /* The apex is intentionally left NULL. */ + bond(*farleft, *farright); + lprevself(*farleft); + lnextself(*farright); + bond(*farleft, *farright); + lprevself(*farleft); + lnextself(*farright); + bond(*farleft, *farright); + if (b->verbose > 2) { + printf(" Creating "); + printtriangle(m, b, farleft); + printf(" Creating "); + printtriangle(m, b, farright); + } + /* Ensure that the origin of `farleft' is sortarray[0]. */ + lprev(*farright, *farleft); + return; + } + else if (vertices == 3) { + /* The triangulation of three vertices is either a triangle (with */ + /* three bounding triangles) or two edges (with four bounding */ + /* triangles). In either case, four triangles are created. */ + maketriangle(m, b, &midtri); + maketriangle(m, b, &tri1); + maketriangle(m, b, &tri2); + maketriangle(m, b, &tri3); + area = counterclockwise(m, b, sortarray[0], sortarray[1], sortarray[2]); + if (area == 0.0) { + /* Three collinear vertices; the triangulation is two edges. */ + setorg(midtri, sortarray[0]); + setdest(midtri, sortarray[1]); + setorg(tri1, sortarray[1]); + setdest(tri1, sortarray[0]); + setorg(tri2, sortarray[2]); + setdest(tri2, sortarray[1]); + setorg(tri3, sortarray[1]); + setdest(tri3, sortarray[2]); + /* All apices are intentionally left NULL. */ + bond(midtri, tri1); + bond(tri2, tri3); + lnextself(midtri); + lprevself(tri1); + lnextself(tri2); + lprevself(tri3); + bond(midtri, tri3); + bond(tri1, tri2); + lnextself(midtri); + lprevself(tri1); + lnextself(tri2); + lprevself(tri3); + bond(midtri, tri1); + bond(tri2, tri3); + /* Ensure that the origin of `farleft' is sortarray[0]. */ + otricopy(tri1, *farleft); + /* Ensure that the destination of `farright' is sortarray[2]. */ + otricopy(tri2, *farright); + } + else { + /* The three vertices are not collinear; the triangulation is one */ + /* triangle, namely `midtri'. */ + setorg(midtri, sortarray[0]); + setdest(tri1, sortarray[0]); + setorg(tri3, sortarray[0]); + /* Apices of tri1, tri2, and tri3 are left NULL. */ + if (area > 0.0) { + /* The vertices are in counterclockwise order. */ + setdest(midtri, sortarray[1]); + setorg(tri1, sortarray[1]); + setdest(tri2, sortarray[1]); + setapex(midtri, sortarray[2]); + setorg(tri2, sortarray[2]); + setdest(tri3, sortarray[2]); + } + else { + /* The vertices are in clockwise order. */ + setdest(midtri, sortarray[2]); + setorg(tri1, sortarray[2]); + setdest(tri2, sortarray[2]); + setapex(midtri, sortarray[1]); + setorg(tri2, sortarray[1]); + setdest(tri3, sortarray[1]); + } + /* The topology does not depend on how the vertices are ordered. */ + bond(midtri, tri1); + lnextself(midtri); + bond(midtri, tri2); + lnextself(midtri); + bond(midtri, tri3); + lprevself(tri1); + lnextself(tri2); + bond(tri1, tri2); + lprevself(tri1); + lprevself(tri3); + bond(tri1, tri3); + lnextself(tri2); + lprevself(tri3); + bond(tri2, tri3); + /* Ensure that the origin of `farleft' is sortarray[0]. */ + otricopy(tri1, *farleft); + /* Ensure that the destination of `farright' is sortarray[2]. */ + if (area > 0.0) { + otricopy(tri2, *farright); + } + else { + lnext(*farleft, *farright); + } + } + if (b->verbose > 2) { + printf(" Creating "); + printtriangle(m, b, &midtri); + printf(" Creating "); + printtriangle(m, b, &tri1); + printf(" Creating "); + printtriangle(m, b, &tri2); + printf(" Creating "); + printtriangle(m, b, &tri3); + } + return; + } + else { + /* Split the vertices in half. */ + divider = vertices >> 1; + /* Recursively triangulate each half. */ + divconqrecurse(m, b, sortarray, divider, 1 - axis, farleft, &innerleft); + divconqrecurse(m, b, &sortarray[divider], vertices - divider, 1 - axis, &innerright, + farright); + if (b->verbose > 1) { + printf(" Joining triangulations with %d and %d vertices.\n", divider, vertices - divider); + } + /* Merge the two triangulations into one. */ + mergehulls(m, b, farleft, &innerleft, &innerright, farright, axis); + } +} + +long removeghosts(struct mesh *m, struct behavior *b, struct otri *startghost) { + struct otri searchedge; + struct otri dissolveedge; + struct otri deadtriangle; + vertex markorg; + long hullsize; + triangle ptr; /* Temporary variable used by sym(). */ + + if (b->verbose) { + printf(" Removing ghost triangles.\n"); + } + /* Find an edge on the convex hull to start point location from. */ + lprev(*startghost, searchedge); + symself(searchedge); + m->dummytri[0] = encode(searchedge); + /* Remove the bounding box and count the convex hull edges. */ + otricopy(*startghost, dissolveedge); + hullsize = 0; + do { + hullsize++; + lnext(dissolveedge, deadtriangle); + lprevself(dissolveedge); + symself(dissolveedge); + /* If no PSLG is involved, set the boundary markers of all the vertices */ + /* on the convex hull. If a PSLG is used, this step is done later. */ + if (!b->poly) { + /* Watch out for the case where all the input vertices are collinear. */ + if (dissolveedge.tri != m->dummytri) { + org(dissolveedge, markorg); + if (vertexmark(markorg) == 0) { + setvertexmark(markorg, 1); + } + } + } + /* Remove a bounding triangle from a convex hull triangle. */ + dissolve(dissolveedge); + /* Find the next bounding triangle. */ + sym(deadtriangle, dissolveedge); + /* Delete the bounding triangle. */ + triangledealloc(m, deadtriangle.tri); + } while (!otriequal(dissolveedge, *startghost)); + return hullsize; +} + +/*****************************************************************************/ +/* */ +/* divconqdelaunay() Form a Delaunay triangulation by the divide-and- */ +/* conquer method. */ +/* */ +/* Sorts the vertices, calls a recursive procedure to triangulate them, and */ +/* removes the bounding box, setting boundary markers as appropriate. */ +/* */ +/*****************************************************************************/ + +long divconqdelaunay(struct mesh *m, struct behavior *b) { + vertex *sortarray; + struct otri hullleft, hullright; + int divider; + int i, j; + + if (b->verbose) { + printf(" Sorting vertices.\n"); + } + + /* Allocate an array of pointers to vertices for sorting. */ + sortarray = (vertex *) trimalloc(m->invertices * (int) sizeof(vertex)); + traversalinit(&m->vertices); + for (i = 0; i < m->invertices; i++) { + sortarray[i] = vertextraverse(m); + } + /* Sort the vertices. */ + vertexsort(sortarray, m->invertices); + /* Discard duplicate vertices, which can really mess up the algorithm. */ + i = 0; + for (j = 1; j < m->invertices; j++) { + if ((sortarray[i][0] == sortarray[j][0]) && (sortarray[i][1] == sortarray[j][1])) { + if (!b->quiet) { + printf( + "Warning: A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n", sortarray[j][0], sortarray[j][1]); + } + setvertextype(sortarray[j], UNDEADVERTEX); + m->undeads++; + } + else { + i++; + sortarray[i] = sortarray[j]; + } + } + i++; + if (b->dwyer) { + /* Re-sort the array of vertices to accommodate alternating cuts. */ + divider = i >> 1; + if (i - divider >= 2) { + if (divider >= 2) { + alternateaxes(sortarray, divider, 1); + } + alternateaxes(&sortarray[divider], i - divider, 1); + } + } + + if (b->verbose) { + printf(" Forming triangulation.\n"); + } + + /* Form the Delaunay triangulation. */ + divconqrecurse(m, b, sortarray, i, 0, &hullleft, &hullright); + trifree((VOID *) sortarray); + + return removeghosts(m, b, &hullleft); +} + +/** **/ +/** **/ +/********* Divide-and-conquer Delaunay triangulation ends here *********/ + +/********* General mesh construction routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* delaunay() Form a Delaunay triangulation. */ +/* */ +/*****************************************************************************/ + +long delaunay(struct mesh *m, struct behavior *b) { + long hulledges; + + m->eextras = 0; + initializetrisubpools(m, b); + +#ifdef REDUCED + if (!b->quiet) { + printf( "Constructing Delaunay triangulation by divide-and-conquer method.\n"); + } + hulledges = divconqdelaunay(m, b); +#else /* not REDUCED */ + if (!b->quiet) + { + printf("Constructing Delaunay triangulation "); + if (b->incremental) + { + printf("by incremental method.\n"); + } + else if (b->sweepline) + { + printf("by sweepline method.\n"); + } + else + { + printf("by divide-and-conquer method.\n"); + } + } + if (b->incremental) + { + hulledges = incrementaldelaunay(m, b); + } + else if (b->sweepline) + { + hulledges = sweeplinedelaunay(m, b); + } + else + { + hulledges = divconqdelaunay(m, b); + } +#endif /* not REDUCED */ + + if (m->triangles.items == 0) { + /* The input vertices were all collinear, so there are no triangles. */ + return 0l; + } + else { + return hulledges; + } +} + +/** **/ +/** **/ +/********* General mesh construction routines end here *********/ + +/********* Segment insertion begins here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* finddirection() Find the first triangle on the path from one point */ +/* to another. */ +/* */ +/* Finds the triangle that intersects a line segment drawn from the */ +/* origin of `searchtri' to the point `searchpoint', and returns the result */ +/* in `searchtri'. The origin of `searchtri' does not change, even though */ +/* the triangle returned may differ from the one passed in. This routine */ +/* is used to find the direction to move in to get from one point to */ +/* another. */ +/* */ +/* The return value notes whether the destination or apex of the found */ +/* triangle is collinear with the two points in question. */ +/* */ +/*****************************************************************************/ + +enum finddirectionresult finddirection(struct mesh *m, struct behavior *b, struct otri *searchtri, + vertex searchpoint) { + struct otri checktri; + vertex startvertex; + vertex leftvertex, rightvertex; + REAL leftccw, rightccw; + int leftflag, rightflag; + triangle ptr; /* Temporary variable used by onext() and oprev(). */ + + org(*searchtri, startvertex); + dest(*searchtri, rightvertex); + apex(*searchtri, leftvertex); + /* Is `searchpoint' to the left? */ + leftccw = counterclockwise(m, b, searchpoint, startvertex, leftvertex); + leftflag = leftccw > 0.0; + /* Is `searchpoint' to the right? */ + rightccw = counterclockwise(m, b, startvertex, searchpoint, rightvertex); + rightflag = rightccw > 0.0; + if (leftflag && rightflag) { + /* `searchtri' faces directly away from `searchpoint'. We could go left */ + /* or right. Ask whether it's a triangle or a boundary on the left. */ + onext(*searchtri, checktri); + if (checktri.tri == m->dummytri) { + leftflag = 0; + } + else { + rightflag = 0; + } + } + while (leftflag) { + /* Turn left until satisfied. */ + onextself(*searchtri); + if (searchtri->tri == m->dummytri) { + printf("Internal error in finddirection(): Unable to find a\n"); + printf(" triangle leading from (%.12g, %.12g) to", startvertex[0], startvertex[1]); + printf(" (%.12g, %.12g).\n", searchpoint[0], searchpoint[1]); + internalerror(); + } + apex(*searchtri, leftvertex); + rightccw = leftccw; + leftccw = counterclockwise(m, b, searchpoint, startvertex, leftvertex); + leftflag = leftccw > 0.0; + } + while (rightflag) { + /* Turn right until satisfied. */ + oprevself(*searchtri); + if (searchtri->tri == m->dummytri) { + printf("Internal error in finddirection(): Unable to find a\n"); + printf(" triangle leading from (%.12g, %.12g) to", startvertex[0], startvertex[1]); + printf(" (%.12g, %.12g).\n", searchpoint[0], searchpoint[1]); + internalerror(); + } + dest(*searchtri, rightvertex); + leftccw = rightccw; + rightccw = counterclockwise(m, b, startvertex, searchpoint, rightvertex); + rightflag = rightccw > 0.0; + } + if (leftccw == 0.0) { + return LEFTCOLLINEAR; + } + else if (rightccw == 0.0) { + return RIGHTCOLLINEAR; + } + else { + return WITHIN; + } +} + +/*****************************************************************************/ +/* */ +/* segmentintersection() Find the intersection of an existing segment */ +/* and a segment that is being inserted. Insert */ +/* a vertex at the intersection, splitting an */ +/* existing subsegment. */ +/* */ +/* The segment being inserted connects the apex of splittri to endpoint2. */ +/* splitsubseg is the subsegment being split, and MUST adjoin splittri. */ +/* Hence, endpoints of the subsegment being split are the origin and */ +/* destination of splittri. */ +/* */ +/* On completion, splittri is a handle having the newly inserted */ +/* intersection point as its origin, and endpoint1 as its destination. */ +/* */ +/*****************************************************************************/ + +void segmentintersection(struct mesh *m, struct behavior *b, struct otri *splittri, + struct osub *splitsubseg, vertex endpoint2) { + struct osub opposubseg; + vertex endpoint1; + vertex torg, tdest; + vertex leftvertex, rightvertex; + vertex newvertex; + enum insertvertexresult success; + //enum finddirectionresult collinear; + REAL ex, ey; + REAL tx, ty; + REAL etx, ety; + REAL split, denom; + int i; + triangle ptr; /* Temporary variable used by onext(). */ + subseg sptr; /* Temporary variable used by snext(). */ + + /* Find the other three segment endpoints. */ + apex(*splittri, endpoint1); + org(*splittri, torg); + dest(*splittri, tdest); + /* Segment intersection formulae; see the Antonio reference. */ + tx = tdest[0] - torg[0]; + ty = tdest[1] - torg[1]; + ex = endpoint2[0] - endpoint1[0]; + ey = endpoint2[1] - endpoint1[1]; + etx = torg[0] - endpoint2[0]; + ety = torg[1] - endpoint2[1]; + denom = ty * ex - tx * ey; + if (denom == 0.0) { + printf("Internal error in segmentintersection():"); + printf(" Attempt to find intersection of parallel segments.\n"); + internalerror(); + return; + } + split = (ey * etx - ex * ety) / denom; + /* Create the new vertex. */ + newvertex = (vertex) poolalloc(&m->vertices); + /* Interpolate its coordinate and attributes. */ + for (i = 0; i < 2 + m->nextras; i++) { + newvertex[i] = torg[i] + split * (tdest[i] - torg[i]); + } + setvertexmark(newvertex, mark(*splitsubseg)); + setvertextype(newvertex, INPUTVERTEX); + if (b->verbose > 1) { + printf( + " Splitting subsegment (%.12g, %.12g) (%.12g, %.12g) at (%.12g, %.12g).\n", torg[0], torg[1], tdest[0], tdest[1], newvertex[0], newvertex[1]); + } + /* Insert the intersection vertex. This should always succeed. */ + success = insertvertex(m, b, newvertex, splittri, splitsubseg, 0, 0); + if (success != SUCCESSFULVERTEX) { + printf("Internal error in segmentintersection():\n"); + printf(" Failure to split a segment.\n"); + internalerror(); + return; + } + /* Record a triangle whose origin is the new vertex. */ + setvertex2tri(newvertex, encode(*splittri)); + if (m->steinerleft > 0) { + m->steinerleft--; + } + + /* Divide the segment into two, and correct the segment endpoints. */ + ssymself(*splitsubseg); + spivot(*splitsubseg, opposubseg); + sdissolve(*splitsubseg); + sdissolve(opposubseg); + do { + setsegorg(*splitsubseg, newvertex); + snextself(*splitsubseg); + } while (splitsubseg->ss != m->dummysub); + do { + setsegorg(opposubseg, newvertex); + snextself(opposubseg); + } while (opposubseg.ss != m->dummysub); + + /* Inserting the vertex may have caused edge flips. We wish to rediscover */ + /* the edge connecting endpoint1 to the new intersection vertex. */ + + // FIXME collinear = + finddirection(m, b, splittri, endpoint1); + + dest(*splittri, rightvertex); + apex(*splittri, leftvertex); + if ((leftvertex[0] == endpoint1[0]) && (leftvertex[1] == endpoint1[1])) { + onextself(*splittri); + } + else if ((rightvertex[0] != endpoint1[0]) || (rightvertex[1] != endpoint1[1])) { + printf("Internal error in segmentintersection():\n"); + printf(" Topological inconsistency after splitting a segment.\n"); + internalerror(); + return; + } + /* `splittri' should have destination endpoint1. */ +} + +/*****************************************************************************/ +/* */ +/* scoutsegment() Scout the first triangle on the path from one endpoint */ +/* to another, and check for completion (reaching the */ +/* second endpoint), a collinear vertex, or the */ +/* intersection of two segments. */ +/* */ +/* Returns one if the entire segment is successfully inserted, and zero if */ +/* the job must be finished by conformingedge() or constrainededge(). */ +/* */ +/* If the first triangle on the path has the second endpoint as its */ +/* destination or apex, a subsegment is inserted and the job is done. */ +/* */ +/* If the first triangle on the path has a destination or apex that lies on */ +/* the segment, a subsegment is inserted connecting the first endpoint to */ +/* the collinear vertex, and the search is continued from the collinear */ +/* vertex. */ +/* */ +/* If the first triangle on the path has a subsegment opposite its origin, */ +/* then there is a segment that intersects the segment being inserted. */ +/* Their intersection vertex is inserted, splitting the subsegment. */ +/* */ +/*****************************************************************************/ + +int scoutsegment(struct mesh *m, struct behavior *b, struct otri *searchtri, vertex endpoint2, + int newmark) { + struct otri crosstri; + struct osub crosssubseg; + vertex leftvertex, rightvertex; + enum finddirectionresult collinear; + subseg sptr; /* Temporary variable used by tspivot(). */ + + collinear = finddirection(m, b, searchtri, endpoint2); + dest(*searchtri, rightvertex); + apex(*searchtri, leftvertex); + if (((leftvertex[0] == endpoint2[0]) && (leftvertex[1] == endpoint2[1])) + || ((rightvertex[0] == endpoint2[0]) && (rightvertex[1] == endpoint2[1]))) { + /* The segment is already an edge in the mesh. */ + if ((leftvertex[0] == endpoint2[0]) && (leftvertex[1] == endpoint2[1])) { + lprevself(*searchtri); + } + /* Insert a subsegment, if there isn't already one there. */ + insertsubseg(m, b, searchtri, newmark); + return 1; + } + else if (collinear == LEFTCOLLINEAR) { + /* We've collided with a vertex between the segment's endpoints. */ + /* Make the collinear vertex be the triangle's origin. */ + lprevself(*searchtri); + insertsubseg(m, b, searchtri, newmark); + /* Insert the remainder of the segment. */ + return scoutsegment(m, b, searchtri, endpoint2, newmark); + } + else if (collinear == RIGHTCOLLINEAR) { + /* We've collided with a vertex between the segment's endpoints. */ + insertsubseg(m, b, searchtri, newmark); + /* Make the collinear vertex be the triangle's origin. */ + lnextself(*searchtri); + /* Insert the remainder of the segment. */ + return scoutsegment(m, b, searchtri, endpoint2, newmark); + } + else { + lnext(*searchtri, crosstri); + tspivot(crosstri, crosssubseg); + /* Check for a crossing segment. */ + if (crosssubseg.ss == m->dummysub) { + return 0; + } + else { + /* Insert a vertex at the intersection. */ + segmentintersection(m, b, &crosstri, &crosssubseg, endpoint2); + if (error_set) + return -1; + otricopy(crosstri, *searchtri); + insertsubseg(m, b, searchtri, newmark); + /* Insert the remainder of the segment. */ + return scoutsegment(m, b, searchtri, endpoint2, newmark); + } + } +} + +/*****************************************************************************/ +/* */ +/* delaunayfixup() Enforce the Delaunay condition at an edge, fanning out */ +/* recursively from an existing vertex. Pay special */ +/* attention to stacking inverted triangles. */ +/* */ +/* This is a support routine for inserting segments into a constrained */ +/* Delaunay triangulation. */ +/* */ +/* The origin of fixuptri is treated as if it has just been inserted, and */ +/* the local Delaunay condition needs to be enforced. It is only enforced */ +/* in one sector, however, that being the angular range defined by */ +/* fixuptri. */ +/* */ +/* This routine also needs to make decisions regarding the "stacking" of */ +/* triangles. (Read the description of constrainededge() below before */ +/* reading on here, so you understand the algorithm.) If the position of */ +/* the new vertex (the origin of fixuptri) indicates that the vertex before */ +/* it on the polygon is a reflex vertex, then "stack" the triangle by */ +/* doing nothing. (fixuptri is an inverted triangle, which is how stacked */ +/* triangles are identified.) */ +/* */ +/* Otherwise, check whether the vertex before that was a reflex vertex. */ +/* If so, perform an edge flip, thereby eliminating an inverted triangle */ +/* (popping it off the stack). The edge flip may result in the creation */ +/* of a new inverted triangle, depending on whether or not the new vertex */ +/* is visible to the vertex three edges behind on the polygon. */ +/* */ +/* If neither of the two vertices behind the new vertex are reflex */ +/* vertices, fixuptri and fartri, the triangle opposite it, are not */ +/* inverted; hence, ensure that the edge between them is locally Delaunay. */ +/* */ +/* `leftside' indicates whether or not fixuptri is to the left of the */ +/* segment being inserted. (Imagine that the segment is pointing up from */ +/* endpoint1 to endpoint2.) */ +/* */ +/*****************************************************************************/ + +void delaunayfixup(struct mesh *m, struct behavior *b, struct otri *fixuptri, int leftside) { + struct otri neartri; + struct otri fartri; + struct osub faredge; + vertex nearvertex, leftvertex, rightvertex, farvertex; + triangle ptr; /* Temporary variable used by sym(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + lnext(*fixuptri, neartri); + sym(neartri, fartri); + /* Check if the edge opposite the origin of fixuptri can be flipped. */ + if (fartri.tri == m->dummytri) { + return; + } + tspivot(neartri, faredge); + if (faredge.ss != m->dummysub) { + return; + } + /* Find all the relevant vertices. */ + apex(neartri, nearvertex); + org(neartri, leftvertex); + dest(neartri, rightvertex); + apex(fartri, farvertex); + /* Check whether the previous polygon vertex is a reflex vertex. */ + if (leftside) { + if (counterclockwise(m, b, nearvertex, leftvertex, farvertex) <= 0.0) { + /* leftvertex is a reflex vertex too. Nothing can */ + /* be done until a convex section is found. */ + return; + } + } + else { + if (counterclockwise(m, b, farvertex, rightvertex, nearvertex) <= 0.0) { + /* rightvertex is a reflex vertex too. Nothing can */ + /* be done until a convex section is found. */ + return; + } + } + if (counterclockwise(m, b, rightvertex, leftvertex, farvertex) > 0.0) { + /* fartri is not an inverted triangle, and farvertex is not a reflex */ + /* vertex. As there are no reflex vertices, fixuptri isn't an */ + /* inverted triangle, either. Hence, test the edge between the */ + /* triangles to ensure it is locally Delaunay. */ + if (incircle(m, b, leftvertex, farvertex, rightvertex, nearvertex) <= 0.0) { + return; + } + /* Not locally Delaunay; go on to an edge flip. */ + } /* else fartri is inverted; remove it from the stack by flipping. */ + flip(m, b, &neartri); + lprevself(*fixuptri); + /* Restore the origin of fixuptri after the flip. */ + /* Recursively process the two triangles that result from the flip. */ + delaunayfixup(m, b, fixuptri, leftside); + delaunayfixup(m, b, &fartri, leftside); +} + +/*****************************************************************************/ +/* */ +/* constrainededge() Force a segment into a constrained Delaunay */ +/* triangulation by deleting the triangles it */ +/* intersects, and triangulating the polygons that */ +/* form on each side of it. */ +/* */ +/* Generates a single subsegment connecting `endpoint1' to `endpoint2'. */ +/* The triangle `starttri' has `endpoint1' as its origin. `newmark' is the */ +/* boundary marker of the segment. */ +/* */ +/* To insert a segment, every triangle whose interior intersects the */ +/* segment is deleted. The union of these deleted triangles is a polygon */ +/* (which is not necessarily monotone, but is close enough), which is */ +/* divided into two polygons by the new segment. This routine's task is */ +/* to generate the Delaunay triangulation of these two polygons. */ +/* */ +/* You might think of this routine's behavior as a two-step process. The */ +/* first step is to walk from endpoint1 to endpoint2, flipping each edge */ +/* encountered. This step creates a fan of edges connected to endpoint1, */ +/* including the desired edge to endpoint2. The second step enforces the */ +/* Delaunay condition on each side of the segment in an incremental manner: */ +/* proceeding along the polygon from endpoint1 to endpoint2 (this is done */ +/* independently on each side of the segment), each vertex is "enforced" */ +/* as if it had just been inserted, but affecting only the previous */ +/* vertices. The result is the same as if the vertices had been inserted */ +/* in the order they appear on the polygon, so the result is Delaunay. */ +/* */ +/* In truth, constrainededge() interleaves these two steps. The procedure */ +/* walks from endpoint1 to endpoint2, and each time an edge is encountered */ +/* and flipped, the newly exposed vertex (at the far end of the flipped */ +/* edge) is "enforced" upon the previously flipped edges, usually affecting */ +/* only one side of the polygon (depending upon which side of the segment */ +/* the vertex falls on). */ +/* */ +/* The algorithm is complicated by the need to handle polygons that are not */ +/* convex. Although the polygon is not necessarily monotone, it can be */ +/* triangulated in a manner similar to the stack-based algorithms for */ +/* monotone polygons. For each reflex vertex (local concavity) of the */ +/* polygon, there will be an inverted triangle formed by one of the edge */ +/* flips. (An inverted triangle is one with negative area - that is, its */ +/* vertices are arranged in clockwise order - and is best thought of as a */ +/* wrinkle in the fabric of the mesh.) Each inverted triangle can be */ +/* thought of as a reflex vertex pushed on the stack, waiting to be fixed */ +/* later. */ +/* */ +/* A reflex vertex is popped from the stack when a vertex is inserted that */ +/* is visible to the reflex vertex. (However, if the vertex behind the */ +/* reflex vertex is not visible to the reflex vertex, a new inverted */ +/* triangle will take its place on the stack.) These details are handled */ +/* by the delaunayfixup() routine above. */ +/* */ +/*****************************************************************************/ + +void constrainededge(struct mesh *m, struct behavior *b, struct otri *starttri, vertex endpoint2, + int newmark) { + struct otri fixuptri, fixuptri2; + struct osub crosssubseg; + vertex endpoint1; + vertex farvertex; + REAL area; + int collision; + int done; + triangle ptr; /* Temporary variable used by sym() and oprev(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + org(*starttri, endpoint1); + lnext(*starttri, fixuptri); + flip(m, b, &fixuptri); + /* `collision' indicates whether we have found a vertex directly */ + /* between endpoint1 and endpoint2. */ + collision = 0; + done = 0; + do { + org(fixuptri, farvertex); + /* `farvertex' is the extreme point of the polygon we are "digging" */ + /* to get from endpoint1 to endpoint2. */ + if ((farvertex[0] == endpoint2[0]) && (farvertex[1] == endpoint2[1])) { + oprev(fixuptri, fixuptri2); + /* Enforce the Delaunay condition around endpoint2. */ + delaunayfixup(m, b, &fixuptri, 0); + delaunayfixup(m, b, &fixuptri2, 1); + done = 1; + } + else { + /* Check whether farvertex is to the left or right of the segment */ + /* being inserted, to decide which edge of fixuptri to dig */ + /* through next. */ + area = counterclockwise(m, b, endpoint1, endpoint2, farvertex); + if (area == 0.0) { + /* We've collided with a vertex between endpoint1 and endpoint2. */ + collision = 1; + oprev(fixuptri, fixuptri2); + /* Enforce the Delaunay condition around farvertex. */ + delaunayfixup(m, b, &fixuptri, 0); + delaunayfixup(m, b, &fixuptri2, 1); + done = 1; + } + else { + if (area > 0.0) { /* farvertex is to the left of the segment. */ + oprev(fixuptri, fixuptri2); + /* Enforce the Delaunay condition around farvertex, on the */ + /* left side of the segment only. */ + delaunayfixup(m, b, &fixuptri2, 1); + /* Flip the edge that crosses the segment. After the edge is */ + /* flipped, one of its endpoints is the fan vertex, and the */ + /* destination of fixuptri is the fan vertex. */ + lprevself(fixuptri); + } + else { /* farvertex is to the right of the segment. */ + delaunayfixup(m, b, &fixuptri, 0); + /* Flip the edge that crosses the segment. After the edge is */ + /* flipped, one of its endpoints is the fan vertex, and the */ + /* destination of fixuptri is the fan vertex. */ + oprevself(fixuptri); + } + /* Check for two intersecting segments. */ + tspivot(fixuptri, crosssubseg); + if (crosssubseg.ss == m->dummysub) { + flip(m, b, &fixuptri); /* May create inverted triangle at left. */ + } + else { + /* We've collided with a segment between endpoint1 and endpoint2. */ + collision = 1; + /* Insert a vertex at the intersection. */ + segmentintersection(m, b, &fixuptri, &crosssubseg, endpoint2); + done = 1; + } + } + } + } while (!done); + /* Insert a subsegment to make the segment permanent. */ + insertsubseg(m, b, &fixuptri, newmark); + /* If there was a collision with an interceding vertex, install another */ + /* segment connecting that vertex with endpoint2. */ + if (collision) { + /* Insert the remainder of the segment. */ + if (!scoutsegment(m, b, &fixuptri, endpoint2, newmark)) { + constrainededge(m, b, &fixuptri, endpoint2, newmark); + } + } +} + +/*****************************************************************************/ +/* */ +/* insertsegment() Insert a PSLG segment into a triangulation. */ +/* */ +/*****************************************************************************/ + +void insertsegment(struct mesh *m, struct behavior *b, vertex endpoint1, vertex endpoint2, + int newmark) { + struct otri searchtri1, searchtri2; + triangle encodedtri; + vertex checkvertex; + triangle ptr; /* Temporary variable used by sym(). */ + + if (b->verbose > 1) { + printf( " Connecting (%.12g, %.12g) to (%.12g, %.12g).\n", + endpoint1[0], endpoint1[1], endpoint2[0], endpoint2[1]); + } + + /* Find a triangle whose origin is the segment's first endpoint. */ + checkvertex = (vertex) NULL; + encodedtri = vertex2tri(endpoint1); + if (encodedtri != (triangle) NULL) { + decode(encodedtri, searchtri1); + org(searchtri1, checkvertex); + } + if (checkvertex != endpoint1) { + /* Find a boundary triangle to search from. */ + searchtri1.tri = m->dummytri; + searchtri1.orient = 0; + symself(searchtri1); + /* Search for the segment's first endpoint by point location. */ + if (locate(m, b, endpoint1, &searchtri1) != ONVERTEX) { + printf( "Internal error in insertsegment(): Unable to locate PSLG vertex\n"); + printf(" (%.12g, %.12g) in triangulation.\n", endpoint1[0], endpoint1[1]); + internalerror(); + } + } + /* Remember this triangle to improve subsequent point location. */ + otricopy(searchtri1, m->recenttri); + /* Scout the beginnings of a path from the first endpoint */ + /* toward the second. */ + if (scoutsegment(m, b, &searchtri1, endpoint2, newmark)) { + /* The segment was easily inserted. */ + return; + } + /* The first endpoint may have changed if a collision with an intervening */ + /* vertex on the segment occurred. */ + org(searchtri1, endpoint1); + + /* Find a triangle whose origin is the segment's second endpoint. */ + checkvertex = (vertex) NULL; + encodedtri = vertex2tri(endpoint2); + if (encodedtri != (triangle) NULL) { + decode(encodedtri, searchtri2); + org(searchtri2, checkvertex); + } + if (checkvertex != endpoint2) { + /* Find a boundary triangle to search from. */ + searchtri2.tri = m->dummytri; + searchtri2.orient = 0; + symself(searchtri2); + /* Search for the segment's second endpoint by point location. */ + if (locate(m, b, endpoint2, &searchtri2) != ONVERTEX) { + printf( "Internal error in insertsegment(): Unable to locate PSLG vertex\n"); + printf(" (%.12g, %.12g) in triangulation.\n", endpoint2[0], endpoint2[1]); + internalerror(); + } + } + /* Remember this triangle to improve subsequent point location. */ + otricopy(searchtri2, m->recenttri); + /* Scout the beginnings of a path from the second endpoint */ + /* toward the first. */ + if (scoutsegment(m, b, &searchtri2, endpoint1, newmark)) { + /* The segment was easily inserted. */ + return; + } + /* The second endpoint may have changed if a collision with an intervening */ + /* vertex on the segment occurred. */ + org(searchtri2, endpoint2); + +#ifndef REDUCED +#ifndef CDT_ONLY + if (b->splitseg) + { + /* Insert vertices to force the segment into the triangulation. */ + conformingedge(m, b, endpoint1, endpoint2, newmark); + } + else + { +#endif /* not CDT_ONLY */ +#endif /* not REDUCED */ + /* Insert the segment directly into the triangulation. */ + constrainededge(m, b, &searchtri1, endpoint2, newmark); +#ifndef REDUCED +#ifndef CDT_ONLY +} +#endif /* not CDT_ONLY */ +#endif /* not REDUCED */ +} + +/*****************************************************************************/ +/* */ +/* markhull() Cover the convex hull of a triangulation with subsegments. */ +/* */ +/*****************************************************************************/ + +void markhull(struct mesh *m, struct behavior *b) { + struct otri hulltri; + struct otri nexttri; + struct otri starttri; + triangle ptr; /* Temporary variable used by sym() and oprev(). */ + + /* Find a triangle handle on the hull. */ + hulltri.tri = m->dummytri; + hulltri.orient = 0; + symself(hulltri); + /* Remember where we started so we know when to stop. */ + otricopy(hulltri, starttri); + /* Go once counterclockwise around the convex hull. */ + do { + /* Create a subsegment if there isn't already one here. */ + insertsubseg(m, b, &hulltri, 1); + /* To find the next hull edge, go clockwise around the next vertex. */ + lnextself(hulltri); + oprev(hulltri, nexttri); + while (nexttri.tri != m->dummytri) { + otricopy(nexttri, hulltri); + oprev(hulltri, nexttri); + } + } while (!otriequal(hulltri, starttri)); +} + +/*****************************************************************************/ +/* */ +/* formskeleton() Create the segments of a triangulation, including PSLG */ +/* segments and edges on the convex hull. */ +/* */ +/* The PSLG segments are read from a .poly file. The return value is the */ +/* number of segments in the file. */ +/* */ +/*****************************************************************************/ + +void formskeleton(struct mesh *m, struct behavior *b, int *segmentlist, int *segmentmarkerlist, + int numberofsegments) { + char polyfilename[6]; + int index; + vertex endpoint1, endpoint2; + int segmentmarkers; + int end1, end2; + int boundmarker; + int i; + + if (b->poly) { + if (!b->quiet) { + printf("Recovering segments in Delaunay triangulation.\n"); + } + strcpy(polyfilename, "input"); + m->insegments = numberofsegments; + segmentmarkers = segmentmarkerlist != (int *) NULL; + index = 0; + /* If the input vertices are collinear, there is no triangulation, */ + /* so don't try to insert segments. */ + if (m->triangles.items == 0) { + return; + } + + /* If segments are to be inserted, compute a mapping */ + /* from vertices to triangles. */ + if (m->insegments > 0) { + makevertexmap(m, b); + if (b->verbose) { + printf(" Recovering PSLG segments.\n"); + } + } + + boundmarker = 0; + /* Read and insert the segments. */ + for (i = 0; i < m->insegments; i++) { + end1 = segmentlist[index++]; + end2 = segmentlist[index++]; + if (segmentmarkers) { + boundmarker = segmentmarkerlist[i]; + } + if ((end1 < b->firstnumber) || (end1 >= b->firstnumber + m->invertices)) { + if (!b->quiet) { + printf( "Warning: Invalid first endpoint of segment %d in %s.\n", + b->firstnumber + i, polyfilename); + } + } + else if ((end2 < b->firstnumber) || (end2 >= b->firstnumber + m->invertices)) { + if (!b->quiet) { + printf( "Warning: Invalid second endpoint of segment %d in %s.\n", + b->firstnumber + i, polyfilename); + } + } + else { + /* Find the vertices numbered `end1' and `end2'. */ + endpoint1 = getvertex(m, b, end1); + endpoint2 = getvertex(m, b, end2); + if ((endpoint1[0] == endpoint2[0]) && (endpoint1[1] == endpoint2[1])) { + if (!b->quiet) { + printf( "Warning: Endpoints of segment %d are coincident in %s.\n", + b->firstnumber + i, polyfilename); + } + } + else { + insertsegment(m, b, endpoint1, endpoint2, boundmarker); + } + } + } + } + else { + m->insegments = 0; + } + if (b->convex || !b->poly) { + /* Enclose the convex hull with subsegments. */ + if (b->verbose) { + printf(" Enclosing convex hull with segments.\n"); + } + markhull(m, b); + } +} + +/** **/ +/** **/ +/********* Segment insertion ends here *********/ + +/********* Carving out holes and concavities begins here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* infecthull() Virally infect all of the triangles of the convex hull */ +/* that are not protected by subsegments. Where there are */ +/* subsegments, set boundary markers as appropriate. */ +/* */ +/*****************************************************************************/ + +void infecthull(struct mesh *m, struct behavior *b) { + struct otri hulltri; + struct otri nexttri; + struct otri starttri; + struct osub hullsubseg; + triangle **deadtriangle; + vertex horg, hdest; + triangle ptr; /* Temporary variable used by sym(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + if (b->verbose) { + printf(" Marking concavities (external triangles) for elimination.\n"); + } + /* Find a triangle handle on the hull. */ + hulltri.tri = m->dummytri; + hulltri.orient = 0; + symself(hulltri); + /* Remember where we started so we know when to stop. */ + otricopy(hulltri, starttri); + /* Go once counterclockwise around the convex hull. */ + do { + /* Ignore triangles that are already infected. */ + if (!infected(hulltri)) { + /* Is the triangle protected by a subsegment? */ + tspivot(hulltri, hullsubseg); + if (hullsubseg.ss == m->dummysub) { + /* The triangle is not protected; infect it. */ + if (!infected(hulltri)) { + infect(hulltri); + deadtriangle = (triangle **) poolalloc(&m->viri); + *deadtriangle = hulltri.tri; + } + } + else { + /* The triangle is protected; set boundary markers if appropriate. */ + if (mark(hullsubseg) == 0) { + setmark(hullsubseg, 1); + org(hulltri, horg); + dest(hulltri, hdest); + if (vertexmark(horg) == 0) { + setvertexmark(horg, 1); + } + if (vertexmark(hdest) == 0) { + setvertexmark(hdest, 1); + } + } + } + } + /* To find the next hull edge, go clockwise around the next vertex. */ + lnextself(hulltri); + oprev(hulltri, nexttri); + while (nexttri.tri != m->dummytri) { + otricopy(nexttri, hulltri); + oprev(hulltri, nexttri); + } + } while (!otriequal(hulltri, starttri)); +} + +/*****************************************************************************/ +/* */ +/* plague() Spread the virus from all infected triangles to any neighbors */ +/* not protected by subsegments. Delete all infected triangles. */ +/* */ +/* This is the procedure that actually creates holes and concavities. */ +/* */ +/* This procedure operates in two phases. The first phase identifies all */ +/* the triangles that will die, and marks them as infected. They are */ +/* marked to ensure that each triangle is added to the virus pool only */ +/* once, so the procedure will terminate. */ +/* */ +/* The second phase actually eliminates the infected triangles. It also */ +/* eliminates orphaned vertices. */ +/* */ +/*****************************************************************************/ + +void plague(struct mesh *m, struct behavior *b) { + struct otri testtri; + struct otri neighbor; + triangle **virusloop; + triangle **deadtriangle; + struct osub neighborsubseg; + vertex testvertex; + vertex norg, ndest; + vertex deadorg, deaddest, deadapex; + int killorg; + triangle ptr; /* Temporary variable used by sym() and onext(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + if (b->verbose) { + printf(" Marking neighbors of marked triangles.\n"); + } + /* Loop through all the infected triangles, spreading the virus to */ + /* their neighbors, then to their neighbors' neighbors. */ + traversalinit(&m->viri); + virusloop = (triangle **) traverse(&m->viri); + while (virusloop != (triangle **) NULL) { + testtri.tri = *virusloop; + /* A triangle is marked as infected by messing with one of its pointers */ + /* to subsegments, setting it to an illegal value. Hence, we have to */ + /* temporarily uninfect this triangle so that we can examine its */ + /* adjacent subsegments. */ + uninfect(testtri); + if (b->verbose > 2) { + /* Assign the triangle an orientation for convenience in */ + /* checking its vertices. */ + testtri.orient = 0; + org(testtri, deadorg); + dest(testtri, deaddest); + apex(testtri, deadapex); + printf( + " Checking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", deadorg[0], deadorg[1], deaddest[0], deaddest[1], deadapex[0], deadapex[1]); + } + /* Check each of the triangle's three neighbors. */ + for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { + /* Find the neighbor. */ + sym(testtri, neighbor); + /* Check for a subsegment between the triangle and its neighbor. */ + tspivot(testtri, neighborsubseg); + /* Check if the neighbor is nonexistent or already infected. */ + if ((neighbor.tri == m->dummytri) || infected(neighbor)) { + if (neighborsubseg.ss != m->dummysub) { + /* There is a subsegment separating the triangle from its */ + /* neighbor, but both triangles are dying, so the subsegment */ + /* dies too. */ + subsegdealloc(m, neighborsubseg.ss); + if (neighbor.tri != m->dummytri) { + /* Make sure the subsegment doesn't get deallocated again */ + /* later when the infected neighbor is visited. */ + uninfect(neighbor); + tsdissolve(neighbor); + infect(neighbor); + } + } + } + else { /* The neighbor exists and is not infected. */ + if (neighborsubseg.ss == m->dummysub) { + /* There is no subsegment protecting the neighbor, so */ + /* the neighbor becomes infected. */ + if (b->verbose > 2) { + org(neighbor, deadorg); + dest(neighbor, deaddest); + apex(neighbor, deadapex); + printf( + " Marking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", deadorg[0], deadorg[1], deaddest[0], deaddest[1], deadapex[0], deadapex[1]); + } + infect(neighbor); + /* Ensure that the neighbor's neighbors will be infected. */ + deadtriangle = (triangle **) poolalloc(&m->viri); + *deadtriangle = neighbor.tri; + } + else { /* The neighbor is protected by a subsegment. */ + /* Remove this triangle from the subsegment. */ + stdissolve(neighborsubseg); + /* The subsegment becomes a boundary. Set markers accordingly. */ + if (mark(neighborsubseg) == 0) { + setmark(neighborsubseg, 1); + } + org(neighbor, norg); + dest(neighbor, ndest); + if (vertexmark(norg) == 0) { + setvertexmark(norg, 1); + } + if (vertexmark(ndest) == 0) { + setvertexmark(ndest, 1); + } + } + } + } + /* Remark the triangle as infected, so it doesn't get added to the */ + /* virus pool again. */ + infect(testtri); + virusloop = (triangle **) traverse(&m->viri); + } + + if (b->verbose) { + printf(" Deleting marked triangles.\n"); + } + + traversalinit(&m->viri); + virusloop = (triangle **) traverse(&m->viri); + while (virusloop != (triangle **) NULL) { + testtri.tri = *virusloop; + + /* Check each of the three corners of the triangle for elimination. */ + /* This is done by walking around each vertex, checking if it is */ + /* still connected to at least one live triangle. */ + for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { + org(testtri, testvertex); + /* Check if the vertex has already been tested. */ + if (testvertex != (vertex) NULL) { + killorg = 1; + /* Mark the corner of the triangle as having been tested. */ + setorg(testtri, NULL); + /* Walk counterclockwise about the vertex. */ + onext(testtri, neighbor); + /* Stop upon reaching a boundary or the starting triangle. */ + while ((neighbor.tri != m->dummytri) && (!otriequal(neighbor, testtri))) { + if (infected(neighbor)) { + /* Mark the corner of this triangle as having been tested. */ + setorg(neighbor, NULL); + } + else { + /* A live triangle. The vertex survives. */ + killorg = 0; + } + /* Walk counterclockwise about the vertex. */ + onextself(neighbor); + } + /* If we reached a boundary, we must walk clockwise as well. */ + if (neighbor.tri == m->dummytri) { + /* Walk clockwise about the vertex. */ + oprev(testtri, neighbor); + /* Stop upon reaching a boundary. */ + while (neighbor.tri != m->dummytri) { + if (infected(neighbor)) { + /* Mark the corner of this triangle as having been tested. */ + setorg(neighbor, NULL); + } + else { + /* A live triangle. The vertex survives. */ + killorg = 0; + } + /* Walk clockwise about the vertex. */ + oprevself(neighbor); + } + } + if (killorg) { + if (b->verbose > 1) { + printf(" Deleting vertex (%.12g, %.12g)\n", testvertex[0], testvertex[1]); + } + setvertextype(testvertex, UNDEADVERTEX); + m->undeads++; + } + } + } + + /* Record changes in the number of boundary edges, and disconnect */ + /* dead triangles from their neighbors. */ + for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { + sym(testtri, neighbor); + if (neighbor.tri == m->dummytri) { + /* There is no neighboring triangle on this edge, so this edge */ + /* is a boundary edge. This triangle is being deleted, so this */ + /* boundary edge is deleted. */ + m->hullsize--; + } + else { + /* Disconnect the triangle from its neighbor. */ + dissolve(neighbor); + /* There is a neighboring triangle on this edge, so this edge */ + /* becomes a boundary edge when this triangle is deleted. */ + m->hullsize++; + } + } + /* Return the dead triangle to the pool of triangles. */ + triangledealloc(m, testtri.tri); + virusloop = (triangle **) traverse(&m->viri); + } + /* Empty the virus pool. */ + poolrestart(&m->viri); +} + +/*****************************************************************************/ +/* */ +/* regionplague() Spread regional attributes and/or area constraints */ +/* (from a .poly file) throughout the mesh. */ +/* */ +/* This procedure operates in two phases. The first phase spreads an */ +/* attribute and/or an area constraint through a (segment-bounded) region. */ +/* The triangles are marked to ensure that each triangle is added to the */ +/* virus pool only once, so the procedure will terminate. */ +/* */ +/* The second phase uninfects all infected triangles, returning them to */ +/* normal. */ +/* */ +/*****************************************************************************/ + +void regionplague(struct mesh *m, struct behavior *b, REAL attribute, REAL area) { + struct otri testtri; + struct otri neighbor; + triangle **virusloop; + triangle **regiontri; + struct osub neighborsubseg; + vertex regionorg, regiondest, regionapex; + triangle ptr; /* Temporary variable used by sym() and onext(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + if (b->verbose > 1) { + printf(" Marking neighbors of marked triangles.\n"); + } + /* Loop through all the infected triangles, spreading the attribute */ + /* and/or area constraint to their neighbors, then to their neighbors' */ + /* neighbors. */ + traversalinit(&m->viri); + virusloop = (triangle **) traverse(&m->viri); + while (virusloop != (triangle **) NULL) { + testtri.tri = *virusloop; + /* A triangle is marked as infected by messing with one of its pointers */ + /* to subsegments, setting it to an illegal value. Hence, we have to */ + /* temporarily uninfect this triangle so that we can examine its */ + /* adjacent subsegments. */ + uninfect(testtri); + if (b->regionattrib) { + /* Set an attribute. */ + setelemattribute(testtri, m->eextras, attribute); + } + if (b->vararea) { + /* Set an area constraint. */ + setareabound(testtri, area); + } + if (b->verbose > 2) { + /* Assign the triangle an orientation for convenience in */ + /* checking its vertices. */ + testtri.orient = 0; + org(testtri, regionorg); + dest(testtri, regiondest); + apex(testtri, regionapex); + printf( " Checking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", + regionorg[0], regionorg[1], regiondest[0], regiondest[1], + regionapex[0], regionapex[1]); + } + /* Check each of the triangle's three neighbors. */ + for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) { + /* Find the neighbor. */ + sym(testtri, neighbor); + /* Check for a subsegment between the triangle and its neighbor. */ + tspivot(testtri, neighborsubseg); + /* Make sure the neighbor exists, is not already infected, and */ + /* isn't protected by a subsegment. */ + if ((neighbor.tri != m->dummytri) && !infected(neighbor) + && (neighborsubseg.ss == m->dummysub)) { + if (b->verbose > 2) { + org(neighbor, regionorg); + dest(neighbor, regiondest); + apex(neighbor, regionapex); + printf( " Marking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", + regionorg[0], regionorg[1], regiondest[0], regiondest[1], + regionapex[0], regionapex[1]); + } + /* Infect the neighbor. */ + infect(neighbor); + /* Ensure that the neighbor's neighbors will be infected. */ + regiontri = (triangle **) poolalloc(&m->viri); + *regiontri = neighbor.tri; + } + } + /* Remark the triangle as infected, so it doesn't get added to the */ + /* virus pool again. */ + infect(testtri); + virusloop = (triangle **) traverse(&m->viri); + } + + /* Uninfect all triangles. */ + if (b->verbose > 1) { + printf(" Unmarking marked triangles.\n"); + } + traversalinit(&m->viri); + virusloop = (triangle **) traverse(&m->viri); + while (virusloop != (triangle **) NULL) { + testtri.tri = *virusloop; + uninfect(testtri); + virusloop = (triangle **) traverse(&m->viri); + } + /* Empty the virus pool. */ + poolrestart(&m->viri); +} + +/*****************************************************************************/ +/* */ +/* carveholes() Find the holes and infect them. Find the area */ +/* constraints and infect them. Infect the convex hull. */ +/* Spread the infection and kill triangles. Spread the */ +/* area constraints. */ +/* */ +/* This routine mainly calls other routines to carry out all these */ +/* functions. */ +/* */ +/*****************************************************************************/ + +void carveholes(struct mesh *m, struct behavior *b, REAL *holelist, int holes, REAL *regionlist, + int regions) { + struct otri searchtri; + struct otri triangleloop; + struct otri *regiontris; + triangle **holetri; + triangle **regiontri; + vertex searchorg, searchdest; + enum locateresult intersect; + int i; + triangle ptr; /* Temporary variable used by sym(). */ + + if (!(b->quiet || (b->noholes && b->convex))) { + printf("Removing unwanted triangles.\n"); + if (b->verbose && (holes > 0)) { + printf(" Marking holes for elimination.\n"); + } + } + + if (regions > 0) { + /* Allocate storage for the triangles in which region points fall. */ + regiontris = (struct otri *) trimalloc(regions * (int) sizeof(struct otri)); + } + else { + regiontris = (struct otri *) NULL; + } + + if (((holes > 0) && !b->noholes) || !b->convex || (regions > 0)) { + /* Initialize a pool of viri to be used for holes, concavities, */ + /* regional attributes, and/or regional area constraints. */ + poolinit(&m->viri, sizeof(triangle *), VIRUSPERBLOCK, VIRUSPERBLOCK, 0); + } + + if (!b->convex) { + /* Mark as infected any unprotected triangles on the boundary. */ + /* This is one way by which concavities are created. */ + infecthull(m, b); + } + + if ((holes > 0) && !b->noholes) { + /* Infect each triangle in which a hole lies. */ + for (i = 0; i < 2 * holes; i += 2) { + /* Ignore holes that aren't within the bounds of the mesh. */ + if ((holelist[i] >= m->xmin) && (holelist[i] <= m->xmax) && (holelist[i + 1] >= m->ymin) + && (holelist[i + 1] <= m->ymax)) { + /* Start searching from some triangle on the outer boundary. */ + searchtri.tri = m->dummytri; + searchtri.orient = 0; + symself(searchtri); + /* Ensure that the hole is to the left of this boundary edge; */ + /* otherwise, locate() will falsely report that the hole */ + /* falls within the starting triangle. */ + org(searchtri, searchorg); + dest(searchtri, searchdest); + if (counterclockwise(m, b, searchorg, searchdest, &holelist[i]) > 0.0) { + /* Find a triangle that contains the hole. */ + intersect = locate(m, b, &holelist[i], &searchtri); + if ((intersect != OUTSIDE) && (!infected(searchtri))) { + /* Infect the triangle. This is done by marking the triangle */ + /* as infected and including the triangle in the virus pool. */ + infect(searchtri); + holetri = (triangle **) poolalloc(&m->viri); + *holetri = searchtri.tri; + } + } + } + } + } + + /* Now, we have to find all the regions BEFORE we carve the holes, because */ + /* locate() won't work when the triangulation is no longer convex. */ + /* (Incidentally, this is the reason why regional attributes and area */ + /* constraints can't be used when refining a preexisting mesh, which */ + /* might not be convex; they can only be used with a freshly */ + /* triangulated PSLG.) */ + if (regions > 0) { + /* Find the starting triangle for each region. */ + for (i = 0; i < regions; i++) { + regiontris[i].tri = m->dummytri; + /* Ignore region points that aren't within the bounds of the mesh. */ + if ((regionlist[4 * i] >= m->xmin) && (regionlist[4 * i] <= m->xmax) + && (regionlist[4 * i + 1] >= m->ymin) && (regionlist[4 * i + 1] <= m->ymax)) { + /* Start searching from some triangle on the outer boundary. */ + searchtri.tri = m->dummytri; + searchtri.orient = 0; + symself(searchtri); + /* Ensure that the region point is to the left of this boundary */ + /* edge; otherwise, locate() will falsely report that the */ + /* region point falls within the starting triangle. */ + org(searchtri, searchorg); + dest(searchtri, searchdest); + if (counterclockwise(m, b, searchorg, searchdest, ®ionlist[4 * i]) > 0.0) { + /* Find a triangle that contains the region point. */ + intersect = locate(m, b, ®ionlist[4 * i], &searchtri); + if ((intersect != OUTSIDE) && (!infected(searchtri))) { + /* Record the triangle for processing after the */ + /* holes have been carved. */ + otricopy(searchtri, regiontris[i]); + } + } + } + } + } + + if (m->viri.items > 0) { + /* Carve the holes and concavities. */ + plague(m, b); + } + /* The virus pool should be empty now. */ + + if (regions > 0) { + if (!b->quiet) { + if (b->regionattrib) { + if (b->vararea) { + printf("Spreading regional attributes and area constraints.\n"); + } + else { + printf("Spreading regional attributes.\n"); + } + } + else { + printf("Spreading regional area constraints.\n"); + } + } + if (b->regionattrib && !b->refine) { + /* Assign every triangle a regional attribute of zero. */ + traversalinit(&m->triangles); + triangleloop.orient = 0; + triangleloop.tri = triangletraverse(m); + while (triangleloop.tri != (triangle *) NULL) { + setelemattribute(triangleloop, m->eextras, 0.0); + triangleloop.tri = triangletraverse(m); + } + } + for (i = 0; i < regions; i++) { + if (regiontris[i].tri != m->dummytri) { + /* Make sure the triangle under consideration still exists. */ + /* It may have been eaten by the virus. */ + if (!deadtri(regiontris[i].tri)) { + /* Put one triangle in the virus pool. */ + infect(regiontris[i]); + regiontri = (triangle **) poolalloc(&m->viri); + *regiontri = regiontris[i].tri; + /* Apply one region's attribute and/or area constraint. */ + regionplague(m, b, regionlist[4 * i + 2], regionlist[4 * i + 3]); + /* The virus pool should be empty now. */ + } + } + } + if (b->regionattrib && !b->refine) { + /* Note the fact that each triangle has an additional attribute. */ + m->eextras++; + } + } + + /* Free up memory. */ + if (((holes > 0) && !b->noholes) || !b->convex || (regions > 0)) { + pooldeinit(&m->viri); + } + if (regions > 0) { + trifree((VOID *) regiontris); + } +} + +/** **/ +/** **/ +/********* Carving out holes and concavities ends here *********/ + +/*****************************************************************************/ +/* */ +/* highorder() Create extra nodes for quadratic subparametric elements. */ +/* */ +/*****************************************************************************/ + +void highorder(struct mesh *m, struct behavior *b) { + struct otri triangleloop, trisym; + struct osub checkmark; + vertex newvertex; + vertex torg, tdest; + int i; + triangle ptr; /* Temporary variable used by sym(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + if (!b->quiet) { + printf("Adding vertices for second-order triangles.\n"); + } + /* The following line ensures that dead items in the pool of nodes */ + /* cannot be allocated for the extra nodes associated with high */ + /* order elements. This ensures that the primary nodes (at the */ + /* corners of elements) will occur earlier in the output files, and */ + /* have lower indices, than the extra nodes. */ + m->vertices.deaditemstack = (VOID *) NULL; + + traversalinit(&m->triangles); + triangleloop.tri = triangletraverse(m); + /* To loop over the set of edges, loop over all triangles, and look at */ + /* the three edges of each triangle. If there isn't another triangle */ + /* adjacent to the edge, operate on the edge. If there is another */ + /* adjacent triangle, operate on the edge only if the current triangle */ + /* has a smaller pointer than its neighbor. This way, each edge is */ + /* considered only once. */ + while (triangleloop.tri != (triangle *) NULL) { + for (triangleloop.orient = 0; triangleloop.orient < 3; triangleloop.orient++) { + sym(triangleloop, trisym); + if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) { + org(triangleloop, torg); + dest(triangleloop, tdest); + /* Create a new node in the middle of the edge. Interpolate */ + /* its attributes. */ + newvertex = (vertex) poolalloc(&m->vertices); + for (i = 0; i < 2 + m->nextras; i++) { + newvertex[i] = 0.5 * (torg[i] + tdest[i]); + } + /* Set the new node's marker to zero or one, depending on */ + /* whether it lies on a boundary. */ + setvertexmark(newvertex, trisym.tri == m->dummytri); + setvertextype(newvertex, trisym.tri == m->dummytri ? FREEVERTEX : SEGMENTVERTEX); + if (b->usesegments) { + tspivot(triangleloop, checkmark); + /* If this edge is a segment, transfer the marker to the new node. */ + if (checkmark.ss != m->dummysub) { + setvertexmark(newvertex, mark(checkmark)); + setvertextype(newvertex, SEGMENTVERTEX); + } + } + if (b->verbose > 1) { + printf(" Creating (%.12g, %.12g).\n", newvertex[0], newvertex[1]); + } + /* Record the new node in the (one or two) adjacent elements. */ + triangleloop.tri[m->highorderindex + triangleloop.orient] = (triangle) newvertex; + if (trisym.tri != m->dummytri) { + trisym.tri[m->highorderindex + trisym.orient] = (triangle) newvertex; + } + } + } + triangleloop.tri = triangletraverse(m); + } +} + +/*****************************************************************************/ +/* */ +/* transfernodes() Read the vertices from memory. */ +/* */ +/*****************************************************************************/ + +void transfernodes(struct mesh *m, struct behavior *b, REAL *pointlist, REAL *pointattriblist, + int *pointmarkerlist, int numberofpoints, int numberofpointattribs) { + vertex vertexloop; + REAL x, y; + int i, j; + int coordindex; + int attribindex; + + m->invertices = numberofpoints; + m->mesh_dim = 2; + m->nextras = numberofpointattribs; + m->readnodefile = 0; + if (m->invertices < 3) { + printf("Error: Input must have at least three input vertices.\n"); + triexit(1); + } + if (m->nextras == 0) { + b->weighted = 0; + } + + initializevertexpool(m, b); + + /* Read the vertices. */ + coordindex = 0; + attribindex = 0; + for (i = 0; i < m->invertices; i++) { + vertexloop = (vertex) poolalloc(&m->vertices); + /* Read the vertex coordinates. */ + x = vertexloop[0] = pointlist[coordindex++]; + y = vertexloop[1] = pointlist[coordindex++]; + /* Read the vertex attributes. */ + for (j = 0; j < numberofpointattribs; j++) { + vertexloop[2 + j] = pointattriblist[attribindex++]; + } + if (pointmarkerlist != (int *) NULL) { + /* Read a vertex marker. */ + setvertexmark(vertexloop, pointmarkerlist[i]); + } + else { + /* If no markers are specified, they default to zero. */ + setvertexmark(vertexloop, 0); + } + + // ---------------------------------------------- + for (j = (i - 1) * 2; j >= 0; j -= 2){ + if (x == pointlist[j] && y == pointlist[j+1]){ + printf("skip duplicate %d\n", j >> 1); + setvertextype(vertexloop, UNDEADVERTEX); + vertexloop[0] = 0xffffffff; + vertexloop[1] = 0xffffffff; + break; + } + } + if (j >= 0) + continue; + // ---------------------------------------------- + + setvertextype(vertexloop, INPUTVERTEX); + /* Determine the smallest and largest x and y coordinates. */ + if (i == 0) { + m->xmin = m->xmax = x; + m->ymin = m->ymax = y; + } + else { + m->xmin = (x < m->xmin) ? x : m->xmin; + m->xmax = (x > m->xmax) ? x : m->xmax; + m->ymin = (y < m->ymin) ? y : m->ymin; + m->ymax = (y > m->ymax) ? y : m->ymax; + } + } + + /* Nonexistent x value used as a flag to mark circle events in sweepline */ + /* Delaunay algorithm. */ + m->xminextreme = 10 * m->xmin - 9 * m->xmax; +} + +/*****************************************************************************/ +/* */ +/* writenodes() Number the vertices and write them to a .node file. */ +/* */ +/* To save memory, the vertex numbers are written over the boundary markers */ +/* after the vertices are written to a file. */ +/* */ +/*****************************************************************************/ + +void writenodes(struct mesh *m, struct behavior *b, REAL **pointlist, REAL **pointattriblist, + int **pointmarkerlist) { + REAL *plist; + REAL *palist; + int *pmlist; + int coordindex; + int attribindex; + vertex vertexloop; + long outvertices; + int vertexnumber; + int i; + + if (b->jettison) { + outvertices = m->vertices.items - m->undeads; + } + else { + outvertices = m->vertices.items; + } + + if (!b->quiet) { + printf("Writing vertices.\n"); + } + /* Allocate memory for output vertices if necessary. */ + if (*pointlist == (REAL *) NULL) { + *pointlist = (REAL *) trimalloc((int) (outvertices * 2 * sizeof(REAL))); + } + /* Allocate memory for output vertex attributes if necessary. */ + if ((m->nextras > 0) && (*pointattriblist == (REAL *) NULL)) { + *pointattriblist = (REAL *) trimalloc((int) (outvertices * m->nextras * sizeof(REAL))); + } + /* Allocate memory for output vertex markers if necessary. */ + if (!b->nobound && (*pointmarkerlist == (int *) NULL)) { + *pointmarkerlist = (int *) trimalloc((int) (outvertices * sizeof(int))); + } + plist = *pointlist; + palist = *pointattriblist; + pmlist = *pointmarkerlist; + coordindex = 0; + attribindex = 0; + + traversalinit(&m->vertices); + vertexnumber = b->firstnumber; + vertexloop = vertextraverse(m); + while (vertexloop != (vertex) NULL) { + if (!b->jettison || (vertextype(vertexloop) != UNDEADVERTEX)) { + /* X and y coordinates. */ + plist[coordindex++] = vertexloop[0]; + plist[coordindex++] = vertexloop[1]; + /* Vertex attributes. */ + for (i = 0; i < m->nextras; i++) { + palist[attribindex++] = vertexloop[2 + i]; + } + if (!b->nobound) { + /* Copy the boundary marker. */ + pmlist[vertexnumber - b->firstnumber] = vertexmark(vertexloop); + } + setvertexmark(vertexloop, vertexnumber); + vertexnumber++; + } + vertexloop = vertextraverse(m); + } +} + +/*****************************************************************************/ +/* */ +/* numbernodes() Number the vertices. */ +/* */ +/* Each vertex is assigned a marker equal to its number. */ +/* */ +/* Used when writenodes() is not called because no .node file is written. */ +/* */ +/*****************************************************************************/ + +void numbernodes(struct mesh *m, struct behavior *b) { + vertex vertexloop; + int vertexnumber; + + traversalinit(&m->vertices); + vertexnumber = b->firstnumber; + vertexloop = vertextraverse(m); + while (vertexloop != (vertex) NULL) { + setvertexmark(vertexloop, vertexnumber); + if (!b->jettison || (vertextype(vertexloop) != UNDEADVERTEX)) { + vertexnumber++; + } + vertexloop = vertextraverse(m); + } +} + +/*****************************************************************************/ +/* */ +/* writeelements() Write the triangles to an .ele file. */ +/* */ +/*****************************************************************************/ + +void writeelements(struct mesh *m, struct behavior *b, INDICE **trianglelist, + REAL **triangleattriblist) { + INDICE *tlist; + REAL *talist; + int vertexindex; + int attribindex; + struct otri triangleloop; + vertex p1, p2, p3; + vertex mid1, mid2, mid3; + long elementnumber; + int i; + + if (!b->quiet) { + printf("Writing triangles.\n"); + } + /* Allocate memory for output triangles if necessary. */ + if (*trianglelist == (INDICE *) NULL) { + *trianglelist = (INDICE *) trimalloc( + (INDICE) (m->triangles.items * ((b->order + 1) * (b->order + 2) / 2) * sizeof(int))); + } + /* Allocate memory for output triangle attributes if necessary. */ + if ((m->eextras > 0) && (*triangleattriblist == (REAL *) NULL)) { + *triangleattriblist = (REAL *) trimalloc( + (int) (m->triangles.items * m->eextras * sizeof(REAL))); + } + tlist = *trianglelist; + talist = *triangleattriblist; + vertexindex = 0; + attribindex = 0; + + traversalinit(&m->triangles); + triangleloop.tri = triangletraverse(m); + triangleloop.orient = 0; + elementnumber = b->firstnumber; + while (triangleloop.tri != (triangle *) NULL) { + org(triangleloop, p1); + dest(triangleloop, p2); + apex(triangleloop, p3); + if (b->order == 1) { + tlist[vertexindex++] = vertexmark(p1); + tlist[vertexindex++] = vertexmark(p2); + tlist[vertexindex++] = vertexmark(p3); + } + else { + mid1 = (vertex) triangleloop.tri[m->highorderindex + 1]; + mid2 = (vertex) triangleloop.tri[m->highorderindex + 2]; + mid3 = (vertex) triangleloop.tri[m->highorderindex]; + tlist[vertexindex++] = vertexmark(p1); + tlist[vertexindex++] = vertexmark(p2); + tlist[vertexindex++] = vertexmark(p3); + tlist[vertexindex++] = vertexmark(mid1); + tlist[vertexindex++] = vertexmark(mid2); + tlist[vertexindex++] = vertexmark(mid3); + } + + for (i = 0; i < m->eextras; i++) { + talist[attribindex++] = elemattribute(triangleloop, i); + } + + triangleloop.tri = triangletraverse(m); + elementnumber++; + } +} + +/*****************************************************************************/ +/* */ +/* writepoly() Write the segments and holes to a .poly file. */ +/* */ +/*****************************************************************************/ + +void writepoly(struct mesh *m, struct behavior *b, int **segmentlist, int **segmentmarkerlist) { + int *slist; + int *smlist; + int index; + struct osub subsegloop; + vertex endpoint1, endpoint2; + long subsegnumber; + + if (!b->quiet) { + printf("Writing segments.\n"); + } + /* Allocate memory for output segments if necessary. */ + if (*segmentlist == (int *) NULL) { + *segmentlist = (int *) trimalloc((int) (m->subsegs.items * 2 * sizeof(int))); + } + /* Allocate memory for output segment markers if necessary. */ + if (!b->nobound && (*segmentmarkerlist == (int *) NULL)) { + *segmentmarkerlist = (int *) trimalloc((int) (m->subsegs.items * sizeof(int))); + } + slist = *segmentlist; + smlist = *segmentmarkerlist; + index = 0; + + traversalinit(&m->subsegs); + subsegloop.ss = subsegtraverse(m); + subsegloop.ssorient = 0; + subsegnumber = b->firstnumber; + while (subsegloop.ss != (subseg *) NULL) { + sorg(subsegloop, endpoint1); + sdest(subsegloop, endpoint2); + /* Copy indices of the segment's two endpoints. */ + slist[index++] = vertexmark(endpoint1); + slist[index++] = vertexmark(endpoint2); + if (!b->nobound) { + /* Copy the boundary marker. */ + smlist[subsegnumber - b->firstnumber] = mark(subsegloop); + } + + subsegloop.ss = subsegtraverse(m); + subsegnumber++; + } +} + +/*****************************************************************************/ +/* */ +/* writeedges() Write the edges to an .edge file. */ +/* */ +/*****************************************************************************/ + +void writeedges(struct mesh *m, struct behavior *b, int **edgelist, int **edgemarkerlist) { + int *elist; + int *emlist; + int index; + struct otri triangleloop, trisym; + struct osub checkmark; + vertex p1, p2; + long edgenumber; + triangle ptr; /* Temporary variable used by sym(). */ + subseg sptr; /* Temporary variable used by tspivot(). */ + + if (!b->quiet) { + printf("Writing edges.\n"); + } + /* Allocate memory for edges if necessary. */ + if (*edgelist == (int *) NULL) { + *edgelist = (int *) trimalloc((int) (m->edges * 2 * sizeof(int))); + } + /* Allocate memory for edge markers if necessary. */ + if (!b->nobound && (*edgemarkerlist == (int *) NULL)) { + *edgemarkerlist = (int *) trimalloc((int) (m->edges * sizeof(int))); + } + elist = *edgelist; + emlist = *edgemarkerlist; + index = 0; + + traversalinit(&m->triangles); + triangleloop.tri = triangletraverse(m); + edgenumber = b->firstnumber; + /* To loop over the set of edges, loop over all triangles, and look at */ + /* the three edges of each triangle. If there isn't another triangle */ + /* adjacent to the edge, operate on the edge. If there is another */ + /* adjacent triangle, operate on the edge only if the current triangle */ + /* has a smaller pointer than its neighbor. This way, each edge is */ + /* considered only once. */ + while (triangleloop.tri != (triangle *) NULL) { + for (triangleloop.orient = 0; triangleloop.orient < 3; triangleloop.orient++) { + sym(triangleloop, trisym); + if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) { + org(triangleloop, p1); + dest(triangleloop, p2); + elist[index++] = vertexmark(p1); + elist[index++] = vertexmark(p2); + if (b->nobound) { + } + else { + /* Edge number, indices of two endpoints, and a boundary marker. */ + /* If there's no subsegment, the boundary marker is zero. */ + if (b->usesegments) { + tspivot(triangleloop, checkmark); + if (checkmark.ss == m->dummysub) { + emlist[edgenumber - b->firstnumber] = 0; + } + else { + emlist[edgenumber - b->firstnumber] = mark(checkmark); + } + } + else { + emlist[edgenumber - b->firstnumber] = trisym.tri == m->dummytri; + } + } + edgenumber++; + } + } + triangleloop.tri = triangletraverse(m); + } +} + +/*****************************************************************************/ +/* */ +/* writevoronoi() Write the Voronoi diagram to a .v.node and .v.edge */ +/* file. */ +/* */ +/* The Voronoi diagram is the geometric dual of the Delaunay triangulation. */ +/* Hence, the Voronoi vertices are listed by traversing the Delaunay */ +/* triangles, and the Voronoi edges are listed by traversing the Delaunay */ +/* edges. */ +/* */ +/* WARNING: In order to assign numbers to the Voronoi vertices, this */ +/* procedure messes up the subsegments or the extra nodes of every */ +/* element. Hence, you should call this procedure last. */ +/* */ +/*****************************************************************************/ + +void writevoronoi(struct mesh *m, struct behavior *b, REAL **vpointlist, REAL **vpointattriblist, + int **vpointmarkerlist, int **vedgelist, int **vedgemarkerlist, REAL **vnormlist) { + REAL *plist; + REAL *palist; + int *elist; + REAL *normlist; + int coordindex; + int attribindex; + struct otri triangleloop, trisym; + vertex torg, tdest, tapex; + REAL circumcenter[2]; + REAL xi, eta; + long vnodenumber, vedgenumber; + int p1, p2; + int i; + triangle ptr; /* Temporary variable used by sym(). */ + + if (!b->quiet) { + printf("Writing Voronoi vertices.\n"); + } + /* Allocate memory for Voronoi vertices if necessary. */ + if (*vpointlist == (REAL *) NULL) { + *vpointlist = (REAL *) trimalloc((int) (m->triangles.items * 2 * sizeof(REAL))); + } + /* Allocate memory for Voronoi vertex attributes if necessary. */ + if (*vpointattriblist == (REAL *) NULL) { + *vpointattriblist = (REAL *) trimalloc( + (int) (m->triangles.items * m->nextras * sizeof(REAL))); + } + *vpointmarkerlist = (int *) NULL; + plist = *vpointlist; + palist = *vpointattriblist; + coordindex = 0; + attribindex = 0; + + traversalinit(&m->triangles); + triangleloop.tri = triangletraverse(m); + triangleloop.orient = 0; + vnodenumber = b->firstnumber; + while (triangleloop.tri != (triangle *) NULL) { + org(triangleloop, torg); + dest(triangleloop, tdest); + apex(triangleloop, tapex); + findcircumcenter(m, b, torg, tdest, tapex, circumcenter, &xi, &eta, 0); + /* X and y coordinates. */ + plist[coordindex++] = circumcenter[0]; + plist[coordindex++] = circumcenter[1]; + for (i = 2; i < 2 + m->nextras; i++) { + /* Interpolate the vertex attributes at the circumcenter. */ + palist[attribindex++] = torg[i] + xi * (tdest[i] - torg[i]) + eta * (tapex[i] - torg[i]); + } + + *(int *) (triangleloop.tri + 6) = (int) vnodenumber; + triangleloop.tri = triangletraverse(m); + vnodenumber++; + } + + if (!b->quiet) { + printf("Writing Voronoi edges.\n"); + } + /* Allocate memory for output Voronoi edges if necessary. */ + if (*vedgelist == (int *) NULL) { + *vedgelist = (int *) trimalloc((int) (m->edges * 2 * sizeof(int))); + } + *vedgemarkerlist = (int *) NULL; + /* Allocate memory for output Voronoi norms if necessary. */ + if (*vnormlist == (REAL *) NULL) { + *vnormlist = (REAL *) trimalloc((int) (m->edges * 2 * sizeof(REAL))); + } + elist = *vedgelist; + normlist = *vnormlist; + coordindex = 0; + + traversalinit(&m->triangles); + triangleloop.tri = triangletraverse(m); + vedgenumber = b->firstnumber; + /* To loop over the set of edges, loop over all triangles, and look at */ + /* the three edges of each triangle. If there isn't another triangle */ + /* adjacent to the edge, operate on the edge. If there is another */ + /* adjacent triangle, operate on the edge only if the current triangle */ + /* has a smaller pointer than its neighbor. This way, each edge is */ + /* considered only once. */ + while (triangleloop.tri != (triangle *) NULL) { + for (triangleloop.orient = 0; triangleloop.orient < 3; triangleloop.orient++) { + sym(triangleloop, trisym); + if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) { + /* Find the number of this triangle (and Voronoi vertex). */ + p1 = *(int *) (triangleloop.tri + 6); + if (trisym.tri == m->dummytri) { + org(triangleloop, torg); + dest(triangleloop, tdest); + /* Copy an infinite ray. Index of one endpoint, and -1. */ + elist[coordindex] = p1; + normlist[coordindex++] = tdest[1] - torg[1]; + elist[coordindex] = -1; + normlist[coordindex++] = torg[0] - tdest[0]; + } + else { + /* Find the number of the adjacent triangle (and Voronoi vertex). */ + p2 = *(int *) (trisym.tri + 6); + /* Finite edge. Write indices of two endpoints. */ + elist[coordindex] = p1; + normlist[coordindex++] = 0.0; + elist[coordindex] = p2; + normlist[coordindex++] = 0.0; + } + vedgenumber++; + } + } + triangleloop.tri = triangletraverse(m); + } +} + +void writeneighbors(struct mesh *m, struct behavior *b, int **neighborlist) { + int *nlist; + int index; + struct otri triangleloop, trisym; + long elementnumber; + int neighbor1, neighbor2, neighbor3; + triangle ptr; /* Temporary variable used by sym(). */ + + if (!b->quiet) { + printf("Writing neighbors.\n"); + } + /* Allocate memory for neighbors if necessary. */ + if (*neighborlist == (int *) NULL) { + *neighborlist = (int *) trimalloc((int) (m->triangles.items * 3 * sizeof(int))); + } + nlist = *neighborlist; + index = 0; + + traversalinit(&m->triangles); + triangleloop.tri = triangletraverse(m); + triangleloop.orient = 0; + elementnumber = b->firstnumber; + while (triangleloop.tri != (triangle *) NULL) { + *(int *) (triangleloop.tri + 6) = (int) elementnumber; + triangleloop.tri = triangletraverse(m); + elementnumber++; + } + *(int *) (m->dummytri + 6) = -1; + + traversalinit(&m->triangles); + triangleloop.tri = triangletraverse(m); + elementnumber = b->firstnumber; + while (triangleloop.tri != (triangle *) NULL) { + triangleloop.orient = 1; + sym(triangleloop, trisym); + neighbor1 = *(int *) (trisym.tri + 6); + triangleloop.orient = 2; + sym(triangleloop, trisym); + neighbor2 = *(int *) (trisym.tri + 6); + triangleloop.orient = 0; + sym(triangleloop, trisym); + neighbor3 = *(int *) (trisym.tri + 6); + nlist[index++] = neighbor1; + nlist[index++] = neighbor2; + nlist[index++] = neighbor3; + + triangleloop.tri = triangletraverse(m); + elementnumber++; + } +} + +/** **/ +/** **/ +/********* File I/O routines end here *********/ + +/*****************************************************************************/ +/* */ +/* main() or triangulate() Gosh, do everything. */ +/* */ +/* The sequence is roughly as follows. Many of these steps can be skipped, */ +/* depending on the command line switches. */ +/* */ +/* - Initialize constants and parse the command line. */ +/* - Read the vertices from a file and either */ +/* - triangulate them (no -r), or */ +/* - read an old mesh from files and reconstruct it (-r). */ +/* - Insert the PSLG segments (-p), and possibly segments on the convex */ +/* hull (-c). */ +/* - Read the holes (-p), regional attributes (-pA), and regional area */ +/* constraints (-pa). Carve the holes and concavities, and spread the */ +/* regional attributes and area constraints. */ +/* - Enforce the constraints on minimum angle (-q) and maximum area (-a). */ +/* Also enforce the conforming Delaunay property (-q and -a). */ +/* - Compute the number of edges in the resulting mesh. */ +/* - Promote the mesh's linear triangles to higher order elements (-o). */ +/* - Write the output files and print the statistics. */ +/* - Check the consistency and Delaunay property of the mesh (-C). */ +/* */ +/*****************************************************************************/ + +void triangulate(struct behavior *command, struct triangulateio *in, struct triangulateio *out, + struct triangulateio *vorout) { + struct mesh m; + struct behavior *b = command; + REAL *holearray; /* Array of holes. */ + REAL *regionarray; /* Array of regional attributes and area constraints. */ + + triangleinit(&m); + //parsecommandline(1, &triswitches, &b); + m.steinerleft = b->steiner; + + transfernodes(&m, b, in->pointlist, in->pointattributelist, in->pointmarkerlist, + in->numberofpoints, in->numberofpointattributes); + +#ifdef CDT_ONLY + m.hullsize = delaunay(&m, b); /* Triangulate the vertices. */ +#else /* not CDT_ONLY */ + if (b->refine) + { + /* Read and reconstruct a mesh. */ + m.hullsize = reconstruct(&m, b, in->trianglelist, + in->triangleattributelist, in->trianglearealist, + in->numberoftriangles, in->numberofcorners, + in->numberoftriangleattributes, + in->segmentlist, in->segmentmarkerlist, + in->numberofsegments); + } + else + { + m.hullsize = delaunay(&m, b); /* Triangulate the vertices. */ + } +#endif /* not CDT_ONLY */ + + /* Ensure that no vertex can be mistaken for a triangular bounding */ + /* box vertex in insertvertex(). */ + m.infvertex1 = (vertex) NULL; + m.infvertex2 = (vertex) NULL; + m.infvertex3 = (vertex) NULL; + + if (b->usesegments) { + m.checksegments = 1; /* Segments will be introduced next. */ + if (!b->refine) { + /* Insert PSLG segments and/or convex hull segments. */ + formskeleton(&m, b, in->segmentlist, in->segmentmarkerlist, in->numberofsegments); + } + } + + if (b->poly && (m.triangles.items > 0)) { + holearray = in->holelist; + m.holes = in->numberofholes; + regionarray = in->regionlist; + m.regions = in->numberofregions; + if (!b->refine) { + /* Carve out holes and concavities. */ + carveholes(&m, b, holearray, m.holes, regionarray, m.regions); + } + } + else { + /* Without a PSLG, there can be no holes or regional attributes */ + /* or area constraints. The following are set to zero to avoid */ + /* an accidental free() later. */ + m.holes = 0; + m.regions = 0; + } + +#ifndef CDT_ONLY + if (b->quality && (m.triangles.items > 0)) + { + enforcequality(&m, b); /* Enforce angle and area constraints. */ + } +#endif /* not CDT_ONLY */ + +#ifndef CDT_ONLY + if (b->quality) + { + printf("Quality milliseconds: %ld\n", + 1000l * (tv5.tv_sec - tv4.tv_sec) + + (tv5.tv_usec - tv4.tv_usec) / 1000l); + } +#endif /* not CDT_ONLY */ + + /* Calculate the number of edges. */ + m.edges = (3l * m.triangles.items + m.hullsize) / 2l; + + if (b->order > 1) { + highorder(&m, b); /* Promote elements to higher polynomial order. */ + } + if (!b->quiet) { + printf("\n"); + } + + if (b->jettison) { + out->numberofpoints = m.vertices.items - m.undeads; + } + else { + out->numberofpoints = m.vertices.items; + } + out->numberofpointattributes = m.nextras; + out->numberoftriangles = m.triangles.items; + out->numberofcorners = (b->order + 1) * (b->order + 2) / 2; + out->numberoftriangleattributes = m.eextras; + out->numberofedges = m.edges; + if (b->usesegments) { + out->numberofsegments = m.subsegs.items; + } + else { + out->numberofsegments = m.hullsize; + } + if (vorout != (struct triangulateio *) NULL) { + vorout->numberofpoints = m.triangles.items; + vorout->numberofpointattributes = m.nextras; + vorout->numberofedges = m.edges; + } + + /* If not using iteration numbers, don't write a .node file if one was */ + /* read, because the original one would be overwritten! */ + if (b->nonodewritten || (b->noiterationnum && m.readnodefile)) { + if (!b->quiet) { + printf("NOT writing vertices.\n"); + } + numbernodes(&m, b); /* We must remember to number the vertices. */ + } + else { + /* writenodes() numbers the vertices too. */ + writenodes(&m, b, &out->pointlist, &out->pointattributelist, &out->pointmarkerlist); + } + if (b->noelewritten) { + if (!b->quiet) { + printf("NOT writing triangles.\n"); + } + } + else { + writeelements(&m, b, &out->trianglelist, &out->triangleattributelist); + } + /* The -c switch (convex switch) causes a PSLG to be written */ + /* even if none was read. */ + if (b->poly || b->convex) { + /* If not using iteration numbers, don't overwrite the .poly file. */ + if (b->nopolywritten || b->noiterationnum) { + if (!b->quiet) { + printf("NOT writing segments.\n"); + } + } + else { + writepoly(&m, b, &out->segmentlist, &out->segmentmarkerlist); + out->numberofholes = m.holes; + out->numberofregions = m.regions; + if (b->poly) { + out->holelist = in->holelist; + out->regionlist = in->regionlist; + } + else { + out->holelist = (REAL *) NULL; + out->regionlist = (REAL *) NULL; + } + } + } + if (b->edgesout) { + writeedges(&m, b, &out->edgelist, &out->edgemarkerlist); + } + if (b->voronoi) { + writevoronoi(&m, b, &vorout->pointlist, &vorout->pointattributelist, &vorout->pointmarkerlist, + &vorout->edgelist, &vorout->edgemarkerlist, &vorout->normlist); + } + if (b->neighbors) { + writeneighbors(&m, b, &out->neighborlist); + } + + if (!b->quiet) { + statistics(&m, b); + } + +#ifndef REDUCED + if (b->docheck) + { + checkmesh(&m, b); + checkdelaunay(&m, b); + } +#endif /* not REDUCED */ + + triangledeinit(&m, b); +} diff --git a/vtm/jni/triangle/triangle.h b/vtm/jni/triangle/triangle.h new file mode 100644 index 00000000..4112a2e3 --- /dev/null +++ b/vtm/jni/triangle/triangle.h @@ -0,0 +1,360 @@ +/*****************************************************************************/ +/* */ +/* (triangle.h) */ +/* */ +/* Include file for programs that call Triangle. */ +/* */ +/* Accompanies Triangle Version 1.6 */ +/* July 28, 2005 */ +/* */ +/* Copyright 1996, 2005 */ +/* Jonathan Richard Shewchuk */ +/* 2360 Woolsey #H */ +/* Berkeley, California 94705-1927 */ +/* jrs@cs.berkeley.edu */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* */ +/* How to call Triangle from another program */ +/* */ +/* */ +/* If you haven't read Triangle's instructions (run "triangle -h" to read */ +/* them), you won't understand what follows. */ +/* */ +/* Triangle must be compiled into an object file (triangle.o) with the */ +/* TRILIBRARY symbol defined (generally by using the -DTRILIBRARY compiler */ +/* switch). The makefile included with Triangle will do this for you if */ +/* you run "make trilibrary". The resulting object file can be called via */ +/* the procedure triangulate(). */ +/* */ +/* If the size of the object file is important to you, you may wish to */ +/* generate a reduced version of triangle.o. The REDUCED symbol gets rid */ +/* of all features that are primarily of research interest. Specifically, */ +/* the -DREDUCED switch eliminates Triangle's -i, -F, -s, and -C switches. */ +/* The CDT_ONLY symbol gets rid of all meshing algorithms above and beyond */ +/* constrained Delaunay triangulation. Specifically, the -DCDT_ONLY switch */ +/* eliminates Triangle's -r, -q, -a, -u, -D, -Y, -S, and -s switches. */ +/* */ +/* IMPORTANT: These definitions (TRILIBRARY, REDUCED, CDT_ONLY) must be */ +/* made in the makefile or in triangle.c itself. Putting these definitions */ +/* in this file (triangle.h) will not create the desired effect. */ +/* */ +/* */ +/* The calling convention for triangulate() follows. */ +/* */ +/* void triangulate(triswitches, in, out, vorout) */ +/* char *triswitches; */ +/* struct triangulateio *in; */ +/* struct triangulateio *out; */ +/* struct triangulateio *vorout; */ +/* */ +/* `triswitches' is a string containing the command line switches you wish */ +/* to invoke. No initial dash is required. Some suggestions: */ +/* */ +/* - You'll probably find it convenient to use the `z' switch so that */ +/* points (and other items) are numbered from zero. This simplifies */ +/* indexing, because the first item of any type always starts at index */ +/* [0] of the corresponding array, whether that item's number is zero or */ +/* one. */ +/* - You'll probably want to use the `Q' (quiet) switch in your final code, */ +/* but you can take advantage of Triangle's printed output (including the */ +/* `V' switch) while debugging. */ +/* - If you are not using the `q', `a', `u', `D', `j', or `s' switches, */ +/* then the output points will be identical to the input points, except */ +/* possibly for the boundary markers. If you don't need the boundary */ +/* markers, you should use the `N' (no nodes output) switch to save */ +/* memory. (If you do need boundary markers, but need to save memory, a */ +/* good nasty trick is to set out->pointlist equal to in->pointlist */ +/* before calling triangulate(), so that Triangle overwrites the input */ +/* points with identical copies.) */ +/* - The `I' (no iteration numbers) and `g' (.off file output) switches */ +/* have no effect when Triangle is compiled with TRILIBRARY defined. */ +/* */ +/* `in', `out', and `vorout' are descriptions of the input, the output, */ +/* and the Voronoi output. If the `v' (Voronoi output) switch is not used, */ +/* `vorout' may be NULL. `in' and `out' may never be NULL. */ +/* */ +/* Certain fields of the input and output structures must be initialized, */ +/* as described below. */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* */ +/* The `triangulateio' structure. */ +/* */ +/* Used to pass data into and out of the triangulate() procedure. */ +/* */ +/* */ +/* Arrays are used to store points, triangles, markers, and so forth. In */ +/* all cases, the first item in any array is stored starting at index [0]. */ +/* However, that item is item number `1' unless the `z' switch is used, in */ +/* which case it is item number `0'. Hence, you may find it easier to */ +/* index points (and triangles in the neighbor list) if you use the `z' */ +/* switch. Unless, of course, you're calling Triangle from a Fortran */ +/* program. */ +/* */ +/* Description of fields (except the `numberof' fields, which are obvious): */ +/* */ +/* `pointlist': An array of point coordinates. The first point's x */ +/* coordinate is at index [0] and its y coordinate at index [1], followed */ +/* by the coordinates of the remaining points. Each point occupies two */ +/* REALs. */ +/* `pointattributelist': An array of point attributes. Each point's */ +/* attributes occupy `numberofpointattributes' REALs. */ +/* `pointmarkerlist': An array of point markers; one int per point. */ +/* */ +/* `trianglelist': An array of triangle corners. The first triangle's */ +/* first corner is at index [0], followed by its other two corners in */ +/* counterclockwise order, followed by any other nodes if the triangle */ +/* represents a nonlinear element. Each triangle occupies */ +/* `numberofcorners' ints. */ +/* `triangleattributelist': An array of triangle attributes. Each */ +/* triangle's attributes occupy `numberoftriangleattributes' REALs. */ +/* `trianglearealist': An array of triangle area constraints; one REAL per */ +/* triangle. Input only. */ +/* `neighborlist': An array of triangle neighbors; three ints per */ +/* triangle. Output only. */ +/* */ +/* `segmentlist': An array of segment endpoints. The first segment's */ +/* endpoints are at indices [0] and [1], followed by the remaining */ +/* segments. Two ints per segment. */ +/* `segmentmarkerlist': An array of segment markers; one int per segment. */ +/* */ +/* `holelist': An array of holes. The first hole's x and y coordinates */ +/* are at indices [0] and [1], followed by the remaining holes. Two */ +/* REALs per hole. Input only, although the pointer is copied to the */ +/* output structure for your convenience. */ +/* */ +/* `regionlist': An array of regional attributes and area constraints. */ +/* The first constraint's x and y coordinates are at indices [0] and [1], */ +/* followed by the regional attribute at index [2], followed by the */ +/* maximum area at index [3], followed by the remaining area constraints. */ +/* Four REALs per area constraint. Note that each regional attribute is */ +/* used only if you select the `A' switch, and each area constraint is */ +/* used only if you select the `a' switch (with no number following), but */ +/* omitting one of these switches does not change the memory layout. */ +/* Input only, although the pointer is copied to the output structure for */ +/* your convenience. */ +/* */ +/* `edgelist': An array of edge endpoints. The first edge's endpoints are */ +/* at indices [0] and [1], followed by the remaining edges. Two ints per */ +/* edge. Output only. */ +/* `edgemarkerlist': An array of edge markers; one int per edge. Output */ +/* only. */ +/* `normlist': An array of normal vectors, used for infinite rays in */ +/* Voronoi diagrams. The first normal vector's x and y magnitudes are */ +/* at indices [0] and [1], followed by the remaining vectors. For each */ +/* finite edge in a Voronoi diagram, the normal vector written is the */ +/* zero vector. Two REALs per edge. Output only. */ +/* */ +/* */ +/* Any input fields that Triangle will examine must be initialized. */ +/* Furthermore, for each output array that Triangle will write to, you */ +/* must either provide space by setting the appropriate pointer to point */ +/* to the space you want the data written to, or you must initialize the */ +/* pointer to NULL, which tells Triangle to allocate space for the results. */ +/* The latter option is preferable, because Triangle always knows exactly */ +/* how much space to allocate. The former option is provided mainly for */ +/* people who need to call Triangle from Fortran code, though it also makes */ +/* possible some nasty space-saving tricks, like writing the output to the */ +/* same arrays as the input. */ +/* */ +/* Triangle will not free() any input or output arrays, including those it */ +/* allocates itself; that's up to you. You should free arrays allocated by */ +/* Triangle by calling the trifree() procedure defined below. (By default, */ +/* trifree() just calls the standard free() library procedure, but */ +/* applications that call triangulate() may replace trimalloc() and */ +/* trifree() in triangle.c to use specialized memory allocators.) */ +/* */ +/* Here's a guide to help you decide which fields you must initialize */ +/* before you call triangulate(). */ +/* */ +/* `in': */ +/* */ +/* - `pointlist' must always point to a list of points; `numberofpoints' */ +/* and `numberofpointattributes' must be properly set. */ +/* `pointmarkerlist' must either be set to NULL (in which case all */ +/* markers default to zero), or must point to a list of markers. If */ +/* `numberofpointattributes' is not zero, `pointattributelist' must */ +/* point to a list of point attributes. */ +/* - If the `r' switch is used, `trianglelist' must point to a list of */ +/* triangles, and `numberoftriangles', `numberofcorners', and */ +/* `numberoftriangleattributes' must be properly set. If */ +/* `numberoftriangleattributes' is not zero, `triangleattributelist' */ +/* must point to a list of triangle attributes. If the `a' switch is */ +/* used (with no number following), `trianglearealist' must point to a */ +/* list of triangle area constraints. `neighborlist' may be ignored. */ +/* - If the `p' switch is used, `segmentlist' must point to a list of */ +/* segments, `numberofsegments' must be properly set, and */ +/* `segmentmarkerlist' must either be set to NULL (in which case all */ +/* markers default to zero), or must point to a list of markers. */ +/* - If the `p' switch is used without the `r' switch, then */ +/* `numberofholes' and `numberofregions' must be properly set. If */ +/* `numberofholes' is not zero, `holelist' must point to a list of */ +/* holes. If `numberofregions' is not zero, `regionlist' must point to */ +/* a list of region constraints. */ +/* - If the `p' switch is used, `holelist', `numberofholes', */ +/* `regionlist', and `numberofregions' is copied to `out'. (You can */ +/* nonetheless get away with not initializing them if the `r' switch is */ +/* used.) */ +/* - `edgelist', `edgemarkerlist', `normlist', and `numberofedges' may be */ +/* ignored. */ +/* */ +/* `out': */ +/* */ +/* - `pointlist' must be initialized (NULL or pointing to memory) unless */ +/* the `N' switch is used. `pointmarkerlist' must be initialized */ +/* unless the `N' or `B' switch is used. If `N' is not used and */ +/* `in->numberofpointattributes' is not zero, `pointattributelist' must */ +/* be initialized. */ +/* - `trianglelist' must be initialized unless the `E' switch is used. */ +/* `neighborlist' must be initialized if the `n' switch is used. If */ +/* the `E' switch is not used and (`in->numberofelementattributes' is */ +/* not zero or the `A' switch is used), `elementattributelist' must be */ +/* initialized. `trianglearealist' may be ignored. */ +/* - `segmentlist' must be initialized if the `p' or `c' switch is used, */ +/* and the `P' switch is not used. `segmentmarkerlist' must also be */ +/* initialized under these circumstances unless the `B' switch is used. */ +/* - `edgelist' must be initialized if the `e' switch is used. */ +/* `edgemarkerlist' must be initialized if the `e' switch is used and */ +/* the `B' switch is not. */ +/* - `holelist', `regionlist', `normlist', and all scalars may be ignored.*/ +/* */ +/* `vorout' (only needed if `v' switch is used): */ +/* */ +/* - `pointlist' must be initialized. If `in->numberofpointattributes' */ +/* is not zero, `pointattributelist' must be initialized. */ +/* `pointmarkerlist' may be ignored. */ +/* - `edgelist' and `normlist' must both be initialized. */ +/* `edgemarkerlist' may be ignored. */ +/* - Everything else may be ignored. */ +/* */ +/* After a call to triangulate(), the valid fields of `out' and `vorout' */ +/* will depend, in an obvious way, on the choice of switches used. Note */ +/* that when the `p' switch is used, the pointers `holelist' and */ +/* `regionlist' are copied from `in' to `out', but no new space is */ +/* allocated; be careful that you don't free() the same array twice. On */ +/* the other hand, Triangle will never copy the `pointlist' pointer (or any */ +/* others); new space is allocated for `out->pointlist', or if the `N' */ +/* switch is used, `out->pointlist' remains uninitialized. */ +/* */ +/* All of the meaningful `numberof' fields will be properly set; for */ +/* instance, `numberofedges' will represent the number of edges in the */ +/* triangulation whether or not the edges were written. If segments are */ +/* not used, `numberofsegments' will indicate the number of boundary edges. */ +/* */ +/*****************************************************************************/ + +#define SINGLE + +#ifdef SINGLE +#define REAL float +#else /* not SINGLE */ +#define REAL double +#endif /* not SINGLE */ + +#define INDICE unsigned short + +typedef struct triangulateio TriangleIO; + +struct triangulateio { + REAL *pointlist; /* In / out */ + REAL *pointattributelist; /* In / out */ + int *pointmarkerlist; /* In / out */ + int numberofpoints; /* In / out */ + int numberofpointattributes; /* In / out */ + + INDICE *trianglelist; /* In / out */ + REAL *triangleattributelist; /* In / out */ + REAL *trianglearealist; /* In only */ + int *neighborlist; /* Out only */ + int numberoftriangles; /* In / out */ + int numberofcorners; /* In / out */ + int numberoftriangleattributes; /* In / out */ + + int *segmentlist; /* In / out */ + int *segmentmarkerlist; /* In / out */ + int numberofsegments; /* In / out */ + + REAL *holelist; /* In / pointer to array copied out */ + int numberofholes; /* In / copied out */ + + REAL *regionlist; /* In / pointer to array copied out */ + int numberofregions; /* In / copied out */ + + int *edgelist; /* Out only */ + int *edgemarkerlist; /* Not used with Voronoi diagram; out only */ + REAL *normlist; /* Used only with Voronoi diagram; out only */ + int numberofedges; /* Out only */ +}; + +/* Data structure for command line switches and file names. This structure + * is used (instead of global variables) to allow reentrancy. + + * Switches for the triangulator. + * poly: -p switch. + * refine: -r switch. + * quality: -q switch. + * minangle: minimum angle bound, specified after -q switch. + * goodangle: cosine squared of minangle. + * offconstant: constant used to place off-center Steiner points. + * vararea: -a switch without number. + * fixedarea: -a switch with number. + * maxarea: maximum area bound, specified after -a switch. + * usertest: -u switch. + * regionattrib: -A switch. + * convex: -c switch. + * weighted: 1 for -w switch, 2 for -W switch. + * jettison: -j switch + * firstnumber: inverse of -z switch. All items are numbered starting + * from `firstnumber'. + * edgesout: -e switch. + * voronoi: -v switch. + * neighbors: -n switch. + * geomview: -g switch. + * nobound: -B switch. + * nopolywritten: -P switch. + * nonodewritten: -N switch. + * noelewritten: -E switch. + * noiterationnum: -I switch. + * noholes: -O switch. + * noexact: -X switch. + * order: element order, specified after -o switch. + * nobisect: count of how often -Y switch is selected. + * steiner: maximum number of Steiner points, specified after -S switch. + * incremental: -i switch. sweepline: -F switch. + * dwyer: inverse of -l switch. + * splitseg: -s switch. + * conformdel: -D switch. docheck: -C switch. + * quiet: -Q switch. verbose: count of how often -V switch is selected. + * usesegments: -p, -r, -q, or -c switch; determines whether segments are + * used at all. + * + * Read the instructions to find out the meaning of these switches. */ + +typedef struct behavior TriangleOptions; + +struct behavior { + int poly, refine, quality, vararea, fixedarea, usertest; + int regionattrib, convex, weighted, jettison; + int firstnumber; + int edgesout, voronoi, neighbors, geomview; + int nobound, nopolywritten, nonodewritten, noelewritten, noiterationnum; + int noholes, noexact, conformdel; + int incremental, sweepline, dwyer; + int splitseg; + int docheck; + int quiet, verbose; + int usesegments; + int order; + int nobisect; + int steiner;REAL minangle, goodangle, offconstant;REAL maxarea; + +}; +void parsecommandline(int argc, char **argv, struct behavior *b); +void triangulate(struct behavior *, struct triangulateio *, struct triangulateio *, + struct triangulateio *); + diff --git a/vtm/jni/triangle/triangle_dbg.c b/vtm/jni/triangle/triangle_dbg.c new file mode 100644 index 00000000..f6374ba3 --- /dev/null +++ b/vtm/jni/triangle/triangle_dbg.c @@ -0,0 +1,441 @@ +#include "triangle_private.h" + +/*****************************************************************************/ +/* */ +/* quality_statistics() Print statistics about the quality of the mesh. */ +/* */ +/*****************************************************************************/ + +void quality_statistics(struct mesh *m, struct behavior *b) { + struct otri triangleloop; + vertex p[3]; + REAL cossquaretable[8]; + REAL ratiotable[16]; + REAL dx[3], dy[3]; + REAL edgelength[3]; + REAL dotproduct; + REAL cossquare; + REAL triarea; + REAL shortest, longest; + REAL trilongest2; + REAL smallestarea, biggestarea; + REAL triminaltitude2; + REAL minaltitude; + REAL triaspect2; + REAL worstaspect; + REAL smallestangle, biggestangle; + REAL radconst, degconst; + int angletable[18]; + int aspecttable[16]; + int aspectindex; + int tendegree; + int acutebiggest; + int i, ii, j, k; + + printf("Mesh quality statistics:\n\n"); + radconst = PI / 18.0; + degconst = 180.0 / PI; + for (i = 0; i < 8; i++) { + cossquaretable[i] = cos(radconst * (REAL) (i + 1)); + cossquaretable[i] = cossquaretable[i] * cossquaretable[i]; + } + for (i = 0; i < 18; i++) { + angletable[i] = 0; + } + + ratiotable[0] = 1.5; + ratiotable[1] = 2.0; + ratiotable[2] = 2.5; + ratiotable[3] = 3.0; + ratiotable[4] = 4.0; + ratiotable[5] = 6.0; + ratiotable[6] = 10.0; + ratiotable[7] = 15.0; + ratiotable[8] = 25.0; + ratiotable[9] = 50.0; + ratiotable[10] = 100.0; + ratiotable[11] = 300.0; + ratiotable[12] = 1000.0; + ratiotable[13] = 10000.0; + ratiotable[14] = 100000.0; + ratiotable[15] = 0.0; + for (i = 0; i < 16; i++) { + aspecttable[i] = 0; + } + + worstaspect = 0.0; + minaltitude = m->xmax - m->xmin + m->ymax - m->ymin; + minaltitude = minaltitude * minaltitude; + shortest = minaltitude; + longest = 0.0; + smallestarea = minaltitude; + biggestarea = 0.0; + worstaspect = 0.0; + smallestangle = 0.0; + biggestangle = 2.0; + acutebiggest = 1; + + traversalinit(&m->triangles); + triangleloop.tri = triangletraverse(m); + triangleloop.orient = 0; + while (triangleloop.tri != (triangle *) NULL) { + org(triangleloop, p[0]); + dest(triangleloop, p[1]); + apex(triangleloop, p[2]); + trilongest2 = 0.0; + + for (i = 0; i < 3; i++) { + j = plus1mod3[i]; + k = minus1mod3[i]; + dx[i] = p[j][0] - p[k][0]; + dy[i] = p[j][1] - p[k][1]; + edgelength[i] = dx[i] * dx[i] + dy[i] * dy[i]; + if (edgelength[i] > trilongest2) { + trilongest2 = edgelength[i]; + } + if (edgelength[i] > longest) { + longest = edgelength[i]; + } + if (edgelength[i] < shortest) { + shortest = edgelength[i]; + } + } + + triarea = counterclockwise(m, b, p[0], p[1], p[2]); + if (triarea < smallestarea) { + smallestarea = triarea; + } + if (triarea > biggestarea) { + biggestarea = triarea; + } + triminaltitude2 = triarea * triarea / trilongest2; + if (triminaltitude2 < minaltitude) { + minaltitude = triminaltitude2; + } + triaspect2 = trilongest2 / triminaltitude2; + if (triaspect2 > worstaspect) { + worstaspect = triaspect2; + } + aspectindex = 0; + while ((triaspect2 > ratiotable[aspectindex] * ratiotable[aspectindex]) && (aspectindex < 15)) { + aspectindex++; + } + aspecttable[aspectindex]++; + + for (i = 0; i < 3; i++) { + j = plus1mod3[i]; + k = minus1mod3[i]; + dotproduct = dx[j] * dx[k] + dy[j] * dy[k]; + cossquare = dotproduct * dotproduct / (edgelength[j] * edgelength[k]); + tendegree = 8; + for (ii = 7; ii >= 0; ii--) { + if (cossquare > cossquaretable[ii]) { + tendegree = ii; + } + } + if (dotproduct <= 0.0) { + angletable[tendegree]++; + if (cossquare > smallestangle) { + smallestangle = cossquare; + } + if (acutebiggest && (cossquare < biggestangle)) { + biggestangle = cossquare; + } + } + else { + angletable[17 - tendegree]++; + if (acutebiggest || (cossquare > biggestangle)) { + biggestangle = cossquare; + acutebiggest = 0; + } + } + } + triangleloop.tri = triangletraverse(m); + } + + shortest = sqrt(shortest); + longest = sqrt(longest); + minaltitude = sqrt(minaltitude); + worstaspect = sqrt(worstaspect); + smallestarea *= 0.5; + biggestarea *= 0.5; + if (smallestangle >= 1.0) { + smallestangle = 0.0; + } + else { + smallestangle = degconst * acos(sqrt(smallestangle)); + } + if (biggestangle >= 1.0) { + biggestangle = 180.0; + } + else { + if (acutebiggest) { + biggestangle = degconst * acos(sqrt(biggestangle)); + } + else { + biggestangle = 180.0 - degconst * acos(sqrt(biggestangle)); + } + } + + printf(" Smallest area: %16.5g | Largest area: %16.5g\n", smallestarea, biggestarea); + printf(" Shortest edge: %16.5g | Longest edge: %16.5g\n", shortest, longest); + printf( + " Shortest altitude: %12.5g | Largest aspect ratio: %8.5g\n\n", minaltitude, worstaspect); + + printf(" Triangle aspect ratio histogram:\n"); + printf( + " 1.1547 - %-6.6g : %8d | %6.6g - %-6.6g : %8d\n", ratiotable[0], aspecttable[0], ratiotable[7], ratiotable[8], aspecttable[8]); + for (i = 1; i < 7; i++) { + printf( + " %6.6g - %-6.6g : %8d | %6.6g - %-6.6g : %8d\n", ratiotable[i - 1], ratiotable[i], aspecttable[i], ratiotable[i + 7], ratiotable[i + 8], aspecttable[i + 8]); + } + printf( + " %6.6g - %-6.6g : %8d | %6.6g - : %8d\n", ratiotable[6], ratiotable[7], aspecttable[7], ratiotable[14], aspecttable[15]); + printf(" (Aspect ratio is longest edge divided by shortest altitude)\n\n"); + + printf(" Smallest angle: %15.5g | Largest angle: %15.5g\n\n", smallestangle, biggestangle); + + printf(" Angle histogram:\n"); + for (i = 0; i < 9; i++) { + printf( + " %3d - %3d degrees: %8d | %3d - %3d degrees: %8d\n", i * 10, i * 10 + 10, angletable[i], i * 10 + 90, i * 10 + 100, angletable[i + 9]); + } + printf("\n"); +} + +/*****************************************************************************/ +/* */ +/* statistics() Print all sorts of cool facts. */ +/* */ +/*****************************************************************************/ + +void statistics(struct mesh *m, struct behavior *b) { + printf("\nStatistics:\n\n"); + printf(" Input vertices: %d\n", m->invertices); + if (b->refine) { + printf(" Input triangles: %d\n", m->inelements); + } + if (b->poly) { + printf(" Input segments: %d\n", m->insegments); + if (!b->refine) { + printf(" Input holes: %d\n", m->holes); + } + } + + printf("\n Mesh vertices: %ld\n", m->vertices.items - m->undeads); + printf(" Mesh triangles: %ld\n", m->triangles.items); + printf(" Mesh edges: %ld\n", m->edges); + printf(" Mesh exterior boundary edges: %ld\n", m->hullsize); + if (b->poly || b->refine) { + printf(" Mesh interior boundary edges: %ld\n", m->subsegs.items - m->hullsize); + printf(" Mesh subsegments (constrained edges): %ld\n", m->subsegs.items); + } + printf("\n"); + + if (b->verbose) { + quality_statistics(m, b); + printf("Memory allocation statistics:\n\n"); + printf(" Maximum number of vertices: %ld\n", m->vertices.maxitems); + printf(" Maximum number of triangles: %ld\n", m->triangles.maxitems); + if (m->subsegs.maxitems > 0) { + printf(" Maximum number of subsegments: %ld\n", m->subsegs.maxitems); + } + if (m->viri.maxitems > 0) { + printf(" Maximum number of viri: %ld\n", m->viri.maxitems); + } + if (m->badsubsegs.maxitems > 0) { + printf(" Maximum number of encroached subsegments: %ld\n", m->badsubsegs.maxitems); + } + if (m->badtriangles.maxitems > 0) { + printf(" Maximum number of bad triangles: %ld\n", m->badtriangles.maxitems); + } + if (m->flipstackers.maxitems > 0) { + printf(" Maximum number of stacked triangle flips: %ld\n", m->flipstackers.maxitems); + } + if (m->splaynodes.maxitems > 0) { + printf(" Maximum number of splay tree nodes: %ld\n", m->splaynodes.maxitems); + } + printf( + " Approximate heap memory use (bytes): %ld\n\n", m->vertices.maxitems * m->vertices.itembytes + m->triangles.maxitems * m->triangles.itembytes + m->subsegs.maxitems * m->subsegs.itembytes + m->viri.maxitems * m->viri.itembytes + m->badsubsegs.maxitems * m->badsubsegs.itembytes + m->badtriangles.maxitems * m->badtriangles.itembytes + m->flipstackers.maxitems * m->flipstackers.itembytes + m->splaynodes.maxitems * m->splaynodes.itembytes); + + printf("Algorithmic statistics:\n\n"); + if (!b->weighted) { + printf(" Number of incircle tests: %ld\n", m->incirclecount); + } + else { + printf(" Number of 3D orientation tests: %ld\n", m->orient3dcount); + } + printf(" Number of 2D orientation tests: %ld\n", m->counterclockcount); + if (m->hyperbolacount > 0) { + printf(" Number of right-of-hyperbola tests: %ld\n", m->hyperbolacount); + } + if (m->circletopcount > 0) { + printf(" Number of circle top computations: %ld\n", m->circletopcount); + } + if (m->circumcentercount > 0) { + printf(" Number of triangle circumcenter computations: %ld\n", m->circumcentercount); + } + printf("\n"); + } +} + +/********* Debugging routines begin here *********/ +/** **/ +/** **/ + +/*****************************************************************************/ +/* */ +/* printtriangle() Print out the details of an oriented triangle. */ +/* */ +/* I originally wrote this procedure to simplify debugging; it can be */ +/* called directly from the debugger, and presents information about an */ +/* oriented triangle in digestible form. It's also used when the */ +/* highest level of verbosity (`-VVV') is specified. */ +/* */ +/*****************************************************************************/ + +void printtriangle(struct mesh *m, struct behavior *b, struct otri *t) { + struct otri printtri; + struct osub printsh; + vertex printvertex; + + printf("triangle x%lx with orientation %d:\n", (unsigned long) t->tri, t->orient); + decode(t->tri[0], printtri); + if (printtri.tri == m->dummytri) { + printf(" [0] = Outer space\n"); + } + else { + printf(" [0] = x%lx %d\n", (unsigned long) printtri.tri, printtri.orient); + } + decode(t->tri[1], printtri); + if (printtri.tri == m->dummytri) { + printf(" [1] = Outer space\n"); + } + else { + printf(" [1] = x%lx %d\n", (unsigned long) printtri.tri, printtri.orient); + } + decode(t->tri[2], printtri); + if (printtri.tri == m->dummytri) { + printf(" [2] = Outer space\n"); + } + else { + printf(" [2] = x%lx %d\n", (unsigned long) printtri.tri, printtri.orient); + } + + org(*t, printvertex); + if (printvertex == (vertex) NULL) + printf(" Origin[%d] = NULL\n", (t->orient + 1) % 3 + 3); + else + printf( + " Origin[%d] = x%lx (%.12g, %.12g)\n", (t->orient + 1) % 3 + 3, (unsigned long) printvertex, printvertex[0], printvertex[1]); + dest(*t, printvertex); + if (printvertex == (vertex) NULL) + printf(" Dest [%d] = NULL\n", (t->orient + 2) % 3 + 3); + else + printf( + " Dest [%d] = x%lx (%.12g, %.12g)\n", (t->orient + 2) % 3 + 3, (unsigned long) printvertex, printvertex[0], printvertex[1]); + apex(*t, printvertex); + if (printvertex == (vertex) NULL) + printf(" Apex [%d] = NULL\n", t->orient + 3); + else + printf( + " Apex [%d] = x%lx (%.12g, %.12g)\n", t->orient + 3, (unsigned long) printvertex, printvertex[0], printvertex[1]); + + if (b->usesegments) { + sdecode(t->tri[6], printsh); + if (printsh.ss != m->dummysub) { + printf(" [6] = x%lx %d\n", (unsigned long) printsh.ss, printsh.ssorient); + } + sdecode(t->tri[7], printsh); + if (printsh.ss != m->dummysub) { + printf(" [7] = x%lx %d\n", (unsigned long) printsh.ss, printsh.ssorient); + } + sdecode(t->tri[8], printsh); + if (printsh.ss != m->dummysub) { + printf(" [8] = x%lx %d\n", (unsigned long) printsh.ss, printsh.ssorient); + } + } + + if (b->vararea) { + printf(" Area constraint: %.4g\n", areabound(*t)); + } +} + +/*****************************************************************************/ +/* */ +/* printsubseg() Print out the details of an oriented subsegment. */ +/* */ +/* I originally wrote this procedure to simplify debugging; it can be */ +/* called directly from the debugger, and presents information about an */ +/* oriented subsegment in digestible form. It's also used when the highest */ +/* level of verbosity (`-VVV') is specified. */ +/* */ +/*****************************************************************************/ + +void printsubseg(struct mesh *m, struct behavior *b, struct osub *s) { + struct osub printsh; + struct otri printtri; + vertex printvertex; + + printf( + "subsegment x%lx with orientation %d and mark %d:\n", (unsigned long) s->ss, s->ssorient, mark(*s)); + sdecode(s->ss[0], printsh); + if (printsh.ss == m->dummysub) { + printf(" [0] = No subsegment\n"); + } + else { + printf(" [0] = x%lx %d\n", (unsigned long) printsh.ss, printsh.ssorient); + } + sdecode(s->ss[1], printsh); + if (printsh.ss == m->dummysub) { + printf(" [1] = No subsegment\n"); + } + else { + printf(" [1] = x%lx %d\n", (unsigned long) printsh.ss, printsh.ssorient); + } + + sorg(*s, printvertex); + if (printvertex == (vertex) NULL) + printf(" Origin[%d] = NULL\n", 2 + s->ssorient); + else + printf( + " Origin[%d] = x%lx (%.12g, %.12g)\n", 2 + s->ssorient, (unsigned long) printvertex, printvertex[0], printvertex[1]); + sdest(*s, printvertex); + if (printvertex == (vertex) NULL) + printf(" Dest [%d] = NULL\n", 3 - s->ssorient); + else + printf( + " Dest [%d] = x%lx (%.12g, %.12g)\n", 3 - s->ssorient, (unsigned long) printvertex, printvertex[0], printvertex[1]); + + decode(s->ss[6], printtri); + if (printtri.tri == m->dummytri) { + printf(" [6] = Outer space\n"); + } + else { + printf(" [6] = x%lx %d\n", (unsigned long) printtri.tri, printtri.orient); + } + decode(s->ss[7], printtri); + if (printtri.tri == m->dummytri) { + printf(" [7] = Outer space\n"); + } + else { + printf(" [7] = x%lx %d\n", (unsigned long) printtri.tri, printtri.orient); + } + + segorg(*s, printvertex); + if (printvertex == (vertex) NULL) + printf(" Segment origin[%d] = NULL\n", 4 + s->ssorient); + else + printf( + " Segment origin[%d] = x%lx (%.12g, %.12g)\n", 4 + s->ssorient, (unsigned long) printvertex, printvertex[0], printvertex[1]); + segdest(*s, printvertex); + if (printvertex == (vertex) NULL) + printf(" Segment dest [%d] = NULL\n", 5 - s->ssorient); + else + printf( + " Segment dest [%d] = x%lx (%.12g, %.12g)\n", 5 - s->ssorient, (unsigned long) printvertex, printvertex[0], printvertex[1]); +} + +/** **/ +/** **/ +/********* Debugging routines end here *********/ diff --git a/vtm/jni/triangle/triangle_private.h b/vtm/jni/triangle/triangle_private.h new file mode 100644 index 00000000..22d1667e --- /dev/null +++ b/vtm/jni/triangle/triangle_private.h @@ -0,0 +1,1070 @@ +/* For single precision (which will save some memory and reduce paging), */ +/* define the symbol SINGLE by using the -DSINGLE compiler switch or by */ +/* writing "#define SINGLE" below. */ +/* */ +/* For double precision (which will allow you to refine meshes to a smaller */ +/* edge length), leave SINGLE undefined. */ +/* */ +/* Double precision uses more memory, but improves the resolution of the */ +/* meshes you can generate with Triangle. It also reduces the likelihood */ +/* of a floating exception due to overflow. Finally, it is much faster */ +/* than single precision on 64-bit architectures like the DEC Alpha. I */ +/* recommend double precision unless you want to generate a mesh for which */ +/* you do not have enough memory. */ + +/* #define SINGLE */ + +/* #ifdef SINGLE */ +/* #define REAL float */ +/* #else /\* not SINGLE *\/ */ +/* #define REAL double */ +/* #endif /\* not SINGLE *\/ */ + +/* If yours is not a Unix system, define the NO_TIMER compiler switch to */ +/* remove the Unix-specific timing code. */ + +/* #define NO_TIMER */ + +/* To insert lots of self-checks for internal errors, define the SELF_CHECK */ +/* symbol. This will slow down the program significantly. It is best to */ +/* define the symbol using the -DSELF_CHECK compiler switch, but you could */ +/* write "#define SELF_CHECK" below. If you are modifying this code, I */ +/* recommend you turn self-checks on until your work is debugged. */ + +/* #define SELF_CHECK */ + +/* To compile Triangle as a callable object library (triangle.o), define the */ +/* TRILIBRARY symbol. Read the file triangle.h for details on how to call */ +/* the procedure triangulate() that results. */ + +/* #define TRILIBRARY */ + +/* It is possible to generate a smaller version of Triangle using one or */ +/* both of the following symbols. Define the REDUCED symbol to eliminate */ +/* all features that are primarily of research interest; specifically, the */ +/* -i, -F, -s, and -C switches. Define the CDT_ONLY symbol to eliminate */ +/* all meshing algorithms above and beyond constrained Delaunay */ +/* triangulation; specifically, the -r, -q, -a, -u, -D, -S, and -s */ +/* switches. These reductions are most likely to be useful when */ +/* generating an object library (triangle.o) by defining the TRILIBRARY */ +/* symbol. */ + +/* #define REDUCED */ +/* #define CDT_ONLY */ + +/* On some machines, my exact arithmetic routines might be defeated by the */ +/* use of internal extended precision floating-point registers. The best */ +/* way to solve this problem is to set the floating-point registers to use */ +/* single or double precision internally. On 80x86 processors, this may */ +/* be accomplished by setting the CPU86 symbol for the Microsoft C */ +/* compiler, or the LINUX symbol for the gcc compiler running on Linux. */ +/* */ +/* An inferior solution is to declare certain values as `volatile', thus */ +/* forcing them to be stored to memory and rounded off. Unfortunately, */ +/* this solution might slow Triangle down quite a bit. To use volatile */ +/* values, write "#define volatile" below. Normally, however, */ +/* should be defined to be nothing. ("#define ".) */ +/* */ +/* For more discussion, see http://www.cs.cmu.edu/~quake/robust.pc.html . */ +/* For yet more discussion, see Section 5 of my paper, "Adaptive Precision */ +/* Floating-Point Arithmetic and Fast Robust Geometric Predicates" (also */ +/* available as Section 6.6 of my dissertation). */ + +/* #define CPU86 */ +/* #define LINUX */ + +//#define /* Nothing */ +/* #define volatile */ + +/* Maximum number of characters in a file name (including the null). */ + +#define FILENAMESIZE 2048 + +/* Maximum number of characters in a line read from a file (including the */ +/* null). */ + +#define INPUTLINESIZE 1024 + +/* For efficiency, a variety of data structures are allocated in bulk. The */ +/* following constants determine how many of each structure is allocated */ +/* at once. */ + +#define TRIPERBLOCK 4092 /* Number of triangles allocated at once. */ +#define SUBSEGPERBLOCK 508 /* Number of subsegments allocated at once. */ +#define VERTEXPERBLOCK 4092 /* Number of vertices allocated at once. */ +#define VIRUSPERBLOCK 1020 /* Number of virus triangles allocated at once. */ +/* Number of encroached subsegments allocated at once. */ +#define BADSUBSEGPERBLOCK 252 +/* Number of skinny triangles allocated at once. */ +#define BADTRIPERBLOCK 4092 +/* Number of flipped triangles allocated at once. */ +#define FLIPSTACKERPERBLOCK 252 +/* Number of splay tree nodes allocated at once. */ +#define SPLAYNODEPERBLOCK 508 + +/* The vertex types. A DEADVERTEX has been deleted entirely. An */ +/* UNDEADVERTEX is not part of the mesh, but is written to the output */ +/* .node file and affects the node indexing in the other output files. */ + +#define INPUTVERTEX 0 +#define SEGMENTVERTEX 1 +#define FREEVERTEX 2 +#define DEADVERTEX -32768 +#define UNDEADVERTEX -32767 + +/* The next line is used to outsmart some very stupid compilers. If your */ +/* compiler is smarter, feel free to replace the "int" with "void". */ +/* Not that it matters. */ + +//#define VOID int +#define VOID void + +/* Two constants for algorithms based on random sampling. Both constants */ +/* have been chosen empirically to optimize their respective algorithms. */ + +/* Used for the point location scheme of Mucke, Saias, and Zhu, to decide */ +/* how large a random sample of triangles to inspect. */ + +#define SAMPLEFACTOR 11 + +/* Used in Fortune's sweepline Delaunay algorithm to determine what fraction */ +/* of boundary edges should be maintained in the splay tree for point */ +/* location on the front. */ + +/* A number that speaks for itself, every kissable digit. */ + +#define PI 3.141592653589793238462643383279502884197169399375105820974944592308 + +/* Another fave. */ + +#define SQUAREROOTTWO 1.4142135623730950488016887242096980785696718753769480732 + +/* And here's one for those of you who are intimidated by math. */ + +#define ONETHIRD 0.333333333333333333333333333333333333333333333333333333333333 + +#include +#include +#include +#include +#ifndef NO_TIMER +#include +#endif /* not NO_TIMER */ +#ifdef CPU86 +#include +#endif /* CPU86 */ +#ifdef LINUX +#include +#endif /* LINUX */ +#include "triangle.h" + +#ifdef __ANDROID__ +#include +#define printf(...) __android_log_print(ANDROID_LOG_DEBUG, "Triangle", __VA_ARGS__) +#endif + + +/* A few forward declarations. */ + +/* Labels that signify the result of point location. The result of a */ +/* search indicates that the point falls in the interior of a triangle, on */ +/* an edge, on a vertex, or outside the mesh. */ + +enum locateresult { + INTRIANGLE, ONEDGE, ONVERTEX, OUTSIDE +}; + +/* Labels that signify the result of vertex insertion. The result indicates */ +/* that the vertex was inserted with complete success, was inserted but */ +/* encroaches upon a subsegment, was not inserted because it lies on a */ +/* segment, or was not inserted because another vertex occupies the same */ +/* location. */ + +enum insertvertexresult { + SUCCESSFULVERTEX, ENCROACHINGVERTEX, VIOLATINGVERTEX, DUPLICATEVERTEX +}; + +/* Labels that signify the result of direction finding. The result */ +/* indicates that a segment connecting the two query points falls within */ +/* the direction triangle, along the left edge of the direction triangle, */ +/* or along the right edge of the direction triangle. */ + +enum finddirectionresult { + WITHIN, LEFTCOLLINEAR, RIGHTCOLLINEAR +}; +/*****************************************************************************/ +/* */ +/* The basic mesh data structures */ +/* */ +/* There are three: vertices, triangles, and subsegments (abbreviated */ +/* `subseg'). These three data structures, linked by pointers, comprise */ +/* the mesh. A vertex simply represents a mesh vertex and its properties. */ +/* A triangle is a triangle. A subsegment is a special data structure used */ +/* to represent an impenetrable edge of the mesh (perhaps on the outer */ +/* boundary, on the boundary of a hole, or part of an internal boundary */ +/* separating two triangulated regions). Subsegments represent boundaries, */ +/* defined by the user, that triangles may not lie across. */ +/* */ +/* A triangle consists of a list of three vertices, a list of three */ +/* adjoining triangles, a list of three adjoining subsegments (when */ +/* segments exist), an arbitrary number of optional user-defined */ +/* floating-point attributes, and an optional area constraint. The latter */ +/* is an upper bound on the permissible area of each triangle in a region, */ +/* used for mesh refinement. */ +/* */ +/* For a triangle on a boundary of the mesh, some or all of the neighboring */ +/* triangles may not be present. For a triangle in the interior of the */ +/* mesh, often no neighboring subsegments are present. Such absent */ +/* triangles and subsegments are never represented by NULL pointers; they */ +/* are represented by two special records: `dummytri', the triangle that */ +/* fills "outer space", and `dummysub', the omnipresent subsegment. */ +/* `dummytri' and `dummysub' are used for several reasons; for instance, */ +/* they can be dereferenced and their contents examined without violating */ +/* protected memory. */ +/* */ +/* However, it is important to understand that a triangle includes other */ +/* information as well. The pointers to adjoining vertices, triangles, and */ +/* subsegments are ordered in a way that indicates their geometric relation */ +/* to each other. Furthermore, each of these pointers contains orientation */ +/* information. Each pointer to an adjoining triangle indicates which face */ +/* of that triangle is contacted. Similarly, each pointer to an adjoining */ +/* subsegment indicates which side of that subsegment is contacted, and how */ +/* the subsegment is oriented relative to the triangle. */ +/* */ +/* The data structure representing a subsegment may be thought to be */ +/* abutting the edge of one or two triangle data structures: either */ +/* sandwiched between two triangles, or resting against one triangle on an */ +/* exterior boundary or hole boundary. */ +/* */ +/* A subsegment consists of a list of four vertices--the vertices of the */ +/* subsegment, and the vertices of the segment it is a part of--a list of */ +/* two adjoining subsegments, and a list of two adjoining triangles. One */ +/* of the two adjoining triangles may not be present (though there should */ +/* always be one), and neighboring subsegments might not be present. */ +/* Subsegments also store a user-defined integer "boundary marker". */ +/* Typically, this integer is used to indicate what boundary conditions are */ +/* to be applied at that location in a finite element simulation. */ +/* */ +/* Like triangles, subsegments maintain information about the relative */ +/* orientation of neighboring objects. */ +/* */ +/* Vertices are relatively simple. A vertex is a list of floating-point */ +/* numbers, starting with the x, and y coordinates, followed by an */ +/* arbitrary number of optional user-defined floating-point attributes, */ +/* followed by an integer boundary marker. During the segment insertion */ +/* phase, there is also a pointer from each vertex to a triangle that may */ +/* contain it. Each pointer is not always correct, but when one is, it */ +/* speeds up segment insertion. These pointers are assigned values once */ +/* at the beginning of the segment insertion phase, and are not used or */ +/* updated except during this phase. Edge flipping during segment */ +/* insertion will render some of them incorrect. Hence, don't rely upon */ +/* them for anything. */ +/* */ +/* Other than the exception mentioned above, vertices have no information */ +/* about what triangles, subfacets, or subsegments they are linked to. */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* */ +/* Handles */ +/* */ +/* The oriented triangle (`otri') and oriented subsegment (`osub') data */ +/* structures defined below do not themselves store any part of the mesh. */ +/* The mesh itself is made of `triangle's, `subseg's, and `vertex's. */ +/* */ +/* Oriented triangles and oriented subsegments will usually be referred to */ +/* as "handles." A handle is essentially a pointer into the mesh; it */ +/* allows you to "hold" one particular part of the mesh. Handles are used */ +/* to specify the regions in which one is traversing and modifying the mesh.*/ +/* A single `triangle' may be held by many handles, or none at all. (The */ +/* latter case is not a memory leak, because the triangle is still */ +/* connected to other triangles in the mesh.) */ +/* */ +/* An `otri' is a handle that holds a triangle. It holds a specific edge */ +/* of the triangle. An `osub' is a handle that holds a subsegment. It */ +/* holds either the left or right side of the subsegment. */ +/* */ +/* Navigation about the mesh is accomplished through a set of mesh */ +/* manipulation primitives, further below. Many of these primitives take */ +/* a handle and produce a new handle that holds the mesh near the first */ +/* handle. Other primitives take two handles and glue the corresponding */ +/* parts of the mesh together. The orientation of the handles is */ +/* important. For instance, when two triangles are glued together by the */ +/* bond() primitive, they are glued at the edges on which the handles lie. */ +/* */ +/* Because vertices have no information about which triangles they are */ +/* attached to, I commonly represent a vertex by use of a handle whose */ +/* origin is the vertex. A single handle can simultaneously represent a */ +/* triangle, an edge, and a vertex. */ +/* */ +/*****************************************************************************/ + +/* The triangle data structure. Each triangle contains three pointers to */ +/* adjoining triangles, plus three pointers to vertices, plus three */ +/* pointers to subsegments (declared below; these pointers are usually */ +/* `dummysub'). It may or may not also contain user-defined attributes */ +/* and/or a floating-point "area constraint." It may also contain extra */ +/* pointers for nodes, when the user asks for high-order elements. */ +/* Because the size and structure of a `triangle' is not decided until */ +/* runtime, I haven't simply declared the type `triangle' as a struct. */ + +typedef REAL **triangle; /* Really: typedef triangle *triangle */ + +/* An oriented triangle: includes a pointer to a triangle and orientation. */ +/* The orientation denotes an edge of the triangle. Hence, there are */ +/* three possible orientations. By convention, each edge always points */ +/* counterclockwise about the corresponding triangle. */ + +struct otri { + triangle *tri; + int orient; /* Ranges from 0 to 2. */ +}; + +/* The subsegment data structure. Each subsegment contains two pointers to */ +/* adjoining subsegments, plus four pointers to vertices, plus two */ +/* pointers to adjoining triangles, plus one boundary marker, plus one */ +/* segment number. */ + +typedef REAL **subseg; /* Really: typedef subseg *subseg */ + +/* An oriented subsegment: includes a pointer to a subsegment and an */ +/* orientation. The orientation denotes a side of the edge. Hence, there */ +/* are two possible orientations. By convention, the edge is always */ +/* directed so that the "side" denoted is the right side of the edge. */ + +struct osub { + subseg *ss; + int ssorient; /* Ranges from 0 to 1. */ +}; + +/* The vertex data structure. Each vertex is actually an array of REALs. */ +/* The number of REALs is unknown until runtime. An integer boundary */ +/* marker, and sometimes a pointer to a triangle, is appended after the */ +/* REALs. */ + +typedef REAL *vertex; + +/* A queue used to store encroached subsegments. Each subsegment's vertices */ +/* are stored so that we can check whether a subsegment is still the same. */ + +struct badsubseg { + subseg encsubseg; /* An encroached subsegment. */ + vertex subsegorg, subsegdest; /* Its two vertices. */ +}; + +/* A queue used to store bad triangles. The key is the square of the cosine */ +/* of the smallest angle of the triangle. Each triangle's vertices are */ +/* stored so that one can check whether a triangle is still the same. */ + +struct badtriang { + triangle poortri; /* A skinny or too-large triangle. */ + REAL key; /* cos^2 of smallest (apical) angle. */ + vertex triangorg, triangdest, triangapex; /* Its three vertices. */ + struct badtriang *nexttriang; /* Pointer to next bad triangle. */ +}; + +/* A stack of triangles flipped during the most recent vertex insertion. */ +/* The stack is used to undo the vertex insertion if the vertex encroaches */ +/* upon a subsegment. */ + +struct flipstacker { + triangle flippedtri; /* A recently flipped triangle. */ + struct flipstacker *prevflip; /* Previous flip in the stack. */ +}; + +/* A node in a heap used to store events for the sweepline Delaunay */ +/* algorithm. Nodes do not point directly to their parents or children in */ +/* the heap. Instead, each node knows its position in the heap, and can */ +/* look up its parent and children in a separate array. The `eventptr' */ +/* points either to a `vertex' or to a triangle (in encoded format, so */ +/* that an orientation is included). In the latter case, the origin of */ +/* the oriented triangle is the apex of a "circle event" of the sweepline */ +/* algorithm. To distinguish site events from circle events, all circle */ +/* events are given an invalid (smaller than `xmin') x-coordinate `xkey'. */ + +struct event { + REAL xkey, ykey; /* Coordinates of the event. */ + VOID *eventptr; /* Can be a vertex or the location of a circle event. */ + int heapposition; /* Marks this event's position in the heap. */ +}; + +/* A node in the splay tree. Each node holds an oriented ghost triangle */ +/* that represents a boundary edge of the growing triangulation. When a */ +/* circle event covers two boundary edges with a triangle, so that they */ +/* are no longer boundary edges, those edges are not immediately deleted */ +/* from the tree; rather, they are lazily deleted when they are next */ +/* encountered. (Since only a random sample of boundary edges are kept */ +/* in the tree, lazy deletion is faster.) `keydest' is used to verify */ +/* that a triangle is still the same as when it entered the splay tree; if */ +/* it has been rotated (due to a circle event), it no longer represents a */ +/* boundary edge and should be deleted. */ + +struct splaynode { + struct otri keyedge; /* Lprev of an edge on the front. */ + vertex keydest; /* Used to verify that splay node is still live. */ + struct splaynode *lchild, *rchild; /* Children in splay tree. */ +}; + +/* A type used to allocate memory. firstblock is the first block of items. */ +/* nowblock is the block from which items are currently being allocated. */ +/* nextitem points to the next slab of free memory for an item. */ +/* deaditemstack is the head of a linked list (stack) of deallocated items */ +/* that can be recycled. unallocateditems is the number of items that */ +/* remain to be allocated from nowblock. */ +/* */ +/* Traversal is the process of walking through the entire list of items, and */ +/* is separate from allocation. Note that a traversal will visit items on */ +/* the "deaditemstack" stack as well as live items. pathblock points to */ +/* the block currently being traversed. pathitem points to the next item */ +/* to be traversed. pathitemsleft is the number of items that remain to */ +/* be traversed in pathblock. */ +/* */ +/* alignbytes determines how new records should be aligned in memory. */ +/* itembytes is the length of a record in bytes (after rounding up). */ +/* itemsperblock is the number of items allocated at once in a single */ +/* block. itemsfirstblock is the number of items in the first block, */ +/* which can vary from the others. items is the number of currently */ +/* allocated items. maxitems is the maximum number of items that have */ +/* been allocated at once; it is the current number of items plus the */ +/* number of records kept on deaditemstack. */ + +struct memorypool { + VOID **firstblock, **nowblock; + VOID *nextitem; + VOID *deaditemstack; + VOID **pathblock; + VOID *pathitem; + int alignbytes; + int itembytes; + int itemsperblock; + int itemsfirstblock; + long items, maxitems; + int unallocateditems; + int pathitemsleft; +}; + +/* Global constants. */ + +REAL splitter; /* Used to split REAL factors for exact multiplication. */ +REAL epsilon; /* Floating-point machine epsilon. */ +REAL resulterrbound; +REAL ccwerrboundA, ccwerrboundB, ccwerrboundC; +REAL iccerrboundA, iccerrboundB, iccerrboundC; +REAL o3derrboundA, o3derrboundB, o3derrboundC; + +/* Random number seed is not constant, but I've made it global anyway. */ + +unsigned long randomseed; /* Current random number seed. */ + +/* Mesh data structure. Triangle operates on only one mesh, but the mesh */ +/* structure is used (instead of global variables) to allow reentrancy. */ + +struct mesh { + + /* Variables used to allocate memory for triangles, subsegments, vertices, */ + /* viri (triangles being eaten), encroached segments, bad (skinny or too */ + /* large) triangles, and splay tree nodes. */ + + struct memorypool triangles; + struct memorypool subsegs; + struct memorypool vertices; + struct memorypool viri; + struct memorypool badsubsegs; + struct memorypool badtriangles; + struct memorypool flipstackers; + struct memorypool splaynodes; + + /* Variables that maintain the bad triangle queues. The queues are */ + /* ordered from 4095 (highest priority) to 0 (lowest priority). */ + + struct badtriang *queuefront[4096]; + struct badtriang *queuetail[4096]; + int nextnonemptyq[4096]; + int firstnonemptyq; + + /* Variable that maintains the stack of recently flipped triangles. */ + + struct flipstacker *lastflip; + + /* Other variables. */ + + REAL xmin, xmax, ymin, ymax; /* x and y bounds. */ + REAL xminextreme; /* Nonexistent x value used as a flag in sweepline. */ + int invertices; /* Number of input vertices. */ + int inelements; /* Number of input triangles. */ + int insegments; /* Number of input segments. */ + int holes; /* Number of input holes. */ + int regions; /* Number of input regions. */ + int undeads; /* Number of input vertices that don't appear in the mesh. */ + long edges; /* Number of output edges. */ + int mesh_dim; /* Dimension (ought to be 2). */ + int nextras; /* Number of attributes per vertex. */ + int eextras; /* Number of attributes per triangle. */ + long hullsize; /* Number of edges in convex hull. */ + int steinerleft; /* Number of Steiner points not yet used. */ + int vertexmarkindex; /* Index to find boundary marker of a vertex. */ + int vertex2triindex; /* Index to find a triangle adjacent to a vertex. */ + int highorderindex; /* Index to find extra nodes for high-order elements. */ + int elemattribindex; /* Index to find attributes of a triangle. */ + int areaboundindex; /* Index to find area bound of a triangle. */ + int checksegments; /* Are there segments in the triangulation yet? */ + int checkquality; /* Has quality triangulation begun yet? */ + int readnodefile; /* Has a .node file been read? */ + long samples; /* Number of random samples for point location. */ + + long incirclecount; /* Number of incircle tests performed. */ + long counterclockcount; /* Number of counterclockwise tests performed. */ + long orient3dcount; /* Number of 3D orientation tests performed. */ + long hyperbolacount; /* Number of right-of-hyperbola tests performed. */ + long circumcentercount; /* Number of circumcenter calculations performed. */ + long circletopcount; /* Number of circle top calculations performed. */ + + /* Triangular bounding box vertices. */ + + vertex infvertex1, infvertex2, infvertex3; + + /* Pointer to the `triangle' that occupies all of "outer space." */ + + triangle *dummytri; + triangle *dummytribase; /* Keep base address so we can free() it later. */ + + /* Pointer to the omnipresent subsegment. Referenced by any triangle or */ + /* subsegment that isn't really connected to a subsegment at that */ + /* location. */ + + subseg *dummysub; + subseg *dummysubbase; /* Keep base address so we can free() it later. */ + + /* Pointer to a recently visited triangle. Improves point location if */ + /* proximate vertices are inserted sequentially. */ + + struct otri recenttri; + +}; +/* End of `struct mesh'. */ + + + +/*****************************************************************************/ +/* */ +/* Mesh manipulation primitives. Each triangle contains three pointers to */ +/* other triangles, with orientations. Each pointer points not to the */ +/* first byte of a triangle, but to one of the first three bytes of a */ +/* triangle. It is necessary to extract both the triangle itself and the */ +/* orientation. To save memory, I keep both pieces of information in one */ +/* pointer. To make this possible, I assume that all triangles are aligned */ +/* to four-byte boundaries. The decode() routine below decodes a pointer, */ +/* extracting an orientation (in the range 0 to 2) and a pointer to the */ +/* beginning of a triangle. The encode() routine compresses a pointer to a */ +/* triangle and an orientation into a single pointer. My assumptions that */ +/* triangles are four-byte-aligned and that the `unsigned long' type is */ +/* long enough to hold a pointer are two of the few kludges in this program.*/ +/* */ +/* Subsegments are manipulated similarly. A pointer to a subsegment */ +/* carries both an address and an orientation in the range 0 to 1. */ +/* */ +/* The other primitives take an oriented triangle or oriented subsegment, */ +/* and return an oriented triangle or oriented subsegment or vertex; or */ +/* they change the connections in the data structure. */ +/* */ +/* Below, triangles and subsegments are denoted by their vertices. The */ +/* triangle abc has origin (org) a, destination (dest) b, and apex (apex) */ +/* c. These vertices occur in counterclockwise order about the triangle. */ +/* The handle abc may simultaneously denote vertex a, edge ab, and triangle */ +/* abc. */ +/* */ +/* Similarly, the subsegment ab has origin (sorg) a and destination (sdest) */ +/* b. If ab is thought to be directed upward (with b directly above a), */ +/* then the handle ab is thought to grasp the right side of ab, and may */ +/* simultaneously denote vertex a and edge ab. */ +/* */ +/* An asterisk (*) denotes a vertex whose identity is unknown. */ +/* */ +/* Given this notation, a partial list of mesh manipulation primitives */ +/* follows. */ +/* */ +/* */ +/* For triangles: */ +/* */ +/* sym: Find the abutting triangle; same edge. */ +/* sym(abc) -> ba* */ +/* */ +/* lnext: Find the next edge (counterclockwise) of a triangle. */ +/* lnext(abc) -> bca */ +/* */ +/* lprev: Find the previous edge (clockwise) of a triangle. */ +/* lprev(abc) -> cab */ +/* */ +/* onext: Find the next edge counterclockwise with the same origin. */ +/* onext(abc) -> ac* */ +/* */ +/* oprev: Find the next edge clockwise with the same origin. */ +/* oprev(abc) -> a*b */ +/* */ +/* dnext: Find the next edge counterclockwise with the same destination. */ +/* dnext(abc) -> *ba */ +/* */ +/* dprev: Find the next edge clockwise with the same destination. */ +/* dprev(abc) -> cb* */ +/* */ +/* rnext: Find the next edge (counterclockwise) of the adjacent triangle. */ +/* rnext(abc) -> *a* */ +/* */ +/* rprev: Find the previous edge (clockwise) of the adjacent triangle. */ +/* rprev(abc) -> b** */ +/* */ +/* org: Origin dest: Destination apex: Apex */ +/* org(abc) -> a dest(abc) -> b apex(abc) -> c */ +/* */ +/* bond: Bond two triangles together at the resepective handles. */ +/* bond(abc, bad) */ +/* */ +/* */ +/* For subsegments: */ +/* */ +/* ssym: Reverse the orientation of a subsegment. */ +/* ssym(ab) -> ba */ +/* */ +/* spivot: Find adjoining subsegment with the same origin. */ +/* spivot(ab) -> a* */ +/* */ +/* snext: Find next subsegment in sequence. */ +/* snext(ab) -> b* */ +/* */ +/* sorg: Origin sdest: Destination */ +/* sorg(ab) -> a sdest(ab) -> b */ +/* */ +/* sbond: Bond two subsegments together at the respective origins. */ +/* sbond(ab, ac) */ +/* */ +/* */ +/* For interacting tetrahedra and subfacets: */ +/* */ +/* tspivot: Find a subsegment abutting a triangle. */ +/* tspivot(abc) -> ba */ +/* */ +/* stpivot: Find a triangle abutting a subsegment. */ +/* stpivot(ab) -> ba* */ +/* */ +/* tsbond: Bond a triangle to a subsegment. */ +/* tsbond(abc, ba) */ +/* */ +/*****************************************************************************/ + +/********* Mesh manipulation primitives begin here *********/ +/** **/ +/** **/ + +/* Fast lookup arrays to speed some of the mesh manipulation primitives. */ + +extern int plus1mod3[]; // = { 1, 2, 0 }; +extern int minus1mod3[]; // = { 2, 0, 1 }; + +/********* Primitives for triangles *********/ +/* */ +/* */ + +/* decode() converts a pointer to an oriented triangle. The orientation is */ +/* extracted from the two least significant bits of the pointer. */ + +#define decode(ptr, otri) \ + (otri).orient = (int) ((unsigned long) (ptr) & (unsigned long) 3l); \ + (otri).tri = (triangle *) \ + ((unsigned long) (ptr) ^ (unsigned long) (otri).orient) + +/* encode() compresses an oriented triangle into a single pointer. It */ +/* relies on the assumption that all triangles are aligned to four-byte */ +/* boundaries, so the two least significant bits of (otri).tri are zero. */ + +#define encode(otri) \ + (triangle) ((unsigned long) (otri).tri | (unsigned long) (otri).orient) + +/* The following handle manipulation primitives are all described by Guibas */ +/* and Stolfi. However, Guibas and Stolfi use an edge-based data */ +/* structure, whereas I use a triangle-based data structure. */ + +/* sym() finds the abutting triangle, on the same edge. Note that the edge */ +/* direction is necessarily reversed, because the handle specified by an */ +/* oriented triangle is directed counterclockwise around the triangle. */ + +#define sym(otri1, otri2) \ + ptr = (otri1).tri[(otri1).orient]; \ + decode(ptr, otri2); + +#define symself(otri) \ + ptr = (otri).tri[(otri).orient]; \ + decode(ptr, otri); + +/* lnext() finds the next edge (counterclockwise) of a triangle. */ + +#define lnext(otri1, otri2) \ + (otri2).tri = (otri1).tri; \ + (otri2).orient = plus1mod3[(otri1).orient] + +#define lnextself(otri) \ + (otri).orient = plus1mod3[(otri).orient] + +/* lprev() finds the previous edge (clockwise) of a triangle. */ + +#define lprev(otri1, otri2) \ + (otri2).tri = (otri1).tri; \ + (otri2).orient = minus1mod3[(otri1).orient] + +#define lprevself(otri) \ + (otri).orient = minus1mod3[(otri).orient] + +/* onext() spins counterclockwise around a vertex; that is, it finds the */ +/* next edge with the same origin in the counterclockwise direction. This */ +/* edge is part of a different triangle. */ + +#define onext(otri1, otri2) \ + lprev(otri1, otri2); \ + symself(otri2); + +#define onextself(otri) \ + lprevself(otri); \ + symself(otri); + +/* oprev() spins clockwise around a vertex; that is, it finds the next edge */ +/* with the same origin in the clockwise direction. This edge is part of */ +/* a different triangle. */ + +#define oprev(otri1, otri2) \ + sym(otri1, otri2); \ + lnextself(otri2); + +#define oprevself(otri) \ + symself(otri); \ + lnextself(otri); + +/* dnext() spins counterclockwise around a vertex; that is, it finds the */ +/* next edge with the same destination in the counterclockwise direction. */ +/* This edge is part of a different triangle. */ + +#define dnext(otri1, otri2) \ + sym(otri1, otri2); \ + lprevself(otri2); + +#define dnextself(otri) \ + symself(otri); \ + lprevself(otri); + +/* dprev() spins clockwise around a vertex; that is, it finds the next edge */ +/* with the same destination in the clockwise direction. This edge is */ +/* part of a different triangle. */ + +#define dprev(otri1, otri2) \ + lnext(otri1, otri2); \ + symself(otri2); + +#define dprevself(otri) \ + lnextself(otri); \ + symself(otri); + +/* rnext() moves one edge counterclockwise about the adjacent triangle. */ +/* (It's best understood by reading Guibas and Stolfi. It involves */ +/* changing triangles twice.) */ + +#define rnext(otri1, otri2) \ + sym(otri1, otri2); \ + lnextself(otri2); \ + symself(otri2); + +#define rnextself(otri) \ + symself(otri); \ + lnextself(otri); \ + symself(otri); + +/* rprev() moves one edge clockwise about the adjacent triangle. */ +/* (It's best understood by reading Guibas and Stolfi. It involves */ +/* changing triangles twice.) */ + +#define rprev(otri1, otri2) \ + sym(otri1, otri2); \ + lprevself(otri2); \ + symself(otri2); + +#define rprevself(otri) \ + symself(otri); \ + lprevself(otri); \ + symself(otri); + +/* These primitives determine or set the origin, destination, or apex of a */ +/* triangle. */ + +#define org(otri, vertexptr) \ + vertexptr = (vertex) (otri).tri[plus1mod3[(otri).orient] + 3] + +#define dest(otri, vertexptr) \ + vertexptr = (vertex) (otri).tri[minus1mod3[(otri).orient] + 3] + +#define apex(otri, vertexptr) \ + vertexptr = (vertex) (otri).tri[(otri).orient + 3] + +#define setorg(otri, vertexptr) \ + (otri).tri[plus1mod3[(otri).orient] + 3] = (triangle) vertexptr + +#define setdest(otri, vertexptr) \ + (otri).tri[minus1mod3[(otri).orient] + 3] = (triangle) vertexptr + +#define setapex(otri, vertexptr) \ + (otri).tri[(otri).orient + 3] = (triangle) vertexptr + +/* Bond two triangles together. */ + +#define bond(otri1, otri2) \ + (otri1).tri[(otri1).orient] = encode(otri2); \ + (otri2).tri[(otri2).orient] = encode(otri1) + +/* Dissolve a bond (from one side). Note that the other triangle will still */ +/* think it's connected to this triangle. Usually, however, the other */ +/* triangle is being deleted entirely, or bonded to another triangle, so */ +/* it doesn't matter. */ + +#define dissolve(otri) \ + (otri).tri[(otri).orient] = (triangle) m->dummytri + +/* Copy an oriented triangle. */ + +#define otricopy(otri1, otri2) \ + (otri2).tri = (otri1).tri; \ + (otri2).orient = (otri1).orient + +/* Test for equality of oriented triangles. */ + +#define otriequal(otri1, otri2) \ + (((otri1).tri == (otri2).tri) && \ + ((otri1).orient == (otri2).orient)) + +/* Primitives to infect or cure a triangle with the virus. These rely on */ +/* the assumption that all subsegments are aligned to four-byte boundaries.*/ + +#define infect(otri) \ + (otri).tri[6] = (triangle) \ + ((unsigned long) (otri).tri[6] | (unsigned long) 2l) + +#define uninfect(otri) \ + (otri).tri[6] = (triangle) \ + ((unsigned long) (otri).tri[6] & ~ (unsigned long) 2l) + +/* Test a triangle for viral infection. */ + +#define infected(otri) \ + (((unsigned long) (otri).tri[6] & (unsigned long) 2l) != 0l) + +/* Check or set a triangle's attributes. */ + +#define elemattribute(otri, attnum) \ + ((REAL *) (otri).tri)[m->elemattribindex + (attnum)] + +#define setelemattribute(otri, attnum, value) \ + ((REAL *) (otri).tri)[m->elemattribindex + (attnum)] = value + +/* Check or set a triangle's maximum area bound. */ + +#define areabound(otri) ((REAL *) (otri).tri)[m->areaboundindex] + +#define setareabound(otri, value) \ + ((REAL *) (otri).tri)[m->areaboundindex] = value + +/* Check or set a triangle's deallocation. Its second pointer is set to */ +/* NULL to indicate that it is not allocated. (Its first pointer is used */ +/* for the stack of dead items.) Its fourth pointer (its first vertex) */ +/* is set to NULL in case a `badtriang' structure points to it. */ + +#define deadtri(tria) ((tria)[1] == (triangle) NULL) + +#define killtri(tria) \ + (tria)[1] = (triangle) NULL; \ + (tria)[3] = (triangle) NULL + +/********* Primitives for subsegments *********/ +/* */ +/* */ + +/* sdecode() converts a pointer to an oriented subsegment. The orientation */ +/* is extracted from the least significant bit of the pointer. The two */ +/* least significant bits (one for orientation, one for viral infection) */ +/* are masked out to produce the real pointer. */ + +#define sdecode(sptr, osub) \ + (osub).ssorient = (int) ((unsigned long) (sptr) & (unsigned long) 1l); \ + (osub).ss = (subseg *) \ + ((unsigned long) (sptr) & ~ (unsigned long) 3l) + +/* sencode() compresses an oriented subsegment into a single pointer. It */ +/* relies on the assumption that all subsegments are aligned to two-byte */ +/* boundaries, so the least significant bit of (osub).ss is zero. */ + +#define sencode(osub) \ + (subseg) ((unsigned long) (osub).ss | (unsigned long) (osub).ssorient) + +/* ssym() toggles the orientation of a subsegment. */ + +#define ssym(osub1, osub2) \ + (osub2).ss = (osub1).ss; \ + (osub2).ssorient = 1 - (osub1).ssorient + +#define ssymself(osub) \ + (osub).ssorient = 1 - (osub).ssorient + +/* spivot() finds the other subsegment (from the same segment) that shares */ +/* the same origin. */ + +#define spivot(osub1, osub2) \ + sptr = (osub1).ss[(osub1).ssorient]; \ + sdecode(sptr, osub2) + +#define spivotself(osub) \ + sptr = (osub).ss[(osub).ssorient]; \ + sdecode(sptr, osub) + +/* snext() finds the next subsegment (from the same segment) in sequence; */ +/* one whose origin is the input subsegment's destination. */ + +#define snext(osub1, osub2) \ + sptr = (osub1).ss[1 - (osub1).ssorient]; \ + sdecode(sptr, osub2) + +#define snextself(osub) \ + sptr = (osub).ss[1 - (osub).ssorient]; \ + sdecode(sptr, osub) + +/* These primitives determine or set the origin or destination of a */ +/* subsegment or the segment that includes it. */ + +#define sorg(osub, vertexptr) \ + vertexptr = (vertex) (osub).ss[2 + (osub).ssorient] + +#define sdest(osub, vertexptr) \ + vertexptr = (vertex) (osub).ss[3 - (osub).ssorient] + +#define setsorg(osub, vertexptr) \ + (osub).ss[2 + (osub).ssorient] = (subseg) vertexptr + +#define setsdest(osub, vertexptr) \ + (osub).ss[3 - (osub).ssorient] = (subseg) vertexptr + +#define segorg(osub, vertexptr) \ + vertexptr = (vertex) (osub).ss[4 + (osub).ssorient] + +#define segdest(osub, vertexptr) \ + vertexptr = (vertex) (osub).ss[5 - (osub).ssorient] + +#define setsegorg(osub, vertexptr) \ + (osub).ss[4 + (osub).ssorient] = (subseg) vertexptr + +#define setsegdest(osub, vertexptr) \ + (osub).ss[5 - (osub).ssorient] = (subseg) vertexptr + +/* These primitives read or set a boundary marker. Boundary markers are */ +/* used to hold user-defined tags for setting boundary conditions in */ +/* finite element solvers. */ + +#define mark(osub) (* (int *) ((osub).ss + 8)) + +#define setmark(osub, value) \ + * (int *) ((osub).ss + 8) = value + +/* Bond two subsegments together. */ + +#define sbond(osub1, osub2) \ + (osub1).ss[(osub1).ssorient] = sencode(osub2); \ + (osub2).ss[(osub2).ssorient] = sencode(osub1) + +/* Dissolve a subsegment bond (from one side). Note that the other */ +/* subsegment will still think it's connected to this subsegment. */ + +#define sdissolve(osub) \ + (osub).ss[(osub).ssorient] = (subseg) m->dummysub + +/* Copy a subsegment. */ + +#define subsegcopy(osub1, osub2) \ + (osub2).ss = (osub1).ss; \ + (osub2).ssorient = (osub1).ssorient + +/* Test for equality of subsegments. */ + +#define subsegequal(osub1, osub2) \ + (((osub1).ss == (osub2).ss) && \ + ((osub1).ssorient == (osub2).ssorient)) + +/* Check or set a subsegment's deallocation. Its second pointer is set to */ +/* NULL to indicate that it is not allocated. (Its first pointer is used */ +/* for the stack of dead items.) Its third pointer (its first vertex) */ +/* is set to NULL in case a `badsubseg' structure points to it. */ + +#define deadsubseg(sub) ((sub)[1] == (subseg) NULL) + +#define killsubseg(sub) \ + (sub)[1] = (subseg) NULL; \ + (sub)[2] = (subseg) NULL + +/********* Primitives for interacting triangles and subsegments *********/ +/* */ +/* */ + +/* tspivot() finds a subsegment abutting a triangle. */ + +#define tspivot(otri, osub) \ + sptr = (subseg) (otri).tri[6 + (otri).orient]; \ + sdecode(sptr, osub) + +/* stpivot() finds a triangle abutting a subsegment. It requires that the */ +/* variable `ptr' of type `triangle' be defined. */ + +#define stpivot(osub, otri) \ + ptr = (triangle) (osub).ss[6 + (osub).ssorient]; \ + decode(ptr, otri) + +/* Bond a triangle to a subsegment. */ + +#define tsbond(otri, osub) \ + (otri).tri[6 + (otri).orient] = (triangle) sencode(osub); \ + (osub).ss[6 + (osub).ssorient] = (subseg) encode(otri) + +/* Dissolve a bond (from the triangle side). */ + +#define tsdissolve(otri) \ + (otri).tri[6 + (otri).orient] = (triangle) m->dummysub + +/* Dissolve a bond (from the subsegment side). */ + +#define stdissolve(osub) \ + (osub).ss[6 + (osub).ssorient] = (subseg) m->dummytri + +/********* Primitives for vertices *********/ +/* */ +/* */ + +#define vertexmark(vx) ((int *) (vx))[m->vertexmarkindex] + +#define setvertexmark(vx, value) \ + ((int *) (vx))[m->vertexmarkindex] = value + +#define vertextype(vx) ((int *) (vx))[m->vertexmarkindex + 1] + +#define setvertextype(vx, value) \ + ((int *) (vx))[m->vertexmarkindex + 1] = value + +#define vertex2tri(vx) ((triangle *) (vx))[m->vertex2triindex] + +#define setvertex2tri(vx, value) \ + ((triangle *) (vx))[m->vertex2triindex] = value + +/** **/ +/** **/ +/********* Mesh manipulation primitives end here *********/ + + + +void printtriangle(struct mesh *m, struct behavior *b, struct otri *t); +void printsubseg(struct mesh *m, struct behavior *b, struct osub *s); +void quality_statistics(struct mesh *m, struct behavior *b); +void statistics(struct mesh *m, struct behavior *b); +// provided by triangle.c +void traversalinit(struct memorypool *pool); +REAL counterclockwise(struct mesh *m, struct behavior *b, vertex pa, vertex pb, vertex pc); +triangle *triangletraverse(struct mesh *m); diff --git a/vtm/src/org/oscim/backend/AssetAdapter.java b/vtm/src/org/oscim/backend/AssetAdapter.java new file mode 100644 index 00000000..e9e16a59 --- /dev/null +++ b/vtm/src/org/oscim/backend/AssetAdapter.java @@ -0,0 +1,10 @@ +package org.oscim.backend; + +import java.io.InputStream; + +public abstract class AssetAdapter { + public static AssetAdapter g; + + public abstract InputStream openFileAsStream(String name); + +} diff --git a/vtm/src/org/oscim/backend/BitmapUtils.java b/vtm/src/org/oscim/backend/BitmapUtils.java new file mode 100644 index 00000000..a4f87460 --- /dev/null +++ b/vtm/src/org/oscim/backend/BitmapUtils.java @@ -0,0 +1,71 @@ +/* + * Copyright 2010, 2011, 2012 mapsforge.org + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation, either version 3 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program. If not, see . + */ +package org.oscim.backend; + +import java.io.IOException; +import java.io.InputStream; + +import org.oscim.backend.canvas.Bitmap; + +public final class BitmapUtils { + // private static final String PREFIX_FILE = "file:"; + // private static final String PREFIX_JAR = "jar:"; + // + // private static InputStream createInputStream(String src) throws + // FileNotFoundException { + // if (src.startsWith(PREFIX_JAR)) { + // String name = "/org/oscim/theme/styles/" + + // src.substring(PREFIX_JAR.length()); + // + // InputStream inputStream = BitmapUtils.class.getResourceAsStream(name); + // if (inputStream == null) { + // throw new FileNotFoundException("resource not found: " + src + " " + + // name); + // } + // return inputStream; + // } else if (src.startsWith(PREFIX_FILE)) { + // File file = new File(src.substring(PREFIX_FILE.length())); + // if (!file.exists()) { + // throw new IllegalArgumentException("file does not exist: " + src); + // } else if (!file.isFile()) { + // throw new IllegalArgumentException("not a file: " + src); + // } else if (!file.canRead()) { + // throw new IllegalArgumentException("cannot read file: " + src); + // } + // return new FileInputStream(file); + // } + // throw new IllegalArgumentException("invalid bitmap source: " + src); + // } + + public static Bitmap createBitmap(String src) throws IOException { + if (src == null || src.length() == 0) { + // no image source defined + return null; + } + + // InputStream inputStream = createInputStream(src); + InputStream inputStream = AssetAdapter.g.openFileAsStream(src); + if (inputStream == null) + throw new IllegalArgumentException("invalid bitmap source: " + src); + + Bitmap bitmap = CanvasAdapter.g.decodeBitmap(inputStream); + inputStream.close(); + return bitmap; + } + + private BitmapUtils() { + throw new IllegalStateException(); + } +} diff --git a/src/org/oscim/backend/CanvasAdapter.java b/vtm/src/org/oscim/backend/CanvasAdapter.java similarity index 100% rename from src/org/oscim/backend/CanvasAdapter.java rename to vtm/src/org/oscim/backend/CanvasAdapter.java diff --git a/src/org/oscim/backend/GL20.java b/vtm/src/org/oscim/backend/GL20.java similarity index 100% rename from src/org/oscim/backend/GL20.java rename to vtm/src/org/oscim/backend/GL20.java diff --git a/src/org/oscim/backend/GLAdapter.java b/vtm/src/org/oscim/backend/GLAdapter.java similarity index 87% rename from src/org/oscim/backend/GLAdapter.java rename to vtm/src/org/oscim/backend/GLAdapter.java index ac8367e5..77da351b 100644 --- a/src/org/oscim/backend/GLAdapter.java +++ b/vtm/src/org/oscim/backend/GLAdapter.java @@ -14,8 +14,8 @@ */ package org.oscim.backend; -import com.badlogic.gdx.backends.android.AndroidGL20; +//import com.badlogic.gdx.backends.android.AndroidGL20; public class GLAdapter { - public static GL20 INSTANCE = new AndroidGL20(); + public static GL20 INSTANCE; //= new AndroidGL20(); } diff --git a/src/org/oscim/backend/Log.java b/vtm/src/org/oscim/backend/Log.java similarity index 100% rename from src/org/oscim/backend/Log.java rename to vtm/src/org/oscim/backend/Log.java diff --git a/src/org/oscim/backend/canvas/Bitmap.java b/vtm/src/org/oscim/backend/canvas/Bitmap.java similarity index 100% rename from src/org/oscim/backend/canvas/Bitmap.java rename to vtm/src/org/oscim/backend/canvas/Bitmap.java diff --git a/src/org/oscim/backend/canvas/Canvas.java b/vtm/src/org/oscim/backend/canvas/Canvas.java similarity index 100% rename from src/org/oscim/backend/canvas/Canvas.java rename to vtm/src/org/oscim/backend/canvas/Canvas.java diff --git a/src/org/oscim/backend/canvas/Color.java b/vtm/src/org/oscim/backend/canvas/Color.java similarity index 100% rename from src/org/oscim/backend/canvas/Color.java rename to vtm/src/org/oscim/backend/canvas/Color.java diff --git a/src/org/oscim/backend/canvas/Paint.java b/vtm/src/org/oscim/backend/canvas/Paint.java similarity index 100% rename from src/org/oscim/backend/canvas/Paint.java rename to vtm/src/org/oscim/backend/canvas/Paint.java diff --git a/src/org/oscim/backend/input/KeyEvent.java b/vtm/src/org/oscim/backend/input/KeyEvent.java similarity index 100% rename from src/org/oscim/backend/input/KeyEvent.java rename to vtm/src/org/oscim/backend/input/KeyEvent.java diff --git a/src/org/oscim/backend/input/MotionEvent.java b/vtm/src/org/oscim/backend/input/MotionEvent.java similarity index 70% rename from src/org/oscim/backend/input/MotionEvent.java rename to vtm/src/org/oscim/backend/input/MotionEvent.java index e41cd312..39e7564d 100644 --- a/src/org/oscim/backend/input/MotionEvent.java +++ b/vtm/src/org/oscim/backend/input/MotionEvent.java @@ -14,7 +14,7 @@ */ package org.oscim.backend.input; -public class MotionEvent { +public abstract class MotionEvent { public static final int ACTION_DOWN = 0; public static final int ACTION_UP = 1; @@ -28,34 +28,16 @@ public class MotionEvent { public static final int ACTION_POINTER_INDEX_MASK = 0xff00; public static final int ACTION_POINTER_INDEX_SHIFT = 8; - public int getAction() { - // TODO Auto-generated method stub - return 0; - } + public abstract int getAction(); - public float getX() { - // TODO Auto-generated method stub - return 0; - } + public abstract float getX(); - public float getY() { - // TODO Auto-generated method stub - return 0; - } + public abstract float getY(); - public float getX(int idx) { - // TODO Auto-generated method stub - return 0; - } + public abstract float getX(int idx); - public float getY(int idx) { - // TODO Auto-generated method stub - return 0; - } + public abstract float getY(int idx); - public int getPointerCount() { - // TODO Auto-generated method stub - return 0; - } + public abstract int getPointerCount(); } diff --git a/src/org/oscim/core/BoundingBox.java b/vtm/src/org/oscim/core/BoundingBox.java similarity index 100% rename from src/org/oscim/core/BoundingBox.java rename to vtm/src/org/oscim/core/BoundingBox.java diff --git a/src/org/oscim/core/GeoPoint.java b/vtm/src/org/oscim/core/GeoPoint.java similarity index 100% rename from src/org/oscim/core/GeoPoint.java rename to vtm/src/org/oscim/core/GeoPoint.java diff --git a/src/org/oscim/core/GeometryBuffer.java b/vtm/src/org/oscim/core/GeometryBuffer.java similarity index 100% rename from src/org/oscim/core/GeometryBuffer.java rename to vtm/src/org/oscim/core/GeometryBuffer.java diff --git a/src/org/oscim/core/MapElement.java b/vtm/src/org/oscim/core/MapElement.java similarity index 100% rename from src/org/oscim/core/MapElement.java rename to vtm/src/org/oscim/core/MapElement.java diff --git a/src/org/oscim/core/MapPosition.java b/vtm/src/org/oscim/core/MapPosition.java similarity index 100% rename from src/org/oscim/core/MapPosition.java rename to vtm/src/org/oscim/core/MapPosition.java diff --git a/src/org/oscim/core/MercatorProjection.java b/vtm/src/org/oscim/core/MercatorProjection.java similarity index 100% rename from src/org/oscim/core/MercatorProjection.java rename to vtm/src/org/oscim/core/MercatorProjection.java diff --git a/src/org/oscim/core/PointD.java b/vtm/src/org/oscim/core/PointD.java similarity index 100% rename from src/org/oscim/core/PointD.java rename to vtm/src/org/oscim/core/PointD.java diff --git a/src/org/oscim/core/PointF.java b/vtm/src/org/oscim/core/PointF.java similarity index 100% rename from src/org/oscim/core/PointF.java rename to vtm/src/org/oscim/core/PointF.java diff --git a/src/org/oscim/core/Tag.java b/vtm/src/org/oscim/core/Tag.java similarity index 100% rename from src/org/oscim/core/Tag.java rename to vtm/src/org/oscim/core/Tag.java diff --git a/src/org/oscim/core/TagSet.java b/vtm/src/org/oscim/core/TagSet.java similarity index 100% rename from src/org/oscim/core/TagSet.java rename to vtm/src/org/oscim/core/TagSet.java diff --git a/src/org/oscim/core/Tile.java b/vtm/src/org/oscim/core/Tile.java similarity index 100% rename from src/org/oscim/core/Tile.java rename to vtm/src/org/oscim/core/Tile.java diff --git a/src/org/oscim/layers/InputLayer.java b/vtm/src/org/oscim/layers/InputLayer.java similarity index 100% rename from src/org/oscim/layers/InputLayer.java rename to vtm/src/org/oscim/layers/InputLayer.java diff --git a/src/org/oscim/layers/Layer.java b/vtm/src/org/oscim/layers/Layer.java similarity index 100% rename from src/org/oscim/layers/Layer.java rename to vtm/src/org/oscim/layers/Layer.java diff --git a/src/org/oscim/layers/MapEventLayer.java b/vtm/src/org/oscim/layers/MapEventLayer.java similarity index 100% rename from src/org/oscim/layers/MapEventLayer.java rename to vtm/src/org/oscim/layers/MapEventLayer.java diff --git a/src/org/oscim/layers/labeling/Debug.java b/vtm/src/org/oscim/layers/labeling/Debug.java similarity index 100% rename from src/org/oscim/layers/labeling/Debug.java rename to vtm/src/org/oscim/layers/labeling/Debug.java diff --git a/src/org/oscim/layers/labeling/Label.java b/vtm/src/org/oscim/layers/labeling/Label.java similarity index 100% rename from src/org/oscim/layers/labeling/Label.java rename to vtm/src/org/oscim/layers/labeling/Label.java diff --git a/src/org/oscim/layers/labeling/LabelLayer.java b/vtm/src/org/oscim/layers/labeling/LabelLayer.java similarity index 100% rename from src/org/oscim/layers/labeling/LabelLayer.java rename to vtm/src/org/oscim/layers/labeling/LabelLayer.java diff --git a/src/org/oscim/layers/labeling/TextRenderLayer.java b/vtm/src/org/oscim/layers/labeling/TextRenderLayer.java similarity index 100% rename from src/org/oscim/layers/labeling/TextRenderLayer.java rename to vtm/src/org/oscim/layers/labeling/TextRenderLayer.java diff --git a/src/org/oscim/layers/overlay/BuildingOverlay.java b/vtm/src/org/oscim/layers/overlay/BuildingOverlay.java similarity index 100% rename from src/org/oscim/layers/overlay/BuildingOverlay.java rename to vtm/src/org/oscim/layers/overlay/BuildingOverlay.java diff --git a/src/org/oscim/layers/overlay/GenericOverlay.java b/vtm/src/org/oscim/layers/overlay/GenericOverlay.java similarity index 100% rename from src/org/oscim/layers/overlay/GenericOverlay.java rename to vtm/src/org/oscim/layers/overlay/GenericOverlay.java diff --git a/src/org/oscim/layers/overlay/ItemizedIconOverlay.java b/vtm/src/org/oscim/layers/overlay/ItemizedIconOverlay.java similarity index 100% rename from src/org/oscim/layers/overlay/ItemizedIconOverlay.java rename to vtm/src/org/oscim/layers/overlay/ItemizedIconOverlay.java diff --git a/src/org/oscim/layers/overlay/ItemizedOverlay.java b/vtm/src/org/oscim/layers/overlay/ItemizedOverlay.java similarity index 94% rename from src/org/oscim/layers/overlay/ItemizedOverlay.java rename to vtm/src/org/oscim/layers/overlay/ItemizedOverlay.java index a604233f..7d1be953 100644 --- a/src/org/oscim/layers/overlay/ItemizedOverlay.java +++ b/vtm/src/org/oscim/layers/overlay/ItemizedOverlay.java @@ -303,11 +303,11 @@ public abstract class ItemizedOverlay extends Overlay * y coordinate of point to check * @return true if the hit point is within the marker */ - protected boolean hitTest(final Item item, final android.graphics.drawable.Drawable marker, - final int hitX, - final int hitY) { - return marker.getBounds().contains(hitX, hitY); - } +// protected boolean hitTest(final Item item, final android.graphics.drawable.Drawable marker, +// final int hitX, +// final int hitY) { +// return marker.getBounds().contains(hitX, hitY); +// } /** * Set whether or not to draw the focused item. The default is to draw it, diff --git a/src/org/oscim/layers/overlay/MapScaleBar.java b/vtm/src/org/oscim/layers/overlay/MapScaleBar.java similarity index 100% rename from src/org/oscim/layers/overlay/MapScaleBar.java rename to vtm/src/org/oscim/layers/overlay/MapScaleBar.java diff --git a/src/org/oscim/layers/overlay/Overlay.java b/vtm/src/org/oscim/layers/overlay/Overlay.java similarity index 100% rename from src/org/oscim/layers/overlay/Overlay.java rename to vtm/src/org/oscim/layers/overlay/Overlay.java diff --git a/src/org/oscim/layers/overlay/OverlayItem.java b/vtm/src/org/oscim/layers/overlay/OverlayItem.java similarity index 100% rename from src/org/oscim/layers/overlay/OverlayItem.java rename to vtm/src/org/oscim/layers/overlay/OverlayItem.java diff --git a/src/org/oscim/layers/overlay/OverlayMarker.java b/vtm/src/org/oscim/layers/overlay/OverlayMarker.java similarity index 100% rename from src/org/oscim/layers/overlay/OverlayMarker.java rename to vtm/src/org/oscim/layers/overlay/OverlayMarker.java diff --git a/src/org/oscim/layers/overlay/PathOverlay.java b/vtm/src/org/oscim/layers/overlay/PathOverlay.java similarity index 100% rename from src/org/oscim/layers/overlay/PathOverlay.java rename to vtm/src/org/oscim/layers/overlay/PathOverlay.java diff --git a/src/org/oscim/layers/tile/JobQueue.java b/vtm/src/org/oscim/layers/tile/JobQueue.java similarity index 100% rename from src/org/oscim/layers/tile/JobQueue.java rename to vtm/src/org/oscim/layers/tile/JobQueue.java diff --git a/src/org/oscim/layers/tile/MapTile.java b/vtm/src/org/oscim/layers/tile/MapTile.java similarity index 100% rename from src/org/oscim/layers/tile/MapTile.java rename to vtm/src/org/oscim/layers/tile/MapTile.java diff --git a/src/org/oscim/layers/tile/TileDistanceSort.java b/vtm/src/org/oscim/layers/tile/TileDistanceSort.java similarity index 100% rename from src/org/oscim/layers/tile/TileDistanceSort.java rename to vtm/src/org/oscim/layers/tile/TileDistanceSort.java diff --git a/src/org/oscim/layers/tile/TileLayer.java b/vtm/src/org/oscim/layers/tile/TileLayer.java similarity index 100% rename from src/org/oscim/layers/tile/TileLayer.java rename to vtm/src/org/oscim/layers/tile/TileLayer.java diff --git a/src/org/oscim/layers/tile/TileLoader.java b/vtm/src/org/oscim/layers/tile/TileLoader.java similarity index 100% rename from src/org/oscim/layers/tile/TileLoader.java rename to vtm/src/org/oscim/layers/tile/TileLoader.java diff --git a/src/org/oscim/layers/tile/TileManager.java b/vtm/src/org/oscim/layers/tile/TileManager.java similarity index 100% rename from src/org/oscim/layers/tile/TileManager.java rename to vtm/src/org/oscim/layers/tile/TileManager.java diff --git a/src/org/oscim/layers/tile/TileRenderLayer.java b/vtm/src/org/oscim/layers/tile/TileRenderLayer.java similarity index 100% rename from src/org/oscim/layers/tile/TileRenderLayer.java rename to vtm/src/org/oscim/layers/tile/TileRenderLayer.java diff --git a/src/org/oscim/layers/tile/TileRenderer.java b/vtm/src/org/oscim/layers/tile/TileRenderer.java similarity index 99% rename from src/org/oscim/layers/tile/TileRenderer.java rename to vtm/src/org/oscim/layers/tile/TileRenderer.java index 7bbf0f5d..c5b5bd54 100644 --- a/src/org/oscim/layers/tile/TileRenderer.java +++ b/vtm/src/org/oscim/layers/tile/TileRenderer.java @@ -66,6 +66,7 @@ public class TileRenderer { mProjMatrix.setValue(14, 0); mProjMatrix.multiplyRhs(m.view); + GL.glDepthMask(true); GL.glClear(GL20.GL_DEPTH_BUFFER_BIT); GL.glDepthFunc(GL20.GL_LESS); diff --git a/src/org/oscim/layers/tile/TileSet.java b/vtm/src/org/oscim/layers/tile/TileSet.java similarity index 100% rename from src/org/oscim/layers/tile/TileSet.java rename to vtm/src/org/oscim/layers/tile/TileSet.java diff --git a/src/org/oscim/layers/tile/bitmap/AbstractTileSource.java b/vtm/src/org/oscim/layers/tile/bitmap/AbstractTileSource.java similarity index 100% rename from src/org/oscim/layers/tile/bitmap/AbstractTileSource.java rename to vtm/src/org/oscim/layers/tile/bitmap/AbstractTileSource.java diff --git a/src/org/oscim/layers/tile/bitmap/ArcGISWorldShaded.java b/vtm/src/org/oscim/layers/tile/bitmap/ArcGISWorldShaded.java similarity index 100% rename from src/org/oscim/layers/tile/bitmap/ArcGISWorldShaded.java rename to vtm/src/org/oscim/layers/tile/bitmap/ArcGISWorldShaded.java diff --git a/src/org/oscim/layers/tile/bitmap/BitmapTileLayer.java b/vtm/src/org/oscim/layers/tile/bitmap/BitmapTileLayer.java similarity index 100% rename from src/org/oscim/layers/tile/bitmap/BitmapTileLayer.java rename to vtm/src/org/oscim/layers/tile/bitmap/BitmapTileLayer.java diff --git a/src/org/oscim/layers/tile/bitmap/MapQuestAerial.java b/vtm/src/org/oscim/layers/tile/bitmap/MapQuestAerial.java similarity index 100% rename from src/org/oscim/layers/tile/bitmap/MapQuestAerial.java rename to vtm/src/org/oscim/layers/tile/bitmap/MapQuestAerial.java diff --git a/src/org/oscim/layers/tile/bitmap/NaturalEarth.java b/vtm/src/org/oscim/layers/tile/bitmap/NaturalEarth.java similarity index 100% rename from src/org/oscim/layers/tile/bitmap/NaturalEarth.java rename to vtm/src/org/oscim/layers/tile/bitmap/NaturalEarth.java diff --git a/src/org/oscim/layers/tile/bitmap/OpenStreetMapMapnik.java b/vtm/src/org/oscim/layers/tile/bitmap/OpenStreetMapMapnik.java similarity index 100% rename from src/org/oscim/layers/tile/bitmap/OpenStreetMapMapnik.java rename to vtm/src/org/oscim/layers/tile/bitmap/OpenStreetMapMapnik.java diff --git a/src/org/oscim/layers/tile/bitmap/TileSource.java b/vtm/src/org/oscim/layers/tile/bitmap/TileSource.java similarity index 100% rename from src/org/oscim/layers/tile/bitmap/TileSource.java rename to vtm/src/org/oscim/layers/tile/bitmap/TileSource.java diff --git a/src/org/oscim/layers/tile/geojson/GeoJsonTileLayer.java b/vtm/src/org/oscim/layers/tile/geojson/GeoJsonTileLayer.java similarity index 100% rename from src/org/oscim/layers/tile/geojson/GeoJsonTileLayer.java rename to vtm/src/org/oscim/layers/tile/geojson/GeoJsonTileLayer.java diff --git a/src/org/oscim/layers/tile/test/TestTileLayer.java b/vtm/src/org/oscim/layers/tile/test/TestTileLayer.java similarity index 100% rename from src/org/oscim/layers/tile/test/TestTileLayer.java rename to vtm/src/org/oscim/layers/tile/test/TestTileLayer.java diff --git a/src/org/oscim/layers/tile/vector/MapTileLayer.java b/vtm/src/org/oscim/layers/tile/vector/MapTileLayer.java similarity index 100% rename from src/org/oscim/layers/tile/vector/MapTileLayer.java rename to vtm/src/org/oscim/layers/tile/vector/MapTileLayer.java diff --git a/src/org/oscim/layers/tile/vector/MapTileLoader.java b/vtm/src/org/oscim/layers/tile/vector/MapTileLoader.java similarity index 100% rename from src/org/oscim/layers/tile/vector/MapTileLoader.java rename to vtm/src/org/oscim/layers/tile/vector/MapTileLoader.java diff --git a/src/org/oscim/layers/tile/vector/WayDecorator.java b/vtm/src/org/oscim/layers/tile/vector/WayDecorator.java similarity index 100% rename from src/org/oscim/layers/tile/vector/WayDecorator.java rename to vtm/src/org/oscim/layers/tile/vector/WayDecorator.java diff --git a/src/org/oscim/renderer/BufferObject.java b/vtm/src/org/oscim/renderer/BufferObject.java similarity index 96% rename from src/org/oscim/renderer/BufferObject.java rename to vtm/src/org/oscim/renderer/BufferObject.java index 98a3848f..cb40b192 100644 --- a/src/org/oscim/renderer/BufferObject.java +++ b/vtm/src/org/oscim/renderer/BufferObject.java @@ -52,6 +52,10 @@ public final class BufferObject { bufferType = type; } + if (buf.position() != 0){ + Log.d(TAG, "rewind your buffer: " + buf.position()); + } + GL.glBindBuffer(type, id); // reuse memory allocated for vbo when possible and allocated @@ -60,11 +64,11 @@ public final class BufferObject { GL.glBufferSubData(type, 0, newSize, buf); } else { mBufferMemoryUsage += newSize - size; - size = newSize; - GL.glBufferData(type, size, buf, GL20.GL_DYNAMIC_DRAW); + //GL.glBufferData(type, size, buf, GL20.GL_STATIC_DRAW); } + //GL.glFinish(); } public void bindArrayBuffer() { diff --git a/src/org/oscim/renderer/GLRenderer.java b/vtm/src/org/oscim/renderer/GLRenderer.java similarity index 93% rename from src/org/oscim/renderer/GLRenderer.java rename to vtm/src/org/oscim/renderer/GLRenderer.java index 4380e5fa..8da792ec 100644 --- a/src/org/oscim/renderer/GLRenderer.java +++ b/vtm/src/org/oscim/renderer/GLRenderer.java @@ -135,13 +135,14 @@ public class GLRenderer { mClearColor = GlUtils.colorToFloat(t.getMapBackground()); mUpdateColor = true; } + public static boolean alwaysAllocBuffer = false; /** * Only use on GL Thread! * Get a native ShortBuffer for temporary use. */ public static ShortBuffer getShortBuffer(int size) { - if (tmpBufferSize < size * 2) + if (alwaysAllocBuffer || tmpBufferSize < size * 2) growBuffer(size * 2); else shortBuffer.clear(); @@ -154,7 +155,7 @@ public class GLRenderer { * Get a native FloatBuffer for temporary use. */ public static FloatBuffer getFloatBuffer(int size) { - if (tmpBufferSize < size * 4) + if (alwaysAllocBuffer || tmpBufferSize < size * 4) growBuffer(size * 4); else floatBuffer.clear(); @@ -167,7 +168,7 @@ public class GLRenderer { * Get a native IntBuffer for temporary use. */ public static IntBuffer getIntBuffer(int size) { - if (tmpBufferSize < size * 4) + if (alwaysAllocBuffer || tmpBufferSize < size * 4) growBuffer(size * 4); else intBuffer.clear(); @@ -279,19 +280,23 @@ public class GLRenderer { } /* update layers */ - RenderLayer[] overlays = mMapView.getLayerManager().getRenderLayers(); + RenderLayer[] layers = mMapView.getLayerManager().getRenderLayers(); - for (int i = 0, n = overlays.length; i < n; i++) - overlays[i].update(pos, changed, mMatrices); + for (int i = 0, n = layers.length; i < n; i++) + layers[i].update(pos, changed, mMatrices); - /* draw layers */ - for (int i = 0, n = overlays.length; i < n; i++) { - RenderLayer renderLayer = overlays[i]; + /* compile layers */ + for (int i = 0, n = layers.length; i < n; i++) { + RenderLayer renderLayer = layers[i]; if (renderLayer.newData) { renderLayer.compile(); renderLayer.newData = false; } + } + + for (int i = 0, n = layers.length; i < n; i++) { + RenderLayer renderLayer = layers[i]; if (renderLayer.isReady) renderLayer.render(mMapPosition, mMatrices); @@ -366,7 +371,7 @@ public class GLRenderer { indices[i + 4] = (short) (j + 1); indices[i + 5] = (short) (j + 3); } - + GLRenderer.getShortBuffer(indices.length); shortBuffer.put(indices); shortBuffer.flip(); diff --git a/src/org/oscim/renderer/GLState.java b/vtm/src/org/oscim/renderer/GLState.java similarity index 100% rename from src/org/oscim/renderer/GLState.java rename to vtm/src/org/oscim/renderer/GLState.java diff --git a/src/org/oscim/renderer/RenderLayer.java b/vtm/src/org/oscim/renderer/RenderLayer.java similarity index 100% rename from src/org/oscim/renderer/RenderLayer.java rename to vtm/src/org/oscim/renderer/RenderLayer.java diff --git a/src/org/oscim/renderer/atlas/SpriteManager.java b/vtm/src/org/oscim/renderer/atlas/SpriteManager.java similarity index 100% rename from src/org/oscim/renderer/atlas/SpriteManager.java rename to vtm/src/org/oscim/renderer/atlas/SpriteManager.java diff --git a/src/org/oscim/renderer/atlas/TextureAtlas.java b/vtm/src/org/oscim/renderer/atlas/TextureAtlas.java similarity index 100% rename from src/org/oscim/renderer/atlas/TextureAtlas.java rename to vtm/src/org/oscim/renderer/atlas/TextureAtlas.java diff --git a/src/org/oscim/renderer/atlas/TextureRegion.java b/vtm/src/org/oscim/renderer/atlas/TextureRegion.java similarity index 100% rename from src/org/oscim/renderer/atlas/TextureRegion.java rename to vtm/src/org/oscim/renderer/atlas/TextureRegion.java diff --git a/src/org/oscim/renderer/layers/BasicRenderLayer.java b/vtm/src/org/oscim/renderer/layers/BasicRenderLayer.java similarity index 100% rename from src/org/oscim/renderer/layers/BasicRenderLayer.java rename to vtm/src/org/oscim/renderer/layers/BasicRenderLayer.java diff --git a/src/org/oscim/renderer/layers/BitmapRenderLayer.java b/vtm/src/org/oscim/renderer/layers/BitmapRenderLayer.java similarity index 100% rename from src/org/oscim/renderer/layers/BitmapRenderLayer.java rename to vtm/src/org/oscim/renderer/layers/BitmapRenderLayer.java diff --git a/src/org/oscim/renderer/layers/CustomRenderLayer.java b/vtm/src/org/oscim/renderer/layers/CustomRenderLayer.java similarity index 100% rename from src/org/oscim/renderer/layers/CustomRenderLayer.java rename to vtm/src/org/oscim/renderer/layers/CustomRenderLayer.java diff --git a/src/org/oscim/renderer/layers/CustomRenderLayer2.java b/vtm/src/org/oscim/renderer/layers/CustomRenderLayer2.java similarity index 100% rename from src/org/oscim/renderer/layers/CustomRenderLayer2.java rename to vtm/src/org/oscim/renderer/layers/CustomRenderLayer2.java diff --git a/src/org/oscim/renderer/layers/ExtrusionRenderLayer.java b/vtm/src/org/oscim/renderer/layers/ExtrusionRenderLayer.java similarity index 97% rename from src/org/oscim/renderer/layers/ExtrusionRenderLayer.java rename to vtm/src/org/oscim/renderer/layers/ExtrusionRenderLayer.java index 48e15f81..ff743e7a 100644 --- a/src/org/oscim/renderer/layers/ExtrusionRenderLayer.java +++ b/vtm/src/org/oscim/renderer/layers/ExtrusionRenderLayer.java @@ -14,6 +14,10 @@ */ package org.oscim.renderer.layers; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.ShortBuffer; + import org.oscim.backend.GL20; import org.oscim.backend.GLAdapter; import org.oscim.backend.Log; @@ -60,6 +64,16 @@ public class ExtrusionRenderLayer extends RenderLayer { private final static int SHADER = 0; + private static ShortBuffer getShortBuffer(int size) { + ByteBuffer buf = ByteBuffer + .allocateDirect(size) + .order(ByteOrder.nativeOrder()); + + return buf.asShortBuffer(); + + } + + private boolean initShader() { initialized = true; @@ -120,8 +134,8 @@ public class ExtrusionRenderLayer extends RenderLayer { continue; if (!el.compiled) { - int verticesBytes = el.mNumVertices * 8; - el.compile(GLRenderer.getShortBuffer(verticesBytes)); + int bytes = el.mNumVertices * 8 * 2; + el.compile(getShortBuffer(bytes)); GlUtils.checkGlError("..."); } diff --git a/src/org/oscim/renderer/layers/GridRenderLayer.java b/vtm/src/org/oscim/renderer/layers/GridRenderLayer.java similarity index 100% rename from src/org/oscim/renderer/layers/GridRenderLayer.java rename to vtm/src/org/oscim/renderer/layers/GridRenderLayer.java diff --git a/src/org/oscim/renderer/layers/LabelRenderLayer.java b/vtm/src/org/oscim/renderer/layers/LabelRenderLayer.java similarity index 100% rename from src/org/oscim/renderer/layers/LabelRenderLayer.java rename to vtm/src/org/oscim/renderer/layers/LabelRenderLayer.java diff --git a/src/org/oscim/renderer/layers/TextRenderLayer.java b/vtm/src/org/oscim/renderer/layers/TextRenderLayer.java similarity index 100% rename from src/org/oscim/renderer/layers/TextRenderLayer.java rename to vtm/src/org/oscim/renderer/layers/TextRenderLayer.java diff --git a/src/org/oscim/renderer/layers/test/AtlasRenderLayer.java b/vtm/src/org/oscim/renderer/layers/test/AtlasRenderLayer.java similarity index 100% rename from src/org/oscim/renderer/layers/test/AtlasRenderLayer.java rename to vtm/src/org/oscim/renderer/layers/test/AtlasRenderLayer.java diff --git a/src/org/oscim/renderer/layers/test/ModelRenderLayer.java b/vtm/src/org/oscim/renderer/layers/test/ModelRenderLayer.java similarity index 100% rename from src/org/oscim/renderer/layers/test/ModelRenderLayer.java rename to vtm/src/org/oscim/renderer/layers/test/ModelRenderLayer.java diff --git a/src/org/oscim/renderer/layers/test/SymbolRenderLayer.java b/vtm/src/org/oscim/renderer/layers/test/SymbolRenderLayer.java similarity index 100% rename from src/org/oscim/renderer/layers/test/SymbolRenderLayer.java rename to vtm/src/org/oscim/renderer/layers/test/SymbolRenderLayer.java diff --git a/src/org/oscim/renderer/layers/test/TestRenderLayer.java b/vtm/src/org/oscim/renderer/layers/test/TestRenderLayer.java similarity index 100% rename from src/org/oscim/renderer/layers/test/TestRenderLayer.java rename to vtm/src/org/oscim/renderer/layers/test/TestRenderLayer.java diff --git a/src/org/oscim/renderer/sublayers/BitmapLayer.java b/vtm/src/org/oscim/renderer/sublayers/BitmapLayer.java similarity index 100% rename from src/org/oscim/renderer/sublayers/BitmapLayer.java rename to vtm/src/org/oscim/renderer/sublayers/BitmapLayer.java diff --git a/src/org/oscim/renderer/sublayers/BitmapRenderer.java b/vtm/src/org/oscim/renderer/sublayers/BitmapRenderer.java similarity index 100% rename from src/org/oscim/renderer/sublayers/BitmapRenderer.java rename to vtm/src/org/oscim/renderer/sublayers/BitmapRenderer.java diff --git a/src/org/oscim/renderer/sublayers/ExtrusionLayer.java b/vtm/src/org/oscim/renderer/sublayers/ExtrusionLayer.java similarity index 99% rename from src/org/oscim/renderer/sublayers/ExtrusionLayer.java rename to vtm/src/org/oscim/renderer/sublayers/ExtrusionLayer.java index 0059ea5e..7c81d672 100644 --- a/src/org/oscim/renderer/sublayers/ExtrusionLayer.java +++ b/vtm/src/org/oscim/renderer/sublayers/ExtrusionLayer.java @@ -136,6 +136,7 @@ public class ExtrusionLayer extends Layer { // vector-tile-map does not produce implicty closed // polygons (yet) len -= 2; + Log.d(TAG, "explicit closed poly " + len); } // need at least three points @@ -403,6 +404,8 @@ public class ExtrusionLayer extends Layer { int size = mNumIndices * 2; vboIndices = BufferObject.get(size); vboIndices.loadBufferData(sbuf, size, GL20.GL_ELEMENT_ARRAY_BUFFER); + + GL.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0); // upload vertices sbuf.clear(); @@ -414,7 +417,6 @@ public class ExtrusionLayer extends Layer { vboVertices = BufferObject.get(size); vboVertices.loadBufferData(sbuf, size, GL20.GL_ARRAY_BUFFER); - GL.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0); GL.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0); for (int i = 0; i < 4; i++) @@ -503,7 +505,4 @@ public class ExtrusionLayer extends Layer { ShortBuffer io, int ioffset); - static { - System.loadLibrary("triangle"); - } } diff --git a/src/org/oscim/renderer/sublayers/Layer.java b/vtm/src/org/oscim/renderer/sublayers/Layer.java similarity index 100% rename from src/org/oscim/renderer/sublayers/Layer.java rename to vtm/src/org/oscim/renderer/sublayers/Layer.java diff --git a/src/org/oscim/renderer/sublayers/Layers.java b/vtm/src/org/oscim/renderer/sublayers/Layers.java similarity index 100% rename from src/org/oscim/renderer/sublayers/Layers.java rename to vtm/src/org/oscim/renderer/sublayers/Layers.java diff --git a/src/org/oscim/renderer/sublayers/LineLayer.java b/vtm/src/org/oscim/renderer/sublayers/LineLayer.java similarity index 100% rename from src/org/oscim/renderer/sublayers/LineLayer.java rename to vtm/src/org/oscim/renderer/sublayers/LineLayer.java diff --git a/src/org/oscim/renderer/sublayers/LineRenderer.java b/vtm/src/org/oscim/renderer/sublayers/LineRenderer.java similarity index 100% rename from src/org/oscim/renderer/sublayers/LineRenderer.java rename to vtm/src/org/oscim/renderer/sublayers/LineRenderer.java diff --git a/src/org/oscim/renderer/sublayers/LineTexLayer.java b/vtm/src/org/oscim/renderer/sublayers/LineTexLayer.java similarity index 100% rename from src/org/oscim/renderer/sublayers/LineTexLayer.java rename to vtm/src/org/oscim/renderer/sublayers/LineTexLayer.java diff --git a/src/org/oscim/renderer/sublayers/LineTexRenderer.java b/vtm/src/org/oscim/renderer/sublayers/LineTexRenderer.java similarity index 100% rename from src/org/oscim/renderer/sublayers/LineTexRenderer.java rename to vtm/src/org/oscim/renderer/sublayers/LineTexRenderer.java diff --git a/src/org/oscim/renderer/sublayers/MeshLayer.java b/vtm/src/org/oscim/renderer/sublayers/MeshLayer.java similarity index 98% rename from src/org/oscim/renderer/sublayers/MeshLayer.java rename to vtm/src/org/oscim/renderer/sublayers/MeshLayer.java index b8626e01..6514ef78 100644 --- a/src/org/oscim/renderer/sublayers/MeshLayer.java +++ b/vtm/src/org/oscim/renderer/sublayers/MeshLayer.java @@ -103,8 +103,4 @@ public class MeshLayer extends Layer { public static native int tessGetIndices(long ctx, short[] indices); - - static { - System.loadLibrary("tessellate"); - } } diff --git a/src/org/oscim/renderer/sublayers/MeshRenderer.java b/vtm/src/org/oscim/renderer/sublayers/MeshRenderer.java similarity index 100% rename from src/org/oscim/renderer/sublayers/MeshRenderer.java rename to vtm/src/org/oscim/renderer/sublayers/MeshRenderer.java diff --git a/src/org/oscim/renderer/sublayers/PolygonLayer.java b/vtm/src/org/oscim/renderer/sublayers/PolygonLayer.java similarity index 100% rename from src/org/oscim/renderer/sublayers/PolygonLayer.java rename to vtm/src/org/oscim/renderer/sublayers/PolygonLayer.java diff --git a/src/org/oscim/renderer/sublayers/PolygonRenderer.java b/vtm/src/org/oscim/renderer/sublayers/PolygonRenderer.java similarity index 100% rename from src/org/oscim/renderer/sublayers/PolygonRenderer.java rename to vtm/src/org/oscim/renderer/sublayers/PolygonRenderer.java diff --git a/src/org/oscim/renderer/sublayers/SymbolItem.java b/vtm/src/org/oscim/renderer/sublayers/SymbolItem.java similarity index 100% rename from src/org/oscim/renderer/sublayers/SymbolItem.java rename to vtm/src/org/oscim/renderer/sublayers/SymbolItem.java diff --git a/src/org/oscim/renderer/sublayers/SymbolLayer.java b/vtm/src/org/oscim/renderer/sublayers/SymbolLayer.java similarity index 100% rename from src/org/oscim/renderer/sublayers/SymbolLayer.java rename to vtm/src/org/oscim/renderer/sublayers/SymbolLayer.java diff --git a/src/org/oscim/renderer/sublayers/TextItem.java b/vtm/src/org/oscim/renderer/sublayers/TextItem.java similarity index 100% rename from src/org/oscim/renderer/sublayers/TextItem.java rename to vtm/src/org/oscim/renderer/sublayers/TextItem.java diff --git a/src/org/oscim/renderer/sublayers/TextLayer.java b/vtm/src/org/oscim/renderer/sublayers/TextLayer.java similarity index 100% rename from src/org/oscim/renderer/sublayers/TextLayer.java rename to vtm/src/org/oscim/renderer/sublayers/TextLayer.java diff --git a/src/org/oscim/renderer/sublayers/TextureItem.java b/vtm/src/org/oscim/renderer/sublayers/TextureItem.java similarity index 100% rename from src/org/oscim/renderer/sublayers/TextureItem.java rename to vtm/src/org/oscim/renderer/sublayers/TextureItem.java diff --git a/src/org/oscim/renderer/sublayers/TextureLayer.java b/vtm/src/org/oscim/renderer/sublayers/TextureLayer.java similarity index 100% rename from src/org/oscim/renderer/sublayers/TextureLayer.java rename to vtm/src/org/oscim/renderer/sublayers/TextureLayer.java diff --git a/src/org/oscim/renderer/sublayers/TextureRenderer.java b/vtm/src/org/oscim/renderer/sublayers/TextureRenderer.java similarity index 100% rename from src/org/oscim/renderer/sublayers/TextureRenderer.java rename to vtm/src/org/oscim/renderer/sublayers/TextureRenderer.java diff --git a/src/org/oscim/renderer/sublayers/VertexItem.java b/vtm/src/org/oscim/renderer/sublayers/VertexItem.java similarity index 100% rename from src/org/oscim/renderer/sublayers/VertexItem.java rename to vtm/src/org/oscim/renderer/sublayers/VertexItem.java diff --git a/src/org/oscim/theme/ExternalRenderTheme.java b/vtm/src/org/oscim/theme/ExternalRenderTheme.java similarity index 100% rename from src/org/oscim/theme/ExternalRenderTheme.java rename to vtm/src/org/oscim/theme/ExternalRenderTheme.java diff --git a/src/org/oscim/theme/IRenderCallback.java b/vtm/src/org/oscim/theme/IRenderCallback.java similarity index 100% rename from src/org/oscim/theme/IRenderCallback.java rename to vtm/src/org/oscim/theme/IRenderCallback.java diff --git a/src/org/oscim/theme/IRenderTheme.java b/vtm/src/org/oscim/theme/IRenderTheme.java similarity index 100% rename from src/org/oscim/theme/IRenderTheme.java rename to vtm/src/org/oscim/theme/IRenderTheme.java diff --git a/src/org/oscim/theme/InternalRenderTheme.java b/vtm/src/org/oscim/theme/InternalRenderTheme.java similarity index 78% rename from src/org/oscim/theme/InternalRenderTheme.java rename to vtm/src/org/oscim/theme/InternalRenderTheme.java index 434c3066..abd664bd 100644 --- a/src/org/oscim/theme/InternalRenderTheme.java +++ b/vtm/src/org/oscim/theme/InternalRenderTheme.java @@ -1,6 +1,6 @@ /* * Copyright 2010, 2011, 2012 mapsforge.org - * Copyright 2013 Hannes Janetzek.org + * Copyright 2013 Hannes Janetzek * * This program is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software @@ -17,14 +17,16 @@ package org.oscim.theme; import java.io.InputStream; +import org.oscim.backend.AssetAdapter; + /** * Enumeration of all internal rendering themes. */ public enum InternalRenderTheme implements Theme { - DEFAULT("/org/oscim/theme/styles/default.xml"), - TRONRENDER("/org/oscim/theme/styles/tronrender.xml"), - MAPNIK("/org/oscim/theme/styles/carto.xml"); + DEFAULT("styles/default.xml"), + TRONRENDER("styles/tronrender.xml"), + MAPNIK("styles/carto.xml"); private final String mPath; @@ -34,6 +36,7 @@ public enum InternalRenderTheme implements Theme { @Override public InputStream getRenderThemeAsStream() { - return InternalRenderTheme.class.getResourceAsStream(mPath); + return AssetAdapter.g.openFileAsStream(mPath); + //InternalRenderTheme.class.getResourceAsStream(mPath); } } diff --git a/src/org/oscim/theme/MatchingCacheKey.java b/vtm/src/org/oscim/theme/MatchingCacheKey.java similarity index 100% rename from src/org/oscim/theme/MatchingCacheKey.java rename to vtm/src/org/oscim/theme/MatchingCacheKey.java diff --git a/src/org/oscim/theme/RenderTheme.java b/vtm/src/org/oscim/theme/RenderTheme.java similarity index 100% rename from src/org/oscim/theme/RenderTheme.java rename to vtm/src/org/oscim/theme/RenderTheme.java diff --git a/src/org/oscim/theme/RenderThemeHandler.java b/vtm/src/org/oscim/theme/RenderThemeHandler.java similarity index 98% rename from src/org/oscim/theme/RenderThemeHandler.java rename to vtm/src/org/oscim/theme/RenderThemeHandler.java index 2843756f..4f7062fa 100644 --- a/src/org/oscim/theme/RenderThemeHandler.java +++ b/vtm/src/org/oscim/theme/RenderThemeHandler.java @@ -83,9 +83,13 @@ public class RenderThemeHandler extends DefaultHandler { public static IRenderTheme getRenderTheme(InputStream inputStream) throws SAXException, ParserConfigurationException, IOException { + RenderThemeHandler renderThemeHandler = new RenderThemeHandler(); - XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser() - .getXMLReader(); + + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + + XMLReader xmlReader = factory.newSAXParser().getXMLReader(); xmlReader.setContentHandler(renderThemeHandler); xmlReader.parse(new InputSource(inputStream)); return renderThemeHandler.mRenderTheme; @@ -357,7 +361,7 @@ public class RenderThemeHandler extends DefaultHandler { "missing attribute 'img' for element: " + elementName); - Bitmap bitmap = BitmapUtils.createBitmap("jar:" + img); + Bitmap bitmap = BitmapUtils.createBitmap("styles/" + img); mTextureAtlas = new TextureAtlas(bitmap); } diff --git a/src/org/oscim/theme/Theme.java b/vtm/src/org/oscim/theme/Theme.java similarity index 100% rename from src/org/oscim/theme/Theme.java rename to vtm/src/org/oscim/theme/Theme.java diff --git a/src/org/oscim/theme/renderTheme.xsd b/vtm/src/org/oscim/theme/renderTheme.xsd similarity index 100% rename from src/org/oscim/theme/renderTheme.xsd rename to vtm/src/org/oscim/theme/renderTheme.xsd diff --git a/src/org/oscim/theme/renderinstruction/Area.java b/vtm/src/org/oscim/theme/renderinstruction/Area.java similarity index 100% rename from src/org/oscim/theme/renderinstruction/Area.java rename to vtm/src/org/oscim/theme/renderinstruction/Area.java diff --git a/src/org/oscim/theme/renderinstruction/AreaLevel.java b/vtm/src/org/oscim/theme/renderinstruction/AreaLevel.java similarity index 100% rename from src/org/oscim/theme/renderinstruction/AreaLevel.java rename to vtm/src/org/oscim/theme/renderinstruction/AreaLevel.java diff --git a/src/org/oscim/theme/renderinstruction/Circle.java b/vtm/src/org/oscim/theme/renderinstruction/Circle.java similarity index 100% rename from src/org/oscim/theme/renderinstruction/Circle.java rename to vtm/src/org/oscim/theme/renderinstruction/Circle.java diff --git a/src/org/oscim/theme/renderinstruction/Line.java b/vtm/src/org/oscim/theme/renderinstruction/Line.java similarity index 100% rename from src/org/oscim/theme/renderinstruction/Line.java rename to vtm/src/org/oscim/theme/renderinstruction/Line.java diff --git a/src/org/oscim/theme/renderinstruction/LineSymbol.java b/vtm/src/org/oscim/theme/renderinstruction/LineSymbol.java similarity index 100% rename from src/org/oscim/theme/renderinstruction/LineSymbol.java rename to vtm/src/org/oscim/theme/renderinstruction/LineSymbol.java diff --git a/src/org/oscim/theme/renderinstruction/RenderInstruction.java b/vtm/src/org/oscim/theme/renderinstruction/RenderInstruction.java similarity index 100% rename from src/org/oscim/theme/renderinstruction/RenderInstruction.java rename to vtm/src/org/oscim/theme/renderinstruction/RenderInstruction.java diff --git a/src/org/oscim/theme/renderinstruction/Symbol.java b/vtm/src/org/oscim/theme/renderinstruction/Symbol.java similarity index 100% rename from src/org/oscim/theme/renderinstruction/Symbol.java rename to vtm/src/org/oscim/theme/renderinstruction/Symbol.java diff --git a/src/org/oscim/theme/renderinstruction/Text.java b/vtm/src/org/oscim/theme/renderinstruction/Text.java similarity index 100% rename from src/org/oscim/theme/renderinstruction/Text.java rename to vtm/src/org/oscim/theme/renderinstruction/Text.java diff --git a/src/org/oscim/theme/rule/AnyMatcher.java b/vtm/src/org/oscim/theme/rule/AnyMatcher.java similarity index 100% rename from src/org/oscim/theme/rule/AnyMatcher.java rename to vtm/src/org/oscim/theme/rule/AnyMatcher.java diff --git a/src/org/oscim/theme/rule/AttributeMatcher.java b/vtm/src/org/oscim/theme/rule/AttributeMatcher.java similarity index 100% rename from src/org/oscim/theme/rule/AttributeMatcher.java rename to vtm/src/org/oscim/theme/rule/AttributeMatcher.java diff --git a/src/org/oscim/theme/rule/Closed.java b/vtm/src/org/oscim/theme/rule/Closed.java similarity index 100% rename from src/org/oscim/theme/rule/Closed.java rename to vtm/src/org/oscim/theme/rule/Closed.java diff --git a/src/org/oscim/theme/rule/Element.java b/vtm/src/org/oscim/theme/rule/Element.java similarity index 100% rename from src/org/oscim/theme/rule/Element.java rename to vtm/src/org/oscim/theme/rule/Element.java diff --git a/src/org/oscim/theme/rule/MultiKeyMatcher.java b/vtm/src/org/oscim/theme/rule/MultiKeyMatcher.java similarity index 100% rename from src/org/oscim/theme/rule/MultiKeyMatcher.java rename to vtm/src/org/oscim/theme/rule/MultiKeyMatcher.java diff --git a/src/org/oscim/theme/rule/MultiValueMatcher.java b/vtm/src/org/oscim/theme/rule/MultiValueMatcher.java similarity index 100% rename from src/org/oscim/theme/rule/MultiValueMatcher.java rename to vtm/src/org/oscim/theme/rule/MultiValueMatcher.java diff --git a/src/org/oscim/theme/rule/NegativeMatcher.java b/vtm/src/org/oscim/theme/rule/NegativeMatcher.java similarity index 100% rename from src/org/oscim/theme/rule/NegativeMatcher.java rename to vtm/src/org/oscim/theme/rule/NegativeMatcher.java diff --git a/src/org/oscim/theme/rule/NegativeRule.java b/vtm/src/org/oscim/theme/rule/NegativeRule.java similarity index 100% rename from src/org/oscim/theme/rule/NegativeRule.java rename to vtm/src/org/oscim/theme/rule/NegativeRule.java diff --git a/src/org/oscim/theme/rule/PositiveRule.java b/vtm/src/org/oscim/theme/rule/PositiveRule.java similarity index 100% rename from src/org/oscim/theme/rule/PositiveRule.java rename to vtm/src/org/oscim/theme/rule/PositiveRule.java diff --git a/src/org/oscim/theme/rule/Rule.java b/vtm/src/org/oscim/theme/rule/Rule.java similarity index 100% rename from src/org/oscim/theme/rule/Rule.java rename to vtm/src/org/oscim/theme/rule/Rule.java diff --git a/src/org/oscim/theme/rule/RuleOptimizer.java b/vtm/src/org/oscim/theme/rule/RuleOptimizer.java similarity index 100% rename from src/org/oscim/theme/rule/RuleOptimizer.java rename to vtm/src/org/oscim/theme/rule/RuleOptimizer.java diff --git a/src/org/oscim/theme/rule/SingleKeyMatcher.java b/vtm/src/org/oscim/theme/rule/SingleKeyMatcher.java similarity index 100% rename from src/org/oscim/theme/rule/SingleKeyMatcher.java rename to vtm/src/org/oscim/theme/rule/SingleKeyMatcher.java diff --git a/src/org/oscim/theme/rule/SingleValueMatcher.java b/vtm/src/org/oscim/theme/rule/SingleValueMatcher.java similarity index 100% rename from src/org/oscim/theme/rule/SingleValueMatcher.java rename to vtm/src/org/oscim/theme/rule/SingleValueMatcher.java diff --git a/src/org/oscim/tilesource/ITileDataSink.java b/vtm/src/org/oscim/tilesource/ITileDataSink.java similarity index 100% rename from src/org/oscim/tilesource/ITileDataSink.java rename to vtm/src/org/oscim/tilesource/ITileDataSink.java diff --git a/src/org/oscim/tilesource/ITileDataSource.java b/vtm/src/org/oscim/tilesource/ITileDataSource.java similarity index 100% rename from src/org/oscim/tilesource/ITileDataSource.java rename to vtm/src/org/oscim/tilesource/ITileDataSource.java diff --git a/src/org/oscim/tilesource/MapInfo.java b/vtm/src/org/oscim/tilesource/MapInfo.java similarity index 100% rename from src/org/oscim/tilesource/MapInfo.java rename to vtm/src/org/oscim/tilesource/MapInfo.java diff --git a/src/org/oscim/tilesource/TileSource.java b/vtm/src/org/oscim/tilesource/TileSource.java similarity index 100% rename from src/org/oscim/tilesource/TileSource.java rename to vtm/src/org/oscim/tilesource/TileSource.java diff --git a/src/org/oscim/tilesource/TileSources.java b/vtm/src/org/oscim/tilesource/TileSources.java similarity index 100% rename from src/org/oscim/tilesource/TileSources.java rename to vtm/src/org/oscim/tilesource/TileSources.java diff --git a/src/org/oscim/tilesource/common/LwHttp.java b/vtm/src/org/oscim/tilesource/common/LwHttp.java similarity index 100% rename from src/org/oscim/tilesource/common/LwHttp.java rename to vtm/src/org/oscim/tilesource/common/LwHttp.java diff --git a/src/org/oscim/tilesource/common/PbfDecoder.java b/vtm/src/org/oscim/tilesource/common/PbfDecoder.java similarity index 100% rename from src/org/oscim/tilesource/common/PbfDecoder.java rename to vtm/src/org/oscim/tilesource/common/PbfDecoder.java diff --git a/src/org/oscim/tilesource/common/PbfTileDataSource.java b/vtm/src/org/oscim/tilesource/common/PbfTileDataSource.java similarity index 100% rename from src/org/oscim/tilesource/common/PbfTileDataSource.java rename to vtm/src/org/oscim/tilesource/common/PbfTileDataSource.java diff --git a/src/org/oscim/tilesource/common/UrlTileSource.java b/vtm/src/org/oscim/tilesource/common/UrlTileSource.java similarity index 100% rename from src/org/oscim/tilesource/common/UrlTileSource.java rename to vtm/src/org/oscim/tilesource/common/UrlTileSource.java diff --git a/src/org/oscim/tilesource/mapfile/Deserializer.java b/vtm/src/org/oscim/tilesource/mapfile/Deserializer.java similarity index 100% rename from src/org/oscim/tilesource/mapfile/Deserializer.java rename to vtm/src/org/oscim/tilesource/mapfile/Deserializer.java diff --git a/src/org/oscim/tilesource/mapfile/IndexCache.java b/vtm/src/org/oscim/tilesource/mapfile/IndexCache.java similarity index 100% rename from src/org/oscim/tilesource/mapfile/IndexCache.java rename to vtm/src/org/oscim/tilesource/mapfile/IndexCache.java diff --git a/src/org/oscim/tilesource/mapfile/IndexCacheEntryKey.java b/vtm/src/org/oscim/tilesource/mapfile/IndexCacheEntryKey.java similarity index 100% rename from src/org/oscim/tilesource/mapfile/IndexCacheEntryKey.java rename to vtm/src/org/oscim/tilesource/mapfile/IndexCacheEntryKey.java diff --git a/src/org/oscim/tilesource/mapfile/MapDatabase.java b/vtm/src/org/oscim/tilesource/mapfile/MapDatabase.java similarity index 100% rename from src/org/oscim/tilesource/mapfile/MapDatabase.java rename to vtm/src/org/oscim/tilesource/mapfile/MapDatabase.java diff --git a/src/org/oscim/tilesource/mapfile/MapFileTileSource.java b/vtm/src/org/oscim/tilesource/mapfile/MapFileTileSource.java similarity index 100% rename from src/org/oscim/tilesource/mapfile/MapFileTileSource.java rename to vtm/src/org/oscim/tilesource/mapfile/MapFileTileSource.java diff --git a/src/org/oscim/tilesource/mapfile/Projection.java b/vtm/src/org/oscim/tilesource/mapfile/Projection.java similarity index 100% rename from src/org/oscim/tilesource/mapfile/Projection.java rename to vtm/src/org/oscim/tilesource/mapfile/Projection.java diff --git a/src/org/oscim/tilesource/mapfile/QueryCalculations.java b/vtm/src/org/oscim/tilesource/mapfile/QueryCalculations.java similarity index 100% rename from src/org/oscim/tilesource/mapfile/QueryCalculations.java rename to vtm/src/org/oscim/tilesource/mapfile/QueryCalculations.java diff --git a/src/org/oscim/tilesource/mapfile/QueryParameters.java b/vtm/src/org/oscim/tilesource/mapfile/QueryParameters.java similarity index 100% rename from src/org/oscim/tilesource/mapfile/QueryParameters.java rename to vtm/src/org/oscim/tilesource/mapfile/QueryParameters.java diff --git a/src/org/oscim/tilesource/mapfile/ReadBuffer.java b/vtm/src/org/oscim/tilesource/mapfile/ReadBuffer.java similarity index 100% rename from src/org/oscim/tilesource/mapfile/ReadBuffer.java rename to vtm/src/org/oscim/tilesource/mapfile/ReadBuffer.java diff --git a/src/org/oscim/tilesource/mapfile/header/MapFileHeader.java b/vtm/src/org/oscim/tilesource/mapfile/header/MapFileHeader.java similarity index 100% rename from src/org/oscim/tilesource/mapfile/header/MapFileHeader.java rename to vtm/src/org/oscim/tilesource/mapfile/header/MapFileHeader.java diff --git a/src/org/oscim/tilesource/mapfile/header/MapFileInfo.java b/vtm/src/org/oscim/tilesource/mapfile/header/MapFileInfo.java similarity index 100% rename from src/org/oscim/tilesource/mapfile/header/MapFileInfo.java rename to vtm/src/org/oscim/tilesource/mapfile/header/MapFileInfo.java diff --git a/src/org/oscim/tilesource/mapfile/header/MapFileInfoBuilder.java b/vtm/src/org/oscim/tilesource/mapfile/header/MapFileInfoBuilder.java similarity index 100% rename from src/org/oscim/tilesource/mapfile/header/MapFileInfoBuilder.java rename to vtm/src/org/oscim/tilesource/mapfile/header/MapFileInfoBuilder.java diff --git a/src/org/oscim/tilesource/mapfile/header/OptionalFields.java b/vtm/src/org/oscim/tilesource/mapfile/header/OptionalFields.java similarity index 100% rename from src/org/oscim/tilesource/mapfile/header/OptionalFields.java rename to vtm/src/org/oscim/tilesource/mapfile/header/OptionalFields.java diff --git a/src/org/oscim/tilesource/mapfile/header/RequiredFields.java b/vtm/src/org/oscim/tilesource/mapfile/header/RequiredFields.java similarity index 100% rename from src/org/oscim/tilesource/mapfile/header/RequiredFields.java rename to vtm/src/org/oscim/tilesource/mapfile/header/RequiredFields.java diff --git a/src/org/oscim/tilesource/mapfile/header/SubFileParameter.java b/vtm/src/org/oscim/tilesource/mapfile/header/SubFileParameter.java similarity index 100% rename from src/org/oscim/tilesource/mapfile/header/SubFileParameter.java rename to vtm/src/org/oscim/tilesource/mapfile/header/SubFileParameter.java diff --git a/src/org/oscim/tilesource/mapfile/header/SubFileParameterBuilder.java b/vtm/src/org/oscim/tilesource/mapfile/header/SubFileParameterBuilder.java similarity index 100% rename from src/org/oscim/tilesource/mapfile/header/SubFileParameterBuilder.java rename to vtm/src/org/oscim/tilesource/mapfile/header/SubFileParameterBuilder.java diff --git a/src/org/oscim/tilesource/mapnik/MapnikVectorTileSource.java b/vtm/src/org/oscim/tilesource/mapnik/MapnikVectorTileSource.java similarity index 100% rename from src/org/oscim/tilesource/mapnik/MapnikVectorTileSource.java rename to vtm/src/org/oscim/tilesource/mapnik/MapnikVectorTileSource.java diff --git a/src/org/oscim/tilesource/mapnik/TileDecoder.java b/vtm/src/org/oscim/tilesource/mapnik/TileDecoder.java similarity index 100% rename from src/org/oscim/tilesource/mapnik/TileDecoder.java rename to vtm/src/org/oscim/tilesource/mapnik/TileDecoder.java diff --git a/src/org/oscim/tilesource/oscimap/OSciMap1TileSource.java b/vtm/src/org/oscim/tilesource/oscimap/OSciMap1TileSource.java similarity index 100% rename from src/org/oscim/tilesource/oscimap/OSciMap1TileSource.java rename to vtm/src/org/oscim/tilesource/oscimap/OSciMap1TileSource.java diff --git a/src/org/oscim/tilesource/oscimap/Tags.java b/vtm/src/org/oscim/tilesource/oscimap/Tags.java similarity index 100% rename from src/org/oscim/tilesource/oscimap/Tags.java rename to vtm/src/org/oscim/tilesource/oscimap/Tags.java diff --git a/src/org/oscim/tilesource/oscimap/TileDecoder.java b/vtm/src/org/oscim/tilesource/oscimap/TileDecoder.java similarity index 98% rename from src/org/oscim/tilesource/oscimap/TileDecoder.java rename to vtm/src/org/oscim/tilesource/oscimap/TileDecoder.java index f0dcabae..f97d7ef8 100644 --- a/src/org/oscim/tilesource/oscimap/TileDecoder.java +++ b/vtm/src/org/oscim/tilesource/oscimap/TileDecoder.java @@ -84,12 +84,12 @@ public class TileDecoder extends PbfDecoder { private static final long serialVersionUID = 1L; - @Override - protected boolean removeEldestEntry(Entry e) { - if (size() < MAX_TAGS_CACHE) - return false; - return true; - } +// @Override +// protected boolean removeEldestEntry(Entry e) { +// if (size() < MAX_TAGS_CACHE) +// return false; +// return true; +// } }); private boolean decode() throws IOException { diff --git a/src/org/oscim/tilesource/oscimap2/OSciMap2TileSource.java b/vtm/src/org/oscim/tilesource/oscimap2/OSciMap2TileSource.java similarity index 100% rename from src/org/oscim/tilesource/oscimap2/OSciMap2TileSource.java rename to vtm/src/org/oscim/tilesource/oscimap2/OSciMap2TileSource.java diff --git a/src/org/oscim/tilesource/oscimap2/Tags.java b/vtm/src/org/oscim/tilesource/oscimap2/Tags.java similarity index 100% rename from src/org/oscim/tilesource/oscimap2/Tags.java rename to vtm/src/org/oscim/tilesource/oscimap2/Tags.java diff --git a/src/org/oscim/tilesource/oscimap2/TileData.proto b/vtm/src/org/oscim/tilesource/oscimap2/TileData.proto similarity index 100% rename from src/org/oscim/tilesource/oscimap2/TileData.proto rename to vtm/src/org/oscim/tilesource/oscimap2/TileData.proto diff --git a/src/org/oscim/tilesource/oscimap4/OSciMap4TileSource.java b/vtm/src/org/oscim/tilesource/oscimap4/OSciMap4TileSource.java similarity index 100% rename from src/org/oscim/tilesource/oscimap4/OSciMap4TileSource.java rename to vtm/src/org/oscim/tilesource/oscimap4/OSciMap4TileSource.java diff --git a/src/org/oscim/tilesource/oscimap4/Tags.java b/vtm/src/org/oscim/tilesource/oscimap4/Tags.java similarity index 100% rename from src/org/oscim/tilesource/oscimap4/Tags.java rename to vtm/src/org/oscim/tilesource/oscimap4/Tags.java diff --git a/src/org/oscim/tilesource/oscimap4/TileData_v4.proto b/vtm/src/org/oscim/tilesource/oscimap4/TileData_v4.proto similarity index 100% rename from src/org/oscim/tilesource/oscimap4/TileData_v4.proto rename to vtm/src/org/oscim/tilesource/oscimap4/TileData_v4.proto diff --git a/src/org/oscim/tilesource/oscimap4/TileDecoder.java b/vtm/src/org/oscim/tilesource/oscimap4/TileDecoder.java similarity index 100% rename from src/org/oscim/tilesource/oscimap4/TileDecoder.java rename to vtm/src/org/oscim/tilesource/oscimap4/TileDecoder.java diff --git a/src/org/oscim/tilesource/test/TestTileSource.java b/vtm/src/org/oscim/tilesource/test/TestTileSource.java similarity index 100% rename from src/org/oscim/tilesource/test/TestTileSource.java rename to vtm/src/org/oscim/tilesource/test/TestTileSource.java diff --git a/src/org/oscim/utils/FastMath.java b/vtm/src/org/oscim/utils/FastMath.java similarity index 100% rename from src/org/oscim/utils/FastMath.java rename to vtm/src/org/oscim/utils/FastMath.java diff --git a/src/org/oscim/utils/GeometryUtils.java b/vtm/src/org/oscim/utils/GeometryUtils.java similarity index 100% rename from src/org/oscim/utils/GeometryUtils.java rename to vtm/src/org/oscim/utils/GeometryUtils.java diff --git a/src/org/oscim/utils/GlUtils.java b/vtm/src/org/oscim/utils/GlUtils.java similarity index 58% rename from src/org/oscim/utils/GlUtils.java rename to vtm/src/org/oscim/utils/GlUtils.java index 4fdd960f..f6d12ffa 100644 --- a/src/org/oscim/utils/GlUtils.java +++ b/vtm/src/org/oscim/utils/GlUtils.java @@ -30,51 +30,86 @@ import org.oscim.renderer.GLRenderer; public class GlUtils { private static final GL20 GL = GLAdapter.INSTANCE; + // public static native void setColor(int location, int color, float alpha); + // public static native void setColorBlend(int location, int color1, int + // color2, float mix); - public static native void setColor(int location, int color, float alpha); - public static native void setColorBlend(int location, int color1, int color2, float mix); + public static void setColor(int location, int color, float alpha) { + if (alpha >= 1) + alpha = ((color >>> 24) & 0xff) / 255f; + else if (alpha < 0) + alpha = 0; + else + alpha *= ((color >>> 24) & 0xff) / 255f; + + if (alpha == 1) { + GL.glUniform4f(location, + ((color >>> 16) & 0xff) / 255f, + ((color >>> 8) & 0xff) / 255f, + ((color >>> 0) & 0xff) / 255f, + alpha); + } else { + GL.glUniform4f(location, + ((color >>> 16) & 0xff) / 255f * alpha, + ((color >>> 8) & 0xff) / 255f * alpha, + ((color >>> 0) & 0xff) / 255f * alpha, + alpha); + } + } + + public static void setColorBlend(int location, int color1, int color2, float mix) { + float a1 = (((color1 >>> 24) & 0xff) / 255f) * (1 - mix); + float a2 = (((color2 >>> 24) & 0xff) / 255f) * mix; + + GL.glUniform4f + (location, + ((((color1 >>> 16) & 0xff) / 255f) * a1 + (((color2 >>> 16) & 0xff) / 255f) * a2), + ((((color1 >>> 8) & 0xff) / 255f) * a1 + (((color2 >>> 8) & 0xff) / 255f) * a2), + ((((color1 >>> 0) & 0xff) / 255f) * a1 + (((color2 >>> 0) & 0xff) / 255f) * a2), + (a1 + a2)); + } private static String TAG = "GlUtils"; public static void setTextureParameter(int min_filter, int mag_filter, int wrap_s, int wrap_t) { GL.glTexParameterf(GL20.GL_TEXTURE_2D, - GL20.GL_TEXTURE_MIN_FILTER, - min_filter); + GL20.GL_TEXTURE_MIN_FILTER, + min_filter); GL.glTexParameterf(GL20.GL_TEXTURE_2D, - GL20.GL_TEXTURE_MAG_FILTER, - mag_filter); + GL20.GL_TEXTURE_MAG_FILTER, + mag_filter); GL.glTexParameterf(GL20.GL_TEXTURE_2D, - GL20.GL_TEXTURE_WRAP_S, - wrap_s); // Set U Wrapping + GL20.GL_TEXTURE_WRAP_S, + wrap_s); // Set U Wrapping GL.glTexParameterf(GL20.GL_TEXTURE_2D, - GL20.GL_TEXTURE_WRAP_T, - wrap_t); // Set V Wrapping + GL20.GL_TEXTURE_WRAP_T, + wrap_t); // Set V Wrapping } -// /** -// * @param bitmap -// * ... -// * @return textureId -// */ -// public static int loadTextures(Bitmap bitmap) { -// -// int[] textures = new int[1]; -// GLES20.glGenTextures(1, textures, 0); -// -// int textureID = textures[0]; -// -// GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureID); -// -// setTextureParameter(GLES20.GL_LINEAR, GLES20.GL_LINEAR, -// GLES20.GL_CLAMP_TO_EDGE, GLES20.GL_CLAMP_TO_EDGE); -// -// GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); -// -// return textureID; -// } + // /** + // * @param bitmap + // * ... + // * @return textureId + // */ + // public static int loadTextures(Bitmap bitmap) { + // + // int[] textures = new int[1]; + // GLES20.glGenTextures(1, textures, 0); + // + // int textureID = textures[0]; + // + // GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureID); + // + // setTextureParameter(GLES20.GL_LINEAR, GLES20.GL_LINEAR, + // GLES20.GL_CLAMP_TO_EDGE, GLES20.GL_CLAMP_TO_EDGE); + // + // GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); + // + // return textureID; + // } public static int loadTexture(byte[] pixel, int width, int height, int format, - int min_filter, int mag_filter, int wrap_s, int wrap_t) { + int min_filter, int mag_filter, int wrap_s, int wrap_t) { int[] textureIds = GlUtils.glGenTextures(1); GL.glBindTexture(GL20.GL_TEXTURE_2D, textureIds[0]); @@ -86,7 +121,7 @@ public class GlUtils { buf.position(0); GL.glTexImage2D(GL20.GL_TEXTURE_2D, 0, format, width, height, 0, format, - GL20.GL_UNSIGNED_BYTE, buf); + GL20.GL_UNSIGNED_BYTE, buf); GL.glBindTexture(GL20.GL_TEXTURE_2D, 0); return textureIds[0]; @@ -118,9 +153,9 @@ public class GlUtils { } return loadTexture(pixel, sum, 1, GL20.GL_ALPHA, - GL20.GL_LINEAR, GL20.GL_LINEAR, - //GLES20.GL_NEAREST, GLES20.GL_NEAREST, - GL20.GL_REPEAT, GL20.GL_REPEAT); + GL20.GL_LINEAR, GL20.GL_LINEAR, + // GLES20.GL_NEAREST, GLES20.GL_NEAREST, + GL20.GL_REPEAT, GL20.GL_REPEAT); } /** @@ -210,20 +245,20 @@ public class GlUtils { return oom; } - -// public static void setBlendColors(int handle, float[] c1, float[] c2, float mix) { -// if (mix <= 0f) -// GLES20.glUniform4fv(handle, 1, c1, 0); -// else if (mix >= 1f) -// GLES20.glUniform4fv(handle, 1, c2, 0); -// else { -// GLES20.glUniform4f(handle, -// c1[0] * (1 - mix) + c2[0] * mix, -// c1[1] * (1 - mix) + c2[1] * mix, -// c1[2] * (1 - mix) + c2[2] * mix, -// c1[3] * (1 - mix) + c2[3] * mix); -// } -// } + // public static void setBlendColors(int handle, float[] c1, float[] c2, + // float mix) { + // if (mix <= 0f) + // GLES20.glUniform4fv(handle, 1, c1, 0); + // else if (mix >= 1f) + // GLES20.glUniform4fv(handle, 1, c2, 0); + // else { + // GLES20.glUniform4f(handle, + // c1[0] * (1 - mix) + c2[0] * mix, + // c1[1] * (1 - mix) + c2[1] * mix, + // c1[2] * (1 - mix) + c2[2] * mix, + // c1[3] * (1 - mix) + c2[3] * mix); + // } + // } public static void setColor(int handle, float[] c, float alpha) { if (alpha >= 1) { @@ -236,8 +271,8 @@ public class GlUtils { } GL.glUniform4f(handle, - c[0] * alpha, c[1] * alpha, - c[2] * alpha, c[3] * alpha); + c[0] * alpha, c[1] * alpha, + c[2] * alpha, c[3] * alpha); } } @@ -261,17 +296,16 @@ public class GlUtils { } /** - * public-domain function by Darel Rex Finley - * from http://alienryderflex.com/saturation.html + * public-domain function by Darel Rex Finley from + * http://alienryderflex.com/saturation.html * * @param color * The passed-in RGB values can be on any desired scale, such as * 0 to 1, or 0 to 255. * @param change - * 0.0 creates a black-and-white image. - * 0.5 reduces the color saturation by half. - * 1.0 causes no change. - * 2.0 doubles the color saturation. + * 0.0 creates a black-and-white image. 0.5 reduces the color + * saturation by half. 1.0 causes no change. 2.0 doubles the + * color saturation. */ public static void changeSaturation(float color[], float change) { float r = color[0]; @@ -283,7 +317,6 @@ public class GlUtils { color[2] = FastMath.clampN((float) (p + (b - p) * change)); } - public static void glUniform4fv(int location, int count, float[] val) { FloatBuffer buf = GLRenderer.getFloatBuffer(count * 4); buf.put(val); @@ -295,12 +328,16 @@ public class GlUtils { IntBuffer buf = GLRenderer.getIntBuffer(num); GL.glGenBuffers(num, buf); int[] ret = new int[num]; + buf.position(0); + buf.limit(num); buf.get(ret); return ret; } + public static void glDeleteBuffers(int num, int[] ids) { IntBuffer buf = GLRenderer.getIntBuffer(num); buf.put(ids, 0, num); + buf.position(0); GL.glDeleteBuffers(num, buf); } @@ -308,65 +345,73 @@ public class GlUtils { IntBuffer buf = GLRenderer.getIntBuffer(num); GL.glGenTextures(num, buf); int[] ret = new int[num]; + buf.position(0); + buf.limit(num); buf.get(ret); return ret; } + public static void glDeleteTextures(int num, int[] ids) { IntBuffer buf = GLRenderer.getIntBuffer(num); buf.put(ids, 0, num); + buf.position(0); GL.glDeleteTextures(num, buf); } -// private final static float[] mIdentity = { -// 1, 0, 0, 0, -// 0, 1, 0, 0, -// 0, 0, 1, 0, -// 0, 0, 0, 1 }; -// -// public static void setTileMatrix(float[] matrix, float tx, float ty, float s) { -// System.arraycopy(mIdentity, 0, matrix, 0, 16); -// // scale tile relative to map scale -// matrix[0] = matrix[5] = s / GLRenderer.COORD_SCALE; -// // translate relative to map center -// matrix[12] = tx * s; -// matrix[13] = ty * s; -// } -// -// public static void setTranslation(float[] matrix, float x, float y, float z) { -// System.arraycopy(mIdentity, 0, matrix, 0, 16); -// matrix[12] = x; -// matrix[13] = y; -// matrix[14] = z; -// } -// -// public static void setMatrix(float[] matrix, float tx, float ty, float scale) { -// System.arraycopy(mIdentity, 0, matrix, 0, 16); -// matrix[12] = tx; -// matrix[13] = ty; -// matrix[0] = scale; -// matrix[5] = scale; -// //matrix[10] = scale; -// } -// -// public static void setIdentity(float[] matrix) { -// System.arraycopy(mIdentity, 0, matrix, 0, 16); -// } -// -// public static void setScaleM(float[] matrix, float sx, float sy, float sz) { -// System.arraycopy(mIdentity, 0, matrix, 0, 16); -// matrix[0] = sx; -// matrix[5] = sy; -// matrix[10] = sz; -// } -// -// public static void addOffsetM(float[] matrix, int delta) { -// // from http://www.mathfor3dgameprogramming.com/code/Listing9.1.cpp -// // float n = MapViewPosition.VIEW_NEAR; -// // float f = MapViewPosition.VIEW_FAR; -// // float pz = 1; -// // float epsilon = -2.0f * f * n * delta / ((f + n) * pz * (pz + delta)); -// float epsilon = 1.0f / (1 << 11); -// -// matrix[10] *= 1.0f + epsilon * delta; -// } + // private final static float[] mIdentity = { + // 1, 0, 0, 0, + // 0, 1, 0, 0, + // 0, 0, 1, 0, + // 0, 0, 0, 1 }; + // + // public static void setTileMatrix(float[] matrix, float tx, float ty, + // float s) { + // System.arraycopy(mIdentity, 0, matrix, 0, 16); + // // scale tile relative to map scale + // matrix[0] = matrix[5] = s / GLRenderer.COORD_SCALE; + // // translate relative to map center + // matrix[12] = tx * s; + // matrix[13] = ty * s; + // } + // + // public static void setTranslation(float[] matrix, float x, float y, float + // z) { + // System.arraycopy(mIdentity, 0, matrix, 0, 16); + // matrix[12] = x; + // matrix[13] = y; + // matrix[14] = z; + // } + // + // public static void setMatrix(float[] matrix, float tx, float ty, float + // scale) { + // System.arraycopy(mIdentity, 0, matrix, 0, 16); + // matrix[12] = tx; + // matrix[13] = ty; + // matrix[0] = scale; + // matrix[5] = scale; + // //matrix[10] = scale; + // } + // + // public static void setIdentity(float[] matrix) { + // System.arraycopy(mIdentity, 0, matrix, 0, 16); + // } + // + // public static void setScaleM(float[] matrix, float sx, float sy, float + // sz) { + // System.arraycopy(mIdentity, 0, matrix, 0, 16); + // matrix[0] = sx; + // matrix[5] = sy; + // matrix[10] = sz; + // } + // + // public static void addOffsetM(float[] matrix, int delta) { + // // from http://www.mathfor3dgameprogramming.com/code/Listing9.1.cpp + // // float n = MapViewPosition.VIEW_NEAR; + // // float f = MapViewPosition.VIEW_FAR; + // // float pz = 1; + // // float epsilon = -2.0f * f * n * delta / ((f + n) * pz * (pz + delta)); + // float epsilon = 1.0f / (1 << 11); + // + // matrix[10] *= 1.0f + epsilon * delta; + // } } diff --git a/src/org/oscim/utils/IOUtils.java b/vtm/src/org/oscim/utils/IOUtils.java similarity index 100% rename from src/org/oscim/utils/IOUtils.java rename to vtm/src/org/oscim/utils/IOUtils.java diff --git a/src/org/oscim/utils/Interpolation.java b/vtm/src/org/oscim/utils/Interpolation.java similarity index 100% rename from src/org/oscim/utils/Interpolation.java rename to vtm/src/org/oscim/utils/Interpolation.java diff --git a/src/org/oscim/utils/LRUCache.java b/vtm/src/org/oscim/utils/LRUCache.java similarity index 100% rename from src/org/oscim/utils/LRUCache.java rename to vtm/src/org/oscim/utils/LRUCache.java diff --git a/src/org/oscim/utils/LineClipper.java b/vtm/src/org/oscim/utils/LineClipper.java similarity index 100% rename from src/org/oscim/utils/LineClipper.java rename to vtm/src/org/oscim/utils/LineClipper.java diff --git a/src/org/oscim/utils/MathUtils.java b/vtm/src/org/oscim/utils/MathUtils.java similarity index 100% rename from src/org/oscim/utils/MathUtils.java rename to vtm/src/org/oscim/utils/MathUtils.java diff --git a/src/org/oscim/utils/Matrix4.java b/vtm/src/org/oscim/utils/Matrix4.java similarity index 99% rename from src/org/oscim/utils/Matrix4.java rename to vtm/src/org/oscim/utils/Matrix4.java index b64fc661..9b1912d2 100644 --- a/src/org/oscim/utils/Matrix4.java +++ b/vtm/src/org/oscim/utils/Matrix4.java @@ -26,10 +26,6 @@ public class Matrix4 { private static final GL20 GL = GLAdapter.INSTANCE; - static { - System.loadLibrary("glutils"); - } - private final static String TAG = Matrix4.class.getName(); private final static boolean dbg = false; diff --git a/src/org/oscim/utils/OBB2D.java b/vtm/src/org/oscim/utils/OBB2D.java similarity index 100% rename from src/org/oscim/utils/OBB2D.java rename to vtm/src/org/oscim/utils/OBB2D.java diff --git a/src/org/oscim/utils/PausableThread.java b/vtm/src/org/oscim/utils/PausableThread.java similarity index 100% rename from src/org/oscim/utils/PausableThread.java rename to vtm/src/org/oscim/utils/PausableThread.java diff --git a/src/org/oscim/utils/ScanBox.java b/vtm/src/org/oscim/utils/ScanBox.java similarity index 100% rename from src/org/oscim/utils/ScanBox.java rename to vtm/src/org/oscim/utils/ScanBox.java diff --git a/src/org/oscim/utils/UTF8Decoder.java b/vtm/src/org/oscim/utils/UTF8Decoder.java similarity index 100% rename from src/org/oscim/utils/UTF8Decoder.java rename to vtm/src/org/oscim/utils/UTF8Decoder.java diff --git a/src/org/oscim/utils/Vec2.java b/vtm/src/org/oscim/utils/Vec2.java similarity index 100% rename from src/org/oscim/utils/Vec2.java rename to vtm/src/org/oscim/utils/Vec2.java diff --git a/src/org/oscim/utils/pool/Inlist.java b/vtm/src/org/oscim/utils/pool/Inlist.java similarity index 100% rename from src/org/oscim/utils/pool/Inlist.java rename to vtm/src/org/oscim/utils/pool/Inlist.java diff --git a/src/org/oscim/utils/pool/LList.java b/vtm/src/org/oscim/utils/pool/LList.java similarity index 100% rename from src/org/oscim/utils/pool/LList.java rename to vtm/src/org/oscim/utils/pool/LList.java diff --git a/src/org/oscim/utils/pool/Pool.java b/vtm/src/org/oscim/utils/pool/Pool.java similarity index 100% rename from src/org/oscim/utils/pool/Pool.java rename to vtm/src/org/oscim/utils/pool/Pool.java diff --git a/src/org/oscim/utils/pool/SyncPool.java b/vtm/src/org/oscim/utils/pool/SyncPool.java similarity index 100% rename from src/org/oscim/utils/pool/SyncPool.java rename to vtm/src/org/oscim/utils/pool/SyncPool.java diff --git a/src/org/oscim/utils/quadtree/QuadTree.java b/vtm/src/org/oscim/utils/quadtree/QuadTree.java similarity index 100% rename from src/org/oscim/utils/quadtree/QuadTree.java rename to vtm/src/org/oscim/utils/quadtree/QuadTree.java diff --git a/src/org/oscim/utils/quadtree/QuadTreeIndex.java b/vtm/src/org/oscim/utils/quadtree/QuadTreeIndex.java similarity index 100% rename from src/org/oscim/utils/quadtree/QuadTreeIndex.java rename to vtm/src/org/oscim/utils/quadtree/QuadTreeIndex.java diff --git a/src/org/oscim/view/DebugSettings.java b/vtm/src/org/oscim/view/DebugSettings.java similarity index 100% rename from src/org/oscim/view/DebugSettings.java rename to vtm/src/org/oscim/view/DebugSettings.java diff --git a/src/org/oscim/view/LayerManager.java b/vtm/src/org/oscim/view/LayerManager.java similarity index 100% rename from src/org/oscim/view/LayerManager.java rename to vtm/src/org/oscim/view/LayerManager.java diff --git a/src/org/oscim/view/MapAnimator.java b/vtm/src/org/oscim/view/MapAnimator.java similarity index 100% rename from src/org/oscim/view/MapAnimator.java rename to vtm/src/org/oscim/view/MapAnimator.java diff --git a/src/org/oscim/view/MapRenderCallback.java b/vtm/src/org/oscim/view/MapRenderCallback.java similarity index 100% rename from src/org/oscim/view/MapRenderCallback.java rename to vtm/src/org/oscim/view/MapRenderCallback.java diff --git a/src/org/oscim/view/MapView.java b/vtm/src/org/oscim/view/MapView.java similarity index 100% rename from src/org/oscim/view/MapView.java rename to vtm/src/org/oscim/view/MapView.java diff --git a/src/org/oscim/view/MapViewPosition.java b/vtm/src/org/oscim/view/MapViewPosition.java similarity index 100% rename from src/org/oscim/view/MapViewPosition.java rename to vtm/src/org/oscim/view/MapViewPosition.java