diff --git a/samples/java/jniviewer/.classpath b/samples/java/jniviewer/.classpath new file mode 100644 index 0000000000..51769745b2 --- /dev/null +++ b/samples/java/jniviewer/.classpath @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/samples/java/jniviewer/.externalToolBuilders/C++ Builder.launch b/samples/java/jniviewer/.externalToolBuilders/C++ Builder.launch new file mode 100644 index 0000000000..cbe7a127ce --- /dev/null +++ b/samples/java/jniviewer/.externalToolBuilders/C++ Builder.launch @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/samples/java/jniviewer/.gitignore b/samples/java/jniviewer/.gitignore new file mode 100644 index 0000000000..890072f0ac --- /dev/null +++ b/samples/java/jniviewer/.gitignore @@ -0,0 +1,4 @@ +/assets +/bin +/gen +/libs diff --git a/samples/java/jniviewer/.project b/samples/java/jniviewer/.project new file mode 100644 index 0000000000..c258add62f --- /dev/null +++ b/samples/java/jniviewer/.project @@ -0,0 +1,43 @@ + + + occtJniActivity + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.ui.externaltools.ExternalToolBuilder + auto,full,incremental, + + + LaunchConfigHandle + <project>/.externalToolBuilders/C++ Builder.launch + + + + + 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/samples/java/jniviewer/.settings/org.eclipse.jdt.core.prefs b/samples/java/jniviewer/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000000..b080d2ddc8 --- /dev/null +++ b/samples/java/jniviewer/.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/samples/java/jniviewer/AndroidManifest.xml b/samples/java/jniviewer/AndroidManifest.xml new file mode 100644 index 0000000000..c0cbbbbfe8 --- /dev/null +++ b/samples/java/jniviewer/AndroidManifest.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/java/jniviewer/ReadMe.md b/samples/java/jniviewer/ReadMe.md new file mode 100644 index 0000000000..c1dd21d7f9 --- /dev/null +++ b/samples/java/jniviewer/ReadMe.md @@ -0,0 +1,58 @@ +OCCT JniViewer sample for Android {#samples_java_android_occt} +================== + +This sample demonstrates simple way of using OCCT libraries in Android application written using Java. + +The connection between Java and OCCT (C++) level is provided by proxy library, libTKJniSample.so, written in C++ with exported JNI methods of Java class OcctJniRenderer. +The proxy library contains single C++ class OcctJni_Viewer encapsulating OCCT viewer and providing functionality to manipulate this viewer +and to import OCCT shapes from several supported formats of CAD files (IGES, STEP, BREP). + +This sample demonstrates indirect method of wrapping C++ to Java using manually created proxy library. +Alternative method is available, wrapping individual OCCT classes to Java equivalents so that their full API is available to Java user +and the code can be programmed on Java level similarly to C++ one. +See description of OCCT Java Wrapper in Advanced Samples and Tools on OCCT web site at +http://www.opencascade.org/support/products/advsamples + +Run Eclipse from ADT (Android Developer Tools) for building the sample. To import sample project perform +~~~~ + File -> Import... -> Android -> Existing Android code into Workspace +~~~~ +and specify this directory. The project re-build will be started immediately right after importation if "Build automatically" option is turned on (default in Eclipse). +Proxy library compilation and packaging is performed by NDK build script, called by "C++ Builder" configured within Eclipse project. +The path to "ndk-build" tool from Android NDK (Native Development Kit) should be specified in Eclipse project properties: +~~~~ + Project -> Properties -> Builders -> C++ Builder -> Edit -> Location +~~~~ + +Now paths to OCCT C++ libraries and additional components should be specified in "jni/Android.mk" file: +~~~~ +OCCT_ROOT := $(LOCAL_PATH)/../../../.. + +FREETYPE_INC := $(OCCT_ROOT)/../freetype/include/freetype2 +FREETYPE_LIBS := $(OCCT_ROOT)/../freetype/libs + +FREEIMAGE_INC := $(OCCT_ROOT)/../FreeImage/include +FREEIMAGE_LIBS := $(OCCT_ROOT)/../FreeImage/libs + +OCCT_INC := $(OCCT_ROOT)/inc +OCCT_LIBS := $(OCCT_ROOT)/and/libs +~~~~ +The list of extra components (Freetype, FreeImage) depends on OCCT configuration. +Variable $(TARGET_ARCH_ABI) is used within this script to refer to active architecture. +E.g. for 32-bit ARM build (see variable *APP_ABI* in "jni/Application.mk") +the folder *OCCT_LIBS* should contain sub-folder "armeabi-v7a" with OCCT libraries. + +FreeImage is optional and does not required for this sample, however you should include all extra libraries used for OCCT building +and load the explicitly from Java code within OcctJniActivity::loadNatives() method, including toolkits from OCCT itself in proper order: +~~~~ + if (!loadLibVerbose ("TKernel", aLoaded, aFailed) + || !loadLibVerbose ("TKMath", aLoaded, aFailed) + || !loadLibVerbose ("TKG2d", aLoaded, aFailed) +~~~~ +Note that C++ STL library is not part of Android system. +Thus application must package this library as well as extra component. +"gnustl_shared" STL implementation is expected within this sample. + +After successful build, the application can be packaged to Android: +- Deploy and run application on connected device or emulator directly from Eclipse using adb interface by menu items "Run" and "Debug". This would sign package with debug certificate. +- Prepare signed end-user package using wizard File -> Export -> Android -> Export Android Application. diff --git a/samples/java/jniviewer/jni/Android.mk b/samples/java/jniviewer/jni/Android.mk new file mode 100644 index 0000000000..912d5e3be3 --- /dev/null +++ b/samples/java/jniviewer/jni/Android.mk @@ -0,0 +1,215 @@ +LOCAL_PATH:= $(call my-dir) + +STL_INC := $(NDK_ROOT)/sources/cxx-stl/gnu-libstdc++/$(NDK_TOOLCHAIN_VERSION)/include $(NDK_ROOT)/sources/cxx-stl/gnu-libstdc++/$(NDK_TOOLCHAIN_VERSION)/libs/$(TARGET_ARCH_ABI)/include +#STL_LIB := $(NDK_ROOT)/sources/cxx-stl/gnu-libstdc++/$(NDK_TOOLCHAIN_VERSION)/libs/$(TARGET_ARCH_ABI)/libgnustl_static.a +STL_LIB := $(NDK_ROOT)/sources/cxx-stl/gnu-libstdc++/$(NDK_TOOLCHAIN_VERSION)/libs/$(TARGET_ARCH_ABI)/libgnustl_shared.so + +OCCT_ROOT := $(LOCAL_PATH)/../../../.. + +FREETYPE_INC := $(OCCT_ROOT)/../freetype/include/freetype2 +FREETYPE_LIBS := $(OCCT_ROOT)/../freetype/libs + +FREEIMAGE_INC := $(OCCT_ROOT)/../FreeImage/include +FREEIMAGE_LIBS := $(OCCT_ROOT)/../FreeImage/libs + +OCCT_INC := $(OCCT_ROOT)/inc +OCCT_LIBS := $(OCCT_ROOT)/and/libs + +ASSETDIR := $(LOCAL_PATH)/../assets + +$(ASSETDIR)/Shaders: $(ASSETDIR) + -mkdir -p $(ASSETDIR) + -mkdir -p $(ASSETDIR)/Shaders + cp -f -r $(OCCT_ROOT)/src/Shaders/*.* $(ASSETDIR)/Shaders + +$(ASSETDIR)/SHMessage: $(ASSETDIR) + -mkdir -p $(ASSETDIR) + -mkdir -p $(ASSETDIR)/SHMessage + cp -f -r $(OCCT_ROOT)/src/SHMessage/*.* $(ASSETDIR)/SHMessage + +$(ASSETDIR)/XSMessage: $(ASSETDIR) + -mkdir -p $(ASSETDIR) + -mkdir -p $(ASSETDIR)/XSMessage + cp -f -r $(OCCT_ROOT)/src/XSMessage/*.* $(ASSETDIR)/XSMessage + +$(ASSETDIR)/TObj: $(ASSETDIR) + -mkdir -p $(ASSETDIR) + -mkdir -p $(ASSETDIR)/TObj + cp -f -r $(OCCT_ROOT)/src/TObj/*.msg $(ASSETDIR)/TObj + +$(ASSETDIR)/UnitsAPI: $(ASSETDIR) + -mkdir -p $(ASSETDIR) + -mkdir -p $(ASSETDIR)/UnitsAPI + cp -f -r $(OCCT_ROOT)/src/UnitsAPI/*.dat $(ASSETDIR)/UnitsAPI + +pre_all: $(ASSETDIR)/Shaders $(ASSETDIR)/SHMessage $(ASSETDIR)/XSMessage $(ASSETDIR)/TObj $(ASSETDIR)/UnitsAPI + +jniall: pre_all all + +# STL libs +include $(CLEAR_VARS) +LOCAL_MODULE := SharedStl +LOCAL_EXPORT_C_INCLUDES := $(STL_INC) +LOCAL_SRC_FILES := $(STL_LIB) +include $(PREBUILT_SHARED_LIBRARY) + +# 3rd-parties used in OCCT +include $(CLEAR_VARS) +LOCAL_MODULE := FreeType +LOCAL_EXPORT_C_INCLUDES := $(FREETYPE_INC) +LOCAL_SRC_FILES := $(FREETYPE_LIBS)/$(TARGET_ARCH_ABI)/libfreetype.so +include $(PREBUILT_SHARED_LIBRARY) + +#include $(CLEAR_VARS) +#LOCAL_MODULE := FreeImage +#LOCAL_EXPORT_C_INCLUDES := $(FREEIMAGE_INC) +#LOCAL_SRC_FILES := $(FREEIMAGE_LIBS)/$(TARGET_ARCH_ABI)/libfreeimage.so +#include $(PREBUILT_SHARED_LIBRARY) + +# OCCT core +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKernel +LOCAL_EXPORT_C_INCLUDES := $(OCCT_INC) +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKernel.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKMath +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKMath.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKG2d +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKG2d.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKG3d +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKG3d.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKGeomBase +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKGeomBase.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKBRep +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKBRep.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKGeomAlgo +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKGeomAlgo.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKTopAlgo +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKTopAlgo.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKShHealing +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKShHealing.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKMesh +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKMesh.so +include $(PREBUILT_SHARED_LIBRARY) + +# OCCT Exchange +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKPrim +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKPrim.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKBO +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKBO.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKBool +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKBool.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKFillet +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKFillet.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKOffset +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKOffset.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKXSBase +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKXSBase.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKIGES +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKIGES.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKSTEPBase +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKSTEPBase.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKSTEPAttr +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKSTEPAttr.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKSTEP209 +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKSTEP209.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKSTEP +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKSTEP.so +include $(PREBUILT_SHARED_LIBRARY) + +# OCCT visualization +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKService +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKService.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKHLR +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKHLR.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKV3d +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKV3d.so +include $(PREBUILT_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := OcctTKOpenGl +LOCAL_SRC_FILES := $(OCCT_LIBS)/$(TARGET_ARCH_ABI)/libTKOpenGl.so +include $(PREBUILT_SHARED_LIBRARY) + +# our sample +include $(CLEAR_VARS) +LOCAL_MODULE := libTKJniSample +LOCAL_C_INCLUDES := $(STL_INC) +#LOCAL_STATIC_LIBRARIES := $(STL_LIB) does not work +LOCAL_CFLAGS := -Wall +LOCAL_CPP_EXTENSION := .cxx .cpp +LOCAL_CPP_FEATURES := rtti exceptions +LOCAL_SRC_FILES := OcctJni_Viewer.cxx OcctJni_Window.cxx OcctJni_MsgPrinter.cxx +LOCAL_SHARED_LIBRARIES := OcctTKernel OcctTKMath OcctTKG2d OcctTKG3d OcctTKGeomBase OcctTKBRep OcctTKGeomAlgo OcctTKTopAlgo OcctTKShHealing OcctTKMesh OcctTKPrim +LOCAL_SHARED_LIBRARIES += OcctTKIGES OcctTKSTEP OcctTKXSBase +LOCAL_SHARED_LIBRARIES += OcctTKService OcctTKHLR OcctTKV3d OcctTKOpenGl +LOCAL_SHARED_LIBRARIES += SharedStl +LOCAL_LDLIBS := -llog -lGLESv2 -lEGL + +#LOCAL_LDLIBS += $(STL_LIB) + +include $(BUILD_SHARED_LIBRARY) diff --git a/samples/java/jniviewer/jni/Application.mk b/samples/java/jniviewer/jni/Application.mk new file mode 100644 index 0000000000..1d6b63382a --- /dev/null +++ b/samples/java/jniviewer/jni/Application.mk @@ -0,0 +1,8 @@ +NDK_TOOLCHAIN_VERSION := 4.8 +APP_PLATFORM := android-15 + +APP_ABI := armeabi-v7a +#APP_ABI := all + +#APP_STL := gnustl_static +#APP_STL := stlport_static diff --git a/samples/java/jniviewer/jni/OcctJni_MsgPrinter.cxx b/samples/java/jniviewer/jni/OcctJni_MsgPrinter.cxx new file mode 100644 index 0000000000..8eef25f58c --- /dev/null +++ b/samples/java/jniviewer/jni/OcctJni_MsgPrinter.cxx @@ -0,0 +1,102 @@ +// Copyright (c) 2014 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include +#include + +#include + +IMPLEMENT_STANDARD_HANDLE (OcctJni_MsgPrinter, Message_Printer) +IMPLEMENT_STANDARD_RTTIEXT(OcctJni_MsgPrinter, Message_Printer) + +// ======================================================================= +// function : OcctJni_MsgPrinter +// purpose : +// ======================================================================= +OcctJni_MsgPrinter::OcctJni_MsgPrinter (JNIEnv* theJEnv, + jobject theJObj) +: myJEnv (theJEnv), + myJObj (theJEnv->NewGlobalRef (theJObj)), + myJMet (NULL) +{ + jclass aJClass = theJEnv->GetObjectClass (theJObj); + myJMet = theJEnv->GetMethodID (aJClass, "postMessage", "(Ljava/lang/String;)V"); + if (myJMet == NULL) + { + __android_log_write (ANDROID_LOG_FATAL, "jniSample", "Broken initialization of OcctJni_MsgPrinter!"); + } +} + +// ======================================================================= +// function : ~OcctJni_MsgPrinter +// purpose : +// ======================================================================= +OcctJni_MsgPrinter::~OcctJni_MsgPrinter() +{ + //myJEnv->DeleteGlobalRef (myJObj); +} + +// ======================================================================= +// function : Send +// purpose : +// ======================================================================= +void OcctJni_MsgPrinter::Send (const TCollection_ExtendedString& theString, + const Message_Gravity theGravity, + const Standard_Boolean theToPutEndl) const +{ + if (theGravity >= myTraceLevel) + { + const TCollection_AsciiString aStr (theString); + OcctJni_MsgPrinter::Send (aStr, theGravity, theToPutEndl); + } +} + +// ======================================================================= +// function : Send +// purpose : +// ======================================================================= +void OcctJni_MsgPrinter::Send (const TCollection_AsciiString& theString, + const Message_Gravity theGravity, + const Standard_Boolean theToPutEndl) const +{ + if (theGravity < myTraceLevel) + { + return; + } + + ///__android_log_write (ANDROID_LOG_DEBUG, "OcctJni_MsgPrinter", (TCollection_AsciiString(" @@ ") + theString).ToCString()); + if (myJMet == NULL) + { + return; + } + + jstring aJStr = myJEnv->NewStringUTF ((theString + "\n").ToCString()); + myJEnv->CallObjectMethod (myJObj, myJMet, aJStr); + myJEnv->DeleteLocalRef (aJStr); +} + +// ======================================================================= +// function : Send +// purpose : +// ======================================================================= +void OcctJni_MsgPrinter::Send (const Standard_CString& theString, + const Message_Gravity theGravity, + const Standard_Boolean theToPutEndl) const +{ + if (theGravity >= myTraceLevel) + { + OcctJni_MsgPrinter::Send (TCollection_AsciiString (theString), theGravity, theToPutEndl); + } +} diff --git a/samples/java/jniviewer/jni/OcctJni_MsgPrinter.hxx b/samples/java/jniviewer/jni/OcctJni_MsgPrinter.hxx new file mode 100644 index 0000000000..0735033b4d --- /dev/null +++ b/samples/java/jniviewer/jni/OcctJni_MsgPrinter.hxx @@ -0,0 +1,62 @@ +// Copyright (c) 2014 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#ifndef OcctJni_MsgPrinter_H +#define OcctJni_MsgPrinter_H + +#include + +#include + +// Class providing connection between messenger interfaces in C++ and Java layers. +class OcctJni_MsgPrinter : public Message_Printer +{ +public: + + //! Default constructor + OcctJni_MsgPrinter (JNIEnv* theJEnv, + jobject theJObj); + + //! Destructor. + ~OcctJni_MsgPrinter(); + + //! Redirection to TCollection_AsciiString method + virtual void Send (const TCollection_ExtendedString& theString, + const Message_Gravity theGravity, + const Standard_Boolean theToPutEndl) const; + + //! Redirection to TCollection_AsciiString method + virtual void Send (const Standard_CString& theString, + const Message_Gravity theGravity, + const Standard_Boolean theToPutEndl) const; + + //! Main printing method + virtual void Send (const TCollection_AsciiString& theString, + const Message_Gravity theGravity, + const Standard_Boolean theToPutEndl) const; + +private: + + JNIEnv* myJEnv; + jobject myJObj; + jmethodID myJMet; + +public: + + DEFINE_STANDARD_RTTI(OcctJni_MsgPrinter) + +}; + +DEFINE_STANDARD_HANDLE(OcctJni_MsgPrinter, Message_Printer) + +#endif // OcctJni_MsgPrinter_H diff --git a/samples/java/jniviewer/jni/OcctJni_Viewer.cxx b/samples/java/jniviewer/jni/OcctJni_Viewer.cxx new file mode 100644 index 0000000000..750f030e91 --- /dev/null +++ b/samples/java/jniviewer/jni/OcctJni_Viewer.cxx @@ -0,0 +1,810 @@ +// Copyright (c) 2014 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include +#include + +#include + +//! @return true if file exists +static bool isFileExist (const TCollection_AsciiString& thePath) +{ + struct stat64 aStatBuffer; + return stat64 (thePath.ToCString(), &aStatBuffer) == 0; +} + +//! Cut-off the last split character from the path and everything after it. +static TCollection_AsciiString getParentDir (const TCollection_AsciiString& thePath) +{ + TCollection_AsciiString aPath = thePath; + char* aSplitter = (char* )aPath.ToCString(); + for (char* anIter = aSplitter; *anIter != '\0'; ++anIter) + { + if (*anIter == '\\' + || *anIter == '/') + { + aSplitter = anIter; + } + } + *aSplitter = '\0'; // cut off file name or trailing folder + return TCollection_AsciiString (aPath.ToCString()); +} + +//! Set environment variable theVarName indicating location of resource +//! file theFile so as to correspond to actual location of this file. +//! +//! The resource file is searched in directory where Test.Draw.dll is located, +//! and if not found - also in subdirectory ../res from there. +//! If file is found, environment variable is set for C subsystem. +//! Otherwise, environment is not changed. +//! +//! If theToAddFileName is true, complete file name is set as value of the variable, +//! if theToAddFileName is false, only path is set. +Standard_Boolean setResourceEnv (const TCollection_AsciiString& theVarName, + const TCollection_AsciiString& theRoot, + const TCollection_AsciiString& theFile, + const Standard_Boolean theToAddFileName) +{ + // use location of current assembly to figure out possible location of resource + TCollection_AsciiString aBaseDir = theRoot; + + // check the same directory where binary is located + if (!isFileExist (aBaseDir + "/" + theFile)) + { + // check subdirectory ../res + aBaseDir = getParentDir (aBaseDir) + "/res"; + if (!isFileExist (aBaseDir + "/" + theFile)) + { + return Standard_False; + } + } + + // set C library environment + if (theToAddFileName) + { + aBaseDir = aBaseDir + "/" + theFile; + } + + OSD_Environment anEnv (theVarName, aBaseDir); + anEnv.Build(); + return Standard_True; +} + +// ======================================================================= +// function : OcctJni_Viewer +// purpose : +// ======================================================================= +OcctJni_Viewer::OcctJni_Viewer() +{ + // prepare necessary environment + TCollection_AsciiString aResRoot = "/data/data/com.opencascade.jnisample/files"; + + setResourceEnv ("CSF_TObjMessage", aResRoot + "/TObj", "TObj.msg", Standard_False); + setResourceEnv ("CSF_UnitsLexicon", aResRoot + "/UnitsAPI", "Lexi_Expr.dat", Standard_True); + setResourceEnv ("CSF_UnitsDefinition", aResRoot + "/UnitsAPI", "Units.dat", Standard_True); + setResourceEnv ("CSF_ShadersDirectory", aResRoot + "/Shaders", "Declarations.glsl", Standard_False); + setResourceEnv ("CSF_XSMessage", aResRoot + "/XSMessage", "XSTEP.us", Standard_False); + setResourceEnv ("CSF_SHMessage", aResRoot + "/XSMessage", "SHAPE.us", Standard_False); + //setResourceEnv ("CSF_PluginDefaults", "Plugin", Standard_False); + + // make sure OCCT loads the dictionary + //UnitsAPI::SetLocalSystem (UnitsAPI_SI); + + // load messages for TObj + Message_MsgFile::LoadFromEnv ("CSF_TObjMessage", "TObj", "msg"); +} + +// ======================================================================= +// function : init +// purpose : +// ======================================================================= +bool OcctJni_Viewer::init() +{ + EGLint aCfgId = 0; + int aWidth = 0, aHeight = 0; + EGLDisplay anEglDisplay = eglGetCurrentDisplay(); + EGLContext anEglContext = eglGetCurrentContext(); + EGLSurface anEglSurf = eglGetCurrentSurface (EGL_DRAW); + if (anEglDisplay == EGL_NO_DISPLAY + || anEglContext == EGL_NO_CONTEXT + || anEglSurf == EGL_NO_SURFACE) + { + Message::DefaultMessenger()->Send ("Error: No active EGL context!", Message_Fail); + release(); + return false; + } + + eglQuerySurface (anEglDisplay, anEglSurf, EGL_WIDTH, &aWidth); + eglQuerySurface (anEglDisplay, anEglSurf, EGL_HEIGHT, &aHeight); + eglQuerySurface (anEglDisplay, anEglSurf, EGL_CONFIG_ID, &aCfgId); + const EGLint aConfigAttribs[] = { EGL_CONFIG_ID, aCfgId, EGL_NONE }; + EGLint aNbConfigs = 0; + void* anEglConfig = NULL; + if (eglChooseConfig (anEglDisplay, aConfigAttribs, &anEglConfig, 1, &aNbConfigs) != EGL_TRUE) + { + Message::DefaultMessenger()->Send ("Error: EGL does not provide compatible configurations!", Message_Fail); + release(); + return false; + } + + TCollection_AsciiString anEglInfo = TCollection_AsciiString() + + "\n EGLVersion: " + eglQueryString (anEglDisplay, EGL_VERSION) + + "\n EGLVendor: " + eglQueryString (anEglDisplay, EGL_VENDOR) + + "\n EGLClient APIs: " + eglQueryString (anEglDisplay, EGL_CLIENT_APIS) + + "\n GLvendor: " + (const char* )glGetString (GL_VENDOR) + + "\n GLdevice: " + (const char* )glGetString (GL_RENDERER) + + "\n GLversion: " + (const char* )glGetString (GL_VERSION) + " [GLSL: " + (const char* )glGetString (GL_SHADING_LANGUAGE_VERSION) + "]"; + ::Message::DefaultMessenger()->Send (anEglInfo, Message_Info); + + if (!myViewer.IsNull()) + { + Handle(OpenGl_GraphicDriver) aDriver = Handle(OpenGl_GraphicDriver)::DownCast (myViewer->Driver()); + Handle(OcctJni_Window) aWindow = Handle(OcctJni_Window)::DownCast (myView->Window()); + if (!aDriver->InitEglContext (anEglDisplay, anEglContext, anEglConfig)) + { + Message::DefaultMessenger()->Send ("Error: OpenGl_GraphicDriver can not be initialized!", Message_Fail); + release(); + return false; + } + + aWindow->SetSize (aWidth, aHeight); + myView->SetWindow (aWindow, (Aspect_RenderingContext )anEglContext, NULL, NULL); + return true; + } + + Handle(OpenGl_GraphicDriver) aDriver = new OpenGl_GraphicDriver (NULL, Standard_False); + aDriver->ChangeOptions().buffersNoSwap = Standard_True; +//aDriver->ChangeOptions().glslWarnings = Standard_True; /// for debug only! + if (!aDriver->InitEglContext (anEglDisplay, anEglContext, anEglConfig)) + { + Message::DefaultMessenger()->Send ("Error: OpenGl_GraphicDriver can not be initialized!", Message_Fail); + release(); + return false; + } + + // create viewer + myViewer = new V3d_Viewer (aDriver, TCollection_ExtendedString("Viewer").ToExtString(), "", 1000.0, + V3d_XposYnegZpos, Quantity_NOC_BLACK, V3d_ZBUFFER, V3d_GOURAUD, V3d_WAIT, + Standard_True, Standard_False); + myViewer->SetDefaultLights(); + myViewer->SetLightOn(); + + // create AIS context + myContext = new AIS_InteractiveContext (myViewer); + //myContext->SetDisplayMode (AIS_WireFrame); + myContext->SetDisplayMode (AIS_Shaded); + + Handle(OcctJni_Window) aWindow = new OcctJni_Window (aWidth, aHeight); + myView = myViewer->CreateView(); + + myView->SetWindow (aWindow, (Aspect_RenderingContext )anEglContext, NULL, NULL); + myView->TriedronDisplay (Aspect_TOTP_RIGHT_LOWER, Quantity_NOC_WHITE, 0.08, V3d_ZBUFFER); + + initContent(); + return true; +} + +// ======================================================================= +// function : release +// purpose : +// ======================================================================= +void OcctJni_Viewer::release() +{ + myContext.Nullify(); + myView.Nullify(); + myViewer.Nullify(); +} + +// ======================================================================= +// function : resize +// purpose : +// ======================================================================= +void OcctJni_Viewer::resize (int theWidth, + int theHeight) +{ + if (myContext.IsNull()) + { + Message::DefaultMessenger()->Send ("Resize failed - view is unavailable", Message_Fail); + return; + } + + Handle(OpenGl_GraphicDriver) aDriver = Handle(OpenGl_GraphicDriver)::DownCast (myViewer->Driver()); + Handle(OcctJni_Window) aWindow = Handle(OcctJni_Window)::DownCast (myView->Window()); + aWindow->SetSize (theWidth, theHeight); + //myView->MustBeResized(); // can be used instead of SetWindow() when EGLsurface has not been changed + + EGLContext anEglContext = eglGetCurrentContext(); + myView->SetWindow (aWindow, (Aspect_RenderingContext )anEglContext, NULL, NULL); + //saveSnapshot ("/sdcard/Download/tt.png", theWidth, theHeight); +} + +// ======================================================================= +// function : initContent +// purpose : +// ======================================================================= +void OcctJni_Viewer::initContent() +{ + myContext->RemoveAll (Standard_False); + + OSD_Timer aTimer; + aTimer.Start(); + if (!myShape.IsNull()) + { + Handle(AIS_Shape) aShapePrs = new AIS_Shape (myShape); + myContext->Display (aShapePrs, Standard_False); + } + else + { + BRepPrimAPI_MakeBox aBuilder (1.0, 2.0, 3.0); + Handle(AIS_Shape) aShapePrs = new AIS_Shape (aBuilder.Shape()); + myContext->Display (aShapePrs, Standard_False); + } + myView->FitAll(); + + aTimer.Stop(); + Message::DefaultMessenger()->Send (TCollection_AsciiString() + "Presentation computed in " + aTimer.ElapsedTime() + " seconds", Message_Info); +} + +//! Load shape from IGES file +static TopoDS_Shape loadIGES (const TCollection_AsciiString& thePath) +{ + TopoDS_Shape aShape; + IGESControl_Reader aReader; + IFSelect_ReturnStatus aReadStatus = IFSelect_RetFail; + try + { + aReadStatus = aReader.ReadFile (thePath.ToCString()); + } + catch (Standard_Failure) + { + Message::DefaultMessenger()->Send ("Error: IGES reader, computation error", Message_Fail); + return aShape; + } + + if (aReadStatus != IFSelect_RetDone) + { + Message::DefaultMessenger()->Send ("Error: IGES reader, bad file format", Message_Fail); + return aShape; + } + + // now perform the translation + aReader.TransferRoots(); + if (aReader.NbShapes() <= 0) + { + Handle(XSControl_WorkSession) aWorkSession = new XSControl_WorkSession(); + aWorkSession->SelectNorm ("IGES"); + aReader.SetWS (aWorkSession, Standard_True); + aReader.SetReadVisible (Standard_False); + aReader.TransferRoots(); + } + if (aReader.NbShapes() <= 0) + { + Message::DefaultMessenger()->Send ("Error: IGES reader, no shapes has been found", Message_Fail); + return aShape; + } + return aReader.OneShape(); + /*TopoDS_Shape anImportedShape = aReader.OneShape(); + + // apply sewing on the imported shape + BRepBuilderAPI_Sewing aTool (0.0); + aTool.SetNonManifoldMode (Standard_False); + aTool.SetFloatingEdgesMode(Standard_True); + aTool.Load (anImportedShape); + aTool.Perform(); + TopoDS_Shape aSewedShape = aTool.SewedShape(); + + if (aSewedShape.IsNull()) + { + Message::DefaultMessenger()->Send ("Error: Sewing result is empty", Message_Fail); + return aShape; + } + if (aSewedShape.IsSame(anImportedShape)) + { + aShape = anImportedShape; + } + else + { + // apply shape healing + ShapeFix_Shape aShapeFixer(aSewedShape); + aShapeFixer.FixSolidMode() = 1; + aShapeFixer.FixFreeShellMode() = 1; + aShapeFixer.FixFreeFaceMode() = 1; + aShapeFixer.FixFreeWireMode() = 0; + aShapeFixer.FixSameParameterMode() = 0; + aShapeFixer.FixVertexPositionMode() = 0; + aShape = aShapeFixer.Perform() ? aShapeFixer.Shape() : aSewedShape; + } + return aShape;*/ +} + +//! Load shape from STEP file +static TopoDS_Shape loadSTEP (const TCollection_AsciiString& thePath) +{ + STEPControl_Reader aReader; + IFSelect_ReturnStatus aReadStatus = IFSelect_RetFail; + try + { + aReadStatus = aReader.ReadFile (thePath.ToCString()); + } + catch (Standard_Failure) + { + Message::DefaultMessenger()->Send ("Error: STEP reader, computation error", Message_Fail); + return TopoDS_Shape(); + } + + if (aReadStatus != IFSelect_RetDone) + { + Message::DefaultMessenger()->Send ("Error: STEP reader, bad file format", Message_Fail); + return TopoDS_Shape(); + } + else if (aReader.NbRootsForTransfer() <= 0) + { + Message::DefaultMessenger()->Send ("Error: STEP reader, shape is empty", Message_Fail); + return TopoDS_Shape(); + } + + // now perform the translation + aReader.TransferRoots(); + return aReader.OneShape(); +} + +// ======================================================================= +// function : open +// purpose : +// ======================================================================= +bool OcctJni_Viewer::open (const TCollection_AsciiString& thePath) +{ + myShape.Nullify(); + if (!myContext.IsNull()) + { + myContext->RemoveAll (Standard_False); + } + if (thePath.IsEmpty()) + { + return false; + } + + OSD_Timer aTimer; + aTimer.Start(); + TCollection_AsciiString aFormatStr; + const Standard_Integer aLen = thePath.Length(); + if (aLen >= 5 + && thePath.Value (aLen - 4) == '.') + { + aFormatStr = thePath.SubString (aLen - 3, aLen); + } + else if (aLen >= 4 + && thePath.Value (aLen - 3) == '.') + { + aFormatStr = thePath.SubString (aLen - 2, aLen); + } + else if (aLen >= 3 + && thePath.Value (aLen - 2) == '.') + { + aFormatStr = thePath.SubString (aLen - 1, aLen); + } + aFormatStr.LowerCase(); + + TopoDS_Shape aShape; + if (aFormatStr == "stp" + || aFormatStr == "step") + { + aShape = loadSTEP (thePath); + } + else if (aFormatStr == "igs" + || aFormatStr == "iges") + { + aShape = loadIGES (thePath); + } + else + // if (aFormatStr == "brep" + // || aFormatStr == "rle") + { + BRep_Builder aBuilder; + if (!BRepTools::Read (aShape, thePath.ToCString(), aBuilder)) + { + Message::DefaultMessenger()->Send (TCollection_AsciiString() + "Error: file '" + thePath + "' can not be opened!", Message_Info); + return false; + } + } + if (aShape.IsNull()) + { + return false; + } + aTimer.Stop(); + Message::DefaultMessenger()->Send (TCollection_AsciiString() + "File '" + thePath + "' loaded in " + aTimer.ElapsedTime() + " seconds", Message_Info); + + myShape = aShape; + if (myContext.IsNull()) + { + return true; + } + + aTimer.Reset(); + aTimer.Start(); + + Handle(AIS_Shape) aShapePrs = new AIS_Shape (aShape); + myContext->Display (aShapePrs, Standard_False); + myView->FitAll(); + + aTimer.Stop(); + Message::DefaultMessenger()->Send (TCollection_AsciiString() + "Presentation computed in " + aTimer.ElapsedTime() + " seconds", Message_Info); + return true; +} + +// ======================================================================= +// function : saveSnapshot +// purpose : +// ======================================================================= +bool OcctJni_Viewer::saveSnapshot (const TCollection_AsciiString& thePath, + int theWidth, + int theHeight) +{ + if (myContext.IsNull() + || thePath.IsEmpty()) + { + Message::DefaultMessenger()->Send ("Image dump failed - view is unavailable", Message_Fail); + return false; + } + + if (theWidth < 1 + || theHeight < 1) + { + myView->Window()->Size (theWidth, theHeight); + } + if (theWidth < 1 + || theHeight < 1) + { + Message::DefaultMessenger()->Send ("Image dump failed - view is unavailable", Message_Fail); + return false; + } + + Image_AlienPixMap anAlienImage; + if (!anAlienImage.InitTrash (Image_PixMap::ImgBGRA, theWidth, theHeight)) + { + Message::DefaultMessenger()->Send (TCollection_AsciiString() + "RGBA image " + theWidth + "x" + theHeight + " allocation failed", Message_Fail); + return false; + } + + // OpenGL ES does not support fetching data in BGRA format + // while FreeImage does not support RGBA format. + Image_PixMap anImage; + anImage.InitWrapper (Image_PixMap::ImgRGBA, + anAlienImage.ChangeData(), + anAlienImage.SizeX(), + anAlienImage.SizeY(), + anAlienImage.SizeRowBytes()); + if (!myView->ToPixMap (anImage, theWidth, theHeight, Graphic3d_BT_RGBA)) + { + Message::DefaultMessenger()->Send (TCollection_AsciiString() + "View dump to the image " + theWidth + "x" + theHeight + " failed", Message_Fail); + } + + for (Standard_Size aRow = 0; aRow < anAlienImage.SizeY(); ++aRow) + { + for (Standard_Size aCol = 0; aCol < anAlienImage.SizeX(); ++aCol) + { + Image_ColorRGBA& aPixel = anAlienImage.ChangeValue (aRow, aCol); + std::swap (aPixel.r(), aPixel.b()); + //aPixel.a() = 1.0; + } + } + + if (!anAlienImage.Save (thePath)) + { + Message::DefaultMessenger()->Send (TCollection_AsciiString() + "Image saving to path '" + thePath + "' failed", Message_Fail); + return false; + } + Message::DefaultMessenger()->Send (TCollection_AsciiString() + "View " + theWidth + "x" + theHeight + " dumped to image '" + thePath + "'", Message_Info); + return true; +} + +// ======================================================================= +// function : redraw +// purpose : +// ======================================================================= +void OcctJni_Viewer::redraw() +{ + if (myView.IsNull()) + { + return; + } + + myView->Redraw(); +} + +// ======================================================================= +// function : fitAll +// purpose : +// ======================================================================= +void OcctJni_Viewer::fitAll() +{ + if (myView.IsNull()) + { + return; + } + + myView->FitAll(); +} + +// ======================================================================= +// function : startRotation +// purpose : +// ======================================================================= +void OcctJni_Viewer::startRotation (int theStartX, + int theStartY) +{ + if (myView.IsNull()) + { + return; + } + + myView->StartRotation (theStartX, theStartY, 0.45); +} + +// ======================================================================= +// function : onRotation +// purpose : +// ======================================================================= +void OcctJni_Viewer::onRotation (int theX, + int theY) +{ + if (myView.IsNull()) + { + return; + } + + myView->Rotation (theX, theY); +} + +// ======================================================================= +// function : onPanning +// purpose : +// ======================================================================= +void OcctJni_Viewer::onPanning (int theDX, + int theDY) +{ + if (myView.IsNull()) + { + return; + } + + myView->Pan (theDX, theDY); +} + +// ======================================================================= +// function : onClick +// purpose : +// ======================================================================= +void OcctJni_Viewer::onClick (int theX, + int theY) +{ + if (myView.IsNull()) + { + return; + } + + myContext->MoveTo (theX, theY, myView, Standard_False); + myContext->Select (Standard_True); +} + +// ======================================================================= +// function : stopAction +// purpose : +// ======================================================================= +void OcctJni_Viewer::stopAction() +{ + if (myView.IsNull()) + { + return; + } +} + +#define jexp extern "C" JNIEXPORT + +jexp jlong JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppCreate (JNIEnv* theEnv, + jobject theObj) +{ + return jlong(new OcctJni_Viewer()); +} + +jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppDestroy (JNIEnv* theEnv, + jobject theObj, + jlong theCppPtr) +{ + delete (OcctJni_Viewer* )theCppPtr; + + Handle(Message_Messenger) aMsgMgr = Message::DefaultMessenger(); + aMsgMgr->RemovePrinters (STANDARD_TYPE (OcctJni_MsgPrinter)); +} + +jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppRelease (JNIEnv* theEnv, + jobject theObj, + jlong theCppPtr) +{ + ((OcctJni_Viewer* )theCppPtr)->release(); +} + +jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppInit (JNIEnv* theEnv, + jobject theObj, + jlong theCppPtr) +{ + Handle(Message_Messenger) aMsgMgr = Message::DefaultMessenger(); + aMsgMgr->RemovePrinters (STANDARD_TYPE (OcctJni_MsgPrinter)); + aMsgMgr->AddPrinter (new OcctJni_MsgPrinter (theEnv, theObj)); + ((OcctJni_Viewer* )theCppPtr)->init(); +} + +jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppResize (JNIEnv* theEnv, + jobject theObj, + jlong theCppPtr, + jint theWidth, + jint theHeight) +{ + ((OcctJni_Viewer* )theCppPtr)->resize (theWidth, theHeight); +} + +jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppOpen (JNIEnv* theEnv, + jobject theObj, + jlong theCppPtr, + jstring thePath) +{ + const char* aPathPtr = theEnv->GetStringUTFChars (thePath, 0); + const TCollection_AsciiString aPath (aPathPtr); + theEnv->ReleaseStringUTFChars (thePath, aPathPtr); + ((OcctJni_Viewer* )theCppPtr)->open (aPath); +} + +jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppRedraw (JNIEnv* theEnv, + jobject theObj, + jlong theCppPtr) +{ + ((OcctJni_Viewer* )theCppPtr)->redraw(); +} + +jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetAxoProj (JNIEnv* theEnv, + jobject theObj, + jlong theCppPtr) +{ + ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_XposYnegZpos); +} + +jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetXposProj (JNIEnv* theEnv, + jobject theObj, + jlong theCppPtr) +{ + ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_Xpos); +} + +jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetYposProj (JNIEnv* theEnv, + jobject theObj, + jlong theCppPtr) +{ + ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_Ypos); +} + +jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetZposProj (JNIEnv* theEnv, + jobject theObj, + jlong theCppPtr) +{ + ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_Zpos); +} + +jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetXnegProj (JNIEnv* theEnv, + jobject theObj, + jlong theCppPtr) +{ + ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_Xneg); +} + +jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetYnegProj (JNIEnv* theEnv, + jobject theObj, + jlong theCppPtr) +{ + ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_Yneg); +} + +jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppSetZnegProj (JNIEnv* theEnv, + jobject theObj, + jlong theCppPtr) +{ + ((OcctJni_Viewer* )theCppPtr)->setProj (V3d_Zneg); +} + +jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppFitAll (JNIEnv* theEnv, + jobject theObj, + jlong theCppPtr) +{ + ((OcctJni_Viewer* )theCppPtr)->fitAll(); +} + +jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppStartRotation (JNIEnv* theEnv, + jobject theObj, + jlong theCppPtr, + jint theStartX, + jint theStartY) +{ + ((OcctJni_Viewer* )theCppPtr)->startRotation (theStartX, theStartY); +} + +jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppOnRotation (JNIEnv* theEnv, + jobject theObj, + jlong theCppPtr, + jint theX, + jint theY) +{ + ((OcctJni_Viewer* )theCppPtr)->onRotation (theX, theY); +} + +jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppOnPanning (JNIEnv* theEnv, + jobject theObj, + jlong theCppPtr, + jint theDX, + jint theDY) +{ + ((OcctJni_Viewer* )theCppPtr)->onPanning (theDX, theDY); +} + +jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppOnClick (JNIEnv* theEnv, + jobject theObj, + jlong theCppPtr, + jint theX, + jint theY) +{ + ((OcctJni_Viewer* )theCppPtr)->onClick (theX, theY); +} + +jexp void JNICALL Java_com_opencascade_jnisample_OcctJniRenderer_cppStopAction (JNIEnv* theEnv, + jobject theObj, + jlong theCppPtr) +{ + ((OcctJni_Viewer* )theCppPtr)->stopAction(); +} + +jexp jlong JNICALL Java_com_opencascade_jnisample_OcctJniActivity_cppOcctMajorVersion (JNIEnv* theEnv, + jobject theObj) +{ + return OCC_VERSION_MAJOR; +} + +jexp jlong JNICALL Java_com_opencascade_jnisample_OcctJniActivity_cppOcctMinorVersion (JNIEnv* theEnv, + jobject theObj) +{ + return OCC_VERSION_MINOR; +} + +jexp jlong JNICALL Java_com_opencascade_jnisample_OcctJniActivity_cppOcctMicroVersion (JNIEnv* theEnv, + jobject theObj) +{ + return OCC_VERSION_MAINTENANCE; +} diff --git a/samples/java/jniviewer/jni/OcctJni_Viewer.hxx b/samples/java/jniviewer/jni/OcctJni_Viewer.hxx new file mode 100644 index 0000000000..6bd0a41f3e --- /dev/null +++ b/samples/java/jniviewer/jni/OcctJni_Viewer.hxx @@ -0,0 +1,88 @@ +// Copyright (c) 2014 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +#include +#include +#include +#include + +//! Main C++ back-end for activity. +class OcctJni_Viewer +{ + +public: + + //! Empty constructor + OcctJni_Viewer(); + + //! Initialize the viewer + bool init(); + + //! Release the viewer + void release(); + + //! Resize the viewer + void resize (int theWidth, + int theHeight); + + //! Open CAD file + bool open (const TCollection_AsciiString& thePath); + + //! Take snapshot + bool saveSnapshot (const TCollection_AsciiString& thePath, + int theWidth = 0, + int theHeight = 0); + + //! Viewer update. + void redraw(); + + //! Move camera + void setProj (V3d_TypeOfOrientation theProj) { if (!myView.IsNull()) myView->SetProj (theProj); } + + //! Fit All. + void fitAll(); + + //! Start rotation (remember first point position) + void startRotation (int theStartX, + int theStartY); + + //! Perform rotation (relative to first point) + void onRotation (int theX, + int theY); + + //! Perform panning + void onPanning (int theDX, + int theDY); + + //! Perform selection + void onClick (int theX, + int theY); + + //! Stop previously started action + void stopAction(); + +protected: + + //! Reset viewer content. + void initContent(); + +protected: + + Handle(V3d_Viewer) myViewer; + Handle(V3d_View) myView; + Handle(AIS_InteractiveContext) myContext; + TopoDS_Shape myShape; + +}; diff --git a/samples/java/jniviewer/jni/OcctJni_Window.cxx b/samples/java/jniviewer/jni/OcctJni_Window.cxx new file mode 100644 index 0000000000..8f50abde3b --- /dev/null +++ b/samples/java/jniviewer/jni/OcctJni_Window.cxx @@ -0,0 +1,17 @@ +// Copyright (c) 2014 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include + +IMPLEMENT_STANDARD_HANDLE (OcctJni_Window, Aspect_Window) +IMPLEMENT_STANDARD_RTTIEXT(OcctJni_Window, Aspect_Window) diff --git a/samples/java/jniviewer/jni/OcctJni_Window.hxx b/samples/java/jniviewer/jni/OcctJni_Window.hxx new file mode 100644 index 0000000000..d73c4a6629 --- /dev/null +++ b/samples/java/jniviewer/jni/OcctJni_Window.hxx @@ -0,0 +1,106 @@ +// Copyright (c) 2014 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#ifndef OcctJni_Window_H +#define OcctJni_Window_H + +#include + +//! This class defines dummy window +class OcctJni_Window : public Aspect_Window +{ + +public: + + //! Creates a wrapper over existing Window handle + OcctJni_Window (const int theWidth, const int theHeight) + : myWidth (theWidth), myHeight(theHeight) {} + + //! Returns native Window handle + virtual Aspect_Drawable NativeHandle() const { return 0; } + + //! Returns parent of native Window handle + virtual Aspect_Drawable NativeParentHandle() const { return 0; } + + virtual void Destroy() {} + + //! Opens the window + virtual void Map() const {} + + //! Closes the window + virtual void Unmap() const {} + + //! Applies the resizing to the window + virtual Aspect_TypeOfResize DoResize() const { return Aspect_TOR_UNKNOWN; } + + //! Apply the mapping change to the window + virtual Standard_Boolean DoMapping() const { return Standard_True; } + + //! Returns True if the window is opened + virtual Standard_Boolean IsMapped() const { return Standard_True; } + + //! Returns The Window RATIO equal to the physical WIDTH/HEIGHT dimensions + virtual Quantity_Ratio Ratio() const { return 1.0; } + + //! Returns The Window POSITION in PIXEL + virtual void Position (Standard_Integer& theX1, + Standard_Integer& theY1, + Standard_Integer& theX2, + Standard_Integer& theY2) const + { + theX1 = 0; + theX2 = myWidth; + theY1 = 0; + theY2 = myHeight; + } + + //! Set The Window POSITION in PIXEL + virtual void SetPosition (const Standard_Integer theX1, + const Standard_Integer theY1, + const Standard_Integer theX2, + const Standard_Integer theY2) + { + myWidth = theX2 - theX1; + myHeight = theY2 - theY1; + } + + //! Returns The Window SIZE in PIXEL + virtual void Size (Standard_Integer& theWidth, + Standard_Integer& theHeight) const + { + theWidth = myWidth; + theHeight = myHeight; + } + + //! Set The Window SIZE in PIXEL + virtual void SetSize (const Standard_Integer theWidth, + const Standard_Integer theHeight) + { + myWidth = theWidth; + myHeight = theHeight; + } + +private: + + int myWidth; + int myHeight; + +public: + + DEFINE_STANDARD_RTTI(OcctJni_Window) + +}; + +DEFINE_STANDARD_HANDLE(OcctJni_Window, Aspect_Window) + +#endif // OcctJni_Window_H diff --git a/samples/java/jniviewer/project.properties b/samples/java/jniviewer/project.properties new file mode 100644 index 0000000000..0840b4a059 --- /dev/null +++ b/samples/java/jniviewer/project.properties @@ -0,0 +1,14 @@ +# 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 edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-15 diff --git a/samples/java/jniviewer/res/drawable-hdpi/close_l.png b/samples/java/jniviewer/res/drawable-hdpi/close_l.png new file mode 100644 index 0000000000..0125c4aaf8 Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/close_l.png differ diff --git a/samples/java/jniviewer/res/drawable-hdpi/close_p.png b/samples/java/jniviewer/res/drawable-hdpi/close_p.png new file mode 100644 index 0000000000..b5ce8bdfd0 Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/close_p.png differ diff --git a/samples/java/jniviewer/res/drawable-hdpi/fit.png b/samples/java/jniviewer/res/drawable-hdpi/fit.png new file mode 100644 index 0000000000..70daee140c Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/fit.png differ diff --git a/samples/java/jniviewer/res/drawable-hdpi/ic_launcher.png b/samples/java/jniviewer/res/drawable-hdpi/ic_launcher.png new file mode 100755 index 0000000000..d27ba82c09 Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/ic_launcher.png differ diff --git a/samples/java/jniviewer/res/drawable-hdpi/info.png b/samples/java/jniviewer/res/drawable-hdpi/info.png new file mode 100644 index 0000000000..88d27c8e51 Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/info.png differ diff --git a/samples/java/jniviewer/res/drawable-hdpi/info_image.png b/samples/java/jniviewer/res/drawable-hdpi/info_image.png new file mode 100644 index 0000000000..bac9bead0f Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/info_image.png differ diff --git a/samples/java/jniviewer/res/drawable-hdpi/message.png b/samples/java/jniviewer/res/drawable-hdpi/message.png new file mode 100644 index 0000000000..a3dc8cc209 Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/message.png differ diff --git a/samples/java/jniviewer/res/drawable-hdpi/open.png b/samples/java/jniviewer/res/drawable-hdpi/open.png new file mode 100644 index 0000000000..68e52659f9 Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/open.png differ diff --git a/samples/java/jniviewer/res/drawable-hdpi/open_l.png b/samples/java/jniviewer/res/drawable-hdpi/open_l.png new file mode 100644 index 0000000000..6069ce3893 Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/open_l.png differ diff --git a/samples/java/jniviewer/res/drawable-hdpi/open_p.png b/samples/java/jniviewer/res/drawable-hdpi/open_p.png new file mode 100644 index 0000000000..c0898e6d6a Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/open_p.png differ diff --git a/samples/java/jniviewer/res/drawable-hdpi/proj_back.png b/samples/java/jniviewer/res/drawable-hdpi/proj_back.png new file mode 100755 index 0000000000..77ab199ab5 Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/proj_back.png differ diff --git a/samples/java/jniviewer/res/drawable-hdpi/proj_bottom.png b/samples/java/jniviewer/res/drawable-hdpi/proj_bottom.png new file mode 100755 index 0000000000..b0bb012866 Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/proj_bottom.png differ diff --git a/samples/java/jniviewer/res/drawable-hdpi/proj_front.png b/samples/java/jniviewer/res/drawable-hdpi/proj_front.png new file mode 100755 index 0000000000..b93d4d3e09 Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/proj_front.png differ diff --git a/samples/java/jniviewer/res/drawable-hdpi/proj_left.png b/samples/java/jniviewer/res/drawable-hdpi/proj_left.png new file mode 100755 index 0000000000..bce95df901 Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/proj_left.png differ diff --git a/samples/java/jniviewer/res/drawable-hdpi/proj_right.png b/samples/java/jniviewer/res/drawable-hdpi/proj_right.png new file mode 100755 index 0000000000..8cdb338b4c Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/proj_right.png differ diff --git a/samples/java/jniviewer/res/drawable-hdpi/proj_top.png b/samples/java/jniviewer/res/drawable-hdpi/proj_top.png new file mode 100755 index 0000000000..4ad098fc80 Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/proj_top.png differ diff --git a/samples/java/jniviewer/res/drawable-hdpi/view.png b/samples/java/jniviewer/res/drawable-hdpi/view.png new file mode 100644 index 0000000000..76ce87949a Binary files /dev/null and b/samples/java/jniviewer/res/drawable-hdpi/view.png differ diff --git a/samples/java/jniviewer/res/drawable-mdpi/ic_launcher.png b/samples/java/jniviewer/res/drawable-mdpi/ic_launcher.png new file mode 100755 index 0000000000..4b86dbf5e9 Binary files /dev/null and b/samples/java/jniviewer/res/drawable-mdpi/ic_launcher.png differ diff --git a/samples/java/jniviewer/res/drawable-xhdpi/ic_launcher.png b/samples/java/jniviewer/res/drawable-xhdpi/ic_launcher.png new file mode 100755 index 0000000000..cd79bea162 Binary files /dev/null and b/samples/java/jniviewer/res/drawable-xhdpi/ic_launcher.png differ diff --git a/samples/java/jniviewer/res/drawable-xxhdpi/ic_launcher.png b/samples/java/jniviewer/res/drawable-xxhdpi/ic_launcher.png new file mode 100755 index 0000000000..a34301f386 Binary files /dev/null and b/samples/java/jniviewer/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/samples/java/jniviewer/res/layout/activity_main.xml b/samples/java/jniviewer/res/layout/activity_main.xml new file mode 100644 index 0000000000..e7f3ebfd66 --- /dev/null +++ b/samples/java/jniviewer/res/layout/activity_main.xml @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/java/jniviewer/res/values/id.xml b/samples/java/jniviewer/res/values/id.xml new file mode 100644 index 0000000000..56bde943a6 --- /dev/null +++ b/samples/java/jniviewer/res/values/id.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/samples/java/jniviewer/res/values/strings.xml b/samples/java/jniviewer/res/values/strings.xml new file mode 100644 index 0000000000..6f2a431604 --- /dev/null +++ b/samples/java/jniviewer/res/values/strings.xml @@ -0,0 +1,35 @@ + + + OpenCASCADE JNI Java Sample + #484848 + #0099CC + #66252525 + + .png + .jpg + + + .brep + .rle + .iges + .igs + .step + .stp + + wireframe/shading + color + material + transparency + show/hide hidden lines + + OpenCASCADE JNI Java Sample

+

Simple viewer for BREP, STEP and IGES files.

+

Driven by Open CASCADE Technology %d.%d.%d.

+

Copyright 2014 OPEN CASCADE SAS.

+

+

http://www.opencascade.com

+

http://www.opencascade.org

+ ]]> +
+
diff --git a/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniActivity.java b/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniActivity.java new file mode 100644 index 0000000000..7cb5366496 --- /dev/null +++ b/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniActivity.java @@ -0,0 +1,778 @@ +// Copyright (c) 2014 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +package com.opencascade.jnisample; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.List; + +import android.app.Activity; +import android.content.Context; + +import android.content.Intent; +import android.content.res.AssetManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Point; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.Environment; + +import android.text.Html; +import android.text.Html.ImageGetter; +import android.text.Spanned; +import android.util.TypedValue; +import android.view.Display; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.view.ViewGroup.LayoutParams; +import android.widget.TextView; +import android.widget.Toast; + +//! Main activity +public class OcctJniActivity extends Activity implements OnClickListener +{ + + //! Auxiliary method to print temporary info messages + public static void printShortInfo (Activity theActivity, + CharSequence theInfo) + { + Context aCtx = theActivity.getApplicationContext(); + Toast aToast = Toast.makeText (aCtx, theInfo, Toast.LENGTH_LONG); + aToast.show(); + } + + //! Load single native library + private static boolean loadLibVerbose (String theLibName, + StringBuilder theLoadedInfo, + StringBuilder theFailedInfo) + { + try + { + System.loadLibrary (theLibName); + theLoadedInfo.append ("Info: native library \""); + theLoadedInfo.append (theLibName); + theLoadedInfo.append ("\" has been loaded\n"); + return true; + } + catch (java.lang.UnsatisfiedLinkError theError) + { + theFailedInfo.append ("Error: native library \""); + theFailedInfo.append (theLibName); + theFailedInfo.append ("\" is unavailable:\n " + theError.getMessage()); + return false; + } + catch (SecurityException theError) + { + theFailedInfo.append ("Error: native library \""); + theFailedInfo.append (theLibName); + theFailedInfo.append ("\" can not be loaded for security reasons:\n " + theError.getMessage()); + return false; + } + } + + public static boolean wasNativesLoadCalled = false; + public static boolean areNativeLoaded = false; + public static String nativeLoaded = ""; + public static String nativeFailed = ""; + + //! Auxiliary method to load native libraries + public boolean loadNatives() + { + if (wasNativesLoadCalled) + { + return areNativeLoaded; + } + wasNativesLoadCalled = true; + StringBuilder aLoaded = new StringBuilder(); + StringBuilder aFailed = new StringBuilder(); + + // copy OCCT resources + String aResFolder = getFilesDir().getAbsolutePath(); + copyAssetFolder (getAssets(), "Shaders", aResFolder + "/Shaders"); + copyAssetFolder (getAssets(), "SHMessage", aResFolder + "/SHMessage"); + copyAssetFolder (getAssets(), "XSMessage", aResFolder + "/XSMessage"); + copyAssetFolder (getAssets(), "TObj", aResFolder + "/TObj"); + copyAssetFolder (getAssets(), "UnitsAPI", aResFolder + "/UnitsAPI"); + + // C++ runtime + loadLibVerbose ("gnustl_shared", aLoaded, aFailed); + + // 3rd-parties + loadLibVerbose ("freetype", aLoaded, aFailed); + loadLibVerbose ("freeimage", aLoaded, aFailed); + + if (// OCCT modeling + !loadLibVerbose ("TKernel", aLoaded, aFailed) + || !loadLibVerbose ("TKMath", aLoaded, aFailed) + || !loadLibVerbose ("TKG2d", aLoaded, aFailed) + || !loadLibVerbose ("TKG3d", aLoaded, aFailed) + || !loadLibVerbose ("TKGeomBase", aLoaded, aFailed) + || !loadLibVerbose ("TKBRep", aLoaded, aFailed) + || !loadLibVerbose ("TKGeomAlgo", aLoaded, aFailed) + || !loadLibVerbose ("TKTopAlgo", aLoaded, aFailed) + || !loadLibVerbose ("TKShHealing", aLoaded, aFailed) + || !loadLibVerbose ("TKMesh", aLoaded, aFailed) + // exchange + || !loadLibVerbose ("TKPrim", aLoaded, aFailed) + || !loadLibVerbose ("TKBO", aLoaded, aFailed) + || !loadLibVerbose ("TKBool", aLoaded, aFailed) + || !loadLibVerbose ("TKFillet", aLoaded, aFailed) + || !loadLibVerbose ("TKOffset", aLoaded, aFailed) + || !loadLibVerbose ("TKXSBase", aLoaded, aFailed) + || !loadLibVerbose ("TKIGES", aLoaded, aFailed) + || !loadLibVerbose ("TKSTEPBase", aLoaded, aFailed) + || !loadLibVerbose ("TKSTEPAttr", aLoaded, aFailed) + || !loadLibVerbose ("TKSTEP209", aLoaded, aFailed) + || !loadLibVerbose ("TKSTEP", aLoaded, aFailed) + // OCCT Visualization + || !loadLibVerbose ("TKService", aLoaded, aFailed) + || !loadLibVerbose ("TKHLR", aLoaded, aFailed) + || !loadLibVerbose ("TKV3d", aLoaded, aFailed) + || !loadLibVerbose ("TKOpenGl", aLoaded, aFailed) + // application code + || !loadLibVerbose ("TKJniSample", aLoaded, aFailed)) + { + nativeLoaded = aLoaded.toString(); + nativeFailed = aFailed.toString(); + areNativeLoaded = false; + //exitWithError (theActivity, "Broken apk?\n" + theFailedInfo); + return false; + } + nativeLoaded = aLoaded.toString(); + areNativeLoaded = true; + return true; + } + + //! Create activity + @Override protected void onCreate (Bundle theBundle) + { + super.onCreate (theBundle); + + boolean isLoaded = loadNatives(); + if (!isLoaded) + { + printShortInfo (this, nativeFailed); + OcctJniLogger.postMessage (nativeLoaded + "\n" + nativeFailed); + } + + setContentView (R.layout.activity_main); + + myOcctView = (OcctJniView )findViewById (R.id.custom_view); + myMessageTextView = (TextView )findViewById (R.id.message_view); + OcctJniLogger.setTextView (myMessageTextView); + + createViewAndButtons (Configuration.ORIENTATION_LANDSCAPE); + + myButtonPreferSize = defineButtonSize ((LinearLayout )findViewById (R.id.panel_menu)); + ImageButton aScrollBtn = (ImageButton )findViewById (R.id.scroll_btn); + aScrollBtn.setY (myButtonPreferSize); + aScrollBtn.setOnTouchListener (new View.OnTouchListener() + { + @Override + public boolean onTouch (View theView, MotionEvent theEvent) + { + return onScrollBtnTouch (theView, theEvent); + } + }); + + onConfigurationChanged (getResources().getConfiguration()); + + Intent anIntent = getIntent(); + Uri aDataUrl = anIntent != null ? anIntent.getData() : null; + String aDataPath = aDataUrl != null ? aDataUrl.getPath() : ""; + myOcctView.open (aDataPath); + myLastPath = aDataPath; + } + + //! Handle scroll events + private boolean onScrollBtnTouch (View theView, + MotionEvent theEvent) + { + switch (theEvent.getAction()) + { + case MotionEvent.ACTION_DOWN: + { + LinearLayout aPanelMenu = (LinearLayout )findViewById (R.id.panel_menu); + boolean isLandscape = (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE); + if (aPanelMenu.getVisibility() == View.VISIBLE) + { + aPanelMenu.setVisibility (View.GONE); + if (!isLandscape) + { + ((ImageButton )theView).setImageResource (R.drawable.open_p); + theView.setY (0); + } + else + { + ((ImageButton )theView).setImageResource (R.drawable.open_l); + theView.setX (0); + } + } + else + { + aPanelMenu.setVisibility (View.VISIBLE); + if (!isLandscape) + { + ((ImageButton )theView).setImageResource (R.drawable.close_p); + theView.setY (myButtonPreferSize); + } + else + { + ((ImageButton )theView).setImageResource (R.drawable.close_l); + theView.setX (myButtonPreferSize); + } + } + break; + } + } + return false; + } + + //! Initialize views and buttons + private void createViewAndButtons (int theOrientation) + { + // open button + ImageButton anOpenButton = (ImageButton )findViewById (R.id.open); + anOpenButton.setOnClickListener (this); + + // fit all + ImageButton aFitAllButton = (ImageButton )findViewById (R.id.fit); + aFitAllButton.setOnClickListener (this); + aFitAllButton.setOnTouchListener (new View.OnTouchListener() + { + @Override + public boolean onTouch (View theView, MotionEvent theEvent) + { + return onTouchButton (theView, theEvent); + } + }); + + // message + ImageButton aMessageButton = (ImageButton )findViewById (R.id.message); + aMessageButton.setOnClickListener (this); + + // info + ImageButton anInfoButton = (ImageButton )findViewById (R.id.info); + anInfoButton.setOnClickListener (this); + + // font for text view + TextView anInfoView = (TextView )findViewById (R.id.info_view); + anInfoView.setTextSize (TypedValue.COMPLEX_UNIT_SP, 18); + + // add submenu buttons + createSubmenuBtn (R.id.view, R.id.view_group, + Arrays.asList (R.id.proj_front, R.id.proj_top, R.id.proj_left, + R.id.proj_back, R.id.proj_bottom, R.id.proj_right), + Arrays.asList (R.drawable.proj_front, R.drawable.proj_top, R.drawable.proj_left, + R.drawable.proj_back, R.drawable.proj_bottom, R.drawable.proj_right), + 4); + } + + @Override protected void onNewIntent (Intent theIntent) + { + super.onNewIntent (theIntent); + setIntent (theIntent); + } + + @Override protected void onDestroy() + { + super.onDestroy(); + OcctJniLogger.setTextView (null); + } + + @Override protected void onPause() + { + super.onPause(); + myOcctView.onPause(); + } + + @Override protected void onResume() + { + super.onResume(); + myOcctView.onResume(); + + Intent anIntent = getIntent(); + Uri aDataUrl = anIntent != null ? anIntent.getData() : null; + String aDataPath = aDataUrl != null ? aDataUrl.getPath() : ""; + if (!aDataPath.equals (myLastPath)) + { + myOcctView.open (aDataPath); + myLastPath = aDataPath; + } + } + + //! Copy folder from assets + private boolean copyAssetFolder (AssetManager theAssetMgr, + String theAssetFolder, + String theFolderPathTo) + { + try + { + String[] aFiles = theAssetMgr.list (theAssetFolder); + File aFolder = new File (theFolderPathTo); + aFolder.mkdirs(); + boolean isOk = true; + for (String aFileIter : aFiles) + { + if (aFileIter.contains (".")) + { + isOk &= copyAsset (theAssetMgr, + theAssetFolder + "/" + aFileIter, + theFolderPathTo + "/" + aFileIter); + } + else + { + isOk &= copyAssetFolder (theAssetMgr, + theAssetFolder + "/" + aFileIter, + theFolderPathTo + "/" + aFileIter); + } + } + return isOk; + } + catch (Exception theError) + { + theError.printStackTrace(); + return false; + } + } + + //! Copy single file from assets + private boolean copyAsset (AssetManager theAssetMgr, + String thePathFrom, + String thePathTo) + { + try + { + InputStream aStreamIn = theAssetMgr.open (thePathFrom); + File aFileTo = new File (thePathTo); + aFileTo.createNewFile(); + OutputStream aStreamOut = new FileOutputStream (thePathTo); + copyStreamContent (aStreamIn, aStreamOut); + aStreamIn.close(); + aStreamIn = null; + aStreamOut.flush(); + aStreamOut.close(); + aStreamOut = null; + return true; + } + catch (Exception theError) + { + theError.printStackTrace(); + return false; + } + } + + //! Copy single file + private static void copyStreamContent (InputStream theIn, + OutputStream theOut) throws IOException + { + byte[] aBuffer = new byte[1024]; + int aNbReadBytes = 0; + while ((aNbReadBytes = theIn.read (aBuffer)) != -1) + { + theOut.write (aBuffer, 0, aNbReadBytes); + } + } + + //! Show/hide text view + private void switchTextView (TextView theTextView, + ImageButton theClickedBtn, + boolean theToSwitchOn) + { + if (theTextView != null + && theTextView.getVisibility() == View.GONE + && theToSwitchOn) + { + theTextView.setVisibility (View.VISIBLE); + theClickedBtn.setBackgroundColor (getResources().getColor(R.color.pressedBtnColor)); + setTextViewPosition (theTextView); + } + else + { + theTextView.setVisibility (View.GONE); + theClickedBtn.setBackgroundColor (getResources().getColor (R.color.btnColor)); + } + } + + //! Setup text view position + private void setTextViewPosition (TextView theTextView) + { + if (theTextView.getVisibility() != View.VISIBLE) + { + return; + } + + if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) + { + theTextView.setX (myButtonPreferSize); + theTextView.setY (0); + } + else + { + theTextView.setX (0); + theTextView.setY (myButtonPreferSize); + } + } + + @Override + public void onClick (View theButton) + { + ImageButton aClickedBtn = (ImageButton )theButton; + switch (aClickedBtn.getId()) + { + case R.id.message: + { + switchTextView ((TextView )findViewById (R.id.info_view), + (ImageButton )findViewById (R.id.info), false); + switchTextView (myMessageTextView, aClickedBtn, true); + return; + } + case R.id.info: + { + String aText = getString (R.string.info_html); + aText = String.format (aText, cppOcctMajorVersion(), cppOcctMinorVersion(), cppOcctMicroVersion()); + Spanned aSpanned = Html.fromHtml (aText, new ImageGetter() + { + @Override + public Drawable getDrawable (String theSource) + { + Resources aResources = getResources(); + int anId = aResources.getIdentifier (theSource, "drawable", getPackageName()); + Drawable aRes = aResources.getDrawable (anId); + aRes.setBounds (0, 0, aRes.getIntrinsicWidth(), aRes.getIntrinsicHeight()); + return aRes; + } + }, null); + + TextView anInfoView = (TextView )findViewById (R.id.info_view); + anInfoView.setText (aSpanned); + switchTextView (myMessageTextView, (ImageButton ) findViewById (R.id.message), false); + switchTextView (anInfoView, aClickedBtn, true); + return; + } + case R.id.fit: + { + myOcctView.fitAll(); + return; + } + case R.id.proj_front: + { + myOcctView.setProj (OcctJniRenderer.TypeOfOrientation.Xpos); + return; + } + case R.id.proj_left: + { + myOcctView.setProj (OcctJniRenderer.TypeOfOrientation.Yneg); + return; + } + case R.id.proj_top: + { + myOcctView.setProj (OcctJniRenderer.TypeOfOrientation.Zpos); + return; + } + case R.id.proj_back: + { + myOcctView.setProj (OcctJniRenderer.TypeOfOrientation.Xneg); + return; + } + case R.id.proj_right: + { + myOcctView.setProj (OcctJniRenderer.TypeOfOrientation.Ypos); + return; + } + case R.id.proj_bottom: + { + myOcctView.setProj (OcctJniRenderer.TypeOfOrientation.Zneg); + return; + } + case R.id.open: + { + File aPath = Environment.getExternalStorageDirectory(); + aClickedBtn.setBackgroundColor (getResources().getColor(R.color.pressedBtnColor)); + if (myFileOpenDialog == null) + { + myFileOpenDialog = new OcctJniFileDialog (this, aPath); + myFileOpenDialog.setFileEndsWith (".brep"); + myFileOpenDialog.setFileEndsWith (".rle"); + myFileOpenDialog.setFileEndsWith (".iges"); + myFileOpenDialog.setFileEndsWith (".igs"); + myFileOpenDialog.setFileEndsWith (".step"); + myFileOpenDialog.setFileEndsWith (".stp"); + myFileOpenDialog.addFileListener (new OcctJniFileDialog.FileSelectedListener() + { + public void fileSelected (File theFile) + { + if (theFile != null && myOcctView != null) + { + myOcctView.open (theFile.getPath()); + } + } + }); + myFileOpenDialog.addDialogDismissedListener (new OcctJniFileDialog.DialogDismissedListener() + { + @Override + public void dialogDismissed() + { + ImageButton openButton = (ImageButton )findViewById (R.id.open); + openButton.setBackgroundColor (getResources().getColor(R.color.btnColor)); + } + }); + } + myFileOpenDialog.showDialog(); + return; + } + } + } + + private void createSubmenuBtn (int theParentBtnId, + int theParentLayoutId, + final List theNewButtonIds, + final List theNewButtonImageIds, + int thePosition) + { + int aPosInList = 0; + final ImageButton aParentBtn = (ImageButton )findViewById (theParentBtnId); + + ViewGroup.LayoutParams aParams = null; + LinearLayout parentLayout = (LinearLayout ) findViewById (theParentLayoutId); + for (Integer newButtonId : theNewButtonIds) + { + ImageButton aNewButton = (ImageButton )findViewById (newButtonId); + if (aNewButton == null) + { + aNewButton = (ImageButton )new ImageButton (this); + aNewButton.setId (newButtonId); + aNewButton.setImageResource (theNewButtonImageIds.get (aPosInList)); + aNewButton.setLayoutParams (aParams); + parentLayout.addView (aNewButton); + } + + aNewButton.setOnClickListener (this); + aNewButton.setVisibility (View.GONE); + + aNewButton.setOnTouchListener (new View.OnTouchListener() + { + @Override + public boolean onTouch (View theView, MotionEvent theEvent) + { + return onTouchButton (theView, theEvent); + } + }); + ++aPosInList; + } + + if (aParentBtn != null) + { + aParentBtn.setOnTouchListener (null); + aParentBtn.setOnTouchListener (new View.OnTouchListener() + { + @Override + public boolean onTouch (View theView, MotionEvent theEvent) + { + if (theEvent.getAction () == MotionEvent.ACTION_DOWN) + { + Boolean isVisible = false; + for (Integer aNewButtonId : theNewButtonIds) + { + ImageButton anBtn = (ImageButton )findViewById (aNewButtonId); + if (anBtn != null) + { + if (anBtn.getVisibility() == View.GONE) + { + anBtn.setVisibility (View.VISIBLE); + isVisible = true; + } + else + { + anBtn.setVisibility (View.GONE); + } + } + } + aParentBtn.setBackgroundColor (!isVisible ? getResources().getColor(R.color.btnColor) : getResources().getColor(R.color.pressedBtnColor)); + } + return false; + } + }); + } + } + + //! Implements onTouch functionality + private boolean onTouchButton (View theView, + MotionEvent theEvent) + { + switch (theEvent.getAction()) + { + case MotionEvent.ACTION_DOWN: + ((ImageButton )theView).setBackgroundColor (getResources().getColor (R.color.pressedBtnColor)); + break; + case MotionEvent.ACTION_UP: + ((ImageButton )theView).setBackgroundColor (getResources().getColor (R.color.btnColor)); + break; + } + return false; + } + + //! Handle configuration change event + @Override + public void onConfigurationChanged (Configuration theNewConfig) + { + super.onConfigurationChanged (theNewConfig); + LinearLayout aLayoutPanelMenu = (LinearLayout )findViewById (R.id.panel_menu); + LayoutParams aPanelMenuLayoutParams = aLayoutPanelMenu.getLayoutParams(); + + LinearLayout aLayoutViewGroup = (LinearLayout )findViewById (R.id.view_group); + LayoutParams aViewGroupLayoutParams = aLayoutViewGroup.getLayoutParams(); + ImageButton aScrollBtn = (ImageButton )findViewById (R.id.scroll_btn); + LayoutParams aScrollBtnLayoutParams = aScrollBtn.getLayoutParams(); + + myButtonPreferSize = defineButtonSize ((LinearLayout )findViewById (R.id.panel_menu)); + defineButtonSize ((LinearLayout )findViewById (R.id.view_group)); + + switch (theNewConfig.orientation) + { + case Configuration.ORIENTATION_PORTRAIT: + { + setHorizontal (aLayoutPanelMenu, aPanelMenuLayoutParams); + setHorizontal (aLayoutViewGroup, aViewGroupLayoutParams); + aLayoutViewGroup.setGravity (Gravity.BOTTOM); + + aScrollBtnLayoutParams.height = LayoutParams.WRAP_CONTENT; + aScrollBtnLayoutParams.width = LayoutParams.MATCH_PARENT; + aScrollBtn.setLayoutParams (aScrollBtnLayoutParams); + if (aLayoutPanelMenu.getVisibility() == View.VISIBLE) + { + aScrollBtn.setImageResource (R.drawable.close_p); + aScrollBtn.setY (myButtonPreferSize); + aScrollBtn.setX (0); + } + else + { + aScrollBtn.setImageResource (R.drawable.open_p); + aScrollBtn.setY (0); + aScrollBtn.setX (0); + } + break; + } + case Configuration.ORIENTATION_LANDSCAPE: + { + setVertical (aLayoutPanelMenu, aPanelMenuLayoutParams); + setVertical (aLayoutViewGroup, aViewGroupLayoutParams); + aLayoutViewGroup.setGravity (Gravity.RIGHT); + + aScrollBtnLayoutParams.height = LayoutParams.MATCH_PARENT; + aScrollBtnLayoutParams.width = LayoutParams.WRAP_CONTENT; + aScrollBtn.setLayoutParams (aScrollBtnLayoutParams); + if (aLayoutPanelMenu.getVisibility() == View.VISIBLE) + { + aScrollBtn.setImageResource (R.drawable.close_l); + aScrollBtn.setX (myButtonPreferSize); + aScrollBtn.setY (0); + } + else + { + aScrollBtn.setImageResource (R.drawable.open_l); + aScrollBtn.setY (0); + aScrollBtn.setX (0); + } + break; + } + } + setTextViewPosition (myMessageTextView); + setTextViewPosition ((TextView )findViewById (R.id.info_view)); + } + + private void setHorizontal (LinearLayout theLayout, + LayoutParams theLayoutParams) + { + theLayout.setOrientation (LinearLayout.HORIZONTAL); + theLayoutParams.height = LayoutParams.WRAP_CONTENT; + theLayoutParams.width = LayoutParams.MATCH_PARENT; + theLayout.setLayoutParams (theLayoutParams); + } + + private void setVertical (LinearLayout theLayout, + LayoutParams theLayoutParams) + { + theLayout.setOrientation (LinearLayout.VERTICAL); + theLayoutParams.height = LayoutParams.MATCH_PARENT; + theLayoutParams.width = LayoutParams.WRAP_CONTENT; + theLayout.setLayoutParams (theLayoutParams); + } + + //! Define button size + private int defineButtonSize (LinearLayout theLayout) + { + boolean isLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; + Display aDisplay = getWindowManager().getDefaultDisplay(); + Point aDispPnt = new Point(); + aDisplay.getSize (aDispPnt); + + int aNbChildren = theLayout.getChildCount(); + int aHeight = aDispPnt.y / aNbChildren; + int aWidth = aDispPnt.x / aNbChildren; + int aResultSize = 0; + for (int aChildIter = 0; aChildIter < aNbChildren; ++aChildIter) + { + View aView = theLayout.getChildAt (aChildIter); + if (aView instanceof ImageButton) + { + ImageButton aButton = (ImageButton )aView; + if (isLandscape) + { + aButton.setMinimumWidth (aHeight); + } + else + { + aButton.setMinimumHeight (aWidth); + } + } + } + if (isLandscape) + { + aResultSize = aHeight; + } + else + { + aResultSize = aWidth; + } + return aResultSize; + } + + //! OCCT major version + private native long cppOcctMajorVersion(); + + //! OCCT minor version + private native long cppOcctMinorVersion(); + + //! OCCT micro version + private native long cppOcctMicroVersion(); + + private OcctJniView myOcctView; + private TextView myMessageTextView; + private String myLastPath; + private OcctJniFileDialog myFileOpenDialog; + private int myButtonPreferSize = 65; + +} diff --git a/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniFileDialog.java b/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniFileDialog.java new file mode 100644 index 0000000000..e12cc1aa2b --- /dev/null +++ b/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniFileDialog.java @@ -0,0 +1,376 @@ +// Copyright (c) 2014 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +package com.opencascade.jnisample; + +import java.io.File; +import java.io.FilenameFilter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import com.opencascade.jnisample.ListenerList.FireHandler; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.graphics.Color; +import android.os.Environment; +import android.view.View; +import android.view.ViewGroup.LayoutParams; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.Spinner; + +//! Simple open file dialog +public class OcctJniFileDialog +{ + + public enum DialogMode + { + FileOpen, FileExport, FileSave + } + + private static final String PARENT_DIR = ".."; + private String[] myFileList; + private File myCurrentPath; + private DialogMode myDialogMode = DialogMode.FileOpen; + + private ListenerList myFileListenerList = new ListenerList(); + private ListenerList myDialogDismissedList = new ListenerList(); + private final Activity myActivity; + private List myFileEndsWith; + private EditText myFileNameInput; + private Spinner myFileExtSpinner; + int myCurrentExtPositionInList = 0; + + public interface FileSelectedListener + { + void fileSelected (File theFile); + } + + public interface DialogDismissedListener + { + void dialogDismissed(); + } + + //! Main constructor. + public OcctJniFileDialog (Activity theActivity, + File thePath) + { + myActivity = theActivity; + if (!thePath.exists()) + { + thePath = Environment.getExternalStorageDirectory(); + } + loadFileList (thePath); + } + + //! Create new dialog + public Dialog createFileDialog() + { + final Object[] anObjWrapper = new Object[1]; + Dialog aDialog = null; + AlertDialog.Builder aBuilder = new AlertDialog.Builder (myActivity); + + aBuilder.setTitle (myCurrentPath.getPath()); + LinearLayout aTitleLayout = new LinearLayout (myActivity); + aTitleLayout.setLayoutParams (new LinearLayout.LayoutParams (LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); + aTitleLayout.setOrientation (LinearLayout.VERTICAL); + + ListView list = new ListView (myActivity); + list.setScrollingCacheEnabled(false); + list.setBackgroundColor (Color.parseColor ("#33B5E5")); + + list.setAdapter (new ArrayAdapter (myActivity, android.R.layout.select_dialog_item, myFileList)); + list.setOnItemClickListener (new AdapterView.OnItemClickListener () + { + + public void onItemClick (AdapterView arg0, View view, int pos, long id) + { + String fileChosen = myFileList[pos]; + File aChosenFile = getChosenFile (fileChosen); + if (aChosenFile.isDirectory()) + { + loadFileList (aChosenFile); + ((Dialog )anObjWrapper[0]).cancel(); + ((Dialog )anObjWrapper[0]).dismiss(); + showDialog(); + } + else + { + if (myDialogMode == DialogMode.FileOpen) + { + ((Dialog )anObjWrapper[0]).cancel(); + ((Dialog )anObjWrapper[0]).dismiss(); + fireFileSelectedEvent (aChosenFile); + } + else + { + myFileNameInput.setText (aChosenFile.getName()); + } + } + } + }); + list.setLayoutParams (new LinearLayout.LayoutParams (LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, 0.6f)); + aTitleLayout.addView (list); + + if (myDialogMode == DialogMode.FileSave + || myDialogMode == DialogMode.FileExport) + { + myFileNameInput = new EditText (myActivity); + myFileExtSpinner = new Spinner (myActivity); + ArrayAdapter adapter = null; + if (myDialogMode == DialogMode.FileExport) + { + adapter = ArrayAdapter.createFromResource (myActivity, R.array.ext_to_exp, + android.R.layout.simple_spinner_item); + } + else + { + adapter = ArrayAdapter.createFromResource (myActivity, R.array.ext_to_save, + android.R.layout.simple_spinner_item); + } + // Specify the layout to use when the list of choices appears + adapter.setDropDownViewResource (android.R.layout.simple_spinner_dropdown_item); + // Apply the adapter to the spinner + myFileExtSpinner.setAdapter (adapter); + myFileExtSpinner.setSelection (myCurrentExtPositionInList); + + myFileExtSpinner.setOnItemSelectedListener (new AdapterView.OnItemSelectedListener() + { + + @Override + public void onNothingSelected (AdapterView theParentView) + { + // your code here + } + + @Override + public void onItemSelected (AdapterView theParent, View theView, int thePosition, long theId) + { + if (myCurrentExtPositionInList != thePosition) + { + myCurrentExtPositionInList = thePosition; + setFileEndsWith (Arrays.asList (myFileExtSpinner.getSelectedItem().toString())); + loadFileList (myCurrentPath); + ((Dialog )anObjWrapper[0]).cancel(); + ((Dialog )anObjWrapper[0]).dismiss(); + showDialog(); + } + } + }); + + myFileExtSpinner.setLayoutParams (new LinearLayout.LayoutParams (LayoutParams.MATCH_PARENT, + LayoutParams.WRAP_CONTENT, 0.2f)); + // titleLayout.addView(fileExtSpinner); + myFileNameInput.setLayoutParams (new LinearLayout.LayoutParams (LayoutParams.MATCH_PARENT, + LayoutParams.WRAP_CONTENT, 0.2f)); + LinearLayout aControlsView = new LinearLayout (myActivity); + + aControlsView.addView (myFileNameInput); + aControlsView.addView (myFileExtSpinner); + + aTitleLayout.addView (aControlsView); + aBuilder.setView (aTitleLayout); + aBuilder.setPositiveButton ("OK", new DialogInterface.OnClickListener() + { + @Override + public void onClick (DialogInterface theDialog, int theWhich) + { + if (theWhich >= 0) + { + String aFileChosen = myFileList[theWhich]; + File aChosenFile = getChosenFile (aFileChosen); + fireFileSelectedEvent (aChosenFile); + } + } + }).setNegativeButton ("Cancel", null); + } + else + { + aBuilder.setNegativeButton ("Cancel", null); + } + + aBuilder.setView (aTitleLayout); + + aDialog = aBuilder.show(); + aDialog.setOnDismissListener (new DialogInterface.OnDismissListener() + { + @Override + public void onDismiss (DialogInterface theDialog) + { + fireDialogDismissedEvent(); + } + }); + anObjWrapper[0] = aDialog; + return aDialog; + } + + public void addFileListener (FileSelectedListener theListener) + { + myFileListenerList.add (theListener); + } + + public void addDialogDismissedListener (DialogDismissedListener theListener) + { + myDialogDismissedList.add (theListener); + } + + //! Show file dialog + public void showDialog() + { + createFileDialog().show(); + } + + private void fireFileSelectedEvent (final File theFile) + { + myFileListenerList.fireEvent (new FireHandler() + { + public void fireEvent (FileSelectedListener theListener) + { + theListener.fileSelected (theFile); + } + }); + } + + private void fireDialogDismissedEvent() + { + myDialogDismissedList.fireEvent (new FireHandler() + { + public void fireEvent (DialogDismissedListener theListener) + { + theListener.dialogDismissed(); + } + }); + } + + private void loadFileList (File thePath) + { + myCurrentPath = thePath; + List aList = new ArrayList(); + if (thePath.exists()) + { + if (thePath.getParentFile() != null) + { + aList.add (PARENT_DIR); + } + FilenameFilter aFilter = new FilenameFilter() + { + public boolean accept (File theDir, String theFilename) + { + File aSel = new File (theDir, theFilename); + if (!aSel.canRead()) + { + return false; + } + boolean isEndWith = false; + if (myFileEndsWith != null) + { + for (String aFileExtIter : myFileEndsWith) + { + if (theFilename.toLowerCase().endsWith (aFileExtIter)) + { + isEndWith = true; + break; + } + } + } + return isEndWith || aSel.isDirectory(); + } + }; + String[] aFileList1 = thePath.list (aFilter); + if (aFileList1 != null) + { + for (String aFileIter : aFileList1) + { + aList.add (aFileIter); + } + } + } + myFileList = (String[] )aList.toArray (new String[] {}); + } + + private File getChosenFile (String theFileChosen) + { + if (theFileChosen.equals (PARENT_DIR)) + return myCurrentPath.getParentFile(); + else + return new File (myCurrentPath, theFileChosen); + } + + public void setFileEndsWith (String fileEndsWith) + { + if (myFileEndsWith == null) + { + myFileEndsWith = new ArrayList(); + } + if (myFileEndsWith.indexOf (fileEndsWith) == -1) + { + myFileEndsWith.add (fileEndsWith); + } + } + + public void setFileEndsWith (List theFileEndsWith) + { + myFileEndsWith = theFileEndsWith; + } + + public DialogMode DialogMode() + { + return myDialogMode; + } + + public void DialogMode (DialogMode theMode) + { + myDialogMode = theMode; + } +} + +class ListenerList +{ + private List myListenerList = new ArrayList(); + + public interface FireHandler + { + void fireEvent (L theListener); + } + + public void add (L theListener) + { + myListenerList.add (theListener); + } + + public void fireEvent (FireHandler theFireHandler) + { + List aCopy = new ArrayList (myListenerList); + for (L anIter : aCopy) + { + theFireHandler.fireEvent (anIter); + } + } + + public void remove (L theListener) + { + myListenerList.remove (theListener); + } + + public List getListenerList() + { + return myListenerList; + } +} diff --git a/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniLogger.java b/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniLogger.java new file mode 100644 index 0000000000..cc8b1e4942 --- /dev/null +++ b/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniLogger.java @@ -0,0 +1,71 @@ +// Copyright (c) 2014 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +package com.opencascade.jnisample; + +import java.util.concurrent.locks.ReentrantLock; + +import android.util.Log; +import android.widget.TextView; + +//! Auxiliary class for logging messages +public class OcctJniLogger +{ + + //! Setup text view + public static void setTextView (TextView theTextView) + { + if (myTextView != null) + { + myLog = myTextView.getText().toString(); + } + + myTextView = theTextView; + if (myTextView != null) + { + myTextView.setText (myLog); + myLog = ""; + } + } + + //! Interface implementation + public static void postMessage (String theText) + { + final String aCopy = new String (theText); + Log.e (myTag, theText); + + myMutex.lock(); + final TextView aView = myTextView; + if (aView == null) + { + myLog += aCopy; + myMutex.unlock(); + return; + } + + aView.post (new Runnable() + { + public void run() + { + aView.setText (aView.getText() + aCopy + "\n"); + } + }); + myMutex.unlock(); + } + + private static final String myTag = "occtJniViewer"; + private static final ReentrantLock myMutex = new ReentrantLock (true); + private static TextView myTextView = null; + private static String myLog = ""; + +} diff --git a/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniRenderer.java b/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniRenderer.java new file mode 100644 index 0000000000..731037a9ca --- /dev/null +++ b/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniRenderer.java @@ -0,0 +1,218 @@ +// Copyright (c) 2014 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +package com.opencascade.jnisample; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import android.opengl.GLSurfaceView; + +//! Wrapper for C++ OCCT viewer. +public class OcctJniRenderer implements GLSurfaceView.Renderer +{ + + //! Wrapper for V3d_TypeOfOrientation + enum TypeOfOrientation + { + Xpos, // front + Ypos, // left + Zpos, // top + Xneg, // back + Yneg, // right + Zneg // bottom + }; + + //! Empty constructor. + OcctJniRenderer() + { + if (OcctJniActivity.areNativeLoaded) + { + myCppViewer = cppCreate(); + } + } + + //! Open file. + public void open (String thePath) + { + if (myCppViewer != 0) + { + cppOpen (myCppViewer, thePath); + } + } + + //! Update viewer. + public void onDrawFrame (GL10 theGl) + { + if (myCppViewer != 0) + { + cppRedraw (myCppViewer); + } + } + + //! (re)initialize viewer. + public void onSurfaceChanged (GL10 theGl, int theWidth, int theHeight) + { + if (myCppViewer != 0) + { + cppResize (myCppViewer, theWidth, theHeight); + } + } + + public void onSurfaceCreated (GL10 theGl, EGLConfig theEglConfig) + { + if (myCppViewer != 0) + { + cppInit (myCppViewer); + } + } + + //! Initialize rotation (remember first point position) + public void onStartRotation (int theStartX, int theStartY) + { + if (myCppViewer != 0) + { + cppStartRotation (myCppViewer, theStartX, theStartY); + } + } + + //! Perform rotation (relative to first point) + public void onRotation (int theX, int theY) + { + if (myCppViewer != 0) + { + cppOnRotation (myCppViewer, theX, theY); + } + } + + //! Perform panning + public void onPanning (int theDX, int theDY) + { + if (myCppViewer != 0) + { + cppOnPanning (myCppViewer, theDX, theDY); + } + } + + //! Perform selection + public void onClick (int theX, int theY) + { + if (myCppViewer != 0) + { + cppOnClick (myCppViewer, theX, theY); + } + } + + //! Stop previously active action (e.g. discard first rotation point) + public void onStopAction() + { + if (myCppViewer != 0) + { + cppStopAction (myCppViewer); + } + } + + //! Fit All + public void fitAll() + { + if (myCppViewer != 0) + { + cppFitAll (myCppViewer); + } + } + + //! Move camera + public void setProj (TypeOfOrientation theProj) + { + if (myCppViewer == 0) + { + return; + } + + switch (theProj) + { + case Xpos: cppSetXposProj (myCppViewer); break; + case Ypos: cppSetYposProj (myCppViewer); break; + case Zpos: cppSetZposProj (myCppViewer); break; + case Xneg: cppSetXnegProj (myCppViewer); break; + case Yneg: cppSetYnegProj (myCppViewer); break; + case Zneg: cppSetZnegProj (myCppViewer); break; + } + } + + //! Post message to the text view. + public void postMessage (String theText) + { + OcctJniLogger.postMessage (theText); + } + + //! Create instance of C++ class + private native long cppCreate(); + + //! Destroy instance of C++ class + private native void cppDestroy (long theCppPtr); + + //! Initialize OCCT viewer (steal OpenGL ES context bound to this thread) + private native void cppInit (long theCppPtr); + + //! Resize OCCT viewer + private native void cppResize (long theCppPtr, int theWidth, int theHeight); + + //! Open CAD file + private native void cppOpen (long theCppPtr, String thePath); + + //! Handle detection in the viewer + private native void cppMoveTo (long theCppPtr, int theX, int theY); + + //! Redraw OCCT viewer + private native void cppRedraw (long theCppPtr); + + //! Fit All + private native void cppFitAll (long theCppPtr); + + //! Move camera + private native void cppSetXposProj (long theCppPtr); + + //! Move camera + private native void cppSetYposProj (long theCppPtr); + + //! Move camera + private native void cppSetZposProj (long theCppPtr); + + //! Move camera + private native void cppSetXnegProj (long theCppPtr); + + //! Move camera + private native void cppSetYnegProj (long theCppPtr); + + //! Move camera + private native void cppSetZnegProj (long theCppPtr); + + //! Initialize rotation + private native void cppStartRotation (long theCppPtr, int theStartX, int theStartY); + + //! Perform rotation + private native void cppOnRotation (long theCppPtr, int theX, int theY); + + //! Perform panning + private native void cppOnPanning (long theCppPtr, int theDX, int theDY); + + //! Perform selection + private native void cppOnClick (long theCppPtr, int theX, int theY); + + //! Stop action (rotation / panning / scaling) + private native void cppStopAction (long theCppPtr); + + private long myCppViewer = 0; //!< pointer to c++ class instance + +} diff --git a/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniView.java b/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniView.java new file mode 100644 index 0000000000..7909d9c61d --- /dev/null +++ b/samples/java/jniviewer/src/com/opencascade/jnisample/OcctJniView.java @@ -0,0 +1,332 @@ +// Copyright (c) 2014 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +package com.opencascade.jnisample; + +import android.app.ActionBar.LayoutParams; +import android.content.Context; +import android.graphics.PointF; +import android.opengl.GLSurfaceView; +import android.util.AttributeSet; +import android.util.SparseArray; +import android.view.MotionEvent; +import android.widget.RelativeLayout; + +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; + +//! OpenGL ES 2.0+ view. +//! Performs rendering in parallel thread. +class OcctJniView extends GLSurfaceView +{ + + // ! Default constructor. + public OcctJniView (Context theContext, + AttributeSet theAttrs) + { + super (theContext, theAttrs); + + setPreserveEGLContextOnPause (true); + setEGLContextFactory (new ContextFactory()); + setEGLConfigChooser (new ConfigChooser()); + + RelativeLayout.LayoutParams aLParams = new RelativeLayout.LayoutParams (LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + aLParams.addRule (RelativeLayout.ALIGN_TOP); + + myRenderer = new OcctJniRenderer(); + setRenderer (myRenderer); + } + + //! Open file. + public void open (String thePath) + { + final String aPath = thePath; + queueEvent (new Runnable() { public void run() { myRenderer.open (aPath); }}); + } + + //! Create OpenGL ES 2.0+ context + private static class ContextFactory implements GLSurfaceView.EGLContextFactory + { + private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + public EGLContext createContext (EGL10 theEgl, + EGLDisplay theEglDisplay, + EGLConfig theEglConfig) + { + if (theEglConfig == null) + { + return null; + } + + // reset EGL errors stack + int anError = EGL10.EGL_SUCCESS; + while ((anError = theEgl.eglGetError()) != EGL10.EGL_SUCCESS) {} + + int[] anAttribs = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; + EGLContext aEglContext = theEgl.eglCreateContext (theEglDisplay, theEglConfig, EGL10.EGL_NO_CONTEXT, anAttribs); + + while ((anError = theEgl.eglGetError()) != EGL10.EGL_SUCCESS) + { + OcctJniLogger.postMessage ("Error: eglCreateContext() " + String.format ("0x%x", anError)); + } + return aEglContext; + } + + public void destroyContext (EGL10 theEgl, + EGLDisplay theEglDisplay, + EGLContext theEglContext) + { + theEgl.eglDestroyContext (theEglDisplay, theEglContext); + } + } + + //! Search for RGB24 config with depth and stencil buffers + private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser + { + //! Reset EGL errors stack + private void popEglErrors (EGL10 theEgl) + { + int anError = EGL10.EGL_SUCCESS; + while ((anError = theEgl.eglGetError()) != EGL10.EGL_SUCCESS) + { + OcctJniLogger.postMessage ("EGL Error: " + String.format ("0x%x", anError)); + } + } + + //! Auxiliary method to dump EGL configuration - for debugging purposes + @SuppressWarnings("unused") + private void printConfig (EGL10 theEgl, + EGLDisplay theEglDisplay, + EGLConfig theEglConfig) + { + int[] THE_ATTRIBS = + { + EGL10.EGL_BUFFER_SIZE, EGL10.EGL_ALPHA_SIZE, EGL10.EGL_BLUE_SIZE, EGL10.EGL_GREEN_SIZE, EGL10.EGL_RED_SIZE, EGL10.EGL_DEPTH_SIZE, EGL10.EGL_STENCIL_SIZE, + EGL10.EGL_CONFIG_CAVEAT, + EGL10.EGL_CONFIG_ID, + EGL10.EGL_LEVEL, + EGL10.EGL_MAX_PBUFFER_HEIGHT, EGL10.EGL_MAX_PBUFFER_PIXELS, EGL10.EGL_MAX_PBUFFER_WIDTH, + EGL10.EGL_NATIVE_RENDERABLE, EGL10.EGL_NATIVE_VISUAL_ID, EGL10.EGL_NATIVE_VISUAL_TYPE, + 0x3030, // EGL10.EGL_PRESERVED_RESOURCES, + EGL10.EGL_SAMPLES, EGL10.EGL_SAMPLE_BUFFERS, + EGL10.EGL_SURFACE_TYPE, + EGL10.EGL_TRANSPARENT_TYPE, EGL10.EGL_TRANSPARENT_RED_VALUE, EGL10.EGL_TRANSPARENT_GREEN_VALUE, EGL10.EGL_TRANSPARENT_BLUE_VALUE, + 0x3039, 0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGB, EGL10.EGL_BIND_TO_TEXTURE_RGBA, + 0x303B, 0x303C, // EGL10.EGL_MIN_SWAP_INTERVAL, EGL10.EGL_MAX_SWAP_INTERVAL + EGL10.EGL_LUMINANCE_SIZE, EGL10.EGL_ALPHA_MASK_SIZE, + EGL10.EGL_COLOR_BUFFER_TYPE, EGL10.EGL_RENDERABLE_TYPE, + 0x3042 // EGL10.EGL_CONFORMANT + }; + String[] THE_NAMES = + { + "EGL_BUFFER_SIZE", "EGL_ALPHA_SIZE", "EGL_BLUE_SIZE", "EGL_GREEN_SIZE", "EGL_RED_SIZE", "EGL_DEPTH_SIZE", "EGL_STENCIL_SIZE", + "EGL_CONFIG_CAVEAT", + "EGL_CONFIG_ID", + "EGL_LEVEL", + "EGL_MAX_PBUFFER_HEIGHT", "EGL_MAX_PBUFFER_PIXELS", "EGL_MAX_PBUFFER_WIDTH", + "EGL_NATIVE_RENDERABLE", "EGL_NATIVE_VISUAL_ID", "EGL_NATIVE_VISUAL_TYPE", + "EGL_PRESERVED_RESOURCES", + "EGL_SAMPLES", "EGL_SAMPLE_BUFFERS", + "EGL_SURFACE_TYPE", + "EGL_TRANSPARENT_TYPE", "EGL_TRANSPARENT_RED_VALUE", "EGL_TRANSPARENT_GREEN_VALUE", "EGL_TRANSPARENT_BLUE_VALUE", + "EGL_BIND_TO_TEXTURE_RGB", "EGL_BIND_TO_TEXTURE_RGBA", + "EGL_MIN_SWAP_INTERVAL", "EGL_MAX_SWAP_INTERVAL", + "EGL_LUMINANCE_SIZE", "EGL_ALPHA_MASK_SIZE", + "EGL_COLOR_BUFFER_TYPE", "EGL_RENDERABLE_TYPE", + "EGL_CONFORMANT" + }; + int[] aValue = new int[1]; + for (int anAttrIter = 0; anAttrIter < THE_ATTRIBS.length; ++anAttrIter) + { + int anAttr = THE_ATTRIBS[anAttrIter]; + String aName = THE_NAMES [anAttrIter]; + if (theEgl.eglGetConfigAttrib (theEglDisplay, theEglConfig, anAttr, aValue)) + { + OcctJniLogger.postMessage (String.format (" %s: %d\n", aName, aValue[0])); + } + else + { + popEglErrors (theEgl); + } + } + } + + //! Interface implementation + public EGLConfig chooseConfig (EGL10 theEgl, + EGLDisplay theEglDisplay) + { + int EGL_OPENGL_ES2_BIT = 4; + int[] aCfgAttribs = + { + EGL10.EGL_RED_SIZE, 8, + EGL10.EGL_GREEN_SIZE, 8, + EGL10.EGL_BLUE_SIZE, 8, + EGL10.EGL_ALPHA_SIZE, 0, + EGL10.EGL_DEPTH_SIZE, 24, + EGL10.EGL_STENCIL_SIZE, 8, + EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL10.EGL_NONE + }; + + EGLConfig aConfigs[] = new EGLConfig[1]; + int[] aNbConfigs = new int[1]; + if (!theEgl.eglChooseConfig (theEglDisplay, aCfgAttribs, aConfigs, 1, aNbConfigs) + || aConfigs[0] == null) + { + aCfgAttribs[4 * 2 + 1] = 16; // try config with smaller depth buffer + popEglErrors (theEgl); + if (!theEgl.eglChooseConfig (theEglDisplay, aCfgAttribs, aConfigs, 1, aNbConfigs) + || aConfigs[0] == null) + { + OcctJniLogger.postMessage ("Error: eglChooseConfig() has failed!"); + return null; + } + } + + //printConfig (theEgl, theEglDisplay, aConfigs[0]); + return aConfigs[0]; + } + } + + //! Callback to handle touch events + @Override public boolean onTouchEvent (MotionEvent theEvent) + { + int aPointerIndex = theEvent.getActionIndex(); + int aPointerId = theEvent.getPointerId (aPointerIndex); + int aMaskedAction = theEvent.getActionMasked(); + switch (aMaskedAction) + { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + { + PointF aPntLast = null; + if (myActivePointers.size() >= 1) + { + aPntLast = myActivePointers.get (myActivePointers.keyAt (0)); + } + + final PointF aPnt = new PointF(); + aPnt.x = theEvent.getX (aPointerIndex); + aPnt.y = theEvent.getY (aPointerIndex); + myActivePointers.put (aPointerId, aPnt); + + switch (myActivePointers.size()) + { + case 1: + { + final int aStartX = (int )aPnt.x; + final int aStartY = (int )aPnt.y; + queueEvent (new Runnable() { public void run() { myRenderer.onStartRotation (aStartX, aStartY); }}); + break; + } + case 2: + { + myPanFrom.x = (aPntLast.x + aPnt.x) * 0.5f; + myPanFrom.y = (aPntLast.y + aPnt.y) * 0.5f; + break; + } + } + + break; + } + case MotionEvent.ACTION_MOVE: + { + for (int aNbPointers = theEvent.getPointerCount(), aPntIter = 0; aPntIter < aNbPointers; ++aPntIter) + { + PointF aPnt = myActivePointers.get (theEvent.getPointerId (aPntIter)); + if (aPnt != null) + { + aPnt.x = theEvent.getX (aPntIter); + aPnt.y = theEvent.getY (aPntIter); + } + } + + switch (myActivePointers.size()) + { + case 1: + { + PointF aPnt = myActivePointers.get (theEvent.getPointerId (0)); + final int anX = (int )aPnt.x; + final int anY = (int )aPnt.y; + queueEvent (new Runnable() { public void run() { myRenderer.onRotation (anX, anY); }}); + break; + } + case 2: + { + PointF aPnt1 = myActivePointers.get (myActivePointers.keyAt (0)); + PointF aPnt2 = myActivePointers.get (myActivePointers.keyAt (1)); + PointF aPntAver = new PointF ((aPnt1.x + aPnt2.x) * 0.5f, + (aPnt1.y + aPnt2.y) * 0.5f); + final int aDX = (int )(aPntAver.x - myPanFrom.x); + final int aDY = (int )(myPanFrom.y -aPntAver.y); + myPanFrom.x = aPntAver.x; + myPanFrom.y = aPntAver.y; + queueEvent (new Runnable() { public void run() { myRenderer.onPanning (aDX, aDY); }}); + } + } + break; + } + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_CANCEL: + { + myActivePointers.remove (aPointerId); + if (myActivePointers.size() == 0) + { + final int aPressX = (int )theEvent.getX (aPointerIndex); + final int aPressY = (int )theEvent.getY (aPointerIndex); + double aPressTimeMs = theEvent.getEventTime() - theEvent.getDownTime(); + if (aPressTimeMs < 100.0) + { + queueEvent (new Runnable() { public void run() { myRenderer.onClick (aPressX, aPressY); }}); + break; + } + } + else if (myActivePointers.size() == 1) + { + PointF aPnt = myActivePointers.get (myActivePointers.keyAt (0)); + final int aStartX = (int )aPnt.x; + final int aStartY = (int )aPnt.y; + queueEvent (new Runnable() { public void run() { myRenderer.onStartRotation (aStartX, aStartY); }}); + } + //queueEvent (new Runnable() { public void run() { myRenderer.onStopAction(); }}); + break; + } + } + ///invalidate(); + return true; + } + + //! Fit All + public void fitAll() + { + queueEvent (new Runnable() { public void run() { myRenderer.fitAll(); }}); + } + + //! Move camera + public void setProj (final OcctJniRenderer.TypeOfOrientation theProj) + { + queueEvent (new Runnable() { public void run() { myRenderer.setProj (theProj); }}); + } + + //! OCCT viewer + private OcctJniRenderer myRenderer = null; + + //! Touch events cache + private SparseArray myActivePointers = new SparseArray(); + + //! Starting point for panning event + private PointF myPanFrom = new PointF (0.0f, 0.0f); + +}