From 60c6b3ce7e75d772c283a6db9a4c190169731bcd Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Thu, 2 Jul 2020 12:49:41 +0200 Subject: [PATCH 001/668] unit tests: fix compilation by setting SRL_VERSION in cmake --- src/codec/CMakeLists.txt | 5 +---- src/common.cmake | 10 ++++++++++ src/serlio/CMakeLists.txt | 3 +-- src/test/CMakeLists.txt | 2 ++ 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/codec/CMakeLists.txt b/src/codec/CMakeLists.txt index 3c0ee354..53d5f392 100644 --- a/src/codec/CMakeLists.txt +++ b/src/codec/CMakeLists.txt @@ -22,10 +22,7 @@ target_include_directories(${CODEC_TARGET} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE ${PRT_INCLUDE_PATH}) -target_compile_definitions(${CODEC_TARGET} PRIVATE - -DSRL_VERSION=\"${SRL_VERSION}\" # quoted to use it as string literal - -DPRT_VERSION_MAJOR=${PRT_VERSION_MAJOR} - -DPRT_VERSION_MINOR=${PRT_VERSION_MINOR}) +set_common_target_definitions(${CODEC_TARGET}) if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") target_compile_options(${CODEC_TARGET} PRIVATE -bigobj -GR -EHsc) diff --git a/src/common.cmake b/src/common.cmake index 86256815..26d5816a 100644 --- a/src/common.cmake +++ b/src/common.cmake @@ -21,6 +21,16 @@ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") endif () +### common target functions + +function(set_common_target_definitions TGT) + target_compile_definitions(${TGT} PRIVATE + -DSRL_VERSION=\"${SRL_VERSION}\" # quoted to use it as string literal + -DPRT_VERSION_MAJOR=${PRT_VERSION_MAJOR} + -DPRT_VERSION_MINOR=${PRT_VERSION_MINOR}) +endfunction() + + ### look for the PRT libraries # if prt_DIR is not provided, download PRT from its github home diff --git a/src/serlio/CMakeLists.txt b/src/serlio/CMakeLists.txt index aa3db37f..903965f3 100644 --- a/src/serlio/CMakeLists.txt +++ b/src/serlio/CMakeLists.txt @@ -69,8 +69,7 @@ endif () set_target_properties(${CLIENT_TARGET} PROPERTIES CXX_STANDARD 14) -target_compile_definitions(${SERLIO_TARGET} PRIVATE - -DSRL_VERSION=\"${SRL_VERSION}\") # quoted to use it as string literal +set_common_target_definitions(${SERLIO_TARGET}) target_include_directories(${SERLIO_TARGET} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 6ad5c49a..b1e1e4d8 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -9,6 +9,8 @@ add_executable(${TEST_TARGET} set_target_properties(${TEST_TARGET} PROPERTIES CXX_STANDARD 14) +set_common_target_definitions(${TEST_TARGET}) + target_compile_definitions(${TEST_TARGET} PRIVATE -DSRL_TEST_EXPORTS -DSERLIO_CODEC_PATH="$" From e0015081a3f5292fcda3791c14065c2d307b9aaa Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Thu, 18 Feb 2021 16:40:15 +0100 Subject: [PATCH 002/668] Revert "Revert "pipeline: activate upload reporting."" This reverts commit e1369e8b --- pipeline.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/pipeline.groovy b/pipeline.groovy index b97adac4..43983c22 100644 --- a/pipeline.groovy +++ b/pipeline.groovy @@ -40,6 +40,7 @@ import com.esri.zrh.jenkins.ce.PrtAppPipelineLibrary // entry point for standalone pipeline def pipeline(String branchName = null) { cepl.runParallel(getTasks(branchName)) + papl.finalizeRun('serlio', myBranch) } // entry point for embedded pipeline From 4d49f8bc917de1a132ad2e6f82216e9c2dca4270 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Thu, 18 Feb 2021 16:41:29 +0100 Subject: [PATCH 003/668] whitespace cleanup --- pipeline.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pipeline.groovy b/pipeline.groovy index 43983c22..579b2dae 100644 --- a/pipeline.groovy +++ b/pipeline.groovy @@ -40,7 +40,7 @@ import com.esri.zrh.jenkins.ce.PrtAppPipelineLibrary // entry point for standalone pipeline def pipeline(String branchName = null) { cepl.runParallel(getTasks(branchName)) - papl.finalizeRun('serlio', myBranch) + papl.finalizeRun('serlio', myBranch) } // entry point for embedded pipeline From e66084b9b7d63d5887e74752dcbc34b7875f68da Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 22 Feb 2021 11:48:23 +0100 Subject: [PATCH 004/668] initiate version 1.2.0 --- src/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 32002113..729d35a2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,8 +11,9 @@ include(common.cmake) ### version set(SRL_VERSION_MAJOR 1) -set(SRL_VERSION_MINOR 1) +set(SRL_VERSION_MINOR 2) set(SRL_VERSION_PATCH 0) +set(SRL_VERSION_PRERELEASE "dev.0") if (NOT SRL_VERSION_BUILD) set(SRL_VERSION_BUILD DEV) From 34e45a78557c78417aacd033eb440f62872bcab7 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Thu, 18 Feb 2021 16:49:53 +0100 Subject: [PATCH 005/668] Update to PRT 2.3 --- src/common.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common.cmake b/src/common.cmake index 26d5816a..7e537174 100644 --- a/src/common.cmake +++ b/src/common.cmake @@ -46,9 +46,9 @@ if (NOT prt_DIR) set(PRT_TC "ac81") endif () - set(PRT_VERSION "2.1.5705") + set(PRT_VERSION "2.3.6821") set(PRT_CLS "${PRT_OS}-${PRT_TC}-x86_64-rel-opt") - set(PRT_URL "https://github.com/esri/esri-cityengine-sdk/releases/download/${PRT_VERSION}/esri_ce_sdk-${PRT_VERSION}-${PRT_CLS}.zip") + set(PRT_URL "https://github.com/esri/cityengine-sdk/releases/download/${PRT_VERSION}/esri_ce_sdk-${PRT_VERSION}-${PRT_CLS}.zip") FetchContent_Declare(prt URL ${PRT_URL}) FetchContent_GetProperties(prt) From 82e6e02997dd3719bbb3f8b0546521071942fcbb Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Thu, 18 Feb 2021 16:50:08 +0100 Subject: [PATCH 006/668] Adapt to API changes in PRT 2.3 --- src/serlio/modifiers/MayaCallbacks.cpp | 23 ++++++++++++++++++++++- src/serlio/modifiers/MayaCallbacks.h | 13 +++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 4b187b34..f219d407 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -401,8 +401,29 @@ prt::Status MayaCallbacks::attrString(size_t /*isIndex*/, int32_t /*shapeID*/, c return prt::STATUS_OK; } +// PRT version >= 2.3 +#if PRT_VERSION_GTE(2, 3) + +prt::Status MayaCallbacks::attrBoolArray(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* key, + const bool* values, size_t size, size_t /*nRows*/) { + mAttributeMapBuilder->setBoolArray(key, values, size); + return prt::STATUS_OK; +} + +prt::Status MayaCallbacks::attrFloatArray(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* key, + const double* values, size_t size, size_t /*nRows*/) { + mAttributeMapBuilder->setFloatArray(key, values, size); + return prt::STATUS_OK; +} + +prt::Status MayaCallbacks::attrStringArray(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* key, + const wchar_t* const* values, size_t size, size_t /*nRows*/) { + mAttributeMapBuilder->setStringArray(key, values, size); + return prt::STATUS_OK; +} + // PRT version >= 2.1 -#if PRT_VERSION_GTE(2, 1) +#elif PRT_VERSION_GTE(2, 1) prt::Status MayaCallbacks::attrBoolArray(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* key, const bool* values, size_t size) { diff --git a/src/serlio/modifiers/MayaCallbacks.h b/src/serlio/modifiers/MayaCallbacks.h index b9027953..f421ed60 100644 --- a/src/serlio/modifiers/MayaCallbacks.h +++ b/src/serlio/modifiers/MayaCallbacks.h @@ -74,8 +74,17 @@ class MayaCallbacks : public IMayaCallbacks { prt::Status attrString(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* /*key*/, const wchar_t* /*value*/) override; -// PRT version >= 2.1 -#if PRT_VERSION_GTE(2, 1) +// PRT version >= 2.3 +#if PRT_VERSION_GTE(2, 3) + + prt::Status attrBoolArray(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* /*key*/, const bool* /*values*/, + size_t /*size*/, size_t /*nRows*/) override; + prt::Status attrFloatArray(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* /*key*/, + const double* /*values*/, size_t /*size*/, size_t /*nRows*/) override; + prt::Status attrStringArray(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* /*key*/, + const wchar_t* const* /*values*/, size_t /*size*/, size_t /*nRows*/) override; + +#elif PRT_VERSION_GTE(2, 1) prt::Status attrBoolArray(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* /*key*/, const bool* /*values*/, size_t /*size*/) override; From d0b444b1fd99e22166ce88228dd76757c6d14b9a Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Fri, 19 Feb 2021 13:08:11 +0100 Subject: [PATCH 007/668] Add changelog entry for PRT 2.3 --- doc/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/changelog.md b/doc/changelog.md index 5ed33369..8edd6282 100644 --- a/doc/changelog.md +++ b/doc/changelog.md @@ -1,5 +1,8 @@ # Serlio ChangeLog +## v1.2.0 (2021-02-XX) +* Updated Procedural Runtime (PRT) to 2.3 (corresponds to CityEngine 2020). + ## v1.1.0 (2020-06-02) * BREAKING CHANGE: Switched to official Autodesk IDs for Serlio custom nodes. (#26) * Added creation of Arnold materials. (#11, #40, #65) From bd05788f95ac96d6f6efb3f7d4795339e1ab55a2 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Fri, 19 Feb 2021 16:01:33 +0100 Subject: [PATCH 008/668] Add build support for Maya 2020 --- src/common.cmake | 26 ++++++++++++++++---------- src/serlio/modifiers/PRTMesh.h | 1 + 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/common.cmake b/src/common.cmake index 7e537174..94770c88 100644 --- a/src/common.cmake +++ b/src/common.cmake @@ -75,19 +75,23 @@ endfunction() if (NOT maya_DIR) if (WIN32) - if (EXISTS "C:/Program Files/Autodesk/Maya2019") - set(maya_DIR "C:/Program Files/Autodesk/Maya2019") - else () - set(maya_DIR "C:/Program Files/Autodesk/Maya2018") - endif () + set(AUTODESK_INSTALL_LOCATION "C:/Program Files/Autodesk") + set(MAYA_BASE_NAME Maya) else () - if (EXISTS "/opt/autodesk/maya2019") - set(maya_DIR "/opt/autodesk/maya2019") - else () - set(maya_DIR "/opt/autodesk/maya2018") - endif () + set(AUTODESK_INSTALL_LOCATION "/usr/autodesk") + set(MAYA_BASE_NAME maya) + endif () + if (EXISTS "${AUTODESK_INSTALL_LOCATION}/${MAYA_BASE_NAME}2020") + set(maya_DIR "${AUTODESK_INSTALL_LOCATION}/${MAYA_BASE_NAME}2020") + elseif (EXISTS "${AUTODESK_INSTALL_LOCATION}/${MAYA_BASE_NAME}2019") + set(maya_DIR "${AUTODESK_INSTALL_LOCATION}/${MAYA_BASE_NAME}2019") + elseif (EXISTS "${AUTODESK_INSTALL_LOCATION}/${MAYA_BASE_NAME}2018") + set(maya_DIR "${AUTODESK_INSTALL_LOCATION}/${MAYA_BASE_NAME}2018") endif () endif () +if (NOT maya_DIR) + message(FATAL_ERROR "Could not detect maya installation below ${AUTODESK_INSTALL_LOCATION}") +endif() message(STATUS "Using maya_DIR = ${maya_DIR} (use '-Dmaya_DIR=xxx' to override)") find_path(maya_INCLUDE_PATH NAMES "maya/MApiVersion.h" PATHS "${maya_DIR}/include" NO_DEFAULT_PATH) @@ -115,6 +119,8 @@ if (maya_DIR MATCHES "[Mm]aya2018") set(maya_VERSION_MAJOR "2018") elseif (maya_DIR MATCHES "[Mm]aya2019") set(maya_VERSION_MAJOR "2019") +elseif (maya_DIR MATCHES "[Mm]aya2020") + set(maya_VERSION_MAJOR "2020") endif () function(srl_add_dependency_maya TGT) diff --git a/src/serlio/modifiers/PRTMesh.h b/src/serlio/modifiers/PRTMesh.h index b3fcb80d..0c49c4b6 100644 --- a/src/serlio/modifiers/PRTMesh.h +++ b/src/serlio/modifiers/PRTMesh.h @@ -21,6 +21,7 @@ #include "maya/MTypes.h" +#include #include class PRTMesh { From 70decf091af2c5df7113023e251b108d95070ac0 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Fri, 19 Feb 2021 16:06:59 +0100 Subject: [PATCH 009/668] CI: add support for Maya 2020 --- pipeline.groovy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pipeline.groovy b/pipeline.groovy index 579b2dae..64f98f43 100644 --- a/pipeline.groovy +++ b/pipeline.groovy @@ -23,8 +23,10 @@ import com.esri.zrh.jenkins.ce.PrtAppPipelineLibrary @Field final List CONFIGS = [ [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC63, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2018', maya: PrtAppPipelineLibrary.Dependencies.MAYA2018 ], [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC63, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2019', maya: PrtAppPipelineLibrary.Dependencies.MAYA2019 ], + [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC63, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2020', maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC141, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2018', maya: PrtAppPipelineLibrary.Dependencies.MAYA2018 ], [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC141, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2019', maya: PrtAppPipelineLibrary.Dependencies.MAYA2019 ], + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC141, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2020', maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], ] @Field final List TEST_CONFIGS = [ From 34c1c1b0c24c08a93f3951e8a837f570e49d73b2 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Fri, 19 Feb 2021 16:09:46 +0100 Subject: [PATCH 010/668] CI: cleanup legacy github repo name --- pipeline.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pipeline.groovy b/pipeline.groovy index 64f98f43..8b6b0474 100644 --- a/pipeline.groovy +++ b/pipeline.groovy @@ -14,7 +14,7 @@ import com.esri.zrh.jenkins.ce.PrtAppPipelineLibrary // -- GLOBAL DEFINITIONS -@Field final String REPO = 'git@github.com:ArcGIS/serlio.git' +@Field final String REPO = 'git@github.com:esri/serlio.git' @Field final String REPO_CREDS = 'jenkins-github-serlio-deployer-key' @Field final String SOURCES = "serlio.git/src" @Field final String BUILD_TARGET = 'package' From bd1ed757b8bac38983be29fba4cabe76ffaad60f Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Fri, 19 Feb 2021 16:26:23 +0100 Subject: [PATCH 011/668] CI: updated pipeline to PRT 2.3 --- pipeline.groovy | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/pipeline.groovy b/pipeline.groovy index 8b6b0474..92461c31 100644 --- a/pipeline.groovy +++ b/pipeline.groovy @@ -21,17 +21,25 @@ import com.esri.zrh.jenkins.ce.PrtAppPipelineLibrary // TODO: abusing grp field to distinguish maya versions per task @Field final List CONFIGS = [ - [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC63, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2018', maya: PrtAppPipelineLibrary.Dependencies.MAYA2018 ], - [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC63, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2019', maya: PrtAppPipelineLibrary.Dependencies.MAYA2019 ], - [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC63, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2020', maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC141, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2018', maya: PrtAppPipelineLibrary.Dependencies.MAYA2018 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC141, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2019', maya: PrtAppPipelineLibrary.Dependencies.MAYA2019 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC141, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2020', maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], + [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC63, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, + cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2018', maya: PrtAppPipelineLibrary.Dependencies.MAYA2018 ], + [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC63, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, + cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2019', maya: PrtAppPipelineLibrary.Dependencies.MAYA2019 ], + [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC63, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, + cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2020', maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC141, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, + cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2018', maya: PrtAppPipelineLibrary.Dependencies.MAYA2018 ], + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC141, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, + cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2019', maya: PrtAppPipelineLibrary.Dependencies.MAYA2019 ], + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC141, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, + cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2020', maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], ] @Field final List TEST_CONFIGS = [ - [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC63, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC141, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64 ], + [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC63, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, + cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201 ], + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC141, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, + cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201 ], ] @@ -75,9 +83,9 @@ Map taskGenSerlioInstallers() { def taskBuildSerlio(cfg) { final String appName = 'serlio' - final List DEPS = [ PrtAppPipelineLibrary.Dependencies.CESDK, cfg.maya ] + final List DEPS = [ cfg.cesdk, cfg.maya ] List defs = [ - [ key: 'prt_DIR', val: PrtAppPipelineLibrary.Dependencies.CESDK.p ], + [ key: 'prt_DIR', val: cfg.cesdk.p ], [ key: 'maya_DIR', val: cfg.maya.p ], [ key: 'SRL_VERSION_BUILD', val: env.BUILD_NUMBER ] ] @@ -96,9 +104,9 @@ def taskBuildSerlio(cfg) { def taskBuildSerlioTests(cfg) { final String appName = 'serlio-test' - final List DEPS = [ PrtAppPipelineLibrary.Dependencies.CESDK ] + final List DEPS = [ cfg.cesdk ] List defs = [ - [ key: 'prt_DIR', val: PrtAppPipelineLibrary.Dependencies.CESDK.p ], + [ key: 'prt_DIR', val: cfg.cesdk.p ], [ key: 'SRL_VERSION_BUILD', val: env.BUILD_NUMBER ] ] @@ -110,7 +118,7 @@ def taskBuildSerlioInstaller(cfg) { final String appName = 'serlio-installer' cepl.cleanCurrentDir() papl.checkout(REPO, myBranch, REPO_CREDS) - final List deps = [ PrtAppPipelineLibrary.Dependencies.CESDK, cfg.maya ] + final List deps = [ cfg.cesdk, cfg.maya ] deps.each { d -> papl.fetchDependency(d, cfg) } // Toolchain definition for building MSI installers. From ff6dbf279c0d4f0ef50bfba237be1fc38f080b2c Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Fri, 19 Feb 2021 16:35:45 +0100 Subject: [PATCH 012/668] cmake cleanup: better encapsulate maya dependency setup --- src/common.cmake | 49 ++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/common.cmake b/src/common.cmake index 94770c88..5d319783 100644 --- a/src/common.cmake +++ b/src/common.cmake @@ -89,30 +89,6 @@ if (NOT maya_DIR) set(maya_DIR "${AUTODESK_INSTALL_LOCATION}/${MAYA_BASE_NAME}2018") endif () endif () -if (NOT maya_DIR) - message(FATAL_ERROR "Could not detect maya installation below ${AUTODESK_INSTALL_LOCATION}") -endif() -message(STATUS "Using maya_DIR = ${maya_DIR} (use '-Dmaya_DIR=xxx' to override)") - -find_path(maya_INCLUDE_PATH NAMES "maya/MApiVersion.h" PATHS "${maya_DIR}/include" NO_DEFAULT_PATH) - -set(MAYA_LIB_DIR "${maya_DIR}/lib") -find_library(maya_LINK_LIB_FOUNDATION NAMES "Foundation" PATHS "${MAYA_LIB_DIR}") -if (maya_LINK_LIB_FOUNDATION) - list(APPEND maya_LINK_LIBRARIES ${maya_LINK_LIB_FOUNDATION}) -endif () -find_library(maya_LINK_LIB_OPENMAYA NAMES "OpenMaya" PATHS "${MAYA_LIB_DIR}") -if (maya_LINK_LIB_OPENMAYA) - list(APPEND maya_LINK_LIBRARIES ${maya_LINK_LIB_OPENMAYA}) -endif () -find_library(maya_LINK_LIB_OPENMAYAUI NAMES "OpenMayaUI" PATHS "${MAYA_LIB_DIR}") -if (maya_LINK_LIB_OPENMAYAUI) - list(APPEND maya_LINK_LIBRARIES ${maya_LINK_LIB_OPENMAYAUI}) -endif () -find_library(maya_LINK_LIB_METADATA NAMES "MetaData" PATHS "${MAYA_LIB_DIR}") -if (maya_LINK_LIB_METADATA) - list(APPEND maya_LINK_LIBRARIES ${maya_LINK_LIB_METADATA}) -endif () # temporary heuristic to detect maya version number if (maya_DIR MATCHES "[Mm]aya2018") @@ -124,6 +100,31 @@ elseif (maya_DIR MATCHES "[Mm]aya2020") endif () function(srl_add_dependency_maya TGT) + if (NOT maya_DIR) + message(FATAL_ERROR "Could not detect maya installation below ${AUTODESK_INSTALL_LOCATION}") + endif() + message(STATUS "Using maya_DIR = ${maya_DIR} (use '-Dmaya_DIR=xxx' to override)") + + find_path(maya_INCLUDE_PATH NAMES "maya/MApiVersion.h" PATHS "${maya_DIR}/include" NO_DEFAULT_PATH) + + set(MAYA_LIB_DIR "${maya_DIR}/lib") + find_library(maya_LINK_LIB_FOUNDATION NAMES "Foundation" PATHS "${MAYA_LIB_DIR}") + if (maya_LINK_LIB_FOUNDATION) + list(APPEND maya_LINK_LIBRARIES ${maya_LINK_LIB_FOUNDATION}) + endif () + find_library(maya_LINK_LIB_OPENMAYA NAMES "OpenMaya" PATHS "${MAYA_LIB_DIR}") + if (maya_LINK_LIB_OPENMAYA) + list(APPEND maya_LINK_LIBRARIES ${maya_LINK_LIB_OPENMAYA}) + endif () + find_library(maya_LINK_LIB_OPENMAYAUI NAMES "OpenMayaUI" PATHS "${MAYA_LIB_DIR}") + if (maya_LINK_LIB_OPENMAYAUI) + list(APPEND maya_LINK_LIBRARIES ${maya_LINK_LIB_OPENMAYAUI}) + endif () + find_library(maya_LINK_LIB_METADATA NAMES "MetaData" PATHS "${MAYA_LIB_DIR}") + if (maya_LINK_LIB_METADATA) + list(APPEND maya_LINK_LIBRARIES ${maya_LINK_LIB_METADATA}) + endif () + if (maya_INCLUDE_PATH) target_include_directories(${TGT} PRIVATE ${maya_INCLUDE_PATH}) endif () From d5779cb3138a363d4b8cd38883f04c273fcb1212 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Fri, 19 Feb 2021 16:45:31 +0100 Subject: [PATCH 013/668] CI: update test configurations for Maya --- pipeline.groovy | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pipeline.groovy b/pipeline.groovy index 92461c31..3a0bfc1b 100644 --- a/pipeline.groovy +++ b/pipeline.groovy @@ -37,9 +37,9 @@ import com.esri.zrh.jenkins.ce.PrtAppPipelineLibrary @Field final List TEST_CONFIGS = [ [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC63, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, - cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201 ], + cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC141, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, - cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201 ], + cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], ] @@ -104,9 +104,10 @@ def taskBuildSerlio(cfg) { def taskBuildSerlioTests(cfg) { final String appName = 'serlio-test' - final List DEPS = [ cfg.cesdk ] + final List DEPS = [ cfg.cesdk, cfg.maya ] List defs = [ [ key: 'prt_DIR', val: cfg.cesdk.p ], + [ key: 'maya_DIR', val: cfg.maya.p ], [ key: 'SRL_VERSION_BUILD', val: env.BUILD_NUMBER ] ] From eafc3e2c654656691bcfb04203f4d02fd65ac00a Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Fri, 19 Feb 2021 16:56:19 +0100 Subject: [PATCH 014/668] Updated docs for Maya 2020 --- doc/build.md | 2 +- doc/changelog.md | 1 + doc/usage.md | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/build.md b/doc/build.md index eadca2da..9d0e528b 100644 --- a/doc/build.md +++ b/doc/build.md @@ -6,7 +6,7 @@ ### All Platforms * License for CityEngine (2019.0 or later), e.g. to author Rule Packages. * CMake 3.13 or later (http://www.cmake.org) -* Autodesk Maya 2018 or 2019 installation or the corresponding development kit +* Autodesk Maya 2018 or later or the corresponding development kit ### Windows * Windows 7, 8.1 or 10 (64bit) diff --git a/doc/changelog.md b/doc/changelog.md index 8edd6282..9b491841 100644 --- a/doc/changelog.md +++ b/doc/changelog.md @@ -2,6 +2,7 @@ ## v1.2.0 (2021-02-XX) * Updated Procedural Runtime (PRT) to 2.3 (corresponds to CityEngine 2020). +* Added support for Maya 2020. ## v1.1.0 (2020-06-02) * BREAKING CHANGE: Switched to official Autodesk IDs for Serlio custom nodes. (#26) diff --git a/doc/usage.md b/doc/usage.md index 9b1f1b59..e66e7ee9 100644 --- a/doc/usage.md +++ b/doc/usage.md @@ -3,14 +3,14 @@ ## Installation ### Windows: Provided Binaries -1. Download the MSI installer for your Maya version (2018 or 2019) from the [desired release](https://github.com/Esri/serlio/releases) +1. Download the MSI installer for your Maya version from the [desired release](https://github.com/Esri/serlio/releases) 1. Run the MSI installer 1. Start Maya 1. You should now see the new menu item "Serlio". ### Windows: Compile from Source 1. Locate the `install` directory where you [built](build.md) the plugin, let's call it `PLUGINDIR` -1. Locate the Maya.env file in your home, usually its in `\Documents\maya\2019` +1. Locate the Maya.env file in your home, usually its in `\Documents\maya\2020` 1. Edit Maya.env as follows: ``` :: replace with the actual path @@ -28,7 +28,7 @@ * ... download the [desired release](https://github.com/Esri/serlio/releases) * ... or [build](build.md) them yourself, which will result in an `install` directory 1. Either way, let's call the directory with the Serlio binaries `PLUGINDIR` -1. Locate the Maya.env file in your home, e.g.: `~/maya/2019/Maya.env` +1. Locate the Maya.env file in your home, e.g.: `~/maya/2020/Maya.env` 1. Edit Maya.env as follows: ``` PLUGINDIR= # replace with the actual path From 6dd934f5433b2f97d2e4f780f2342658742d0bf2 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 22 Feb 2021 09:24:52 +0100 Subject: [PATCH 015/668] CI: update to MSVC 14.2 to match PRT 2.3 --- pipeline.groovy | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pipeline.groovy b/pipeline.groovy index 3a0bfc1b..a427847a 100644 --- a/pipeline.groovy +++ b/pipeline.groovy @@ -27,18 +27,18 @@ import com.esri.zrh.jenkins.ce.PrtAppPipelineLibrary cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2019', maya: PrtAppPipelineLibrary.Dependencies.MAYA2019 ], [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC63, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2020', maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC141, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC142, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2018', maya: PrtAppPipelineLibrary.Dependencies.MAYA2018 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC141, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC142, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2019', maya: PrtAppPipelineLibrary.Dependencies.MAYA2019 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC141, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC142, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2020', maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], ] @Field final List TEST_CONFIGS = [ [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC63, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC141, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC142, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], ] From f6ee12aa2514241b644cbc5df8d84ff223e88ba6 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 22 Feb 2021 09:29:22 +0100 Subject: [PATCH 016/668] CI: avoid parallel pipeline runs --- Jenkinsfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 0eac63bc..f1dc56d5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -16,6 +16,8 @@ import com.esri.zrh.jenkins.PslFactory psl.runsHere('production') env.PIPELINE_ARCHIVING_ALLOWED = "true" +properties([ disableConcurrentBuilds() ]) + // -- LOAD & RUN PIPELINE From 01884e2a2a3b48b3949f417e5eb02160272b94fa Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 22 Feb 2021 10:40:56 +0100 Subject: [PATCH 017/668] windows installer: add Maya 2020 --- deploy/PackageContents.xml.in | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/deploy/PackageContents.xml.in b/deploy/PackageContents.xml.in index 5027bb7d..d3556388 100644 --- a/deploy/PackageContents.xml.in +++ b/deploy/PackageContents.xml.in @@ -49,4 +49,11 @@ + + + + + + + From f1493ef20d3da4d2167a4bef2f6bcefd21651533 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 22 Feb 2021 10:27:45 +0100 Subject: [PATCH 018/668] add new icon and install into icons subdirectory --- src/serlio/CMakeLists.txt | 2 + src/serlio/icons/serlio.xpm | 2127 +++++++++++++++++++++++++++++++++++ 2 files changed, 2129 insertions(+) create mode 100644 src/serlio/icons/serlio.xpm diff --git a/src/serlio/CMakeLists.txt b/src/serlio/CMakeLists.txt index 903965f3..7460db0b 100644 --- a/src/serlio/CMakeLists.txt +++ b/src/serlio/CMakeLists.txt @@ -145,3 +145,5 @@ install(FILES ${SRL_PRT_EXT_LIBRARIES} DESTINATION ${INSTALL_FOLDER_PREFIX}/plug install(DIRECTORY ${PROJECT_SOURCE_DIR}/../doc DESTINATION ${INSTALL_FOLDER_PREFIX}) install(FILES ${PROJECT_SOURCE_DIR}/../README.md ${PROJECT_SOURCE_DIR}/../LICENSE DESTINATION ${INSTALL_FOLDER_PREFIX}) +# icons +install(FILES icons/serlio.xpm DESTINATION ${INSTALL_FOLDER_PREFIX}/icons) \ No newline at end of file diff --git a/src/serlio/icons/serlio.xpm b/src/serlio/icons/serlio.xpm new file mode 100644 index 00000000..efaa3ce4 --- /dev/null +++ b/src/serlio/icons/serlio.xpm @@ -0,0 +1,2127 @@ +/* XPM */ +static char *dummy[]={ +"382 444 1680 2", +"Qt c None", +".k c #ffffff", +"ab c #80ffff", +"at c #55aaaa", +"f1 c #55ffaa", +"#R c #aaffff", +"aa c #80bfbf", +"#z c #80ffbf", +"#Q c #66cc99", +"#h c #66cccc", +"#y c #55aaaa", +"bH c #80d5d5", +"bY c #6db6b6", +".Y c #6ddbb6", +"#g c #60bf9f", +"h8 c #60bfbf", +"wL c #60dfbf", +"ym c #80dfbf", +"er c #55c6aa", +".B c #71c6c6", +".X c #4db399", +"bn c #4db3b3", +"d6 c #66b3b3", +"ty c #66ccb3", +"yn c #66cccc", +"aT c #74d1b9", +".A c #55aaaa", +"dL c #55bfaa", +"h. c #6abfbf", +"vF c #6ad5bf", +"gG c #62b1b1", +"hV c #62c4b1", +"aD c #62c4c4", +"aS c #5bb6a4", +"vP c #6dc8b6", +"dp c #55bbaa", +"ai c #66ccbb", +"aC c #50af9f", +"uB c #60bfbf", +"v0 c #60cfbf", +"c6 c #5ab4a5", +"#X c #69c3b4", +"ch c #63c6b8", +"ah c #51aea1", +"fM c #5ec9bc", +"wb c #6bc9bc", +"fL c #59b3a6", +"#F c #66ccbf", +"x2 c #55aa9e", +"eW c #61c2b6", +"#W c #51aea2", +"uT c #68c5b9", +"fo c #59b1a6", +"ey c #64c8b1", +"#n c #64c8bc", +"#E c #55aa9f", +"u6 c #60bfb5", +"wu c #60cab5", +".# c #6acabf", +"wM c #52ad99", +"b4 c #52ada3", +".6 c #66ccb8", +"fG c #62c4b1", +"bu c #62c4ba", +"#m c #4caaa1", +"vf c #5ec6b3", +"wG c #68c6b3", +"bM c #52ad9b", +"ex c #52ada4", +"hr c #64bfb6", +".J c #64c8b6", +"t5 c #4fb09e", +"bc c #61c1b9", +"dR c #5ec4b3", +"wU c #66c4bb", +".5 c #52ad9c", +"tG c #63c5b5", +"ea c #50af9f", +"t# c #60bfaf", +"aZ c #60c7b7", +"fz c #64c1b2", +"dv c #64c1b9", +".l c #64c9b9", +".I c #4bad9e", +"xh c #53ad9e", +"vz c #62c3b4", +"bb c #50afa0", +"gL c #55b1a3", +"aI c #63c6b8", +"tU c #60c1b3", +"vL c #60c8b3", +"xb c #60c8ba", +"aY c #51ae9a", +"v1 c #51aea1", +"du c #4eb19d", +"gr c #55b1a3", +"ao c #62c4b7", +"tA c #60bfb3", +"cO c #60c6b3", +"xG c #51ae9b", +"iG c #5dc1ae", +"vV c #64c1b4", +"xq c #64c7b4", +"aH c #4faa9e", +"da c #4fb09e", +"g6 c #61c2b0", +"uw c #61c2b6", +"gd c #53b2a0", +"#5 c #65c4b8", +"tN c #5dbfb4", +"cn c #63c5b4", +"xC c #63c5b9", +"xP c #4faa99", +"v9 c #60c6b5", +"an c #4eac9b", +"cN c #53ac9b", +"uU c #53b1a1", +"uG c #5ec2b1", +"fT c #51ae9d", +"#L c #62c3b8", +"tg c #60bfaf", +"e6 c #60bfb5", +"c. c #60c5b5", +"xX c #4eac9c", +"wi c #63c6b6", +"#4 c #4dad99", +"wv c #4dad9e", +"cm c #52ad9e", +"tt c #61c2b3", +"#K c #50aa9b", +"fy c #50afa0", +"hv c #5fbeaf", +"#t c #64c3b4", +"y# c #4eac9d", +"eF c #5dbfb5", +"bT c #62c4b5", +"b9 c #52ad9f", +"#b c #60c5b7", +".j c #4caa9c", +"wH c #50aa9c", +"gZ c #50afa1", +"bA c #5ec2b3", +"#s c #4fac99", +"vg c #4fac9e", +"e5 c #4fb09e", +"ek c #60bfb2", +"bS c #51aa9d", +".Q c #62c5b7", +"#a c #4ea99c", +"wV c #4ead9c", +"eE c #52ada0", +"s9 c #5fbeb1", +"bi c #5fc2b6", +"f2 c #5ebfb3", +"bz c #4fab9b", +"vq c #4fab9f", +"dZ c #60c0b4", +".v c #60c4b8", +".P c #4daa9a", +"ej c #51ae9e", +"hQ c #5dbeb2", +"a5 c #61c2b6", +"w5 c #50ab9b", +"bh c #4ead9d", +"dD c #5ec0b4", +".u c #4daa9b", +"vA c #4dae9e", +"dY c #50af9c", +"u# c #50afa0", +"ud c #5fc2b3", +"aN c #5fc2b7", +"xc c #4bad9a", +"if c #5ebfb0", +"a4 c #4eaa9b", +"gT c #51ae9f", +"dg c #60c0b5", +"vM c #50ab9d", +"dC c #4fac9e", +"au c #61c6b7", +"xr c #4eaa9c", +"um c #51ae9f", +"s8 c #5cbfae", +"gy c #50af9d", +"c0 c #5ec0b2", +"aM c #4fac9b", +"df c #4ead9c", +"ac c #5fc2b4", +"xD c #4dab9a", +"ux c #51ae9e", +"xl c #61c3b5", +"yl c #61c6b5", +"gi c #4fac9f", +"ct c #60c0b3", +"vS c #60c3b6", +"as c #4eaa9a", +"w. c #4eaa9d", +"tq c #5ebeae", +"ut c #5ec2b2", +"xN c #4da999", +"cZ c #50ac9c", +"hC c #5cbfaf", +"uh c #5cbfb3", +"#S c #60c2b6", +"gj c #5ec0b0", +"cd c #5ec3b3", +"a# c #4eab9b", +"f0 c #51ae9f", +"s7 c #5dbeae", +"v5 c #60c1b4", +"ff c #5fc2b2", +"cs c #4fad9e", +"#A c #61c2b6", +"xJ c #61c5b6", +"xU c #4ba899", +"#P c #4da99a", +"fF c #50af9d", +"bZ c #5fc4b5", +"wf c #62c4b5", +"cc c #4eab9c", +"u2 c #4eae9c", +"tB c #5dbfb1", +"eP c #60bfb4", +"uN c #60c2b4", +"#i c #63c5b7", +"x8 c #4da99b", +"iM c #5cbdaf", +"ye c #61c6b7", +"#x c #4daa99", +"fe c #4fad9f", +"bI c #60c4b5", +"wD c #4eab9a", +"g0 c #5cbfb1", +"ul c #5fc1b3", +"bX c #4eac9b", +"h1 c #5bbfb1", +"uX c #5ebfb1", +"es c #5ec2b1", +".Z c #61c2b4", +"x1 c #61c5b4", +"vb c #50ad9c", +"eO c #4fae9d", +"tO c #51ae9d", +"bo c #5fc3b3", +"wz c #62c3b3", +"#f c #4ea999", +"wQ c #4eac9c", +"bG c #4daa9a", +"d7 c #60c2b5", +".C c #62c5b7", +"vo c #4fae9b", +"eq c #4eae9c", +"tf c #5bbeae", +"a9 c #60c3b4", +".W c #4daa9b", +"tY c #50af9d", +"s4 c #5abcaf", +"g5 c #4fad9e", +"hz c #4fb09e", +"hq c #52b09e", +"hS c #52b0a1", +"jz c #52b3a1", +"jR c #52b3a3", +"j6 c #54b3a3", +"mX c #54b5a3", +"n# c #54b5a6", +"nD c #57b5a6", +"nE c #57b5a8", +"nG c #57b8a8", +"oj c #59b8a8", +"ol c #59b8ab", +"ok c #59baab", +"pj c #59baad", +"se c #59bdad", +"rE c #5cbdad", +"s1 c #5cbdb0", +"hJ c #5ebfb0", +"vj c #5ebfb3", +"dM c #5ec2b3", +"bm c #4eac9a", +"vx c #4eac9d", +"d5 c #50ad9e", +"up c #5dc1b2", +"aU c #5fc3b4", +".z c #4da899", +"x# c #4dab9c", +"t8 c #4fad9e", +"wZ c #61c1b5", +".a c #61c4b7", +"dq c #60c2b3", +"a8 c #4eaa99", +"vJ c #4eac9b", +"tj c #5bbeb0", +"t4 c #5ebeb2", +"xm c #4ca99b", +"dK c #4fac9d", +"ui c #4fae9d", +"hk c #5dbfb0", +"aE c #5fc3b5", +"c7 c #5ec2b3", +"aR c #4dab9a", +"vT c #4dad9d", +"gF c #50ad9f", +"vE c #60c2b4", +"do c #4eac9c", +"aj c #60c3b3", +"xg c #60c3b5", +"xy c #4ba899", +"tm c #5dbfaf", +"aB c #4dac9c", +"v6 c #4fac9c", +"gn c #4fae9e", +"cy c #5fc2b2", +"c5 c #50ad9d", +"#Y c #5fc3b6", +"xK c #4da99a", +"ag c #4caa9b", +"f8 c #51ae9f", +"ci c #60c2b3", +"vZ c #60c2b5", +".m c #62c4b7", +"wg c #4eab9c", +"cx c #4dab9c", +"fp c #5cc1b2", +"#G c #60c3b4", +"xS c #4daa99", +"uO c #4fac9d", +"fK c #50af9e", +"b5 c #5fc2b3", +"#V c #4ba99b", +"wo c #4dab9b", +"cg c #4faa9b", +"eX c #5dc1b0", +"uJ c #5dc1b2", +"#o c #5fc3b4", +"x3 c #4ca998", +"uY c #4ead9c", +"yf c #4ba999", +"fn c #50ad9d", +"bN c #60c2b4", +"#D c #4daa9a", +"wA c #4daa9c", +"f9 c #5cbfb1", +"ez c #5ec1b3", +"b3 c #4eab9b", +"v. c #50ab9d", +"fU c #5ebfb1", +".7 c #62c3b5", +".i c #4ba898", +"eV c #4fae9c", +"iR c #5dbeb0", +"bv c #61c2b4", +"#l c #4dab99", +"wN c #4eab9b", +"tK c #50af9f", +"wt c #60c2b4", +"tw c #5bbfaf", +"u5 c #5dc1b1", +"eb c #5fc1b3", +"bL c #4daa9c", +"vk c #4dac9c", +".K c #61c3b6", +"go c #5cc0b0", +".4 c #4ca998", +"w0 c #4ca99a", +"ew c #4ead9c", +"tV c #50ad9e", +"bd c #5fc2b5", +"ih c #5dbfb0", +"dS c #5ec1b3", +"bt c #4fab9c", +"vu c #4fad9e", +"ve c #60c1b2", +".H c #4ca899", +"e# c #4fae9d", +"wT c #5ec2b3", +"a0 c #60c2b3", +"w8 c #4dab9a", +"fN c #5ebfb0", +"ya c #4da999", +"ba c #4dab9b", +"vG c #4eab9d", +"dw c #5fc1b3", +"gs c #5cc0b1", +"dQ c #4dac9c", +"ue c #4fac9e", +"aJ c #5fc4b4", +"w4 c #5fc4b5", +"xi c #4da99b", +"aX c #4caa9a", +"gK c #50ae9e", +"db c #5ec1b1", +"vQ c #4dac9d", +"te c #5bbcae", +"dt c #4fab9b", +"ap c #60c4b6", +"xv c #4caa9a", +"uq c #4ead9c", +"gz c #5ebfb1", +"gq c #4fae9f", +"tC c #5dbfb0", +"cP c #5fc1b3", +"vK c #5fc3b3", +".t c #4da99a", +"v2 c #4dab9a", +"gU c #5cc0b0", +"d# c #4eac9c", +"fH c #5ec0b3", +"#6 c #5fc4b4", +"xp c #61c4b4", +"xH c #4eab9a", +"uC c #4fac9d", +"u. c #5fc1b1", +"gc c #4fad9e", +"co c #60c3b4", +"am c #4daa99", +"wc c #4eaa9b", +"gH c #5dbeb1", +"fA c #5ec0b1", +".b c #60c3b6", +"xQ c #4ca999", +"cM c #4dab9c", +"uK c #4fad9c", +"#M c #5fc2b5", +"xB c #61c2b5", +"fS c #4ead9d", +"c# c #5fc3b2", +"#3 c #4cab9a", +"wl c #4eab9a", +"iT c #5dbeaf", +"v8 c #5ec1b3", +"gM c #5ebeb2", +"e7 c #5ec0b2", +"cl c #4fac9c", +"uV c #4fad9d", +"#u c #60c4b5", +"xY c #4da999", +"#J c #4ca99a", +"ww c #4eab9b", +"fx c #4fad9e", +"tu c #5dbeb0", +"bU c #5fc1b5", +"hu c #5dbeb0", +"eG c #5ec0b2", +"b8 c #4eab9b", +"u7 c #4eac9c", +"uQ c #5ec0b2", +"#c c #61c4b5", +".h c #4ca998", +"#r c #4caa99", +"e4 c #4fad9c", +"tH c #50ad9f", +"wq c #5ec1b3", +"bB c #60c1b3", +"wI c #4dab9b", +"bR c #4cab9a", +"vh c #4eab9c", +"hd c #5dbfb1", +"el c #5dc0b1", +"u1 c #5fc0b3", +"x7 c #60c3b4", +".R c #60c3b6", +"## c #4ba998", +"eD c #4eac9d", +"tR c #50ae9e", +"hT c #5cbeaf", +"bj c #5fc1b4", +"wW c #4dab9b", +"by c #4eaa9b", +"d0 c #5ec0b1", +"va c #5fc0b3", +".w c #61c3b6", +"vr c #4dac9c", +"to c #5cbeaf", +"ei c #4eac9c", +"ti c #5bbeaf", +"a6 c #60c1b4", +".O c #4ca998", +"w6 c #4caa9a", +"t1 c #4fae9e", +"t0 c #5dbfb0", +"dE c #5dc0b2", +"vn c #5fc0b3", +"yd c #60c2b5", +"bg c #4dab9b", +"vB c #4eac9b", +"yg c #4ca999", +"dX c #4fab9d", +"w1 c #5fc1b4", +"aO c #5fc2b4", +"xd c #4da999", +"ua c #4fad9c", +"gS c #4eac9e", +"dh c #5ec0b2", +"a3 c #4eaa9a", +"vN c #4eab9c", +"h5 c #5cbeae", +".c c #60c4b5", +"xs c #4daa9a", +"dB c #4dac9c", +"un c #50ac9e", +"av c #5fc3b3", +"x. c #61c3b5", +"gx c #4ead9d", +"c1 c #5fc0b2", +"aL c #4ca99b", +"vW c #4eac9b", +"vI c #5ec2b3", +"de c #4eab9c", +"uy c #4ead9c", +"i1 c #5cbdae", +"ad c #60c3b4", +"xk c #60c3b5", +"xE c #4ba99a", +"ar c #4daa99", +"w# c #4dab9b", +"gh c #50ae9d", +"cu c #5fc1b3", +".g c #4ba999", +"cY c #4eac9c", +"uH c #4fad9d", +"us c #5dc0b1", +"#T c #5fc3b5", +"xx c #61c4b5", +"xO c #4ca898", +"a. c #4daa9a", +"fZ c #4ead9e", +"hA c #5cbeaf", +"tr c #5dbfaf", +"v4 c #5ec1b3", +"ce c #5ec2b3", +"wj c #4eaa9b", +"tc c #5bbeae", +"cr c #4eab9b", +"uR c #4eac9b", +"ge c #5dc0b0", +"fg c #5dc0b1", +"#B c #60c3b4", +"xV c #4ca999", +"fE c #4ead9c", +"we c #5fc2b4", +"b0 c #60c2b4", +"wr c #4cab9a", +"tE c #50ad9e", +"eQ c #5ec0b2", +"uM c #5ec1b2", +".n c #5fc2b4", +"xR c #61c4b6", +"x9 c #4ca998", +"u3 c #4ead9c", +"tk c #5cbeae", +"#O c #4daa99", +"cb c #4eac9c", +"iq c #5cbeaf", +"wn c #5ec2b4", +"wE c #4daa9b", +"fd c #50ad9d", +"tD c #5dbeaf", +"uc c #5dbeb0", +"#j c #60c3b5", +"et c #5dc0b2", +"bJ c #60c1b3", +"x0 c #61c4b6", +"yh c #4ba899", +"vc c #4eac9c", +"wy c #5fc3b4", +"#w c #4caa9a", +"wR c #4caa9b", +"u9 c #5ec1b2", +"bW c #4dac9b", +"eN c #4dac9d", +"tZ c #50ae9e", +"tP c #50ae9f", +".0 c #61c3b6", +"bp c #5fc1b3", +".d c #61c2b5", +"d8 c #5ec0b1", +"wK c #5fc1b4", +"#e c #4ca999", +"w2 c #4caa9a", +"vi c #5dbfb2", +"bF c #4dab9b", +"ep c #4eac9c", +"t9 c #4eac9d", +"hO c #5cbdb0", +"tp c #5dbeb0", +".D c #60c3b6", +"b. c #5fc1b4", +"ug c #5dc0b2", +"dN c #5ec0b3", +"wY c #5fc3b4", +".V c #4daa9a", +"t7 c #5dbeb1", +"vt c #5ec0b2", +"bl c #4daa9a", +"d4 c #4eab9c", +"uj c #4fac9d", +"j. c #5bbdae", +"tx c #5cbeb0", +"gY c #50ae9e", +"dr c #5ec1b3", +"aV c #5ec1b4", +"h# c #5dbfb0", +"w7 c #60c3b4", +".y c #4ca998", +"xn c #4daa99", +"a7 c #4eaa9b", +"vD c #5ec0b2", +"dJ c #4fac9c", +"uu c #4fad9d", +"gE c #50ae9d", +"hE c #5bbdaf", +"c8 c #5fc1b3", +"aF c #60c2b4", +"xz c #4daa9a", +"uk c #5dc0b1", +"vO c #5ec0b2", +"xf c #5fc2b4", +"yb c #4ba899", +"aQ c #4eaa9a", +"dn c #4eac9b", +"uo c #5ebfb1", +"uE c #4ead9d", +"gm c #50ad9e", +"ak c #60c3b4", +"cz c #5ec2b3", +"xu c #5fc2b4", +"xL c #4da999", +"aA c #4daa9a", +"uA c #5dc0b0", +"tX c #5ec0b0", +"vY c #5fc0b2", +"c4 c #4dac9c", +"uP c #4fad9d", +"f7 c #50ad9d", +"#Z c #60c3b5", +"yk c #5bbbac", +"f3 c #5ec0b1", +"fq c #5ec0b2", +"cj c #5ec1b3", +"xF c #60c4b4", +"xT c #4ca898", +"uI c #5ec0b1", +"wa c #5fc1b2", +"af c #4caa9a", +"wp c #4eab9b", +"cw c #4eac9b", +"uZ c #4fad9c", +"fJ c #4fad9d", +"tT c #5dc0b0", +".s c #4ca998", +"je c #5bbdad", +"eY c #5ebfb2", +"b6 c #5fc1b4", +"#H c #5fc3b4", +"x4 c #4ca899", +"yi c #4ca89a", +"uS c #5ec1b1", +"wk c #5fc2b3", +"#U c #4daa99", +"wB c #4eab9a", +"cf c #4eab9b", +"fm c #4fad9c", +".f c #54b5a4", +"tQ c #5dbfb0", +"tL c #4fad9f", +"tb c #5cbeae", +"eA c #5fc0b2", +"bO c #60c1b4", +"#p c #60c2b5", +"xW c #60c3b5", +"#C c #4ca899", +"wO c #4daa9b", +"tM c #5cbeb0", +"u4 c #5fc0b2", +"ws c #5fc1b2", +"b2 c #4dab9a", +"vl c #4eac9b", +"eU c #4fad9d", +"tW c #50ae9e", +".8 c #5fc3b5", +"ec c #5ec0b2", +"bw c #60c2b4", +"y. c #61c3b6", +"#k c #4da999", +"tJ c #5cbfb1", +"vd c #5ec0b3", +"wF c #5fc1b3", +"bK c #4eab9b", +"vv c #4eac9c", +"ev c #4fad9d", +"t6 c #50ae9e", +"tF c #5dbeaf", +"be c #5ec1b3", +".L c #5fc2b4", +"ji c #5bbcae", +"dT c #5ec1b2", +"wS c #60c2b4", +".3 c #4ca999", +"w9 c #4ca99a", +"bs c #4caa9a", +"vH c #4dab9b", +"e. c #4ead9c", +"th c #5cbeb0", +"gk c #5dc0b1", +"vp c #5dc0b2", +"uf c #4fad9d", +"dx c #5fc0b2", +"a1 c #60c2b4", +".G c #4ba999", +"xj c #4baa99", +"b# c #4daa9a", +"vR c #4eab9b", +"vy c #5fc0b3", +"w3 c #60c2b5", +"dP c #4eac9d", +"ur c #4eac9e", +"gJ c #4fae9e", +"fV c #5dc0b1", +"aK c #5fc3b5", +"xw c #4daa9a", +"dc c #5fc1b2", +"xa c #60c3b4", +"aW c #4daa99", +"v3 c #4eab9b", +"ds c #4fab9c", +"uD c #4fac9c", +"gp c #50ad9d", +"tz c #5cbfaf", +"aq c #5fc2b4", +"xI c #4ba899", +"g7 c #5dbeb1", +"cQ c #5ec0b3", +"vU c #5fc1b4", +"xo c #5fc2b5", +"aG c #4daa99", +"wd c #4dab9a", +"d. c #4eac9c", +"uL c #4ead9c", +"gb c #4fae9d", +"uv c #5ec0b2", +"#7 c #61c3b4", +"al c #4daa99", +"iA c #5bbdae", +"hs c #5cbeaf", +"tv c #5cbfaf", +"fO c #5cbfb0", +"fB c #5dc0b1", +"uF c #5dc1b1", +"cp c #5ec1b2", +"v7 c #5fc2b3", +"xA c #5fc3b4", +"wm c #4caa9b", +"cL c #4eab9c", +"uW c #4eac9d", +"fR c #4eac9e", +"yj c #54b4a5", +"yc c #58b8aa", +"jk c #5cbeae", +"hW c #5cbeb0", +"h9 c #5dbeb0", +"t3 c #5ec0b1", +"ca c #5fc0b3", +"#N c #60c2b5", +"xZ c #4ba998", +"#2 c #4caa99", +"ck c #4dab9a", +"u8 c #4ead9c", +"e8 c #5ebfb2", +"wh c #5fc1b3", +"xM c #61c3b6", +"fw c #4fac9e", +"tI c #50ad9e", +"eH c #5dc0b1", +"bV c #5ec2b3", +"#v c #5fc3b4", +"#I c #4da999", +"wJ c #4daa9a", +"b7 c #4eab9b", +"e3 c #4fac9c", +"tS c #4fac9d", +"fI c #5ec0b0", +"em c #5ec0b1", +"u0 c #5ec1b1", +"bC c #5fc2b4", +"#d c #60c3b5", +"#q c #4caa99", +"wX c #4caa9a", +"bQ c #4cab9a", +"vs c #4dac9b", +"eC c #4ead9c", +"t2 c #4ead9d", +"v# c #5ec0b3", +"wC c #5ec1b4", +"x6 c #5fc2b5", +".S c #61c3b6", +"#. c #4daa9a", +"bx c #4daa9b", +"eh c #4eab9d", +"vC c #4fab9c", +"tn c #5cbdaf", +"g. c #5dbfb0", +"d1 c #5dc0b1", +"vm c #5ec0b2", +"bk c #5fc1b3", +"wP c #5fc2b3", +".x c #60c3b5", +".N c #4ca898", +"xe c #4da999", +"bf c #4eaa9a", +"dW c #4fab9c", +"ub c #4fac9c", +"dF c #5ec1b1", +"vw c #5fc1b2", +".e c #5fc2b3", +"xt c #4baa9a", +"a2 c #4caa9b", +"vX c #4dab9b", +"dA c #4ead9d", +"uz c #4ead9e", +"gw c #4fae9e", +"ta c #5bbdaf", +"ts c #5bbeb0", +"hw c #5cbfb1", +"c2 c #5dc1b3", +"di c #5ec1b3", +"aw c #5fc2b4", +"aP c #5fc3b5", +".o c #60c4b6", +"t. c #4aa797", +"tl c #4aa798", +"s2 c #4aa897", +"sW c #4aa898", +"s6 c #4aa998", +"s5 c #4ba797", +"s3 c #4ba798", +".r c #4ba898", +".9 c #4ba899", +"sA c #4ba998", +".M c #4ba999", +"sK c #4baa99", +"sQ c #4baa9a", +"td c #4ca898", +"sH c #4ca899", +"sY c #4ca998", +".F c #4ca999", +"#1 c #4ca99a", +"sk c #4caa99", +"#9 c #4caa9a", +"rQ c #4caa9b", +"dV c #4cab9a", +"dI c #4cab9b", +"eg c #4cab9c", +"sZ c #4da999", +"sj c #4da99a", +"s0 c #4da99b", +"r9 c #4daa99", +"az c #4daa9a", +"br c #4daa9b", +"ef c #4daa9c", +"dH c #4dab9a", +"bE c #4dab9b", +"dz c #4dab9c", +"eu c #4dac9b", +"d3 c #4dac9c", +"eM c #4dac9d", +"sR c #4dad9c", +"gX c #4dad9d", +"dO c #4eaa9b", +"d9 c #4eaa9c", +"cq c #4eab9b", +"c3 c #4eab9c", +"eS c #4eab9d", +"dU c #4eac9b", +"dm c #4eac9c", +"eL c #4eac9d", +"e1 c #4eac9e", +"eo c #4ead9c", +"e2 c #4ead9d", +"ft c #4ead9e", +"f6 c #4eae9d", +"gW c #4eae9e", +"ie c #4eaf9e", +"eR c #4fab9d", +"fc c #4fac9c", +"eT c #4fac9d", +"fv c #4fac9e", +"fY c #4fad9c", +"fb c #4fad9d", +"fu c #4fad9e", +"f5 c #4fad9f", +"fl c #4fae9d", +"fk c #4fae9e", +"gv c #4fae9f", +"gP c #4faf9e", +"gQ c #4faf9f", +"on c #4fafa0", +"hp c #4fb09f", +"ga c #50ad9d", +"fX c #50ad9e", +"fW c #50ad9f", +"hj c #50ae9d", +"fQ c #50ae9e", +"gu c #50ae9f", +"g4 c #50aea0", +"gg c #50af9e", +"gl c #50af9f", +"gR c #50afa0", +"hU c #50afa1", +"hc c #50b09f", +"hb c #50b0a0", +"hZ c #50b0a1", +"id c #50b1a0", +"iO c #50b1a1", +"ht c #51ae9e", +"hi c #51ae9f", +"hN c #51aea0", +"hM c #51af9e", +"hh c #51af9f", +"gV c #51afa0", +"hK c #51afa1", +"ho c #51b09f", +"hn c #51b0a0", +"hD c #51b0a1", +"ib c #51b0a2", +"h2 c #51b1a0", +"h4 c #51b1a1", +"iS c #51b1a2", +"ke c #51b1a3", +"iC c #51b2a1", +"jb c #51b2a2", +"jN c #51b2a3", +"oD c #52aea0", +"hB c #52afa0", +"hL c #52afa1", +"hy c #52b0a0", +"hR c #52b0a1", +"h7 c #52b0a2", +"h3 c #52b1a0", +"h0 c #52b1a1", +"hY c #52b1a2", +"i6 c #52b1a3", +"ic c #52b2a1", +"i4 c #52b2a2", +"d2 c #52b2a3", +"jW c #52b2a4", +"js c #52b3a2", +"jw c #52b3a3", +"jZ c #52b3a4", +"i7 c #53b0a2", +"jg c #53b1a1", +"iN c #53b1a2", +"i5 c #53b1a3", +"iY c #53b2a2", +"jf c #53b2a3", +"dG c #53b2a4", +"jX c #53b3a2", +".q c #53b3a3", +".2 c #53b3a4", +"b1 c #53b3a5", +"k# c #53b4a4", +"kd c #53b4a5", +"mQ c #53b5a4", +"mW c #53b5a5", +"jB c #54b0a3", +"jD c #54b1a2", +"rJ c #54b2a2", +"j5 c #54b2a3", +"ae c #54b2a4", +"j2 c #54b3a3", +".U c #54b3a4", +"bP c #54b3a5", +"ka c #54b4a3", +"ay c #54b4a4", +"fs c #54b4a5", +"f4 c #54b4a6", +"ee c #54b5a4", +"lu c #54b5a5", +"ml c #54b5a6", +"iB c #54b5a7", +"nn c #54b6a5", +"om c #54b6a6", +"lF c #54b6a7", +"qb c #55afa0", +"iz c #55b1a2", +"r6 c #55b1a3", +"pt c #55b2a3", +"e0 c #55b3a4", +"mc c #55b3a5", +"fa c #55b3a6", +"ln c #55b4a4", +"eB c #55b4a5", +"fD c #55b4a6", +"it c #55b4a7", +"gO c #55b5a4", +"g3 c #55b5a5", +"fP c #55b5a6", +"gI c #55b5a7", +"mY c #55b6a5", +"hx c #55b6a6", +"ig c #55b6a7", +"kf c #55b6a8", +"lL c #55b7a7", +"iu c #56b1a3", +"iP c #56b2a3", +"rR c #56b3a3", +"nm c #56b4a5", +"gf c #56b4a6", +"oo c #56b4a7", +"nF c #56b5a5", +"g# c #56b5a6", +"ha c #56b5a7", +"hm c #56b5a8", +"nu c #56b6a5", +"ia c #56b6a6", +"hI c #56b6a7", +"ms c #56b6a8", +"nB c #56b6a9", +"ij c #56b7a6", +"kc c #56b7a7", +"l1 c #56b7a8", +"lP c #56b7a9", +"nc c #56b8a8", +"lM c #56b8a9", +"iQ c #57b2a2", +"ju c #57b2a4", +"kH c #57b3a3", +"po c #57b3a5", +"ry c #57b4a4", +"mB c #57b4a6", +"n8 c #57b5a5", +"jo c #57b5a7", +"nC c #57b6a6", +"jU c #57b6a7", +"lt c #57b6a8", +"nt c #57b6a9", +"m1 c #57b7a7", +"l0 c #57b7a8", +"lh c #57b7a9", +"mR c #57b7aa", +"lO c #57b8a8", +"k0 c #57b8a9", +"kA c #57b8aa", +"kZ c #57b9a9", +"kb c #57b9aa", +"lm c #57b9ab", +"j7 c #57baaa", +"oE c #58b0a2", +"oN c #58b2a3", +"sP c #58b3a5", +"ku c #58b4a5", +"m5 c #58b5a7", +"m4 c #58b6a8", +"ni c #58b7a8", +"lN c #58b7a9", +"nA c #58b8a8", +"kg c #58b8a9", +"j9 c #58b8aa", +"j8 c #58b8ab", +"kh c #58b9a9", +"jV c #58b9aa", +"j4 c #58b9ab", +"k. c #58b9ac", +"j3 c #58baaa", +"jY c #58baab", +"jM c #58baac", +"j1 c #58bbab", +"jA c #58bbac", +"jt c #59b3a5", +"sx c #59b4a6", +"pk c #59b5a6", +"qq c #59b5a7", +"pd c #59b6a7", +"kI c #59b8a9", +"lY c #59b8aa", +"li c #59b9a9", +"jS c #59b9aa", +"jL c #59b9ab", +"jr c #59b9ac", +"j0 c #59baaa", +"jv c #59baab", +"i2 c #59baac", +"iH c #59baad", +"jK c #59bbab", +"iU c #59bbac", +"ii c #59bbad", +"i3 c #59bbae", +"ja c #59bcac", +"iW c #59bcad", +"iX c #59bcae", +"q6 c #5ab3a5", +"r3 c #5ab4a6", +"kW c #5ab5a8", +"mm c #5ab6a7", +"ns c #5ab7a8", +"l2 c #5ab9aa", +"jT c #5abaab", +"j# c #5abaac", +"iV c #5abaad", +"jQ c #5abbab", +"is c #5abbac", +"i. c #5abbad", +"hX c #5abbae", +"ir c #5abcac", +"hF c #5abcad", +"he c #5abcae", +"hf c #5abcaf", +"hG c #5abdad", +"hg c #5abdae", +"hl c #5abdaf", +"oI c #5bb3a4", +"ql c #5bb3a5", +"ip c #5bb4a6", +"i0 c #5bb5a6", +"q2 c #5bb5a7", +"iZ c #5bb6a6", +"mG c #5bb6a8", +"jl c #5bbbac", +"h6 c #5bbbad", +"i# c #5bbbae", +"jj c #5bbcac", +"hH c #5bbcad", +"g1 c #5bbcae", +"g9 c #5bbcaf", +"hP c #5bbdad", +"gA c #5bbdae", +"fj c #5bbdaf", +"fi c #5bbdb0", +"gD c #5bbeae", +"fh c #5bbeaf", +"e9 c #5bbeb0", +"f. c #5bbfaf", +"f# c #5bbfb0", +"ik c #5cb5a6", +"kz c #5cb5a7", +"sD c #5cb6a8", +"kN c #5cb6a9", +"mu c #5cb7a8", +"ne c #5cb8a9", +"nN c #5cb9a9", +"k7 c #5cbaad", +"rU c #5cbbac", +"rp c #5cbbad", +"g8 c #5cbcae", +"g2 c #5cbcaf", +"gC c #5cbdae", +"fC c #5cbdaf", +"fr c #5cbdb0", +"gB c #5cbeae", +"eK c #5cbeaf", +"dk c #5cbeb0", +"dj c #5cbeb1", +"eJ c #5cbfaf", +"dd c #5cbfb0", +"cX c #5cbfb1", +"cR c #5cbfb2", +"c9 c #5cc0b0", +"cT c #5cc0b1", +"cU c #5cc0b2", +"rl c #5db6a7", +"lg c #5db6a8", +"nf c #5db7aa", +"lU c #5dbbac", +"gN c #5dbdaf", +"gt c #5dbdb0", +"eZ c #5dbeaf", +"dl c #5dbeb0", +"dy c #5dbeb1", +"eI c #5dbfaf", +"cV c #5dbfb0", +"cE c #5dbfb1", +"cA c #5dbfb2", +"cS c #5dc0b0", +"cD c #5dc0b1", +"cv c #5dc0b2", +"cK c #5dc0b3", +"cH c #5dc1b1", +"cI c #5dc1b2", +"cJ c #5dc1b3", +"qk c #5eb3a5", +"qw c #5eb3a6", +"oR c #5eb4a6", +"sV c #5eb6a7", +"qr c #5eb7a9", +"le c #5eb7aa", +"mK c #5eb8a9", +"m. c #5eb8aa", +"rI c #5ebcad", +"sa c #5ebdae", +"ed c #5ebfb0", +"cG c #5ebfb1", +"cB c #5ebfb2", +"en c #5ec0b0", +"cC c #5ec0b1", +"bD c #5ec0b2", +"cF c #5ec0b3", +"cW c #5ec1b1", +"bq c #5ec1b2", +"ax c #5ec1b3", +"qR c #5fb3a5", +"pw c #5fb8ab", +"r. c #5fb9ab", +"rg c #5fbbae", +"k1 c #5fbdae", +"wx c #5fc0b3", +"#8 c #5fc1b3", +"#0 c #5fc2b3", +".E c #5fc2b4", +"jJ c #60b6aa", +"oB c #60b8a9", +"kM c #60b8aa", +"oc c #60b9aa", +"qM c #60b9ab", +"q1 c #60b9ac", +"mt c #60baab", +"x5 c #60c2b3", +".T c #60c2b4", +".1 c #60c3b4", +".p c #60c3b5", +"jH c #61b8aa", +"kX c #61b9aa", +"n2 c #61b9ac", +"lQ c #61bdaf", +"qa c #62b6a7", +"iF c #62b7aa", +"rZ c #62b8aa", +"ss c #62b9ab", +"mo c #62b9ac", +"o3 c #62baab", +"o8 c #62baac", +"rY c #62bdb0", +"iL c #63b6aa", +"iD c #63b7a9", +"iI c #63b8aa", +"pn c #63b9ad", +"kS c #64b9aa", +"kY c #64b9ab", +"sB c #64b9ac", +"mp c #64baab", +"sG c #64baac", +"nS c #64bbae", +"kT c #64bdb0", +"q5 c #65b9ab", +"mw c #65baac", +"lH c #65bfb1", +"oA c #66baac", +"jE c #67b9ac", +"jG c #67baad", +"kP c #67bbad", +"oV c #67bbae", +"nH c #67bdaf", +"rD c #67c0b3", +"qx c #68b9ac", +"qU c #68bbae", +"mv c #68bcaf", +"k5 c #68bdaf", +"mq c #68beb0", +"kJ c #69c0b2", +"qS c #6abaad", +"kr c #6abdaf", +"mh c #6abeb0", +"n9 c #6abeb1", +"r2 c #6ac2b4", +"rF c #6bbcae", +"oz c #6bbcaf", +"lC c #6bc1b4", +"mE c #6cbeb2", +"oY c #6dbdb0", +"su c #6dbeb1", +"nX c #6dbeb2", +"mi c #6dc1b3", +"q# c #6ebbae", +"pV c #6ebcaf", +"p4 c #6ec0b3", +"qG c #6fbbaf", +"pU c #6fbcb0", +"qH c #6fbdb0", +"kG c #6fbfb2", +"lG c #6fc0b3", +"si c #6fc3b6", +"pT c #70bdb1", +"sJ c #70beb3", +"kq c #70bfb3", +"mV c #70c0b4", +"mD c #70c1b4", +"kB c #70c3b6", +"oy c #71beb2", +"qX c #71bfb3", +"no c #71c1b4", +"np c #71c2b4", +"lx c #71c3b7", +"pS c #72bdb1", +"rb c #72bfb3", +"pR c #73bdb1", +"pQ c #73beb2", +"qY c #73bfb2", +"od c #73c1b4", +"pi c #73c1b5", +"me c #73c3b7", +"pP c #74bfb2", +"pO c #75bfb4", +"n3 c #75c2b6", +"ox c #76c0b4", +"rh c #76c1b4", +"rm c #76c1b5", +"rx c #76c6b9", +"pN c #77c0b4", +"so c #77c2b6", +"m8 c #77c3b7", +"m7 c #77c4b7", +"ny c #77c4b8", +"lq c #77c6ba", +"r5 c #77c7ba", +"oF c #78c1b4", +"qc c #78c1b5", +"sl c #78c1b6", +"rV c #78c2b6", +"kF c #78c3b6", +"mz c #78c4b8", +"pM c #79c1b5", +"nT c #79c3b8", +"nz c #79c4b8", +"pL c #7ac2b5", +"qN c #7ac3b7", +"m# c #7ac7ba", +"qv c #7bc1b6", +"q. c #7bc2b6", +"op c #7bc3b7", +"ow c #7bc3b8", +"nI c #7bc5b9", +"pK c #7dc2b7", +"oJ c #7dc3b7", +"rc c #7dc4b8", +"pe c #7dc5b9", +"sb c #7ec4b9", +"sM c #7ec5b9", +"mT c #7ec6bb", +"o. c #7ec6bc", +"p5 c #7fc5ba", +"lT c #7fc6bb", +"lj c #7fc9be", +"lA c #80c7bc", +"rM c #80cabe", +"rs c #80cabf", +"oO c #81c5b9", +"ov c #81c6bb", +"nY c #81c8bd", +"l7 c #81cabe", +"pW c #82c5ba", +"oq c #82c5bb", +"rt c #82c7bb", +"pc c #82c8bd", +"pJ c #83c6ba", +"or c #83c6bb", +"os c #83c6bc", +"o4 c #83c7bc", +"sp c #83c8bd", +"r8 c #83cbc1", +"jP c #84c7bc", +"sI c #84c7bd", +"jO c #84c8bd", +"nO c #84c9be", +"qp c #84c9bf", +"nx c #85cabe", +"lb c #85ccc2", +"ot c #86c7bc", +"ou c #86c8bd", +"pp c #86c8be", +"p9 c #87c7bc", +"oe c #87cac0", +"oS c #88c8bd", +"rK c #88cabf", +"l3 c #88ccc2", +"qQ c #89c7bd", +"pI c #89c8be", +"q7 c #89c9be", +"ro c #89cec3", +"r# c #8acac0", +"n4 c #8acbc1", +"nb c #8accc2", +"qy c #8bc9bf", +"sO c #8bcbc1", +"na c #8bccc1", +"o2 c #8cccc1", +"pX c #8dcac0", +"qd c #8dcbc0", +"pl c #8dccc1", +"nU c #8dcdc3", +"k8 c #8dcec4", +"rT c #8dcfc6", +"jq c #8fccc2", +"jp c #8fccc3", +"qC c #8fcdc2", +"kv c #8fcdc3", +"qB c #8fcdc4", +"nJ c #8fcec4", +"pH c #90ccc2", +"rz c #90cdc2", +"lV c #90cfc6", +"rH c #90d1c7", +"r7 c #91cdc4", +"oW c #91cec4", +"mH c #91cec5", +"rf c #91d0c7", +"il c #92cdc3", +"ky c #92cdc4", +"i8 c #92cec4", +"i9 c #93cdc4", +"lp c #93cec4", +"l6 c #93cec5", +"mJ c #93cfc5", +"o# c #93cfc6", +"k2 c #93d2c8", +"qL c #94d0c7", +"qm c #95cec5", +"sC c #95cfc6", +"qT c #95d0c7", +"p8 c #96cec5", +"pG c #96cfc5", +"nZ c #96d0c7", +"q9 c #96d1c8", +"lR c #96d2c9", +"qF c #97cec5", +"pY c #97cfc6", +"oZ c #98d1c8", +"kl c #98d1c9", +"lv c #98d2c8", +"mM c #98d2c9", +"qu c #99cfc6", +"sU c #99d1c7", +"o9 c #99d1c8", +"lZ c #99d2c9", +"rX c #99d4cb", +"oG c #9ad0c7", +"q4 c #9ad1c9", +"kU c #9ad4cc", +"qj c #9bd0c8", +"rk c #9cd2ca", +"pF c #9dd2c9", +"of c #9dd4cb", +"sd c #9dd6ce", +"sr c #9ed4cb", +"n5 c #9ed4cc", +"lI c #9ed5ce", +"oK c #9fd3ca", +"q3 c #9fd4cb", +"rP c #9fd7cf", +"r4 c #a0d4cb", +"sL c #a0d4cc", +"mr c #a0d7ce", +"qe c #a1d4cb", +"nV c #a1d5cd", +"kK c #a1d8cf", +"sF c #a2d5cd", +"ph c #a2d6ce", +"rC c #a2d8d0", +"iy c #a3d4cd", +"pE c #a3d5cc", +"iv c #a3d5cd", +"jy c #a3d6cd", +"nK c #a3d7cf", +"oP c #a4d4cd", +"la c #a4d6cd", +"jx c #a4d6ce", +"pZ c #a5d5cd", +"sX c #a5d6ce", +"kk c #a5d7cf", +"nv c #a5d7d0", +"nq c #a5d8d0", +"lD c #a5d9d1", +"nw c #a6d8d0", +"r1 c #a6dad2", +"qW c #a7d8cf", +"sh c #a7d8d0", +"oa c #a7d9d1", +"mj c #a7d9d2", +"kC c #a7dad2", +"ps c #a9d9d2", +"pD c #aad7d0", +"n0 c #aad9d2", +"st c #abdad2", +"rN c #acd9d2", +"sy c #acdad2", +"md c #acdad3", +"nP c #acdbd3", +"ly c #acdcd4", +"l# c #addad3", +"r0 c #aedad3", +"qz c #afdad3", +"pf c #afdbd4", +"qh c #afdcd5", +"mf c #afddd5", +"og c #b0dcd5", +"pC c #b1dad4", +"qn c #b1dbd4", +"pb c #b1dcd6", +"p0 c #b2dbd4", +"lr c #b2ded8", +"rw c #b2dfd8", +"oT c #b3dbd4", +"qV c #b3ddd6", +"n6 c #b3ddd7", +"qP c #b4dbd5", +"rq c #b4ddd6", +"rd c #b5ddd7", +"sn c #b5ded8", +"ma c #b5dfd9", +"o5 c #b6ded8", +"km c #b6dfd8", +"qt c #b7ddd6", +"pB c #b7ddd7", +"px c #b7ded8", +"o7 c #b7dfd8", +"ks c #b7dfd9", +"nL c #b7dfda", +"qf c #b8ded7", +"sw c #b8dfd9", +"mF c #b8dfda", +"lk c #b8e1db", +"oX c #b9dfd8", +"kj c #b9e0da", +"mZ c #b9e1da", +"m0 c #bae0da", +"m3 c #bbe1db", +"mS c #bbe1dc", +"rr c #bbe2dc", +"mU c #bce1db", +"m2 c #bce1dc", +"l8 c #bce2dd", +"oH c #bde0da", +"pu c #bde1db", +"rL c #bde3dd", +"p1 c #bee1db", +"sN c #bee1dc", +"n1 c #bee2dc", +"pv c #bfe3dd", +"qE c #c0e1dc", +"ru c #c0e2dd", +"nQ c #c0e3de", +"lc c #c0e5df", +"oL c #c1e2dd", +"o0 c #c1e3de", +"pm c #c1e4de", +"kp c #c2e4de", +"mC c #c2e4df", +"pq c #c3e4de", +"ri c #c3e4df", +"l4 c #c3e5e0", +"rn c #c3e6e1", +"qI c #c5e4df", +"oh c #c5e5e0", +"pA c #c6e4df", +"sz c #c6e5e0", +"jn c #c7e5e0", +"qO c #c7e6e1", +"n7 c #c7e6e2", +"k9 c #c7e7e2", +"im c #c8e5e0", +"jd c #c8e5e1", +"jc c #c8e6e1", +"kw c #c8e6e2", +"rS c #c8e8e3", +"lw c #c9e6e1", +"m9 c #c9e7e2", +"n. c #c9e7e3", +"lo c #cae7e2", +"l5 c #cae8e3", +"lW c #cae8e4", +"nl c #cbe8e4", +"s# c #cbe9e5", +"p2 c #cce7e3", +"p. c #cce8e3", +"re c #cce9e5", +"sm c #cde8e4", +"qK c #cde9e5", +"k3 c #cdeae6", +"kO c #cee9e5", +"rG c #ceeae6", +"sq c #cfe9e5", +"q8 c #cfeae6", +"qo c #d0e9e5", +"sf c #d0eae5", +"ob c #d0eae6", +"lS c #d0ebe7", +"sE c #d1eae6", +"pz c #d2eae6", +"nj c #d2ebe7", +"qA c #d3eae6", +"nk c #d3ebe7", +"qs c #d4ebe7", +"rA c #d4ebe8", +"mx c #d4ece8", +"kV c #d4ede9", +"qi c #d5ebe7", +"nR c #d5ece9", +"rW c #d5edea", +"qZ c #d6ece8", +"s. c #d6ece9", +"sT c #d6ede9", +"ra c #d7ede9", +"lJ c #d7eeeb", +"jC c #d8edea", +"kQ c #d8eeea", +"p3 c #d9edea", +"mO c #d9eeeb", +"sc c #d9efec", +"sS c #daeeeb", +"m6 c #daefeb", +"iE c #dbeeeb", +"kR c #dbefec", +"kL c #dbf0ed", +"jI c #dcefec", +"oU c #ddefec", +"nW c #ddf0ed", +"kx c #def0ed", +"mn c #def0ee", +"lE c #def1ee", +"jm c #dff0ed", +"mI c #dff0ee", +"pa c #dff1ee", +"rB c #dff2ef", +"kn c #e0f1ee", +"mA c #e0f1ef", +"rj c #e1f1ee", +"pg c #e1f2ef", +"o6 c #e2f2ef", +"mk c #e2f2f0", +"kD c #e2f3f0", +"nh c #e3f2f0", +"ng c #e3f3f0", +"oM c #e4f2f0", +"jh c #e4f3f0", +"sg c #e4f3f1", +"in c #e5f3f0", +"lB c #e5f3f1", +"lz c #e5f4f2", +"ki c #e6f3f1", +"mL c #e6f4f2", +"p7 c #e7f3f1", +"jF c #e7f4f2", +"oQ c #e8f4f2", +"mg c #e8f5f3", +"qD c #e9f4f2", +"oC c #e9f5f3", +"kE c #eaf5f3", +"p6 c #eaf5f4", +"my c #eaf6f4", +"o1 c #ebf6f4", +"ls c #ebf6f5", +"iK c #ecf6f4", +"iJ c #ecf6f5", +"rv c #ecf7f5", +"oi c #edf7f5", +"mb c #eef7f6", +"qg c #eff7f6", +"nr c #eff8f6", +"sv c #f0f8f6", +"ll c #f0f8f7", +"rO c #f1f8f7", +"mP c #f2f9f8", +"mN c #f3f9f8", +"l9 c #f3f9f9", +"iw c #f4faf9", +"nM c #f5faf9", +"ld c #f5fafa", +"io c #f6fbfa", +"pr c #f7fbfa", +"k6 c #f7fbfb", +"p# c #f8fbfb", +"lf c #f8fcfb", +"py c #f9fcfb", +"l. c #f9fcfc", +"nd c #fafcfc", +"lX c #fafdfc", +"qJ c #fbfdfc", +"k4 c #fbfdfd", +"kt c #fcfdfd", +"q0 c #fcfefd", +"ko c #fdfefe", +"lK c #fefefe", +"ix c #ffffff", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.#.a.b.c.d.e.f.g.h.i.j.kQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.l.m.n.o.p.p.p.p.p.q.r.r.r.r.s.t.u.kQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.v.w.x.p.p.p.p.p.p.p.p.q.r.r.r.r.r.r.r.y.z.AQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.B.C.D.p.p.p.p.E.p.p.E.p.p.p.q.r.F.r.F.F.r.F.r.r.G.H.IQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.J.K.L.p.p.E.p.p.p.p.p.p.p.p.p.E.q.r.r.r.r.r.M.r.M.M.r.M.N.O.PQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.Q.R.S.T.p.E.p.p.E.p.E.p.p.E.E.p.E.p.U.F.r.F.r.r.r.r.r.r.F.r.F.F.r.V.W.XQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.Y.Z.0.1.p.E.p.p.p.E.T.p.1.p.E.p.p.T.p.1.p.2.M.F.M.F.F.r.F.r.r.r.r.r.r.F.r.F.3.4.5QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.6.7.8.p.E.E.p.p.E.E.T.E.p.p.E.p.E.p.E.p.E.E.p.q.r.r.r.r.r.r.r.r.r.9.r.9.9.F.9.F.F.r#.###aQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#b#c#d.T.p.T.1.p.E.p.E.p.p.p.E.E.p.p.E.T.p.T.1.p.E.U.F.F.F.F.F.F.F.F.F.r.F.r.r.r.r.r.r.F.r.F.F#e#f#gQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#h#i#j.E.1.E.p.E.E.p.E.T.p.E.T.E.E.p.p.E.1.E.p.E.E.p.E.T.U.F.r.F.r.r.r.r.r.r.F.r.F.F.F.F.F.F.r.F.r.r.F.r#k#l#mQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#n#o#p.E.T.p.E.p.T.E.p.1.E.p.E.E.p.E.T.E.T.p.E.p.T.E.p.1.E.p.U.F.M.F.M.M.F.M.F.F.9.F.9.9.F.9.F.F.F.F.F.F.M.F.M.M#q#r#sQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#t#u#v.p.E.1.E.E.E.p.E.T.E.E.E.E.T.p.E.p.E.1.E.E.E.p.E.T.E.E.E.E.q.r.F.r.F.F.r.F.r.r.F.r.F.F.r.F.r.r.M.r.M.M.M.M.M.M.F.M.F#w#x#yQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#z#A#B.p.T.E.E.E.p.E.p.T.E.E.1.p.E.T.E.p.T.E.E.E.p.E.p.T.E.E.1.p.E.T.E.2.M.M.M.M.M.F.M.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.r.F.r.r.M#C#D#EQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#F#G#H.E.1.E.E.T.E.T.E.E.E.E.E.p.E.E.E.E.1.E.E.T.E.T.E.E.E.E.E.p.E.E.E.E.1.U.F.F.F.F.F.F.F.F.F.M.F.M.M.r.M.r.r.F.r.F.F.r.F.r.r.F.r.F.F.F.F.F#I#J#KQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#L#M#N.E.1.E.E.E.E.1.E.E.E.E.T.T.E.E.T.E.1.E.E.E.E.1.E.E.E.E.T.T.E.E.T.E.1.E.E.U.F.9.F.9.9.F.9.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F#O#P#QQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#R#S#T.E.T.E.E.E.T.E.E.E.E.E.E.T.E.E.E.E.T.E.E.E.T.E.E.E.E.E.E.T.E.E.E.E.T.E.E.E.T.E.U.F.F.F.F.F.M.F.M.M.F.M.F.F.M.F.M.M.F.M.F.F.M.F.M.M.M.M.M.M.F.M.F.F.M.F.M.M#U#V#WQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#X#Y#Z.E.T.E.E.E.E.T#0.E.T.T.E#0.E.E.E.E.T.E.E.E.E.T#0.E.T.T.E#0.E.E.E.E.T.E.E.E.E.T#0.E.U.F.M.F.M.M.F.M.F.F.F.F.F.F.F.F.F.F.F.F.F.F#1.F#1#1.F#1.F.F.F.F.F.F.M.F.M.M.F.M#2#3#4QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#5#6#7.T.E#0.E#8.E.T.E#0.E.E.E.E.E.E.T.T.E#0.E#8.E.T.E#0.E.E.E.E.E.E.T.T.E#0.E#8.E.T.E#0.E.E.U.F.F.F.F.F#9.F#9#9#1#9#1#1.F#1.F.F.M.F.M.M.F.M.F.F.M.F.M.M.F.M.F.F#9.F#9#9.F#9.F.F.Ma.a#aaQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtabacad.E.E#0#8.E.E.T.E#0.E.E.E.T.E#8#0.E.E#0#8.E.E.T.E#0.E.E.E.T.E#8#0.E.E#0#8.E.E.T.E#0.E.E.E.T.Eae#1.F#1.F.F.F.F.F.F.M.F.M.M.F.M.F.F.F.F.F.F#9.F#9#9#1#9#1#1.F#1.F.F.F.F.F.F#9.F#9#9.F#9.FafagahQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaiajak.E.E.E.E.E.E.E#0.E#8.E.E#8.E#0.E.E.E.E.E.E.E.E#0.E#8.E.E#8.E#0.E.E.E.E.E.E.E.E#0.E#8.E.E#8.E#0.E.U.F#9.F#9#9.F#9.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F.F#1.F#1#1.F#1.F.F.F.F.F.F.F.F.F.F.FalamanQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaoapaq.E#8#0.E#8.E.E#8.E.T.E#0.E.E.E.E.E#8#0.E#8.E.E#8.E.T.E#0.E.E.E.E.E#8#0.E#8.E.E#8.E.T.E#0.E.E.E.E.E#8.U.F.F.F.F.F#9.F#9#9.F#9.F.F.F.F.F.F#9.F#9#9.F#9.F.F.F.F.F.F.F.F.F.F#9.F#9#9.F#9.F.F#9.F#9#9.F#9.F.FarasatQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.kauavaw#8.E#8.E.E#8.E#8.E.E#8.Eax.E#0#8#8.E#8.E.E#8.E#8.E.E#8.Eax.E#0#8#8.E#8.E.E#8.E#8.E.E#8.Eax.E#0#8#8.E#8.Eayaz.Faz.F.F#1.F#1#1#9#1#9#9.F#9.F.F#9.F#9#9.F#9.F.F#9.F#9#9.F#9.F.F.F.F.F.F.F.F.F.F#9.F#9#9.F#9.F.F#9.FaAaBaCQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaDaEaF#8.E.E#8.E#0.E#8.E.E.E#8#8.E#0.E#8.E.E#8.E#0.E#8.E.E.E#8#8.E#0.E#8.E.E#8.E#0.E#8.E.E.E#8#8.E#0.E#8.E.E#8.E#0.E.U#9#9#9#9#9.F#9.F.F.F.F.F.F#9.F#9#9.F#9.F.F#9.F#9#9.F#9.F.F#1.F#1#1#9#1#9#9.F#9.F.Faz.Fazaz.Faz.F.F#1.F#1#1aG.taHQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaIaJaK#8#0#8.E#8.Eax#8.E#0ax#8#0ax#0.E#8#0#8.E#8.Eax#8.E#0ax#8#0ax#0.E#8#0#8.E#8.Eax#8.E#0ax#8#0ax#0.E#8#0#8.E#8.Eax#8.E.U.F#1.F#1#1.F#1.F.F#1.F#1#1#9#1#9#9.F#9.F.F#1.F#1#1.F#1.F.F#9.F#9#9.F#9.F.F#9.F#9#9.F#9.F.F.F.F.F.Faz.Fazaz#1az#1aLaMabQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaNaOaP#8ax.E.Eax#8.E#8#0.E#8.E#8.E.E.E#8ax.E.Eax#8.E#8#0.E#8.E#8.E.E.E#8ax.E.Eax#8.E#8#0.E#8.E#8.E.E.E#8ax.E.Eax#8.E#8#0.E#8ay#9az#9azaz#9az#9#9#1#9#1#1.F#1.F.F#9.F#9#9az#9azaz.Faz.F.F#1.F#1#1az#1azaz#1az#1#1#9#1#9#9#9#9#9#9.F#9.F.Faz.Fazaz#1aQaRaSQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaTaUaV#8#8ax.E.Eax#0ax.E#8ax.Eax#8.Eax#8#8ax.E.Eax#0ax.E#8ax.Eax#8.Eax#8#8ax.E.Eax#0ax.E#8ax.Eax#8.Eax#8#8ax.E.Eax#0ax.E#8ax.Eax#8ay#9.F#9.F.F.F.F.F.Faz.Fazazazazazaz.Faz.F.F#9.F#9#9.F#9.F.F#9.F#9#9.F#9.F.F.F.F.F.Faz.Fazaz.Faz.F.F#9.F#9#9#9#9#9#9.F#9.FaWaXaYQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaZa0a1ax#8.E#8#8#8#8.E#8.E#8#8.E#0#8.Eax#8.E#8#8#8#8.E#8.E#8#8.E#0#8.Eax#8.E#8#8#8#8.E#8.E#8#8.E#0#8.Eax#8.E#8#8#8#8.E#8.E#8#8.E#0#8.E.2az#9az#9#9#9#9#9#9#1#9#1#1#9#1#9#9az#9azaz#9az#9#9az#9azaz#9az#9#9#1#9#1#1az#1azaz#9az#9#9#1#9#1#1az#1azaz.Faz.F.F#1.F#1#1aza2a3a4.kQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQta5a6.e#8#8#8#0ax.Eax#0axax#8.Eax#8#8ax#8#8#8#0ax.Eax#0axax#8.Eax#8#8ax#8#8#8#0ax.Eax#0axax#8.Eax#8#8ax#8#8#8#0ax.Eax#0axax#8.Eax#8#8ax#8#8ae.Faz.Fazaz.Faz.F.Faz.Fazaz.Faz.F.F#1.F#1#1#9#1#9#9az#9azaz.Faz.F.F#9.F#9#9#9#9#9#9.F#9.F.F#9.F#9#9#9#9#9#9#9#9#9#9az#9azaz.Faz.F.Fa7a8.AQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.Ba9b.#0#0ax#8ax.E#8#8ax#8.E#8#0#8axax#0#0ax#8ax.E#8#8ax#8.E#8#0#8axax#0#0ax#8ax.E#8#8ax#8.E#8#0#8axax#0#0ax#8ax.E#8#8ax#8.E#8#0#8axax#0#0ax#8axay#9#9#9#9#9#9#9#9#9az#9azaz#9az#9#9az#9azazazazazaz#1az#1#1#9#1#9#9az#9azazazazazaz#9az#9#9az#9azaz#1az#1#1az#1azazazazazaz#9az#9#9#9#9b#babbQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtbcbdbe#8.E#8ax#8ax#0axax#8#8#0axaxaxax#8.E#8ax#8ax#0axax#8#8#0axaxaxax#8.E#8ax#8ax#0axax#8#8#0axaxaxax#8.E#8ax#8ax#0axax#8#8#0axaxaxax#8.E#8ax#8ax#0.2az#9az#9#9az#9azaz#9az#9#9.F#9.F.F#9.F#9#9#1#9#1#1#9#1#9#9az#9azaz#9az#9#9#1#9#1#1az#1azaz#1az#1#1az#1azaz#9az#9#9.F#9.F.Faz.Fazaz#9az#9#9bfbgbhQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtbibjbk#8#8#8ax#8ax#0ax#8#8ax#0axax#8#8#8#8#8ax#8ax#0ax#8#8ax#0axax#8#8#8#8#8ax#8ax#0ax#8#8ax#0axax#8#8#8#8#8ax#8ax#0ax#8#8ax#0axax#8#8#8#8#8ax#8ax#0ax#8.Uaz#1az#1#1#9#1#9#9az#9azaz#9az#9#9az#9azazazazazazazazazaz#9az#9#9az#9azaz#9az#9#9az#9azaz#9az#9#9az#9azazazazazaz#9az#9#9az#9azaz#1az#1#1#9#1#9blbmbnQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.Ybobpax#8axaxaxax#8bqaxax#8axbqaxax#8ax#8axaxaxax#8bqaxax#8axbqaxax#8ax#8axaxaxax#8bqaxax#8axbqaxax#8ax#8axaxaxax#8bqaxax#8axbqaxax#8ax#8axaxaxax#8bqaxax#8ax.q#9az#9azaz#9az#9#9br#9brbr#9br#9#9#9#9#9#9#9#9#9#9#9#9#9#9az#9azaz#9az#9#9az#9azaz#9az#9#9az#9azaz#9az#9#9#9#9#9#9#9#9#9#9#9#9#9#9az#9azaz#9az#9#9brbsbt.5QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtbubvbw#8bq#8ax#8#8#8ax#8ax#8#8bqax#8#8#8bq#8ax#8#8#8ax#8ax#8#8bqax#8#8#8bq#8ax#8#8#8ax#8ax#8#8bqax#8#8#8bq#8ax#8#8#8ax#8ax#8#8bqax#8#8#8bq#8ax#8#8#8ax#8ax#8#8bqax.Uaz#9az#9#9az#9azaz#9az#9#9az#9azazazazazazbrazbrbrazbrazazazazazaz#9az#9#9az#9azazazazazaz#9az#9#9az#9azazazazazazbrazbrbrazbrazaz#9az#9#9az#9azaz#9az#9bxbybzQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtbAbBbCax#8axax#8#8axbqaxbDax#8axbq#8axax#8axax#8#8axbqaxbDax#8axbq#8axax#8axax#8#8axbqaxbDax#8axbq#8axax#8axax#8#8axbqaxbDax#8axbq#8axax#8axax#8#8axbqaxbDax#8axbq#8ax.2#9az#9azazbEazbEbEbrbEbrbr#9br#9#9#9#9#9#9az#9azaz#9az#9#9#9#9#9#9br#9brbrazbrazaz#9az#9#9az#9azazazazazaz#9az#9#9az#9azaz#9az#9#9az#9azazbEazbEbEbrbEbrbr#9brbFbG#gQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtbHbIbJax#8#8bDbq#8axbqbD#8ax#8#8bqax#8ax#8#8bDbq#8axbqbD#8ax#8#8bqax#8ax#8#8bDbq#8axbqbD#8ax#8#8bqax#8ax#8#8bDbq#8axbqbD#8ax#8#8bqax#8ax#8#8bDbq#8axbqbD#8ax#8#8bqax#8ax#8#8.2brazbrazaz#9az#9#9#9#9#9#9br#9brbrazbrazazbEazbEbEbrbEbrbr#9br#9#9az#9azazbrazbrbr#9br#9#9bE#9bEbEbrbEbrbr#9br#9#9#9#9#9#9br#9brbrazbrazaz#9az#9#9#9#9#9#9br#9brbrbKbLbMQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#nbNbObqbDaxbqax#8axax#8axaxbqbqaxbD#8bqbDaxbqax#8axax#8axaxbqbqaxbD#8bqbDaxbqax#8axax#8axaxbqbqaxbD#8bqbDaxbqax#8axax#8axaxbqbqaxbD#8bqbDaxbqax#8axax#8axaxbqbqaxbD#8bqbDaxbqaxbPbrbEbrbEbEazbEazazazazazazazazazaz#9az#9#9br#9brbr#9br#9#9br#9brbr#9br#9#9#9#9#9#9br#9brbr#9br#9#9az#9azazbEazbEbEazbEazazbrazbrbrbEbrbEbEazbEazazazazazazazazazaz#9azbQbRbSQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtbTbUbVbqax#8ax#8axbDbqaxbDbq#8bDax#8axbqax#8ax#8axbDbqaxbDbq#8bDax#8axbqax#8ax#8axbDbqaxbDbq#8bDax#8axbqax#8ax#8axbDbqaxbDbq#8bDax#8axbqax#8ax#8axbDbqaxbDbq#8bDax#8axbqax#8ax#8axbD.q#9az#9azazbEazbEbE#9bE#9#9az#9azazbEazbEbEazbEazazazazazazbrazbrbrbEbrbEbEazbEazazbEazbEbEazbEazaz#9az#9#9az#9azazazazazaz#9az#9#9az#9azazbEazbEbE#9bE#9#9az#9azazbEazbEbEazbWbXbYQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#zbZb0bD#8bD#8bqbDbDaxbD#8axbD#8bDax#8bD#8bD#8bqbDbDaxbD#8axbD#8bDax#8bD#8bD#8bqbDbDaxbD#8axbD#8bDax#8bD#8bD#8bqbDbDaxbD#8axbD#8bDax#8bD#8bD#8bqbDbDaxbD#8axbD#8bDax#8bD#8bD#8bqbDbDaxbD#8b1bE#9bE#9#9br#9brbrbEbrbEbE#9bE#9#9bE#9bEbE#9bE#9#9bE#9bEbEazbEazaz#9az#9#9az#9azazbEazbEbE#9bE#9#9bE#9bEbEbEbEbEbEazbEazazbEazbEbE#9bE#9#9br#9brbrbEbrbEbE#9bE#9#9bE#9bEbE#9bE#9b2b3b4QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#Fb5b6bDbDaxbqaxbDaxaxbD#8bqaxbDaxaxaxbDbDaxbqaxbDaxaxbD#8bqaxbDaxaxaxbDbDaxbqaxbDaxaxbD#8bqaxbDaxaxaxbDbDaxbqaxbDaxaxbD#8bqaxbDaxaxaxbDbDaxbqaxbDaxaxbD#8bqaxbDaxaxaxbDbDaxbqaxbDaxaxbD#8bqax.2bEbEbEbEbEazbEazazazazazazbEazbEbEazbEazazbEazbEbEazbEazazbrazbrbrbEbrbEbEazbEazazbEazbEbEazbEazazbrazbrbrazbrazazbEazbEbEbEbEbEbEbEbEbEbEazbEazazazazazazbEazbEbEazbEazazbEazbEbEazb7b8b9QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtc.c#cabDbqbDbqaxbDbqbDaxbDaxbDbD#8bqbDbDbqbDbqaxbDbqbDaxbDaxbDbD#8bqbDbDbqbDbqaxbDbqbDaxbDaxbDbD#8bqbDbDbqbDbqaxbDbqbDaxbDaxbDbD#8bqbDbDbqbDbqaxbDbqbDaxbDaxbDbD#8bqbDbDbqbDbqaxbDbqbDaxbDaxbDbD#8.qazbrazbrbr#9br#9#9br#9brbrbEbrbEbEazbEazazbrazbrbrazbrazazbEazbEbEazbEazazbEazbEbE#9bE#9#9az#9azazbEazbEbEbrbEbrbr#9br#9#9az#9azazbrazbrbr#9br#9#9br#9brbrbEbrbEbEazbEazazbrazbrbrazbrazazcbcc#hQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#RcdcebDaxbDaxaxaxbDbDbq#8bDbDaxbDbqaxbDaxbDaxaxaxbDbDbq#8bDbDaxbDbqaxbDaxbDaxaxaxbDbDbq#8bDbDaxbDbqaxbDaxbDaxaxaxbDbDbq#8bDbDaxbDbqaxbDaxbDaxaxaxbDbDbq#8bDbDaxbDbqaxbDaxbDaxaxaxbDbDbq#8bDbDaxbDbqaxbDb1bEbEbEbEbEbEbEbEbEbrbEbrbrazbrazazbEazbEbEbEbEbEbE#9bE#9#9br#9brbrbEbrbEbEbrbEbrbrbEbrbEbEbEbEbEbEazbEazazbEazbEbEbrbEbrbrbEbrbEbEbEbEbEbEbEbEbEbEbrbEbrbrazbrazazbEazbEbEbEbEbEbE#9bE#9#9br#9cfcg#WQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtchcicjaxbDbDbqbDbqbDbDax#8bDbqbDbqbDbDaxbDbDbqbDbqbDbDax#8bDbqbDbqbDbDaxbDbDbqbDbqbDbDax#8bDbqbDbqbDbDaxbDbDbqbDbqbDbDax#8bDbqbDbqbDbDaxbDbDbqbDbqbDbDax#8bDbqbDbqbDbDaxbDbDbqbDbqbDbDax#8bDbqbDbqbDbDaxbDbD.2bEazbEazazazazazazbEazbEbEbEbEbEbEazbEazazbEazbEbEazbEazazbEazbEbEazbEazazazazazazbEazbEbEazbEazazbEazbEbEbEbEbEbEazbEazazbEazbEbEazbEazazazazazazbEazbEbEbEbEbEbEazbEazazbEazbEbEazbEazazbEazbEbEckclcmQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtcncocpbDaxbqbDaxbDbDaxbDbqbDbDaxbDbqbDbDaxbqbDaxbDbDaxbDbqbDbDaxbDbqbDbDaxbqbDaxbDbDaxbDbqbDbDaxbDbqbDbDaxbqbDaxbDbDaxbDbqbDbDaxbDbqbDbDaxbqbDaxbDbDaxbDbqbDbDaxbDbqbDbDaxbqbDaxbDbDaxbDbqbDbDaxbDbqbDbDaxbqbDax.2bEbEbEbEbEbEbEbEbEbrbEbrbrbEbrbEbEbEbEbEbEbrbEbrbrbEbrbEbEbEbEbEbEbrbEbrbrcqbrcqcqbEcqbEbEbrbEbrbrbEbrbEbEazbEazazbrazbrbrbEbrbEbEbEbEbEbEbEbEbEbEbrbEbrbrbEbrbEbEbEbEbEbEbrbEbrbrbEbrbEbEbEbEbEbEbrbEbrcrcsaaQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtabctcubDaxcvbDbDcvbDbDbDbqbDbqcvbqbDbDbDaxcvbDbDcvbDbDbDbqbDbqcvbqbDbDbDaxcvbDbDcvbDbDbDbqbDbqcvbqbDbDbDaxcvbDbDcvbDbDbDbqbDbqcvbqbDbDbDaxcvbDbDcvbDbDbDbqbDbqcvbqbDbDbDaxcvbDbDcvbDbDbDbqbDbqcvbqbDbDbDaxcvbDbDcvbDbD.2brbEbrbEbEazbEazazbEazbEbEazbEazazbrazbrbrbEbrbEbEbEbEbEbEazbEazazbEazbEbEbEbEbEbEazbEazazbEazbEbEbEbEbEbEbEbEbEbEbEbEbEbEbrbEbrbrbEbrbEbEazbEazazbEazbEbEazbEazazbrazbrbrbEbrbEbEbEbEbEbEazbEazazbEazbEbEbEcwcxahQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaicyczbDbqcAcBbDbDbDcCbqcvaxcDcDcDcEcFcBbDcGcDcvcHcHbDcDcCaxcAcAcBcEcvcEcDbDcvbDcIcIcvcIcGbDcEcBcBcAcJcvcIbDcAcCbDbDcEcCcAcKbDbDbDcDcJbDbqcDcGcBbqbqcBbDcDcIbDbDbDcAcFbDbDcBcEcEbqcDcCbqcvaxbDcEbDbDaxbDbqbDbDbDbqbqbDbqbDb1bEbEbEbEbEbEbEbEbEcqbEcqcqbEcqbEbEcqbEcqcqbEcqbEbEbrbEbrbrbrbrbrbrbEbrbEbEcqbEcqcqbEcqbEbEbEbEbEbEbrbEbrbrcqbrcqcqbEcqbEbEbEbEbEbEbEbEbEbEbEbEbEbEcqbEcqcqbEcqbEbEcqbEcqcqbEcqbEbEbrbEbrbrbrbrbrbrbEbrbEbEcqbEcqcLcMcNQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtcOcPcQbqbDcvcCcRcAbDcBcAcGcAcDcDcScvcDcIcBcAcBcTcCbDcCcTcEcTcIbDcDcCcAbDcAcDcvcEcEcDbDcUcvcUcCcGcTcGcAbqcEcUcvcRcAcIbDcDcEcAcvcvcVcAbDcDbDcRcBcTcDcWcDcEcAcvcDcAcEcDbDcDbDcDcCcRcBbDcBcXbDcvcDcGcDcvcEcIbDcvbDcvbDbqbDcvbDcvbq.2bEbEbEbEbEbEbEbEbEbEbEbEbEbrbEbrbrbEbrbEbEbrbEbrbrbEbrbEbEcqbEcqcqbEcqbEbEbrbEbrbrbEbrbEbEbrbEbrbrcqbrcqcqbEcqbEbEbrbEbrbrbEbrbEbEbEbEbEbEbEbEbEbEbEbEbEbEbrbEbrbrbEbrbEbEbrbEbrbrbEbrbEbEcqbEcqcqbEcqbEbEbrbEbrbrbEbrcYcZatQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.kc0c1c2bqcvbDbDbDcCcAcCcAcEcBcDcBbDcGcAbDcXcCcGcDcBcDcBcGcEcBcDcCcGcEcXcHcvbDbDcAcAcBcDcEcvcEbqbDbDcvcUbqcEcvcGcEcEbDcvcAcCcAcWcCcCcEcAbDcUcGcAbDcAbDcvcDcEcDbDbDcBcBcvbqcUcBbDbDcCbDcGcAcEcBcHcBcDbDcvbqcEbDbDbDbDbDbDbDbDbDbqbDbD.UcqbrcqbrbrbEbrbEbEbEbEbEbEbEbEbEbEbEbEbEbEcqbEcqcqbEcqbEbEbEbEbEbEbEbEbEbEbEbEbEbEc3bEc3c3bEc3bEbEbEbEbEbEbEbEbEbEbEbEbEbEcqbEcqcqbrcqbrbrbEbrbEbEbEbEbEbEbEbEbEbEbEbEbEbEcqbEcqcqbEcqbEbEbEbEbEbEbEbEbEbEbEbEbEbEc3bEc3c3c4c5c6QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaDc7c8cvcDbDbDbDcvbDcvcvcDcvbDcDcDcRcBcXcXcBcBcBcTcCcvcvcTcvcDc9cBcEcBcXcEcvcEcvcvbDcXcCcDcvcAcXcEcEcEcUcDcEcvbDcAcvcUcGcVcEcBcVcvcRcvcDcDcCcEbDcXcEcEcAcXcRbDcDcvcTcCcAc9cGcAcGcRcBcAcDc9cTbDcDcBcRcBcTcEcEbDcEcEcCcvcvcDcvbDcDbDcvbDcv.2bEbEbEbEbEbEbEbEbEc3bEc3c3bEc3bEbEbEbEbEbEbEbEbEbEbEbEbEbEcqbEcqcqbrcqbrbrbEbrbEbEbEbEbEbEc3bEc3c3bEc3bEbEbEbEbEbEbEbEbEbEbEbEbEbEbEbEbEbEbEbEbEbEc3bEc3c3bEc3bEbEbEbEbEbEbEbEbEbEbEbEbEbEcqbEcqcqbrcqbrbrbEbrbEbEbEbEbEbEc3bEd.d#daQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaIdbdcbDcDbDbDbDbDcDbDbDbDcvcEcvcEcVcAcvcScEcCcCcEcXbDcEcGcCcEcEcCc9cDbDcEcBcBcBcGcDcvcEcEcEcDbDbDcXcAcBcDcEcEcEbDcDcGcvcvcvcGcGcGddbDbDddcvcvbDcCcDcCcEcEcEcAcAcvcDbDcAcEcvbDbDcBc9cBcAcBcBcDcDbDcDcEcEcScBcBbDcAcEcEbDbDbDcvcvcGddcvbDcDbD.UcqbEcqbEbEbEbEbEbEbEbEbEbEcqbEcqcqbEcqbEbEc3bEc3c3bEc3bEbEcqbEcqcqbEcqbEbEbEbEbEbEc3bEc3c3bEc3bEbEbEbEbEbEc3bEc3c3c3c3c3c3cqc3cqcqbEcqbEbEbEbEbEbEbEbEbEbEcqbEcqcqbEcqbEbEc3bEc3c3bEc3bEbEcqbEcqcqbEcqbEbEbEbEbEbEc3bEc3c3bEc3bEbEbEdedfabQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.kdgdhdibDbDcDbDbDcvbDcDbDbDbDcvcVcBdjcAbDcvddcDcvcDcEddcCcEcBcXcEbDddcCbDcvcVcEbDcAcEcVcBcEcCcEc9cGcEcEcEcEcDcAbDcvcEcVcEcvbDcvcTbDdkcvcvcGcVcEbDcXcvcTcvcCcAcDcXcCddcEcGcAcTcvcAcAcDc9bDcBcCcAcVcBdjcBcBcEcDbDcDcvcAcXcBcEcBcRddcEdlbDbDcvcDbDbD.2bEbEbEbEbEdmbEdmdmc3dmc3c3bEc3bEbEbEbEbEbEbEbEbEbEbEbEbEbEbEbEbEbEc3bEc3c3bEc3bEbEbEbEbEbEcqbEcqcqbEcqbEbEbEbEbEbEbEbEbEbEbEbEbEbEbEbEbEbEdmbEdmdmc3dmc3c3bEc3bEbEbEbEbEbEbEbEbEbEbEbEbEbEbEbEbEbEc3bEc3c3bEc3bEbEbEbEbEbEcqbEcqcqbEcqbEdndodpQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaTdqdrbDbDcDbDbDcDbDbDcDcDbDcvcDcEbDcDcDcDddcvcGddcAcvcDcDcCcAcScVcEcBcEcEcEcBcBcScCcDcTcTcEcUcEcEcEbDcGcGddcEcDcDbDcAcScEcAcAcVdlbDcEbDbDcTcvcvcEcBcGcVcVcEcvcTcXcCcAcCcCcXcAcBddcEcEcXcXbDcvcXcEcDcDcBcBcDcBbDcXcBcEc9c9bDcvcVcEcAcBcAbDddbDbDcVcCcv.2c3bEc3bEbEbEbEbEbEbEbEbEbEc3bEc3c3bEc3bEbEdmbEdmdmc3dmc3c3bEc3bEbEbEbEbEbEc3bEc3c3bEc3bEbEdmbEdmdmc3dmc3c3bEc3bEbEbEbEbEbEc3bEc3c3bEc3bEbEbEbEbEbEbEbEbEbEc3bEc3c3bEc3bEbEdmbEdmdmc3dmc3c3bEc3bEbEbEbEbEbEc3bEc3c3bEc3bEbEdmbEdmdmc3dmc3c3bEdsdtduQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtdvdwdxcDcDbDcEbDcDcEbDcDbDbDcvbDbDbDbDcDcTcDcXcEcEcEcEcVcAbDcvcEcEcAcvcScXcAcXbDddddcBcEcCbDcUcGcEcDbDcEddcCcEcEcXdycDc9bDcEcTbDcAcAcBcDcVcEcEcEcDcEcvcTcvcvcEcGcEcEbDddcXcAdjcDcScVcAcEcEcGcRcAbDbDcAcEc9cDddcGcEcEbDcDcAcBcTcDbDbDcEcScXcAcXcEc9cEcEcDcG.Udzdmc3dmdmbEdmbEbEc3bEc3c3bEc3bEbEbEbEbEbEc3bEc3c3bEc3bEbEc3bEc3c3bEc3bEbEbEbEbEbEc3bEc3c3bEc3bEbEcqbEcqcqdmcqdmdmbEdmbEbEc3bEc3c3dmc3dmdmbEdmbEbEc3bEc3c3bEc3bEbEbEbEbEbEc3bEc3c3bEc3bEbEc3bEc3c3bEc3bEbEbEbEbEbEc3bEc3c3bEc3bEbEcqbEcqcqdmcqdmdAdBdC.kQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtdDdEdFbDcEbDbDcvcDbDbDcvbDcDcEcDbDcEcDbDcEbDcvcvcDcBcDcXcDcTddcVcGdjcXcEdycvbDcAcDcCcAcXcAcEdlcEcBcVcScCddbDcDcTc9cBcCcXcGcDdkc9cvcEcDbDcXbDcAcRcEcvcEcTbDcDcXcTbDcXcVcBcEcGcEcAcEbDcAcDbDcDddcEbDddcTcvdkcAbDcvcDcBcDcAcDc9cXcDcBdycEcBdkcCbDcvcDcCcAcTcAcEcEdGbrdHazazdIdmbEdmdmbEdmbEbEc3bEc3c3dmc3dmdmcqdmcqcqbEcqbEbEc3bEc3c3dmc3dmdmcqdmcqcqdmcqdmdmbEdmbEbEbEbEbEbEbEbEbEbEc3bEc3c3bEc3bEbEbEbEbEbEdmbEdmdmbEdmbEbEc3bEc3c3dmc3dmdmcqdmcqcqbEcqbEbEc3bEc3c3dmc3dmdmcqdmcqcqdmcqdmdmbEdmbEbEbEbEbEbEbEbEbEbEc3bEdJdKdLQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.BdMdNcEbDcEbDbDcEbDcEbDcDcEcEbDcEbDbDcEbDcEbDbDcEcGdlcvddcEcEcDcXcGcDcVcEcVcEcAdjcvcEcAc9dycEcAcXcEcEdycBdycDcCcVcDcXcDcEddddcEcVcEcGcXcEcXbDbDcEcAcVcAcDcEcEcEdlcvbDcXcvcEcGcvcEcGcVcEdddydycAcXcCcCdkcEcVcEcEddcEdjbDcXcEcEcDddcBcDcVcAcEcAcBdycEddbDcScEcEcAcEcDayc3bEc3azazdOdIdzc3dmc3dmdmbEdmbEbEdmbEdmdmbEdmbEbEdmbEdmdmbEdmbEbEbEbEbEbEbEbEbEbEc3bEc3c3bEc3bEbEdmbEdmdmdmdmdmdmbEdmbEbEdmbEdmdmbEdmbEbEc3bEc3c3dmc3dmdmbEdmbEbEdmbEdmdmbEdmbEbEdmbEdmdmbEdmbEbEbEbEbEbEbEbEbEbEc3bEc3c3bEc3bEbEdmbEdmdmdmdmdmdmbEdmbEbEdPdQbbQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtdRdSdTcvcEbDbDbDcEcEbDcDbDcEcDbDcDbDcDcvcEbDbDbDcEcEbDcXcGcEcDcGcVcGddcvcXcDcDcDcVcEcEcXbDdkcDcAcEcAcScDdkcAcBcEdycEcDcSbDdkddcDc9cDcEcAcEcEcBcGdddybDcDcEcEcDcAcXcBcDcvdlbDcEbDcXdycAcEbDcVcVbDcEbDcTcEcXcEcvcCdlcVcEddcBdjcXbDcDbDcXcAddcDcCcBcVcEcAcXbDddc9cEddbDcScvaedzdmdzc3dm#9dUbEazbEdVdVdIdmbEdmdmc3dmc3c3dmc3dmdmbEdmbEbEc3bEc3c3dmc3dmdmbEdmbEbEdmbEdmdmc3dmc3c3c3c3c3c3bEc3bEbEdmbEdmdmc3dmc3c3dmc3dmdmbEdmbEbEbEbEbEbEdmbEdmdmc3dmc3c3dmc3dmdmbEdmbEbEc3bEc3c3dmc3dmdmbEdmbEbEdmbEdmdmc3dmc3c3c3c3c3c3bEc3bEbEdmbEdmdmc3dmdWdXdYQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtdZd0d1cEcEbDcDcEcEcDcEcDbDcEcDcEbDcEcDcEcEbDcDcEcEcDcEcDbDcEcXcEcGcXcVdldlcGddcXcXcXcXcXcBcEcTcVcEdycDcEdycvc9cVddddcXddcEcEcVdycEcVcTcXddbDcEcEcEcXcEcXcEdyddcXcvcEcXcXcXbDcVcEdlcTcEcDbDcEcTcEcGcDcVcEcEbDcTdkcXcSdjcScCcVddcDcAddcXdkdkcAcTdyddddddcEcBcEcXcVcDdydddkdkcDd2dHc3dVc3c3bEc3bEbrbrdHdzdzd3c3dmdmbEdmbEbEc3bEc3c3bEc3bEbEdmbEdmdmbEdmbEbEdmbEdmdmbEdmbEbEbEbEbEbEdmbEdmdmc3dmc3c3bEc3bEbEbEbEbEbEc3bEc3c3bEc3bEbEc3bEc3c3dmc3dmdmbEdmbEbEc3bEc3c3bEc3bEbEdmbEdmdmbEdmbEbEdmbEdmdmbEdmbEbEbEbEbEbEdmbEdmdmc3dmc3c3bEc3bEbEbEbEbEbEc3d4d5d6QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.Yd7d8cEbDcEcDbDcEbDcEcDbDbDcEcEcDbDcEcEbDcEcDbDcEbDcEcDbDbDcEcEcVbDddddbDcXcXcEdlcGdlcVcEbDcXdycTcBcVcVcGcEcDcAcEcAcEcDcCcCcXcVcXcEdydybDdycEcCcXcDcXcTcBcBddcEcDcEcXddbDdkc9bDcXcAcEcEcDcvdlcEcVbDdkcXcGcXcDcBcVbDcVcDcvcGdjcEcXcCdldlcCcVcEcvdkbDdjcDcAcBddcEc9cBcVcVcBdycDcBdk.Udmc3dUdmd3dUdmdmc3d9c3c3c3azdzdVbEdmbEdmdmdmdmdmdmbEdmbEbEc3bEc3c3dmc3dmdmc3dmc3c3dmc3dmdmdmdmdmdmbEdmbEbEdmbEdmdmc3dmc3c3dmc3dmdmdmdmdmdmdmdmdmdmc3dmc3c3bEc3bEbEdmbEdmdmdmdmdmdmbEdmbEbEc3bEc3c3dmc3dmdmc3dmc3c3dmc3dmdmdmdmdmdmbEdmbEbEdmbEdmdmc3dmc3c3dmc3dmdmdmdmdme.e#eaQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtbuebeccDcEcDcEcEbDcEcEcDcEcDcEcEcCbDcEcDcEcDcEcEbDcEcEcDcEcDcEcEcCbDdlcDdlcVcXdkcEcXcXcEcEdddlcXedcEcXddcXcTcVddcGdkcXcDdkcEcEcVcEcCcXcScXcEcEdycBddcVcEdkc9cXdkcScBcXcEcEcDcXdkcDcEdkc9cEcXcDcXcGcEdlcSdlcVdkcXcvdjcXcDcVcTcVdkcCcvdjcTdjcXcVcXcBddddcEdjdddjdjcCcAddcDddc9cEcXbDdjdyeedmefdmdzdzdHegdHbEdmbrc3c3bEdmdmcqegd3dzegdmdzdmdmbEdmbEbEdmbEdmdmdzdmdzdzbEdzbEbEdmbEdmdmbEdmbEbEdmbEdmdmdmdmdmdmbEdmbEbEdmbEdmdmdzdmdzdzbEdzbEbEdmbEdmdmdmdmdmdmdzdmdzdzdmdzdmdmbEdmbEbEdmbEdmdmdzdmdzdzbEdzbEbEdmbEdmdmbEdmbEbEdmbEdmdmdmdmdmdmbEdmbEbEdmbEdmdmdzdmdzdzbEeheiejQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtekelemcEcCcEcDcEcEbDcDcEbDcEcEbDcEcDcEcEcCcEcDcEcEbDcDcEbDcEcEbDcEcDcEcEcVcVcScVcEcEcVdlbDcXdkcEcXcVdydlcDdlcTcXcXcDcTcXcGddcVcAcEcDcEcEendydddkcVcAcXcXbDdydycCcEdddlcXcVddcVcVcEcCcDdycvdkddbDdkcDcEcEcEcEcVdldlbDcDcEcGcXcXcAcXcVcVcEcVcEcTcXcXcAcXcVcCddcEcEcEcXdjdjcGcEc9ddcVcDc9cDcBayeod3dmdmdmc3dmc3d3c3dmdzc3dmd9dmc3cqdmd3dmdzdmdzc3dmc3dmdmdmdmdmdmc3dmc3c3dmc3dmdmdmdmdmdmc3dmc3c3dmc3dmdmbEdmbEbEc3bEc3c3dmc3dmdmdmdmdmdmdmdmdmdmc3dmc3c3dmc3dmdmdmdmdmdmc3dmc3c3dmc3dmdmdmdmdmdmc3dmc3c3dmc3dmdmdmdmdmdmc3dmc3c3dmc3dmdmbEdmbEbEc3bEc3c3dmc3dmdmdmdmdmdmdmdmdmdmepeqerQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtbHesetcCcEcDcEcEcEcCcEcEcEcEcEcEcEcDcEcCcEcDcEcEcEcCcEcEcEcEcEcEcEcDcEcCcEcDcEcEddcGdkcVcVcVdkcXdlddcXcEcVcXdldldleddkcEcXcXdjdjcXddcVcEcVddcEcEcEcDdddydkcXddddcXcEdyendyddcXdkcXcDcEddcEcEdjdjcEc9cXcVcXcDddcVcXcGcXcEdldlcEcEcEcTcXcDcEcVcVcVcEcCdkcEcXcXcVcVdjcXcVcVcEcTdjdjdkcEdjcEdddddkcEaec3dmdzdmd3bEdmbrbrdUbEd3dmdIeudzefd9dzd9brd3dzd3d3dmd3dmdmbEdmbEbEdmbEdmdmdmdmdmdmbEdmbEbEdmbEdmdmdmdmdmdmdmdmdmdmdmdmdmdmc3dmc3c3dmc3dmdmbEdmbEbEdmbEdmdmdzdmdzdzc3dzc3c3dmc3dmdmdmdmdmdmbEdmbEbEdmbEdmdmdmdmdmdmbEdmbEbEdmbEdmdmdmdmdmdmdmdmdmdmdmdmdmdmc3dmc3c3dmc3dmdmbEdmbEbEdmbEevewexQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQteyezeAcEcEcEcEcCcEcDcEcEcEcDcDcEcDcEcCcEcEcEcEcCcEcDcEcEcEcDcDcEcDcEcCcEcEcEcEcCcEcDcVddcEddcEdkcVcEcDdkdldlcXcScEcVcXdkdycVcVcEcDdkcVcXcXcXddedcVcVcEcEcEcEcDcEcEcVenddcXcXdycGdycEcVcXdkcTcTddc9cEcGdjcEdydkcScXcTcEdkcEcXcEcXcEdlcVcEcEcEcXcDcEcDcXcVcVcDcDdycEcXcDcVcVcVcXcGcVcEcVdkdjcDcXcEcDcXeBc3c3dmd3c3dmdmdmc3dmc3c3dmdmdmdmd3d3dmc3c3c3c3dmd3c3d3c3c3brc3c3c3dmc3dmdmdmdmdmdmdmdmdmdmdmdmdmdmc3dmc3c3dmc3dmdmdmdmdmdmdmdmdmdmc3dmc3c3dmc3dmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmc3dmc3c3c3c3c3c3dmc3dmdmdmdmdmdmdmdmdmdmdmdmdmdmc3dmc3c3dmc3dmdmdmdmdmdmdmdmdmdmc3dmc3c3dmc3dmdmdmdmdmdmeCeDeEQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQteFeGeHcDcEcVcEcEcDcEcVcEcEcEcCcEcEcEcEcDcEcVcEcEcDcEcVcEcEcEcCcEcEcEcEcDcEcVcEcEcDcEcVdkcVcVcDcVcXdkdddddkdkcEdkcScEddcEcXcXcGcXdldkdlddcXcVdkcXcTcEeIcVcXcEedcEcEdydyc9cVcVddddcEddcVcEdydycGcEcXddcXcEcEeJcEdjcDdjddcXdkdkcScEddcXcEcDcEcVdlcEcVcEeKdlcXcXcCcXcVdjcEdddkdlcXdkcXcVcVcVddcEcGcVdjcEdkcXaeeoeLc3dmeLeLeLdmeoeodmdmc3efdUdzdzdmegdmdmd9c3c3c3c3c3d3eueMdmeLeLdmeLdmdmc3dmc3c3dmc3dmdmc3dmc3c3dmc3dmdmdmdmdmdmdzdmdzdzdmdzdmdmdmdmdmdmdmdmdmdmdmdmdmdmdzdmdzdzdmdzdmdmc3dmc3c3dmc3dmdmeLdmeLeLdmeLdmdmc3dmc3c3dmc3dmdmc3dmc3c3dmc3dmdmdmdmdmdmdzdmdzdzdmdzdmdmdmdmdmdmdmdmdmdmdmdmdmdmdzdmdzeNeObYQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaaePeQcEcDcEcEcEcEcDcVcEcEcEcEcDcEcEcEcEcDcEcEcEcEcDcVcEcEcEcEcDcEcEcEcEcDcEcEcEcEcDcVcEcEcEdkcVdkcVdlcEcVcVcEdkddcVdkdldkdkcXcVcXcXcXcXcXcXcXdkdlcVeKcEdjdjcVcTcVdddjcVcXcEdkdycEcDeIcEcVddddcXdydydddycScVcVddddcTcVddcEdjdjcDcXdkcEddcDcEcEcXcXcEeIcEcVcEdkcDdkcXdjcXcEcXcVdjcEcDdddycXcVcVcDcVcXddcEddcVcXbPeLdmeLdmeRc3eSdmdmeod3eod3dmeTc3c3c3dUdmdUeudmdmc3c3bEc3cqdmcqeud3dmdmdmdmdmdmdmdmeLdmeLeLdmeLdmdmdmdmdmdmdmdmdmdmdmdmdmdmeLdmeLeLc3eLc3c3dmc3dmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmeLdmeLeLdmeLdmdmdmdmdmdmdmdmdmdmdmdmdmdmeLdmeLeLc3eLc3c3dmc3dmdmdmdmdmdmdmdmdmdmdmeUeVb4QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQteWeXeYcEcVcEcEcEcEcVcEcEcEcVcDcVcEcVcEcEcVcEcEcEcEcVcEcEcEcVcDcVcEcVcEcEcVcEcEcEcEcVcEcEcEcVcDcVdydldydddddkcVcEcXeIcXddcEdkdddlcEcVcEdlcVdkcXdkcXdddldkdlddcDcVdkeKcXcXddcVddcVdkdlcEdycEeIcDdlcVeIcVddeJdydydyddcVdkddcXeJc9eJcEcVcEdjdlcXdkddcEcVcVcXcVddcXcVdleZdlcEcVcXcXcXcVddcVcXcVeKcEdlcXddcXcVcVcVddcEcXe0eLdmeLeoeoeoeLeTeTe1eLeLe2eoeMdmdmc3dmdmdmdmdmdmdmd3d3c3c3eSbEeLeMbreLc3c3dmc3dmdmdmdmdmdmeLdmeLeLdmeLdmdmc3dmc3c3dmc3dmdmdmdmdmdmdmdmdmdmdmdmdmdmeLdmeLeLdmeLdmdmdmdmdmdmdmdmdmdmdmdmdmdmeLdmeLeLc3eLc3c3dmc3dmdmdmdmdmdmeLdmeLeLdmeLdmdmc3dmc3c3dmc3dmdmdmdmdmdmdmdmdmdmdmdmdmdmeLdmeLeLdmeLdmdmdmdmdme3e4e5QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQte6e7e8cEcEcEcVcEcEddcEcEcEcEddcEcEcEcEcEcEcEcVcEcEddcEcEcEcEddcEcEcEcEcEcEcEcVcEcEddcEcEcEcEddcEcEcEdddjdycEeKdldye9dkcEdkdleJcEdkdddkdkdkdkeKdlcEddddddcEcXdddldkdlcEdkdkdyddcXcXeJcVcVcVdjddcEdydydyddcVcVeJcXcEf.dydydycVdddkddddddcEcEcEdkcEcAe9ddcXcXcEddcEcXcXcXdlcVcEcVcEdleKcXcXcEcXf#cVdjcEdycXcXcEcVdjcXdddlfafbdmeTeLeoeoeod3eLdmc3dmfce1dmeLe1dmeLdmc3eTdmc3fcdUeLdmdmc3c3c3c3dUdmdmbEbEbEdmdmeLdmeLeLdmeLdmdmdmdmdmdmeLdmeLeLeTeLeTeTeLeTeLeLdmeLdmdmdmdmdmdmdmdmdmdmeLdmeLeLdmeLdmdmeTdmeTeTdmeTdmdmdmdmdmdmdmdmdmdmdmdmdmdmeLdmeLeLdmeLdmdmdmdmdmdmeLdmeLeLeTeLeTeTeLeTeLeLdmeLdmdmdmdmdmdmdmdmdmdmeLdmeLeLdmeLdmdmeTdmfdfe#hQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#RfffgcEcVddcEdkcEcEcEcVcEcEcVcEcVcEcEcEcVddcEdkcEcEcEcVcEcEcVcEcVcEcEcEcVddcEdkcEcEcEcVcEcEcVcEcVcEcEcEcVfhcVeKcXcXdjcVdddydkcVcVdkdlcEeZf#cEfidkcVcVdkdkcEcVdkeKdkcXcXddeKcEe9cEdlcXdldjcXcVddeJcVdkcEdkdddleKdydydkeIddcEeJdydldydddycVf#cVfjddddcVcVdjcEdkddeJcXcEddcVf#cEe9cXcEdlcVcXdlcVcXddcEcVcEcVeJcVfjdycEcXcVcVcX.UeLeLeTdmeTfbeLfkfle2fleLeLeLeTeLdmeodmd3eodmc3eTdmdmdmd3dUeudmc3c3eRc3c3eTdUeLcqdmbEdmdmdmeLdmeLeLdmeLdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmfbdmfbfbeLfbeLeLdmeLdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmeTdmeTeTdmeTdmdmdmdmdmdmeLdmeLeLdmeLdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmdmfbdmfbfbeLfbeLeLdmeLdmdmdmdmdmdmdmdmdmdmfmfnfoQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtchfpfqcVcVdkcEcEcEcEcVcEcEcVcEcEddddcEcVcVdkcEcEcEcEcVcEcEcVcEcEddddcEcVcVdkcEcEcEcEcVcEcEcVcEcEddddcEcVcVdkcEdydldyeKcXcXeJdddjdkdkdddldkficVcEcVdleJdkcVdkdkcVddddcEeZdde9cXcXcXcXeZdldlddcEcXdddddjddeIeKdjcVdjdycVdycEeIcEcXeJddddcVdlfrdddycVcVeJddcXcVdddddddddjcVdddkdkddcEcEddcXcEddcXdleKeKcEcVeKdkcXcXcXcEcVcEcEeKdlcXfsftfbfufbfbdmeTdmfcdmeodmdme2dmeLfveLeTdmdme2eofbfbeSeTeSeLdmeMdmdmeudmc3c3fcdmfcfcd3dmd3dmfbdmfbfbeLfbeLeLdmeLdmdmdmdmdmdmeLdmeLeLdmeLdmdmdmdmdmdmdmdmdmdmeLdmeLeLdmeLdmdmfbdmfbfbeLfbeLeLdmeLdmdmdmdmdmdmeTdmeTeTdmeTdmdmfbdmfbfbeLfbeLeLdmeLdmdmdmdmdmdmeLdmeLeLdmeLdmdmdmdmdmdmdmdmdmdmeLdmeLeLdmeLdmdmfbdmfbfbeLfbfwfxfyQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtfzfAfBcVcEcEcEcEcVcVcEdkcEcVdkcEcVcEcEcVcEcEcEcEcVcVcEdkcEcVdkcEcVcEcEcVcEcEcEcEcVcVcEdkcEcVdkcEcVcEcEcVcEcEcEcEcVdkdyfrddeJeKcXddddcXdkdydddddydkeIcVeKcXeZfCddeJdkcVeIdkdkdlcVcVddcXdkdldddkdleZcXdleKdkdjcXcXcVeJdde9dkdkfrcEcVcVdyeJcVddddcEeJdldydkdkcVdkcXddddddcVcEdjdjcAdkeKdkfCcXcVdkcXcVcXcEeZcVcXcEdleZddcXe9cVeIddcEcVdkfDftfkfbflfkfbfudmdme1e2fueLdme2eodmeTeLc3fce1d3eLeLdmeLdmc3eSdmeLeMdUd3d3dmdmdmdmdmdzcqc3eMd3eLbEdmeLdmeLeLfbeLfbfbdmfbdmdmeLdmeLeLfbeLfbfbdmfbdmdmeLdmeLeLdmeLdmdmdmdmdmdmeLdmeLeLdmeLdmdmeLdmeLeLdmeLdmdmdmdmdmdmeLdmeLeLdmeLdmdmeLdmeLeLfbeLfbfbdmfbdmdmeLdmeLeLfbeLfbfbdmfbdmdmeLdmeLeLdmeLdmdmdmdmdmdmeLdmeLeLdmeLdmdmeLfEfFaaQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtfGfHfIdkcVcEdkcVcEdkcEcEcVcVcEcEcVcEcVdkcVcEdkcVcEdkcEcEcVcVcEcEcVcEcVdkcVcEdkcVcEdkcEcEcVcVcEcEcVcEcVdkcVcEdkcVcEdkcEdkdkdkdkdyeJdydlfjeKdkeKeJddfjdyddcVcVdkcEeKcVeIfheJcEfidkdkfCcEdleZcVcEcXddcXddfjeZdle9cVcEdkcXcXddddcVddeKdycVfrcVcEeKeIcVe9cVddeJddcEdyddcVcEfhddcEfhcVcEe9cEdjdldlcXdkcVcEcVe9cVcDe9ddcEfCdlcEcVcVdlcXeIcXdde9eBfbdmfcfceLfufbfbfbfbeTdmfceTdme1e1fle2fbfbfvfueLe1eLe2d3dmeLc3eSeLfbeLfbe2eLe2eMeLeTeLdmfbeLeoeMeLdmeLdmdmdmdmdmdmeLdmeLeLdmeLdmdmdmdmdmdmeTdmeTeTdmeTdmdmeLdmeLeLfbeLfbfbeLfbeLeLdmeLdmdmeLdmeLeLfbeLfbfbeLfbeLeLfbeLfbfbeLfbeLeLdmeLdmdmdmdmdmdmeLdmeLeLdmeLdmdmdmdmdmdmeTdmeTeTdmeTdmdmeLdmeLeLfbeLfbfbeLfbeLeLdmeLdmdmeLdmeLfJfKfLQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtfMfNfOcEdkcEcEdkcEdkcVcEdkcEdkcEcVdkdkcEdkcEcEdkcEdkcVcEdkcEdkcEcVdkdkcEdkcEcEdkcEdkcVcEdkcEdkcEcVdkdkcEdkcEcEdkcEdkcVcEdkdkfhdkeKfrfrdjfrdddyfjcXfheJddeKdjfrdddkdkdkcVdkcXcVe9dddkdkdkfidleKdlddfCdkcXfjcXcXfCdkfCddcXdkcXdkcXeIdkdkddeKdkdydkdydkeIcVdkcXeKddeJfhdkdyfrddcVfidddkcVddfhdddkdjdkfrfrdkeKcEcEfhcXdkddcXe9dlfCcEeKdkdkcXe9cVfPfQe2fQdmfceTeLftfbflfbflfbeTfkfceLfbeLflfleofbdmc3fudmfufbe2e2eLeLc3eLdmdmdmd3dmd3eRdmeTeTc3c3dUd3dmdmfbfbfbfbfbfbdmfbdmdmfbdmfbfbdmfbdmdmeLdmeLeLfbeLfbfbdmfbdmdmfbdmfbfbdmfbdmdmfbdmfbfbeLfbeLeLdmeLdmdmdmdmdmdmeTdmeTeTdmeTdmdmfbdmfbfbfbfbfbfbdmfbdmdmfbdmfbfbdmfbdmdmeLdmeLeLfbeLfbfbdmfbdmdmfbdmfbfbdmfbdmdmfbdmfbfbeLfbeLeLdmfRfSfTQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaDfUfVcEddcEcVcVdkcEcVcEdkdkcEcVcEdkcVcEddcEcVcVdkcEcVcEdkdkcEcVcEdkcVcEddcEcVcVdkcEcVcEdkdkcEcVcEdkcVcEddcEcVcVdkcEcVcEdkdkcEdlcVfheJdkdkdkeKeKfrdjcVcVfjeKddeJcXfrdldjfhdydkdkdkcVcVdle9dkdkdkcVfidkdleKdlddcVfjcXcVcXfCdkcXdddlfjeKcXddcXeIeIeKdjeIdjdkfjcVeIdyeKeIcXf#cEcVcVfrcVdlcVe9dkdkeJcXdkcVcEddcEdddleKdkeJcEfhdkcXddcEdkeZdlddcEeKcVfDfufQfWfQfQdmfbfbe2e2eLeLeLflfbfXfbfvfbe1ftfle2flfbeTfbdmdme1eLeLeLfleLfbfXeLfbeLdmfYeMe2fbeSeTeSdmeLc3eMc3dmeLdmdmfbdmfbfbeTfbeTeTfbeTfbfbdmfbdmdmdmdmdmdmfbdmfbfbeLfbeLeLfbeLfbfbdmfbdmdmeLdmeLeLfbeLfbfbeLfbeLeLfbeLfbfbeLfbeLeLeLeLeLeLdmeLdmdmfbdmfbfbeTfbeTeTfbeTfbfbdmfbdmdmdmdmdmdmfbdmfbfbeLfbeLeLfbeLfbfbdmfbdmdmeLdmeLeLfbeLfbfbfZf0f1QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtf2f3dkcEdkcVdkdkcEcVdkdkcVdkcVcEdkcVdkcEdkcVdkdkcEcVdkdkcVdkcVcEdkcVdkcEdkcVdkdkcEcVdkdkcVdkcVcEdkcVdkcEdkcVdkdkcEcVdkdkcVdkcVcEficVfjddfreJfjfjddeKfCdkeKfreJddeKeJfjdkeKdkfifidydkfifieZeKeZcEdkdkdkdkfieZfCfCcEeJdkfjdddkddcXdkeKfCdkdkdddkdkcXeJdkeKdkdkeKdkfjdldkdddkdde9e9cDdlfhfrdddkcVcVdkeJdkcEfheJdkdkcEeJfieKcVfCcVcEdkddddcVe9eZfCfCdlf4eTftftftf5fbfueLfbfteTfte1fuftf6fleTfbdme2fte2e2e1eMe2eLeLfbeTfbfudme2dmd3fbeTeTfbdUfbd3d3eMdmeSeLeTdzfbfcc3fcdzeLdmeLdmdmeLdmeLeLeLeLeLeLdmeLdmdmeLdmeLeLfbeLfbfbdmfbdmdmeLdmeLeLeLeLeLeLfbeLfbfbdmfbdmdmfbdmfbfbdmfbdmdmeLdmeLeLfbeLfbfbeLfbeLeLdmeLdmdmeLdmeLeLeLeLeLeLdmeLdmdmeLdmeLeLfbeLfbfbdmfbdmdmeLdmeLeLeLeLeLeLfbeLfbfbdmfbdmdmfbdmf7f8c6QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#Rf9g.cEdkdkcEdkcVcVdkcEdkcVcEcVdkdkcVcEdkdkcEdkcVcVdkcEdkcVcEcVdkdkcVcEdkdkcEdkcVcVdkcEdkcVcEcVdkdkcVcEdkdkcEdkcVcVdkcEdkcVcEcVdkdkeKdkfjfjdkdkcVeZfhdkfheJdkeKfifCdlddfjfjcVdkddeJfidkfrdldkddeKfieIdle9e9cEe9eIdkeKdldkeZcXcVdkdkddcXfCfCcEfCeZcVdkcXdkddcVcVfheKcVdkdkdkcEfrdleIe9cXdkcVcEeJfrdkdlddfie9dlfhddcSfhcXdkcEcXdlfCdleKcEeKeKcEe9cVddfCg#fQfQfbfQfkfQfkfQfQfufQfufufcfteLe2fufbflfbflgafkfueLfkeoeoe2dmeLfvgae1fufbe1fbeMeLfbfveTeTfbe2e2fbeLeTeLeLeTeLfce2eMfbd3eMfbeLfbfbfbfbfbfbfbfbfbfbeLfbeLeLdmeLdmdmfbdmfbfbfbfbfbfbdmfbdmdmeLdmeLeLfbeLfbfbeLfbeLeLfbeLfbfbfbfbfbfbeLfbeLeLfbeLfbfbeLfbeLeLfbeLfbfbfbfbfbfbfbfbfbfbeLfbeLeLdmeLdmdmfbdmfbfbfbfbfbfbdmfbdmdmeLdmeLeLfbeLfbfbeLfbeLeLgbgcgdQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtbcgecEdkcVdkcVdkdkcVdkdkcVdkcVdkdkcVcEdkcVdkcVdkdkcVdkdkcVdkcVdkdkcVcEdkcVdkcVdkdkcVdkdkcVdkcVdkdkcVcEdkcVdkcVdkdkcVdkdkcVdkcVdkdkcVcEfheZfheZfjdkcVfjdkcVfheKfhfCeKdkficVfrdlfhfjeKeKeKddfreKfrfidldkeKdkeKeIe9fheJfificVfieZeKe9eZcXdkcVdkddfCfjeZe9dkeZfjdddkfrddcVe9eIdkcVfjfrcVeKfrdleKddeKfhcVcEfjcVfrdlfifhddfjfhdddkdddkficVcXfCddfCeJeKe9dde9e9gfggfufQftfueLftfuftfkfufkfQfbfXfQfke1fkfbe1fleLflfXeTfudmeLf6eofkfbeLfbe1eLeLeMeLeofldmeTeTeLfbeLdme2eLeofbeTfbeTfbdme2d3d3fbbEfbfbeLfbeLeLeLeLeLeLfbeLfbfbfbfbfbfbeLfbeLeLfbeLfbfbdmfbdmdmfbdmfbfbeLfbeLeLdmeLdmdmfbdmfbfbeLfbeLeLfbeLfbfbfbfbfbfbdmfbdmdmfbdmfbfbeLfbeLeLeLeLeLeLfbeLfbfbfbfbfbfbeLfbeLeLfbeLfbfbdmfbdmdmfbdmfbfbeLfbeLeLdmeLdmdmfbdmfbghgiabQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtgjgkdkdkcVdkcVdkdkcEcVdkcVdkdkcEdkcVdkdkcVdkcVdkdkcEcVdkcVdkdkcEdkcVdkdkcVdkcVdkdkcEcVdkcVdkdkcEdkcVdkdkcVdkcVdkdkcEcVdkcVdkdkcEdkcVdkdkcVdkeKfheKdkeKe9eKdkdkdldkeJfhfheKfrdlfrdkdydlfreJdkfjdddkdlfrfrdkfrcVdkdkcVeIeKcVfhdkdkdkcVdkeKddfCeKfjfjcXdddkcVfCfCcXfCeKfjdkdkdkeJeKeKcXcVfrcVfrdkcVfreIeKe9eJdkdlfrfrcVcEdkdlfhe9dkfhcVdkeKdkdkeJeKdkdkeKdkcVfDfQfkfQggfQfQfkfQfkftfQftftglfufQfQfbfQfkfkftfle1e1fufvfbgagafkfbfue2fle1fvfbe1fbfbfbfbfbfbeLfbeSeSfbeLfcfYdmfbdmc3eSc3eLeLfbeLe2e2fbdmfbfbfbfbfbfbeLfbeLeLfbeLfbfbfbfbfbfbeLfbeLeLfbeLfbfbfbfbfbfbeLfbeLeLfbeLfbfbfbfbfbfbeLfbeLeLfbeLfbfbdmfbdmdmeLdmeLeLfbeLfbfbfbfbfbfbfbfbfbfbeLfbeLeLfbeLfbfbfbfbfbfbeLfbeLeLfbeLfbfbfbfbfbfbeLfbeLeLfbeLfbfbfbfbfbfbeLgmgndpQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtgocVdkcVdkdkdkcVdkdkdkdkdkdkdkcVdkcVdkcVdkdkdkcVdkdkdkdkdkdkdkcVdkcVdkcVdkdkdkcVdkdkdkdkdkdkdkcVdkcVdkcVdkdkdkcVdkdkdkdkdkdkdkcVdkcVdkcVdkfjfjeKfjdkfhfhfjfifheKfjdldkeKfhfhfheJfCfrfrfrfjfhfreKfjeJfjeJfifrfidlfifrfidkfCfCeKeJdkdkdkdkeKfifCeIe9fCfjfjdkdkdkeKfCeKfCdkfjfjdkddeKdkfheKfifjdkcVdkdldkeIfjeKe9cVfhdkfrfre9e9djdde9eJdkcVfhdkeKcVfidkeKeKdkdkbPe1fQftfQfkfufQfufufbfufkfkftfQftfufue1fueLfkftfufkflfkfufueLfbeTeLfue2fue2fbfufbgae1fbeLe2e2eMfbeTfbfbfbfYe2fce2fbeTfbeTeTc3fceLeMe2dzfbfbeLfbeLeLfbeLfbfbeLfbeLeLeLeLeLeLfbeLfbfbfbfbfbfbeLfbeLeLfbeLfbfbfbfbfbfbeLfbeLeLfbeLfbfbfbfbfbfbfbfbfbfbfbfbfbfbeLfbeLeLfbeLfbfbeLfbeLeLfbeLfbfbeLfbeLeLeLeLeLeLfbeLfbfbfbfbfbfbeLfbeLeLfbeLfbfbfbfbfbfbeLfbeLeLfbeLfbgpgqgrQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtgsdkdkdkcVdkcVdldkdkcVcVdkcVdkcVdkdkdkdkcVdkcVdldkdkcVcVdkcVdkcVdkdkdkdkcVdkcVdldkdkcVcVdkcVdkcVdkdkdkdkcVdkcVdldkdkcVcVdkcVdkcVdkdkdkdkcVdkeZfCfjfjeKeKdkcVfheZfifhe9dkeKfjcVgtfhfheJeJfrdkfCcVfjfrfrfjeKfheKeKfifrdkdle9cVfidkfCeKeKfhcVdkcVfrfCdkeZeIfCeZe9cVdkdkdkfjeZfCeZdldkfjdddddkddeKeIfjfrfrfrcVdkcVdlfjeKddcVfhcVfrdle9dkdkfhdddkcVeKfhdkcVcVfrdldkfPgufuguftfkgufufkfQgufQgugufQgvfQfufkfkfkfQfQfQfXfuftfuftfbfufbfueLfbeTfbfufufuflfbfufbfbftfbe2fbe2eLfbeSeLfueLfuftfbftfbe2fbeTeTfceLfYeMeMfbeLfbfbfufbfufufbfufbfbfbfbfbfbfbfbfbfbeLfbeLeLeLeLeLeLfbeLfbfbfbfbfbfbe2fbe2e2fbe2fbfbeLfbeLeLfueLfufufbfufbfbfbfbfbfbeLfbeLeLfbeLfbfbfufbfufufbfufbfbfbfbfbfbfbfbfbfbeLfbeLeLeLeLeLeLfbeLfbfbfbfbfbfbe2fbe2e2fbe2fbfbeLgwgxgy.kQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQt.kgzeKdldkcVdkeKdkeKdkcVdkeKdkdkcVdkeKdldkcVdkeKdkeKdkcVdkeKdkdkcVdkeKdldkcVdkeKdkeKdkcVdkeKdkdkcVdkeKdldkcVdkeKdkeKdkcVdkeKdkdkcVdkeKdldkcVdkeKdkgAfjeZfCgAdkfjeKfreKeKfheKfigAfjeKdkcVfrfhfjfheKfCfCdlfjdlfhfhfheKdkdddkfCfjfrdkfieKfrfCcVfCfhdkgAfhcVfrgBfCfCdddkeKeKdkdddkgCdkgCfCeZdkeKdkdkeIeKeKeZfieIfjgAdkfCfrcVdkfhe9e9eJfhfhgte9dldkfjfrgDfhcVfheKfidkdkfDfQggfQgggufQgufkfkfkfQfkfkfue2fufufkftfkgufufQfufufQfvfkfbf5flfkfkfXfufXfke1fke2e2fue2fbfbeLfue1ftfbeMfle2fbfbeTeTeSfbeLeMfbeSeTfbeTfYfbfce2fbeoe2e2fbe2e2eLe2eLeLfbeLfbfbeLfbeLeLfbeLfbfbfufbfufufbfufbfbeLfbeLeLfbeLfbfbeLfbeLeLfbeLfbfbfbfbfbfbeLfbeLeLfbeLfbfbfbfbfbfbfbfbfbfbe2fbe2e2eLe2eLeLfbeLfbfbeLfbeLeLfbeLfbfbfufbfufufbfufbfbeLfbeLeLfbeLfbfbeLfbeLeLfbeLfbfbgEgFgGQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQt.kgHdkdkcVeKdkdkdkdkcVdkdkdkdkcVdkdkdkdkcVeKdkdkdkdkcVdkdkdkdkcVdkdkdkdkcVeKdkdkdkdkcVdkdkdkdkcVdkdkdkdkcVeKdkdkdkdkcVdkdkdkdkcVdkdkdkdkcVeKdkdkdkdkdkeKfjfjfCeKfCe9dkdkcVgAdkeKfjfjeKfjdkfidkdlfreKfjfjeJgCfrfCfjfhdlfjfjeKdkeJeKeKfjfrdlfjdkfifCfCeIe9fhfieKdkfrfifCfCeZfhdkdkdkdkddfCfCe9fCdddkdkdkdkddeKdkeKfifjcVdkfrfCdkeIfreKe9e9cVeKdkdke9e9cVfifhfhe9cVfhdkgIglfkgvfugvguf5fQgufkfkfQfkfQglfQfQfQfbfQgaf5fQgvgufQfufQfbfXgafbfufuflflflfbfkgafufuf6f5fkflfufbfbgafufufue2e2fbfbeLfbeLe2fueoftfueLfveLeSfcdzfbe2e2fbe2fbfbfbfbfbfbfbfbfbfufbfufufbfufbfbfbfbfbfbfbfbfbfbfbfbfbfbfufbfufufbfufbfbfbfbfbfbfbfbfbfbe2fbe2e2fue2fufueLfueLeLfbeLfbfbfbfbfbfbfbfbfbfbfbfbfbfbfufbfufufbfufbfbfbfbfbfbfbfbfbfbfbfbfbfbfufbfufufbfufbfbfbfbfbfbfbfbgJgKgLQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtabgMdkeKdkdkdkeKcVeKdkeKeKdleKdkeKdldkeKdkdkdkeKcVeKdkeKeKdleKdkeKdldkeKdkdkdkeKcVeKdkeKeKdleKdkeKdldkeKdkdkdkeKcVeKdkeKeKdleKdkeKdldkeKdkdkdkeKcVeKfhgDgDdkeKfjgAgNe9gAfjdkfjeKcVeKfheKeKdkfjfjeKdlfjfCfhfjfhgCdkfCfCeKeKgNeKfjeKfCeKeKfrfjfrfhddeKfifjfjgNgCe9gDdlfifjdkfCdkgAcVgCfheKeKdkeKfCgCgNfieKfjdkdkfCddeKeKeKeKfrfjdkfCdlfhdkfhe9eKgDeJeKfrfCfCdleKe9gDdkdkgOgugPgufkfQfkglgQgQgugvfQgvfQgRfQfQfQfQfQfQfkfkfkfkgugvfQfQgufbfWgve1gvfbfbfkfbfQfQfbfbfufbf5e2fkf5fbfXfufueLfbeLe2fbeLfbeTeTfYfbfce2fbeoe2e2eTeLeLfbdmfte2e2fufbfbfbfbfbfbe2fbe2e2fbe2fbfbfufbfufueLfueLeLfbeLfbfbfbfbfbfbfufbfufufbfufbfbeLfbeLeLfbeLfbfbfbfbfbfbfbfbfbfbe2fbe2e2fue2fufufbfufbfbfbfbfbfbe2fbe2e2fbe2fbfbfufbfufueLfueLeLfbeLfbfbfbfbfbfbfufbfufufbfufbfbeLfbeLeLgwgSgTQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtgUeKdkdkdldkeKdkdkdkdkdldkdkeKdkdkeKdkdkdldkeKdkdkdkdkdldkdkeKdkdkeKdkdkdldkeKdkdkdkdkdldkdkeKdkdkeKdkdkdldkeKdkdkdkdkdldkdkeKdkdkeKdkdkdldkeKdkdkdkfjeKe9e9eKfjfjeKfjfCeZfjgAfjdkdkdkeKfjfCfhfheKgAfjfjfCfrfCfhfrfjfrfCfCfCgAfrfrfhfjfjeZeKgBfrfjfjfjfrfifigCeKdkeKe9dkdlfifjdkfjfCfCeKe9dkeKdkdleKfCfCgtdkeKfjdkdkdkeKeKeKfjdkdkgAdkdkdldkdkeKe9e9e9gtdkdkfhdkfrfje9gIgVgPglgvgWgPfkglglglfQfQgggufQglgufkguggfQgufQfufugaf5fkfQgufQgugugafWfkfbfuflf6flfkfbfXfWftf5e1e1fle2fXfbfufbfuf5f5ftfufkf5fufufXe2fueogXe2gXeTeTeTfYfbfbeTe2fufufbfufbfbfufbfufufbfufbfbfufbfufufbfufbfbfbfbfbfbfufbfufueLfueLeLfbeLfbfbfufbfufufufufufufufufufue2fue2e2fbe2fbfbfbfbfbfbfufbfufufbfufbfbfufbfufufbfufbfbfufbfufufbfufbfbfbfbfbfbfufbfufueLfueLeLfbeLfbfbfufbfufufufufugYgZ", +"QtQtQtQtQtQtQtQtQtQtQtg0eKdkdkeKdkeKdkdldkeKeKdkfCeKdldkeKdkdkeKdkeKdkdldkeKeKdkfCeKdldkeKdkdkeKdkeKdkdldkeKeKdkfCeKdldkeKdkdkeKdkeKdkdldkeKeKdkfCeKdldkeKdkdkeKdkeKdkdldkeKgAfCg1gAgNfjfhdkfheKfjgAfCdkfCgAgAe9fjeKdlfheKfjeKgAfjeKfjfCdkfCfheKgCfCfrfrgCfjfrfhfjfjfjeKdkgBfjdkfjfhgtdkfjfCdkgCfhgDdkfrdkfjgBfig1gCgNfjeKdkdleKfCgCfCdkfCeKeKfrg2eKdkfheKfjeKgAdkfCdkeKfrdkfjeKgAfhdlfreKe9frg3glglglguglg4gug4gvglglglgRguglgufkfkfQfkfQfkfQglfkfkfQgWfbfbfkfQfQgufQfugugagvfQflflfkfuflfufXgvfufbf5e2flflfbfbfufugafuf5e2fke2eTeTfbfbeTfue2fbe2ftfufvfvdmfvfce2e2fbfbe2fbfbfbfbe2fbe2e2fbe2fbfbfufbfufufbfufbfbfbfbfbfbfufbfufufbfufbfbfbfbfbfbfufbfufufbfufbfbfbfbfbfbfufbfufufufufufufbfufbfbfbfbfbfbfbfbfbfbe2fbe2e2fbe2fbfbfufbfufufbfufbfbfbfbfbfbfufbfufufbfufbfbfbfbfbfbfufbfufug5", +"QtQtQtQtQtQtQtQtQtQtg6g7eKdldkeKeKdkeKeKfCdkeKdkdkeKdkdkeKdldkeKeKdkeKeKfCdkeKdkdkeKdkdkeKdldkeKeKdkeKeKfCdkeKdkdkeKdkdkeKdldkeKeKdkeKeKfCdkeKdkdkeKdkdkeKdldkeKeKdkeKeKfCdkgDfCfCgAfjeKgAfCfjgDgDdkeKgBg8fjgCfjfjgAdkdkeKdlfhfheKfieKgAg1fieKfrfrfhfjfhgCfCfieKeKfhfCfhg9eKeKeKeKfjfjfrfhdkdkeKeKfCgBeKfCfigBfifigCfCfCeKdldkgAgCdkeKgCgCe9gCdkdkeKdkdkfCdkfheKeKfieKeKfCfCeKdkdkfhe9e9gDeKfrgfg4gggRfkglggglggggfkgggPgWgugPglglgugugufQgvfQgvgvgugugugufQgvfkfQgufkguguf5gufWfXfuf5fuflgPfugugufufQf5f5flfkflflfbfbfbfuf5flfufke2fufbfbfbeTfbfbgXe2e2e2fteLfvfvfcfue2fbfue2fufufufufufufbfufbfbfufbfufufufufufufbfufbfbfkfbfkfkfufkfufufbfufbfbfbfbfbfbfufbfufufbfufbfbfbfbfbfbe2fbe2e2fue2fufufbfufbfbfufbfufufufufufufbfufbfbfufbfufufufufufufbfufbfbfkfbfkfkfufkfufufbfufbfbfbfbfbfbg5", +"QtQtQtQtQtQtQtQtQth.h#fCdkeKdkdkeKdkdkeKdkeKeKdkfCdkeKfCdkeKdkdkeKdkdkeKdkeKeKdkfCdkeKfCdkeKdkdkeKdkdkeKdkeKeKdkfCdkeKfCdkeKdkdkeKdkdkeKdkeKeKdkfCdkeKfCdkeKdkdkeKdkdkeKdkeKgDfhg1fjfCfCfjgBfjfCgDe9dkeKfjeKgAfjg1fjfhfjfjgAdkdkfheKfjfjeKgAeKdkfCfhgCg2fjfhfCfrfCfCfjgAfrfhfjdkfCfigBgCfifCe9frfjfifjgCdkeKgDfhfhfifjfjfCgCdkfCgAdkdkeKdleKgAfCgCfjfhfCdkeKfrdkfheKfjfjdkgAfCfrg2fheKfCe9fhfhhag4hbgQhcgRguglglglg4g4gRgQgggvggggglgugugggufkgvgvfQgvfkfQgufQgugvgafufkfQfQfkfQfQgvfbfXf5fufkfufufkfbfufWgQfugPgPe2gPflfbfufufufugvftgWgPftfufvfue2fbfuftfbe2fbeTeTdmfcfbeTeofteLfbfufbfbfufbfufufbfufbfbfbfbfbfbfufbfufufbfufbfbfufbfufufkfufkfkfbfkfbfbfufbfufufkfufkfkfufkfufufufufufufbfufbfbfbfbfbfbfufbfufufbfufbfbfufbfufufbfufbfbfbfbfbfbfufbfufufbfufbfbfufbfufufkfufkfkfbfkfbfbg5", +"QtQtQtQtQtQtQtQtQthddkeKdkeKfjeKeKfCeKdkfCeKdkeKeKdkdkeKdkeKfjeKeKfCeKdkfCeKdkeKeKdkdkeKdkeKfjeKeKfCeKdkfCeKdkeKeKdkdkeKdkeKfjeKeKfCeKdkfCeKdkeKeKdkdkeKdkeKfjeKeKfCeKdkfCeKfjgBgBfjfhfCfjgDhegCfCg1gAfjg1fhdkeKeKdkfjfhfjgCg1gAgAfCeKdkfjeKfifhfhfjdkgAfrfChffheKfCgCfCg9eKfjfCdkfhdkgBeKeKhgfCfCfjfhfigCfjfCgBgBdkfhfjfifCg1gBeKfCeKdkfCeKdkeKeKdkfCfhdkeKfjeKeKfCfCdkfjeKfifjfCdkfrfhfrfCfjg3hhggggggfQg4ggglglfQglguggglgPgug4glgvhhhigRhhgugugugvfkfkgufQguguhhguhhglgugugvgRglgufkgufWgvgvgvflfkflfbfQfbfbgagvflftfkflfufbfXfufufuflfue2fufufbfvfYfbfue2fufufkfufQfQfvhjfbfufbftfbfbfufbfufufQfufQfQfufQfufufkfufkfkfufkfufufbfufbfbfbfbfbfbfufbfufufbfufbfbfbfbfbfbfufbfufufbfufbfbfufbfufufQfufQfQfufQfufufbfufbfbfufbfufufQfufQfQfufQfufufkfufkfkfufkfufufbfufbfbfbfbfbfbfufbfufug5", +"QtQtQtQtQtQtQtQthkeKfjfCdkfCdkdkeKdkfCeKdkfCdkfjdkeKfjfCdkfCdkdkeKdkfCeKdkfCdkfjdkeKfjfCdkfCdkdkeKdkfCeKdkfCdkfjdkeKfjfCdkfCdkdkeKdkfCeKdkfCdkfjdkeKfjfCdkfCdkdkeKdkfCeKdkfChgg9fjgAgAgCfhgAfCfjgCfjfCgAfjg1fjgAdkfhfjfCfjg8e9fjgAdkg1fjdkfCfhhlfjgAfjfjdkfCfjfrfhfjfCgCfCgCfifjfjfhg9g2dkg9eKeKfjfjfCfje9fjeKfjfCfhgAgCfhgAfifigBfCfCfhdkfCdkhedkeKfjfCfCgCfidkgAdkg1fCfrfCfhfjfigAfjfCfrfCfhhmhbglhnggggglgug4glglglhohhglgVggguhpgugQgQfQguggguhhguhhglguhngvgufQgufkfQfQfkfkfkgufkgRgufQfufXfkgvfkgPgPfkfkfWfufkfXgafbfQe2ggfQfbhifufuf5flfuftflfufQfXeThjfbfbgWfbfkf6fufueTfcfbfYfQflftfQfufufbfufbfbfbfbfbfbfufbfufufbfufbfbfkfbfkfkfufkfufufbfufbfbfQfbfQfQfbfQfbfbfufbfufufQfufQfQfbfQfbfbfkfbfkfkfbfkfbfbfQfbfQfQfufQfufufbfufbfbfbfbfbfbfufbfufufbfufbfbfkfbfkfkfufkfufufbfufbfbhq", +"QtQtQtQtQtQtQthrhseKdkeKdkeKeKfCdkeKdkfCfCeKeKdkfCeKdkeKdkeKeKfCdkeKdkfCfCeKeKdkfCeKdkeKdkeKeKfCdkeKdkfCfCeKeKdkfCeKdkeKdkeKeKfCdkeKdkfCfCeKeKdkfCeKdkeKdkeKeKfCdkeKdkfCfCeKgAfjg9fjfhgBeKgAgAgAfjgDfCgCfCgAgAfCfjfhfjeKfjeKeKg8fjgCfjg1fjfheKdkg8eKfhfhfhgAgAfCfreKfrfjfCgCgCdkgCgCfrfCfrfheKg9eKeKeKfjgCfhfhfrg9fjeKgBeKeKeKfjfieKfigCfjfhgCfCfjeKdkeKdkeKeKgCe9gCdkfCfCfCeKdkfCeKe9fhdkgAgAg#g4hbgVhbhnggglhhglg4guglgRgQglgRglgVhbg4g4gQgvhcgRglgufQfQgugugugvglgvglhbgvglgufXhtgvglglguglgufWgufXgvguflfkfuflgPfbfkfQf5gQf5fkgvfkfkfkfugvgafufufbfufufkfWfufkfvfkfufuf6ftfkfueTfkfbfbftfcfteLfkfufkfkfufkfufufQfufQfQfufQfufufufufufufbfufbfbfkfbfkfkfufkfufufkfufkfkfbfkfbfbfufbfufufkfufkfkfufkfufufkfufkfkfbfkfbfbfufbfufufkfufkfkfufkfufufQfufQfQfufQfufufufufufufbfufbfbfkfbfkfkg5", +"QtQtQtQtQtQtQthufCdkfCeKfjfCdkeKfjfCeKfjeKdkfCeKfCdkfCeKfjfCdkeKfjfCeKfjeKdkfCeKfCdkfCeKfjfCdkeKfjfCeKfjeKdkfCeKfCdkfCeKfjfCdkeKfjfCeKfjeKdkfCeKfCdkfCeKfjfCdkeKfjfCeKfjeKdkgCgAfjfjgAhgg1gCfjgDhegAgAhgfCfCfCgAfCe9g1gAg1g1fhgAfjg1gAhegAe9fjeKg1fjfCfhhlfjeKgAg9g1fjfjgCfCfCfCfCfCfCfjg9g2frfjhlg9gBfjgBeKgCgAfCfifjgBgAgCdkfhfjfjfjfhfjfifjgCfCe9g8gAfjfCdlgCfjgCfhgAfhdkfCeKfCeKfCfhhlfheKg#gugRgug4gVhhgVglhogRggg4g4gVg4glhogRhhglg4g4g4gvg4gvguglguhiglgghhfQglfkfkhbglglglfbglfkfkgvfQguguglfuhhfQfuhtfugvf6fkfufufufXf5gvf5gWfkfufbfufbfXf5fufkfkfQfkfQgufvfQfvfufufbftfufvf5fvfufQfvfkgae2fQdmfbfkfbfkfkfbfkfbfbfufbfufufQfufQfQfufQfufufbfufbfbfufbfufufufufufufbfufbfbfufbfufufQfufQfQfufQfufufufufufufufufufufQfufQfQfbfQfbfbfkfbfkfkfbfkfbfbfufbfufufQfufQfQfufQfufufbfufbfbg5", +"QtQtQtQtQtQthvhwfjeKdkfCeKeKfjeKfCeKdkeKfCfjeKdkfjeKdkfCeKeKfjeKfCeKdkeKfCfjeKdkfjeKdkfCeKeKfjeKfCeKdkeKfCfjeKdkfjeKdkfCeKeKfjeKfCeKdkeKfCfjeKdkfjeKdkfCeKeKfjeKfCeKdkeKfCfjgAfhhegAfCgAgAgAhehgg9gAfjgCgAfjgDfCgAgCfCg8gCgAg1gAgAgAeKeKg1fjgAe9g1gAe9fCeKeKhlfhfCeKfjfjfCg1eKfjhlfheKg9fhgChffjfCfCfheKfCfjeKeKfjfCfrfCfCfhfjgBfCeKdkeKgAfjfhfifjeKdkg8gCeKfjeKfCeKdkeKgAfhgCfiheeKfrfCeKfChlhxhnhyglhhgVhbgVhnhbgRhngRgVggg4gugugRfkhcglhhglgVgVgugVggggguggguglhiguhhglguglgvguglgRglglglgQgQglgvglguguglgufQglgvhhf5f5gPf5fkguhigvglfQgvggfkgvf5gufufXfufkfbflgPfbgufuhtfuhjgafbhje2fbfueTfufvfQftgaflfufbfufufkfufkfkfkfkfkfkfufkfufufQfufQfQfufQfufufkfufkfkfQfkfQfQfkfQfkfkfufkfufufbfufbfbfkfbfkfkfQfkfQfQfbfQfbfbfufbfufufQfufQfQfufQfufufkfufkfkfkfkfkfkfufkfufufQfufQfQfufQfufuhz", +"QtQtQtQtQtQthAeKfjeKfjfjeKfjfCeKfjeKfjfjeKdkfjeKfjeKfjfjeKfjfCeKfjeKfjfjeKdkfjeKfjeKfjfjeKfjfCeKfjeKfjfjeKdkfjeKfjeKfjfjeKfjfCeKfjeKfjfjeKdkfjeKfjeKfjfjeKfjfCeKfjeKfjfjeKdkhefjg1fjhehgfjhegAgAg9hgg9hegBfjhefhhggDfjhegCheg1gBfjgAgAfjgAdkheeKhegCg1g1gAhlfCeKg1fhhlg1fhfjhlgAfjeKhlhffhgAfCfhgAfjg9hlfCfhfjfhfjeKfjhgfCfjgAfCg9gBfjg1gBdkheeKhgeKfjg1gBhlg8gChleKhefjeKdkhgfCgAgChefjeKg9fCg#hngRhngugVgug4gRg4hngRgVgVhhgVhBhngugVgQgRgQglgVglgRgRgvguglguhogVgugVgugugugvgugvhhguhhhhgvhhgufXgQgvglgQglgQglhhguglf5gvgPfkgvgPfugufufXf5fkf5fkggftfQfQgufQhtguftggftfufkfvfkfufvfbftftflftfQfXfufXeLfufte2fuftfkfufQfQfufQfufufkfufkfkfQfkfQfQfufQfufufkfufkfkfufkfufufufufufufQfufQfQfQfQfQfQfufQfufufkfufkfkfufkfufufQfufQfQfufQfufufufufufufQfufQfQfufQfufufkfufkfkfQfkfQfQfufQfufuhz", +"QtQtQtQtQthCeKfCeKfCfCeKeKfjeKfjfCeKfCeKfjfCeKfCeKfCfCeKeKfjeKfjfCeKfCeKfjfCeKfCeKfCfCeKeKfjeKfjfCeKfCeKfjfCeKfCeKfCfCeKeKfjeKfjfCeKfCeKfjfCeKfCeKfCfCeKeKfjeKfjfCeKfCeKfjfCgAg1fjg9g9gAgAgAgAhgfjgAgAfjg9g1gAgCgDgAfjgDgDhefChegCgAg8fhfjg1eKfCgAg1g8gAgChegAg1g1eKfCfhfjfjgAfCeKfCg1fCeKg9eKhffjgCfCgCfjg2fhg2fjg9g9eKeKgAeKhgfCfhfCfjg9fCgCgCgDfjfCfjfjfjfjg1gCeKg8fhfjfCeKfCgCgCgCgCeKfjgAfPhngRhnhnhnhnglhnhbgRhbg4g4hDgVgRgVhhgVgVgVg4hcgRgRgVgRhhhhglgVhcgRgvhcgRgRhhguhiglgQhhgQglguhbguguhhguglhhgvglgvgugugufWfWhigvguglgPglgPgPgufkfQfkf5gvfkfkgPfWfkfkgufQgugufkfkfufvfXfufQfQfkhjgWfke2fkfvfvfQeTfQfkgWfQfkfkfufkfufufQfufQfQfufQfufufufufufufQfufQfQfkfQfkfkfkfkfkfkfufkfufufkfufkfkfQfkfQfQfufQfufufQfufQfQfkfQfkfkfufkfufufQfufQfQfkfQfkfkfufkfufufQfufQfQfufQfufufufufufuhq", +"QtQtQtQt#hhEfjfCeKgAfCfCgAfCfCfCeKfjeKgAeKfjfjfCeKgAfCfCgAfCfCfCeKfjeKgAeKfjfjfCeKgAfCfCgAfCfCfCeKfjeKgAeKfjfjfCeKgAfCfCgAfCfCfCeKfjeKgAeKfjfjfCeKgAfCfCgAfCfCfCeKfjeKgAeKfjhgg9g1hFg9g9g1gAgAgCgAhegAhgfjheg9g1gAhegCgAhGg1fCgAgAgAgAgAgAgAgAg1gAgAfCg1heg1g1g8gAg1eKhHgAhlhlfjfhhHfjg1gAg9g1fCfhhlfjgAgChfgAg2fChgg2g9hegCgCfCfjfjgAgAfjg9g9gCeKhggCgAhGfjfjfjgCfjgCfjfhhehgfCeKgAfCgCgAfjfjhIgRhngRhnhngRhngRgVhhgVhnhnguhng4g4gVgRgVglgRg4gRgRglhbgVgVglgVglgvgRgQgRgQglgRguglgugufugvgQgvglglglglglfkgQgQgQgQglgQglglfufQguf5glfuglggftggf5fWglfXfQfQfkggfkfuf5fufWf5fkfugPgPfkfkfXfXfufQfuftfkftfkfufufufufkeLfkfufufkfufkfkfkfkfkfkfkfkfkfkfQfkfQfQfufQfufufQfufQfQfufQfufufQfufQfQfufQfufufufufufufkfufkfkfQfkfQfQfufQfufufkfufkfkfkfkfkfkfufkfufufkfufkfkfkfkfkfkfkfkfkfkfQfkfQfQg5", +"QtQtQtQthJfCeKfCfjfjeKeKfjeKfjeKfCfjfjfCeKfCeKfCfjfjeKeKfjeKfjeKfCfjfjfCeKfCeKfCfjfjeKeKfjeKfjeKfCfjfjfCeKfCeKfCfjfjeKeKfjeKfjeKfCfjfjfCeKfCeKfCfjfjeKeKfjeKfjeKfCfjfjfCeKfCfhg1hehggAg1hegAg9fjgAgAgAgAgAfjfCgAheg9gAfjgAgBhlfhfChefjg1gCfCfCg8g1gAgAgAfjeKg1gBg1heheg1gAg1gAfChlfjfheKhlfhg1gAg1g9g9g9fhfCeKfChfgAfCfCg9fChffjgCgAgAgAfCfCfCfCgAg9gBeKg1gBfjeKfjfjfjg8eKfCeKg8hefjeKeKfjeKgAg#hbgVhngRgRhbgRhbhbhDhbhDhKhyhDhnhnhKhDhKgVhnhLgVgVg4gVg4glglgRgRguhhglglgVgVgVhhhhgRhhglglhhgghMglgvhhgugugVgugVgRhhgRglhbgQglglfkfWguf5gvgQgvgPgvglfkhNguhtg4guggglgggug4hihNgufQfuggfkfufufufufufQfufkfQfQfQfQfXfufQfkfkfQfkfQfQfufQfufugufugugufQgufQfQfkfQfkfkfufkfufufkfufkfkgufkgugufQgufQfQgufQgugufQgufQfQfufQfufufufufufufQfufQfQfQfQfQfQfkfQfkfkfQfkfQfQfufQfufugufugugufQgufQfQhz", +"QtQtQtQthOgAfjgAfCeKfjgAfCgAeKfjgAfCfCeKfjgAfjgAfCeKfjgAfCgAeKfjgAfCfCeKfjgAfjgAfCeKfjgAfCgAeKfjgAfCfCeKfjgAfjgAfCeKfjgAfCgAeKfjgAfCfCeKfjgAfjgAfCeKfjgAfCgAeKfjgAfCfCeKfjgAhehHg9fhg1hFg1hegAhehFg9hefjhghehehGgAfjhgg1hehHgAhegAgAg1gDfjgAfjheg1gBg1hPg1hHfhfjgAg1g1gChehHg1gAfCgAfjhgfjhggAg1gAg1g9eKg1hgfjheg9eKfjgAg9g1fhhlg1g9gCeKgAgAfjhGfCfCfCg1gCgAfhg1gAfjfjgBfjgAfjg1g8gBg1hFfCgAeKfPhnhyhnhyhngVhhhDhDgRhDgVgRgVguguhNhbg4gRhngRhngRgRhBgRgVglhKglhbhngRgVgRgRg4gRgQglhnguhhhiglglguguhhgvgVgRglhDglglguglgvgvglguglhbglfQhhfQgRglg4g4gvgRfWfufWf5gvfXgvfkfkfkftgufufuhtgvfQfkglflgvgufkgufufkfbflftftfQfufQfQfufQfufugufugugufkgufkfkfufkfufufQfufQfQfQfQfQfQgufQgugufugufufufufufufufkfufkfkfufkfufufQfufQfQgufQgugufkgufkfkfufkfufufQfufQfQfufQfufugufugugufkgufkfkfufkfufuhq", +"QtQtQthQfCfjfCeKfCfjfCfjeKfjfCfCgAeKgAfjfCfjfCeKfCfjfCfjeKfjfCfCgAeKgAfjfCfjfCeKfCfjfCfjeKfjfCfCgAeKgAfjfCfjfCeKfCfjfCfjeKfjfCfCgAeKgAfjfCfjfCeKfCfjfCfjeKfjfCfCgAeKgAfjfCfjg1fjg1g9heg9gCg1gCg1hefjhFhefjhggAgBg1hggAhlgAhgg9gAgAgAhegAg1hefCgAfCheg1gAgAg1g1g1hHgAgAfjfChefjgCg1g1fjfjeKfjg2fjhgeKhHfjg1hfg1eKfjhfgChlgCfjgCg9g1fChgfifjgAgCeKfChlfjfjfjfCg9gCgAeKgAgAfCfjfjfjfjg1g1gAeKg1fCg#hDgRhDgRhDhbgRhnglhngRhngVhnhDhnhyhRhhhLhDhnhnhKhngVgVhBhhgRhnhcgRgVgQhohhglgVglgRgRgQhngRglgVguguhiglhhhhglglhhhhglglglglgVglgRgRgvgRgRgufkfuglgQgugPguggggggfQhthNglhig4glgvglg4fQg4fQgugQgufkfkggfkfQhifkhjfkfbgvfkgugufkgufkfkfQfkfQfQfQfQfQfQfkfQfkfkgufkgugufugufufufkfufkfkfQfkfQfQfQfQfQfQgufQgugugugugugufQgufQfQfkfQfkfkfQfkfQfQfkfQfkfkgufkgugufkgufkfkfQfkfQfQfQfQfQfQfkfQfkfkhS", +"QtQtQthTeKgAfjgAgAfCgAfjgAeKgAgAfjfjfCgAeKgAfjgAgAfCgAfjgAeKgAgAfjfjfCgAeKgAfjgAgAfCgAfjgAeKgAgAfjfjfCgAeKgAfjgAgAfCgAfjgAeKgAgAfjfjfCgAeKgAfjgAgAfCgAfjgAeKgAgAfjfjfCgAeKgAhehHhHg1hFfjg1fhhGhHhgheg1hFgAhFg9g1g1gCgAhehGfjhFhGg9hgg9hegCgAhlhghgg1gAgAhegBheg1gAg1gAgAeKgAg1hPg1g1heg1gAgAhFgAhlhlfCg1fhgAg1hHhHg2g1hlhefhhPgAfjhffChFfCg1hfhehegChPfjhgfCg1gCg9gAfCg1gCgAhlgAgAfjgAgAg1gBhgfPhbhDgRhDhDhbhRhnhnhRhngVhDhbhDhbhnhbhnglgRhnhbhbhbhDhnhbgRhLglhKhKguhKgQglhogRgVhBglgVgRhphKgQgVgVgugVglglguggfQglglgvhhhhglfQglgQgRgQhUhUgQgRglglhifQgRg4gQglgPgPfkgufkfkfQgvgvgvgPgPfkfkgugvgRguftguftfufQfufQfQfkfQfkfkgufkgugufkgufkfkfufkfufufQfufQfQfkfQfkfkgufkgugufkgufkfkgufkgugufkgufkfkfkfkfkfkfkfkfkfkfkfkfkfkgufkgugufugufufufQfufQfQfkfQfkfkgufkgugufkgufkfkfufkfufufQfufQfQhz", +"QtQthVhWfjfCfjfCfjfCgAfjfCgAfjfCeKfCgAfjfjfCfjfCfjfCgAfjfCgAfjfCeKfCgAfjfjfCfjfCfjfCgAfjfCgAfjfCeKfCgAfjfjfCfjfCfjfCgAfjfCgAfjfCeKfCgAfjfjfCfjfCfjfCgAfjfCgAfjfCeKfCgAfjfjfCg1g1heg8hFhXg8hFfjfjfhg1hHhgheg1heg9g9g1hgg1g9hghlg1fCgAhGg9hggAgAgAheg1hFhegAgAgAfCfCg1g1fjg1g1g1g1gAfCheheg8gAg1g1gAfCgAfjhlfjhlg1g1g1hefjfChghlfjeKgCgAgAheg2g9g9hlfjhPfjfChPfjfCfCfjgCg9gAgCgAfjfjfCgAfhfCgAgAhIhYhnhRhZhbhnhbhyhyh0hnhyhyhKhnhDhRhDhDhDhKhDhDhDhDhhgVgVhnhngRhnhnglhKhbhphhhcglglgVhhgVhLg4hKg4g4glgugVhihNhiglhhgVgRgRgVhDgVgVgVglgVgQgQglgQglglglfQhhhhguglgRgVglgRglgugugug4g4guglggglgufQhNhNgvg4gvgWfQfkfQfQhNfQgugufugufufufQfufQfQgufQgugugugugugugugugugufkgufkfkfQfkfQfQfQfQfQfQgufQgugugugugugugugugugufQgufQfQgufQgugufkgufkfkfQfkfQfQgufQgugufugufufufQfufQfQgufQguguguguguguhS", +"QtQth1fCfCgAgAfjgAgAfCgAgAfCgAgAfjgAfjfCfCgAgAfjgAgAfCgAgAfCgAgAfjgAfjfCfCgAgAfjgAgAfCgAgAfCgAgAfjgAfjfCfCgAgAfjgAgAfCgAgAfCgAgAfjgAfjfCfCgAgAfjgAgAfCgAgAfCgAgAfjgAfjfCfCgAhFhehFhHgAhFhHg1hHhFg9g1fjgAg9hPhFgAhFhFg9g1gAgAg1hghghGhlg1gAhGhHg9hegAgChGg1fChggAhghegAg1g8hehHfCgAgAfChegAg1g1gAg1hHg1g1fCgAhgfig1gAfjhHhHg1gAhFhlhghlgCfjhPg1gAhFg1g2hegAgCgAhPhlhgfjg9fCgAfjg9g1gAgChGgAfjgAfPh2h3h4hDhyhRgVhRhDhDhDhRhYhbhRgRhbhbhbhnhbhnhbhDhhgRhyhbhbhbhbhngRhKhngVhLhhhKhnhchchngRglhNgRhKgRhhhKglhoglhhgVglgVgugRgVgQgVhbglhhglglhhgVglhigugRgRgRgRfkgVfkgvgQgQgPgvgggvggfQfkhtfkgQfkgvfQfQguhig4gugugugufkfkfQfkfkgufkgugufQgufQfQfkfQfkfkgufkgugufkgufkfkfQfkfQfQgufQgugugugugugufkgufkfkfkfkfkfkfQfkfQfQfkfQfkfkfQfkfQfQgufQgugufQgufQfQfkfQfkfkgufkgugufQgufQfQfkfQfkfkgufkguguhz", +"QtQth5gAg1fCgAfCfCgAfjfCgAfCfCgAgAfCgAgAg1fCgAfCfCgAfjfCgAfCfCgAgAfCgAgAg1fCgAfCfCgAfjfCgAfCfCgAgAfCgAgAg1fCgAfCfCgAfjfCgAfCfCgAgAfCgAgAg1fCgAfCfCgAfjfCgAfCfCgAgAfCgAgAg1fChHgAg1gAgAgAhHhHg1hFhHg1g1gAg1heg1gCgChFheg9hFg1g1g1g1gAhHhehXgAhggAgAhFhggCheg8gAhghGfChGgAhHg1gAg1g8hHg1g1gAfCg1g1g1g1g1hHh6fCgAfCfChghlfjg1g9fCgAgAg1gAhghefChgfCgCg1hfg2hgfCg9hehegCgAgAg1fjgAfjfCgAgAfCg1g8gAgIh7hnh0h0h0hbhyhDhDhbhbhbhbhDhnhRhRhnhRhhhhhDhnhDhDhZhDhRgVgRhKhbhbhngRhnhngVhLgVgVgVhKgVhoh2hhhDhDhKhnhKhKgQhnglglguguglglgVgQgVgVglgVhhhhglhhglglgQgQgQglhUglgRgRfQgVglglglguglglg4guhNgRfkgugvgvglgPg4hNfWgug4g4gggufQfQglfQglglguglgugufkgufkfkfkfkfkfkgufkgugufQgufQfQfkfQfkfkfkfkfkfkgufkgugufQgufQfQgufQgugugugugugufkgufkfkgufkgugugugugugufQgufQfQglfQglglguglgugufkgufkfkfkfkfkfkhS", +"Qth8h9gAfjgAgAfjg1fjgAg1fjgAgAfCgAfjfCgAfjgAgAfjg1fjgAg1fjgAgAfCgAfjfCgAfjgAgAfjg1fjgAg1fjgAgAfCgAfjfCgAfjgAgAfjg1fjgAg1fjgAgAfCgAfjfCgAfjgAgAfjg1fjgAg1fjgAgAfCgAfjfCgAfjgAhFhei.hehFi.gAgAhHgAhFg1g1hFg1hFhHg1h6hghPhHhehehFg1g1fjgAhgg9hghGhghFg9g1hFhehHhegChGhlg1gAhggAhehehXgAhei#g1hHgAfChFgAg1heg1gAhHfjg1heg1g1hlhgg1fjhHg1g1g1fjhghehfhXgAhPhHg9g1hgg2hehlgChPfjhPhghghFg9g1gCgAgAg1iaibhYhDh4ichDhRhRhRhDhRhRhDhnhDhnhyhyhnhnhyhKhnhDhDhbibhDhbhDgRhRhDhngVhnhnhngVgRgRhDglgVhKgVhbhngVgVhigVhLhDgRidh2hKhbgVhNgVgVhNguhngRhbhnhDgVgVgVgugVgVgRglgRglglglfQfQhhgugug4guieglgPgvhNfkhig4gugQggglfkfQfkfkgufkgugufQgufQfQgufQguguglguglglguglguguguguguguglguglglguglgugugugugugufQgufQfQfQfQfQfQgufQgugufkgufkfkgufkgugufQgufQfQfkfQfkfkgufkgugufQgufQfQgufQguguglguglglguglguguhS", +"QtifgAfjg1gAfCgAgAgAfCgAfCgAg1gAgAg1gAfjg1gAfCgAgAgAfCgAfCgAg1gAgAg1gAfjg1gAfCgAgAgAfCgAfCgAg1gAgAg1gAfjg1gAfCgAgAgAfCgAfCgAg1gAgAg1gAfjg1gAfCgAgAgAfCgAfCgAg1gAgAg1gAfjg1gAhHhehei.g1hFgAgAg1gAhFh6hHg1i.hHg1hFhFhFg9hGg1hHhFhFhPhFhFheh6g1gAgAgAg1gAhFgAhGhFhHg1hXheg1hFhgg1gAgAgAg1heg8g1h6hHhPg1gAfjhegAg1heg1gAg1hFfCgAhehggAg1g1g9g1hHg2g1g1gAg9hPfCgAheg1gAhFhgfjg1hegChPhPgDfjg1fjgCg1ighbhRhnh7hDh7hDhDhDhbhRhZhnhRhbgVhyh4hDh4hRhRh4hRhRgVhDhDhDhDhZhDhDhDgVhnhDhDh2hDhDh2hKhnhDhLhnhKhKhchnhcglhLgugVhDhKhngRgVglhNglgRgVglgVgRgVgRgRgVhbgVgPglgVgQgRgVhZgRhbhngRglhihNgVg4glglgRguhNhNglgRhbglg4hcglglgVguglglguglgugufkgufkfkgufkgugugugugugufkgufkfkgufkgugugugugugufkgufkfkgufkguguglguglglguglguguguguguguguguguguglguglglguglguguglguglglguglgugufkgufkfkgufkguguguguguguhz", +"Qtihg1gAfCg1fjg1fCgAg1gAgAg1fCfjgAfCg1gAfCg1fjg1fCgAg1gAgAg1fCfjgAfCg1gAfCg1fjg1fCgAg1gAgAg1fCfjgAfCg1gAfCg1fjg1fCgAg1gAgAg1fCfjgAfCg1gAfCg1fjg1fCgAg1gAgAg1fCfjgAfCg1gAfCg1hei.hHiihehehFh6g1hehFgAi.hFhHh6hXhFg1hFi.hFhHi#gAg1hFg1hHhFg1hFhei#gAheg1gAhGhFg1hgg1gAg1heg1i#hgheg1gAg1gAheg1g1g1hPg1g1gAfCg1heg1g1hPheg1gAh6g1fjhgfjg1hHg9g1fjg1g9hgi#g1hehegCfjgAg9gAhgfjg1hfhXgChHhHhPhGg1g9ijh4h3h4hyhyhRhnh7hDh4hDh0hRhyh4hRgVh4hRh4h4hbhDhnhbhnhnh0h4hDh4hDhDhbhDglgRgVgRhnhnhbhDhDhKgRgVglgRhngRidh2hnhnhKgVgVgVhhhchnglikiliminioioinimiliphngVhngVhhglhhglhbhhhbhZgRhbglfkglgugVgRg4hcglgvfkgVfkfkfQfkgugggvfQgugufkgufkfkglfkglglguglgugufQgufQfQglfQglglfQglfQfQgufQguguglguglglfQglfQfQglfQglglfkglfkfkglfkglglguglgugufkgufkfkfQfkfQfQgufQgugufkgufkfkglfkglglguglgugufQgufQfQhS", +"Qtiqfjg1gAgAgAgAg1gAgAgAfCgAgAg1fjgAfjg1gAgAgAgAg1gAgAgAfCgAgAg1fjgAfjg1gAgAgAgAg1gAgAgAfCgAgAg1fjgAfjg1gAgAgAgAg1gAgAgAfCgAgAg1fjgAfjg1gAgAgAgAg1gAgAgAfCgAgAg1fjgAfjg1gAgAirhHi.hFheheg1i.i.i.hFhHhei.hFhFhHgAhHhHisheg9hFhFg1g1gAg1i.hPhFhFheg1g1hHhHg1hegAhehghghgg1g1hehegAhXhghGhFgAgAgAhHhegAhehHgAgAgAhHg1hPheheg8hHgAh6fjhHfjheg1g1g1gAhHhHgAgAfChggAhefjgAgAg1hPhFhFg1hehggAgAfCgAgDithDhYh4h4h4hDhRh0h7h7hDhDh0h4hDh0hRh0h0gVhRh4hDh4hYhyhRhyhygVhBhDhDh4hKh4h4gVhDhRgVh4hKhDhDhnh2hKgVhLgVhKhDhchnhnglhbhhhnhniuiviwixixixixixixixixiwiyizgRgVhnhDgVhngVhngRhNhbhUhnhbglhnhihhgRhhgRglhbglgRhcguglhNg4gRguglglguglgugugugugugufQgufQfQglfQglglguglguguglguglglguglguguguguguguglguglglguglguguglguglglfQglfQfQgufQguguglguglglguglguguglguglglguglgugugugugugufQgufQfQglfQglglhS", +"#RiAgAg1gAfjg1gAg1gAg1gAg1g1fCgAg1g1gAg1gAfjg1gAg1gAg1gAg1g1fCgAg1g1gAg1gAfjg1gAg1gAg1gAg1g1fCgAg1g1gAg1gAfjg1gAg1gAg1gAg1g1fCgAg1g1gAg1gAfjg1gAg1gAg1gAg1g1fCgAg1g1gAg1gAfji.hFh6hFi.hFi.heg1hFi.i.hFi.hFhei.hHi.hFh6hFhFi.g8gAi.g1hFh6hPgAhHhFh6i.hFhHh6h6g9heg1hehGhFhFg9g1hgg1hei#hGhFhFfjhGg1hHhehXhehehXhHhHhGg1heheheg8hHg1h6hgg1gAg9i#hgg1hHgAhHg1g1fChgg1hehPg1hPgAhHhegAhghHheg1g1fCiBhRh7h4hDhDhbhDh2h4ibh2hDhDiCh7h0h4h0hDhRhRhRhRhDhBhRhDhRhRhRhRhnh4hDhDhDhKh4ibh4h4glhDhbgRhKhbhbhnhbgVhDhnhDh4hnhKglhngViDiEixixixixixixixixixixixixiEiFgRhbgVgVhDgVhbgVgVgVhigRgVgRhZgRgVhngVhbglgRhcg4glhcglglgVgvglfkfkgufkguguglguglglguglgugufQgufQfQgufQgugugugugugufkgufkfkgufkguguglguglglguglguguguguguguguguguguglguglglguglguguglguglglfkglfkfkgufkguguglguglglguglgugufQgufQfQhS", +"iGgAfjgAg1g1gAfjg1gAgAgAgAgAg1gAg1gAfjgAg1g1gAfjg1gAgAgAgAgAg1gAg1gAfjgAg1g1gAfjg1gAgAgAgAgAg1gAg1gAfjgAg1g1gAfjg1gAgAgAgAgAg1gAg1gAfjgAg1g1gAfjg1gAgAgAgAgAg1gAg1gAfjgAg1g1i.hFiHhHhFhFhHhGi.hei.heiii.i.h6hFhei.hFhFgAgAhHhFishHhHhehFh6g1g1hghFhGhPhFhehFh6hHi#g1hegAhFi#hGg9g1g1g1gAgAg1hFhGhFhGfjgAhXg1g1hehHhPgAhHhHgAheheg1g1g1gAg1i#gAg9hehgfjhggAg1g1gAg1gAhlgAhXg1gAgAgAgAhPg1g1gAhXigh4hYhRh0h4hYh0h4h4hDh4h0h0hRhDhKh7h0hDh4hRhYhRh4h2gVh0hnhnhDhyhRhRhRgVh0h4hBh4hDhDhDhDhRhRhnhDhDh4hDidhnhKh2hKhnhDhKh4iIiJixixixixixixixixixixixixixixiKiLgVgRgRgVhnhnhDgVgVhnhnhbglhnglglhUhhgVgRhngRhhgRgVgRglguhNguguguglguglglguglguguglguglglguglguguglguglglglglglglglglglglguglguguguguguguglguglglglglglglfQglfQfQgufQguguglguglglguglguguguguguguglguglglguglguguglguglglguglguguhS", +"iMgAg1g1gAfjg1gAg1gAg1g1gAg1g1gAg1gAg1g1gAfjg1gAg1gAg1g1gAg1g1gAg1gAg1g1gAfjg1gAg1gAg1g1gAg1g1gAg1gAg1g1gAfjg1gAg1gAg1g1gAg1g1gAg1gAg1g1gAfjg1gAg1gAg1g1gAg1g1gAg1gAg1g1gAfji.hFi.i.iHi.hFisi.hHi.hei.iihehei.hHi.hHi.g1gAi.g1gAhHi.hFi.hehei.gAi#g1hFishFhFhFheg1hFi#hehHhehFhehFhGg1hFhHhXg1gAhXhFhFg1hGfjhXgAi#gAhHh6hHg1g1hHg1heg1g1g1g1h6hHg1gAhehehgg1hegAhHhgg1i.gAhlhXgAhXgAhHheg1hFg1igiCh7h4hRh7h7hDh7h7hYhDh0hYhRh0h0iNhDiNhDh0h4hDhYhRhRh2hDgVh4hDh4hYhDhRhKhRgVhDgVhDh4hDiOh4gVhDhRgVhKhDhnhKidhKh2hDhLiPiEixixixixixixixixixixixixixixixixiEiQgVhyhygRhygRgVgVgVgVgVgVglgRgRhbgRhnhbgRhbgugVgRgugRg4gRguglglguglguguguguguguglguglglguglguguglguglglguglguguguguguguglguglglhhglhhhhguhhguguglguglglguglguguglguglglguglguguguguguguglguglglguglguguguguguguglguglglguglguguhS", +"iRgAg1gAg1g1gAg1gAg1g1gAgAg1gAheg1gAg1gAg1g1gAg1gAg1g1gAgAg1gAheg1gAg1gAg1g1gAg1gAg1g1gAgAg1gAheg1gAg1gAg1g1gAg1gAg1g1gAgAg1gAheg1gAg1gAg1g1gAg1gAg1g1gAgAg1gAheg1gAg1gAg1g1irh6i.i.i.hehehXhFiih6irh6hFi.i.i.hehFi.h6hFhFhehFheg1hHg1hFhHhFhFhXg1g1g1hHhHh6hPhFhehFheg1g1i#hgh6hGhFhehGhGhFg1hFhHheg1hehehFgAhFhGhFhHheg1i#gAi.h6gAg1hPhHhegAg1hHh6h6gAgAg1hHheg1g1g1hHhHg1hFg1hghehegAgAg1hPhaiNiCiNh4iChYh0hYiSibhYhDh0ichDh0hYhRh0hYhYhDh4h0h0hRhYhRhRhRh0h4h4hDh4hRhRh7hDiNhYh4hYh4h4hDh4hZhDhRhRh0h0hDh3hKgRhKivixixixixixixixixixixixixixixixixixixivhKgVhnhngVhohngVhbgVgVgVgVhnhnhnhhhbhbgRhbgRgRhRguhRhDhbgVglglguglguguhhguhhhhguhhguguguguguguhhguhhhhglhhglglglglglglguglguguglguglglglglglglguglguguglguglglglglglglguglgugugVgugVgVglgVglglguglguguhhguhhhhguhhguguguguguguhS", +"iTg1gAhFgAheg1g1gAhFg1g1hFg1g1g1gAg1gAhFgAheg1g1gAhFg1g1hFg1g1g1gAg1gAhFgAheg1g1gAhFg1g1hFg1g1g1gAg1gAhFgAheg1g1gAhFg1g1hFg1g1g1gAg1gAhFgAheg1g1gAhFg1g1hFg1g1g1gAg1gAhFgAhei.i.hHiUh6i.iUi.i.hXhFiVhFi.hGiii.i.hFiUhei.iii.i.h6gAi.hHhFhFiWhFhFhFisi#i#i.i.hFh6hFi.hFhFhFhXi#h6g1iig1hFirhFhFhFhFg1hghFhehFhehFhGhFhFhFiWhXhXi#hHhHhHhFgAiXhHheg1heg1h6i.g1i#g1hghehghHhHhFhHg1gAhFg1hehFhHhHgIibiYhDiYichDh0h7hDhRh7iSiSh4h4h0h0hDh0hYh7iSh7h4h0hYichYhYhDhYhRgVhnhDh0h4hRhRhRhRgVh0hDhDhDhKh4iOh4hDh4hnh4h4hDh4iZiwixixixixixixixixixixixixixixixixixixiwi0hngVhKhNhKgVglhhhbhnhDhnhKhKgRhRgVgRhbgRhnhbhbhbhnhnguglguguglguglglglglglglglglglglglglglglguglguguhhguhhhhguhhguguglguglglguglguguguguguguglguglglgVglgVgVgugVguguglguglglglglglglguglguguglguglglglglglglglglglglglglglglhS", +"i1g1g1g1gAg1gAg1hFg1gAgAg1gAhegAg1g1g1g1gAg1gAg1hFg1gAgAg1gAhegAg1g1g1g1gAg1gAg1hFg1gAgAg1gAhegAg1g1g1g1gAg1gAg1hFg1gAgAg1gAhegAg1g1g1g1gAg1gAg1hFg1gAgAg1gAhegAg1g1g1g1gAg1hFi.i2i.hFhHi.hHiWhFi.hXhXi.hHh6hHi.iUi.hehFhFhei.hHi.i.heg1hFi.gAh6iWhHi.hHi.g1hegAi.h6h6ishGhHhHhFisi#hHg1g1gAi3hehFhFhFhFg1g1g1g1irhehGhGi.hFhegAhXhXi#h6hPg1hPg1iWhHheg1heg1i.hHg1g1g1hehgg1gAhehFhHgAgAi#gAhehxh0h7hYibhYi4hYhYh4i4hYi5iNhYi5hYiCi4ichYhYhYi6h0h4h7h4h7hDhRhYhYhDhRhRh4h2iNhyhYibhRh7hRhRh0gVhnhDhDh4hZhDh0hDi7hYi8ixixixixixixixixixixixixixixixixixixixixi9hDhbhRhnhLhRhDhRhnhbgVhbgVgRgVgVgVgVhngVhbhbhRhnh4hngRgVguguglguglglguglgugugVgugVgVglgVglglglglglglguglguguglguglglgVglgVgVglgVglglgVglgVgVglgVglglguglguguguguguguglguglglgVglgVgVgugVguguglguglglguglgugugVgugVgVglgVglglhS", +"j.gAg1gAhFhFg1hFg1gAg1hFg1hFgAg1hFgAg1gAhFhFg1hFg1gAg1hFg1hFgAg1hFgAg1gAhFhFg1hFg1gAg1hFg1hFgAg1hFgAg1gAhFhFg1hFg1gAg1hFg1hFgAg1hFgAg1gAhFhFg1hFg1gAg1hFg1hFgAg1hFgAg1gAhFhFisiUi.hFi.iUi.i.hFisi.i.i.i.isj#i.iii.hHh6iUheiUiii.hFhFj#hHiUiUi.hFg1gAg1iWisiWhFi.isgAi.g1hFiWhHirhFg1hFi.i.ishehehFhghFhGishFg1jag1g1g1iri.irhgg1hFhPhXg1iihFg1i.h6hPg1iUheiWhHg1hFhHg1gAhFhFh6hFhegAgAhFhXhFhggIi5jbiNjbh0iNh0i6i5h7i5hDhRh7hKhRhRhYhDhYh4hDh4h0h0hYh0iSh4h0h4hYhYh0ibh0h0hDh0hDhDh0hDhRhDhRhnhKhRhYhDhYhYh4h0h4h0jcixixixixixixixixixixixixixixixixixixixixjdhKhDhDgRh2hbhKhDhNhNhngVhbhbhDhnibhRgVhRgVhnguhngRgRhnguglglguglgugugVgugVgVglgVglglguglgugugVgugVgVglgVglglgVglgVgVgugVguguguguguguglguglglguglguguglguglglgVglgVgVglgVglglguglguguglguglglguglgugugVgugVgVglgVglglguglguguhS", +"jegAhFg1g1g1g1gAg1hHg1g1gAg1g1g1hFgAhFg1g1g1g1gAg1hHg1g1gAg1g1g1hFgAhFg1g1g1g1gAg1hHg1g1gAg1g1g1hFgAhFg1g1g1g1gAg1hHg1g1gAg1g1g1hFgAhFg1g1g1g1gAg1hHg1g1gAg1g1g1hFgAhFg1g1g1ish6hFisishFhehXi.i.i.hHjah6hFi.i.heiVi.ish6hGh6hFhehFheisheh6i.i.hHi.hHi.g1hHi#hHisiihHisi.i.i.hFhHhFirhFhehFhFhFi.isg1iig1i#hFi.hGg1irhXhHheg1i#hFirhGhFg1g1hXhXg1g1h6g1h6gAg1g1heiWg1i.g1h6hei.gAhehFhHheg1gAg1hmjfhYi4h7hYh0hRhYh0iSh4hYiChYich0hYi5hRiNi5i4hYiYhYh0ibhYhRh0hYh4iCjgh4iNhYhDhYhRh4hYh4hYhYh4i6hDhRh7hnhYhDhDh4h4h4jhixixixixixixixixixixixixixixixixixixixixinhnhnh7hDh0hRhDhDh0h7gVhRhnhnhnhnhbhbhRhnhRhDhbgVhnhbhDglgVgVglgVglglgRglgRgRglgRglglglglglglgVglgVgVgugVguguglguglglglglglglglglglglgVglgVgVgVgVgVgVglgVglglglglglglgVglgVgVglgVglglgVglgVgVglgVglglgRglgRgRglgRglglglglglglhS", +"jig1g1hFgAhFhHhFhFg1hFg1hFgAhFhFg1g1g1hFgAhFhHhFhFg1hFg1hFgAhFhFg1g1g1hFgAhFhHhFhFg1hFg1hFgAhFhFg1g1g1hFgAhFhHhFhFg1hFg1hFgAhFhFg1g1g1hFgAhFhHhFhFg1hFg1hFgAhFhFg1g1g1hFgAhFisisisi.iihFiUhFisiUi.i.i.iUhFi.iriUjai.i.i.iihHiUi.i.hei.irhFhFh6i.i.i.iUi.hFgAiUishFishFishFi.hHhFhFh6hFi.irhFiriri#i.i#iWgAhFi.iihFhFirg1jag1iihFhei.hehFhFhFjjirhFhXiihHi.hHiWhFi.heg1iWhehFh6i.i.g1hFhehFhghFhxh0d2i4hYi5hYi4jbi4iNh0jfi6hYi4hYh4hYh4h0h0hYhYhYhYiCiCh4h0i6iShYhYhDiNhDhDh0hYhYhYhRh0hnh4hYh4hYh7hDh7h4h0hRhngVhDioixixixixixixixixixixixixixixixixixixixixioh2hDhnhDgVhnhnhDhDhDhbhnhngVhRhnhRhngRhRgRgRhngVgVgVhnglglglgVglgVgVglgVglglguglguguglguglglglglglglgVglgVgVglgVglglgVglgVgVglgVglglglglglglglglglglglglglglgVglgVgVgugVguguglguglglglglglglgVglgVgVglgVglglguglguguglguglglhS", +"jkg1hFg1hHg1g1gAg1g1hFg1g1hFg1g1gAg1hFg1hHg1g1gAg1g1hFg1g1hFg1g1gAg1hFg1hHg1g1gAg1g1hFg1g1hFg1g1gAg1hFg1hHg1g1gAg1g1hFg1g1hFg1g1gAg1hFg1hHg1g1gAg1g1hFg1g1hFg1g1gAg1hFg1hHg1i.iWi.i.iih6i.iUhFhFhFhXiUi.i.i.i.i.hFhFisi.i.isi.h6hHisi.i.irhehehFi.i.iUi.h6iUi.i.hFh6hFhFisi.i.hFi.i.iUi#i#hFi.hFhPhFiri#ish6h6hehXi#irhFhehFg1g1g1hehFhXisi.i.hGg1g1iihXg1hehHhHgAg1hFhHhFhehehHh6g1iWg1g1ishehIjfh7jfhRibd2h7jbjbi4hYhYjbiNhYjfjfhYi5i4iNiNiNi6i5i4i4h0hYhYhYi6i6hRhYhYh4iSh4hYiShYibh7hYhRhRhnhRh4hDhRh4h7h0hRhRiwixixixixixixixixixixixixixixixixixixixixiwh7h7h0h0hDhRhRhRhRhRhDhDh2h2hDhnhRh7hnhRhohbhZhbhKhKibgRgVgVgugVguguglguglglgVglgVgVgVgVgVgVgVgVgVgVgugVgugugRgugRgRglgRglglgVglgVgVgVgVgVgVgVgVgVgVglgVglglgVglgVgVglgVglglgRglgRgRgVgRgVgVgugVguguglguglglgVglgVgVgVgVgVgVhS", +"g1hFg1g1g1hFhFg1hFhFg1hFhFg1hFhFg1hFg1g1g1hFhFg1hFhFg1hFhFg1hFhFg1hFg1g1g1hFhFg1hFhFg1hFhFg1hFhFg1hFg1g1g1hFhFg1hFhFg1hFhFg1hFhFg1hFg1g1g1hFhFg1hFhFg1hFhFg1hFhFg1hFg1g1g1hFiUjli2iUi.iUisisisiUi.iUhFi.hFi.i2i.iUiih6iUjai.iUisi.iih6ish6jaiUhFirhFheiii.i.i.iUg1hFg1i.g1isiWhHisisi#i.ishFhFiWi.iri.hFheiri.g1iWhFg1iri.i.hFhFhFjag1hXg1i.iri#hFishFjahFhXhFiih6i.hHg1h6hFiWg1iWiWg1i.hXhei.fPi4d2iYhYjfi5hYiNhYi5jfi4iYh0i4h0iShYi4iChYi4h4i4hYh0i6iSi4h4hYh4h0hYhYh7hYhRh0hYh4h4h0h0h4hYhYhYhRh4hRh4h4h4hRhYh4jmixixixixixixixixixixixixixixixixixixixixjmhnhDhDhDidhDh4hDhDhKhnhDhnhDhbh4hbhnhRhnhRhLhnhDhnhbhbglglglgVglgVgVglgVglglglglglglgVglgVgVglgVglglgRglgRgRgVgRgVgVgVgVgVgVglgVglglglglglglgRglgRgRglgRglglglglglglgVglgVgVglgVglglglglglglgVglgVgVglgVglglglglglglgVglgVgVhS", +"hFg1hFhFi.g1hFg1g1hFhHg1hFg1g1hFhFg1hFhFi.g1hFg1g1hFhHg1hFg1g1hFhFg1hFhFi.g1hFg1g1hFhHg1hFg1g1hFhFg1hFhFi.g1hFg1g1hFhHg1hFg1g1hFhFg1hFhFi.g1hFg1g1hFhHg1hFg1g1hFhFg1hFhFi.g1ishHhHiUisi.i2iViiisisishFjaiUi.jahXhXiii.h6iUi.i.iUiUi.i.j#i.isi.i.heiUhFheishFi.i.i.i.i.iUi2h6hFh6h6iWishFisi.i#hFhFhHisiWiihFhFhFhFisjlh6iWg1hFhFhFhFirhFi.hXiri#i#irirhFhFg1hXirhFhXi.hFi.g1i.g1g1iWhFg1iWg1h6g#iNi4jfi4iYi4i4i4hYi4i4i4h4i5jbi4i4hYi5d2d2hYi6iYiYhYi5i5iNh0i6hYhYi4hYhYhYh7i6h7h7h4hYh0h4i4hRd2i4iNhYhYhRh4hYh4h4jnixixixixixixixixixixixixixixixixixixixixjni7i7hDi7ibibh4ibh0h0hRhRh7hRhDhnhDhDhDh2h7h7hRhRhRhRhbgVglglhnglhnhngVhngVgVglgVglglglglglglgVglgVgVglgVglglglglglglglglglglgVglgVgVgRgVgRgRgVgRgVgVgVgVgVgVglgVglglgVglgVgVgVgVgVgVglgVglglhnglhnhngVhngVgVglgVglglglglglglhS", +"hFg1g1hFhHhFhFg1hFg1hFi.g1hFhHg1hFg1g1hFhHhFhFg1hFg1hFi.g1hFhHg1hFg1g1hFhHhFhFg1hFg1hFi.g1hFhHg1hFg1g1hFhHhFhFg1hFg1hFi.g1hFhHg1hFg1g1hFhHhFhFg1hFg1hFi.g1hFhHg1hFg1g1hFhHhFiUiiisisiii.i.iUj#i.iUi.i.iiisiUiUi.iUiiisi.i.i.ish6iUhFi.i.isisiii.iii.jaiUi.hFirheiih6i.iUh6iUhFg1ishFisj#hFiWishXhFg1i.ish6hFirhFiri#irish6ishFg1iihXhFirhFhFjahFhFhXiii.hFirhHhFjag1hXiihHhei.h6i.i.hFi.heiWhFjoiNiYd2.q.qiNd2d2d2iNiNi5jfi4hYi4jbjbh0h0jbiNiSi6i5h4i4ichYi5h0i5i6hYi4iChYh4h0h0h0hYhYh7h0h4hYh4h0i6hDiNhYiYhYi4i4jpixixixixixixixixixixixixixixixixixixixixjqhDhbh2hnhnhRhYhDhRh0hbh0h2hnh7hDhRhRhDhDhbh2hbgVgVgVhnglgVgVglgVglglgVglgVgVhngVhnhngRhngRgRgVgRgVgVhngVhnhngVhngVgVgVgVgVgVglgVglglglglglglgVglgVgVglgVglglgVglgVgVglgVglglglglglglgVglgVgVglgVglglgVglgVgVhngVhnhngRhngRgRhS", +"hFi.hFg1i.hFg1hFhFhHg1hFg1hFi.hFhFi.hFg1i.hFg1hFhFhHg1hFg1hFi.hFhFi.hFg1i.hFg1hFhFhHg1hFg1hFi.hFhFi.hFg1i.hFg1hFhFhHg1hFg1hFi.hFhFi.hFg1i.hFg1hFhFhHg1hFg1hFi.hFhFi.hFg1i.hFi.iUiUisi.iUhHhFiUiUiUi2i.hXjrisi.i.i.isi.isi.iUiHi.isi2iUhFi2jai.i.i.isi.jai.i.i2iUiUisiri.iii.i.jaiWisg1iUh6hFi2iWhFisisi.j#hFh6isishHhFirhFiriUi.i.i.iWg1ishHhFirirhHhHiig1hFisirhFi.hFg1iihFhXiii.g1h6iWg1hFiifPiNi6i4d2d2iNd2jfjfiYd2h0i4jfjbjfiNjfiNiYhYiYiYi5i5hYi5jbhYi5hYhYhYiYiNiYi4i5jsi4iYi4hYiSi4hYh0h0iNh4hYh4hRi6hRhYhYjtiwixixixixixixixixixixixixixixixixixixiwjuhYhYh0h0h0hYh0h0hRhRiSh7iOh4h0hDh7i7h0hRh0iChRh4hRhRhYgVhnhngVhngVgVglgVglglgRglgRgRgVgRgVgVglgVglglgRglgRgRgVgRgVgVglgVglglgVglgVgVhngVhnhngVhngVgVgRgVgRgRgVgRgVgVhngVhnhngVhngVgVhngVhnhngVhngVgVglgVglglgRglgRgRgVgRgVgVhS", +"hFg1i.hFg1i.g1i.g1hFi.hFhHi.g1hHhFg1i.hFg1i.g1i.g1hFi.hFhHi.g1hHhFg1i.hFg1i.g1i.g1hFi.hFhHi.g1hHhFg1i.hFg1i.g1i.g1hFi.hFhHi.g1hHhFg1i.hFg1i.g1i.g1hFi.hFhHi.g1hHhFg1i.hFg1i.i.jvi.jvi2iUiUj#ishHiUi.i2jviVi2iij#i.iUiVi.isiUi.isiUi.isiUi.iHi.i2hXiUi.i.i.i.i.i.iUhei2ishFisi.i.isiUiihFhHi.i.jliWisi.isi.ish6i.i.iWj#hFiri.hFiri.i.i.iWg1i.hFi.hFhFisirhFishXhHiri.i.hFg1i.hXishXiii.i.h6iVhehId2hYjfhYhYjfh0iNjfi4jf.qd2i4iYi4h0jwh0i4iYhYhYjbh0iYhYjfd2i5i4iNhYhYhYh0h0hYh4i6jbhYi4iYhYhYhYhRiSi4hYi4i4hYhYhYh7iSjxixixixixixixixixixixixixixixixixixixjyh0hRh4hYh4h0h0h2hYhDhni4hniNh0h7h4h0h7hni7hDhDhDhbhDhZhDgRgVgVglgVglglhnglhnhngVhngVgVgRgVgRgRhngRhnhnglhnglglgVglgVgVhngVhnhnglhnglglhnglhnhnglhnglglhnglhnhngVhngVgVglgVglglgRglgRgRgVgRgVgVglgVglglhnglhnhngVhngVgVgRgVgRgRjz", +"hHhFhHi.hFhHhFhHi.hFhHhFg1hFhFi.hHhFhHi.hFhHhFhHi.hFhHhFg1hFhFi.hHhFhHi.hFhHhFhHi.hFhHhFg1hFhFi.hHhFhHi.hFhHhFhHi.hFhHhFg1hFhFi.hHhFhHi.hFhHhFhHi.hFhHhFg1hFhFi.hHhFhHi.hFhHjAisjvjvisiUi.isi2j#hFish6i2i.j#i2isj#isiUiihHiUiUiUisjai.i2isisiUh6iUjaisi.hXisisi.i.i.isi2jaiUirhFj#i.i.iih6iUiUi.jlhFjliihFisish6ishFjlisi#iWhFiHirhHiri.hFhFiWjliHiiirirhFhFhFi.hFhFh6isi.hHhFiri.hFi.iii#heheg#jfiYjfi4.qi6d2i5hYiNd2jfjfi4i5d2d2jfjfiNiNi4hY.qi4jbjfi6i6iNhYi5i6jfiYhYiYiNiYi5i5jfhYi4jfhYi4i6h7i6h7hYiNiShYi4iSd2jBjCixixixixixixixixixixixixixixixixjCjDh0h0hYh0hYhYhRh0h0hYh0h4hYibh7hYiSh7h0hYh0hYh0hLhRh7hRh0gVhnhngVhngVgVgVgVgVgVgRgVgRgRhngRhnhngVhngVgVhngVhnhngRhngRgRgVgRgVgVhngVhnhngVhngVgVhngVhnhngRhngRgRgVgRgVgVhngVhnhngVhngVgVhngVhnhngVhngVgVgVgVgVgVgRgVgRgRhngRhnhnhS", +"i.i.hFi.hFhHi.hFi.hHi.hFi.i.g1hFi.i.hFi.hFhHi.hFi.hHi.hFi.i.g1hFi.i.hFi.hFhHi.hFi.hHi.hFi.i.g1hFi.i.hFi.hFhHi.hFi.hHi.hFi.i.g1hFi.i.hFi.hFhHi.hFi.hHi.hFi.i.g1hFi.i.hFi.hFhHi2jAi2jvisjvi2i2i.iUjrj#iij#iii.iVi2iUiUi2iUi2iii.jai2iUisi2i.isisiiisiriHiUi2i2i.i2isiiiUiijairiUhFi2iUi.iiiiiih6iUi2i2iUi.isjli.isi.isisi.iVj#g1iii.iHirisiririsiUi.i.i.irisishejai.isiihFiii.i.isi.hFi.jaiiiig1iajfiNjfiNjfi4jfhYiNiNhYiNiN.qiNd2iYd2jfd2hYiNi6i6iNi4i5hYhYjfi4iYiYd2i4i6jbi4i4iYhYhDiYiSiShYhYhYhYd2h7i4hYiNiYhYiNh4hYjEjFixixixixixixixixixixixixixixjFjGi4hYh4h0h4hYiShDhRhDh7hYh4hYh7h7hYi7hYi4ibiCh4h4ich4h0iNhDhnglglgVglgVgVhngVhnhngVhngVgVglgVglglgVglgVgVgVgVgVgVglgVglglgVglgVgVhngVhnhngRhngRgRgVgRgVgVgVgVgVgVhngVhnhngRhngRgRhngRhnhnglhnglglgVglgVgVhngVhnhngVhngVgVglgVglglhS", +"i.hFhHhFi.i.hFhHi.hFhHhFhFhFi.hHi.hFhHhFi.i.hFhHi.hFhHhFhFhFi.hHi.hFhHhFi.i.hFhHi.hFhHhFhFhFi.hHi.hFhHhFi.i.hFhHi.hFhHhFhFhFi.hHi.hFhHhFi.i.hFhHi.hFhHhFhFhFi.hHi.hFhHhFi.i.iUisi2iUisiUiUisjvisjviUisiUi2j#isisi2iUjliUiUi.i2i.j#hFisiii2isiUisiUisisi.i.iUi2isi2iUisisiihFiiiri2iUishFhFiriii.i.i.isiUi.i2hFhHiiiWhHiihHisiVjli.i.hFhFisi.iriri.i.hHisishFisiri.hFhHhFiWi.hFi.i.irh6irirhFiiha.q.q.qd2iY.q.q.qd2i5.qi6i5d2iNd2hY.qiSjfd2jfd2iYiYhYiYd2d2iNiShYi4i4iNiYd2jfjfi4hYhYiYi5i5iYi6i4jfi6i4iShYi4hYiYi4hYiYhYjHjIixixixixixixixixixixixixjIjJh7hYhYiSiSi4h4hYi4h0iYh0i4iSi4ibibh0ibh7i7iNhYi4h0hYich4h7h7gVgVgVhngVhnhngRhngRgRhngRhnhngVhngVgVhngVhnhnhnhnhnhnhnhnhnhngVhngVgVgRgVgRgRhngRhnhnhnhnhnhngRhngRgRgVgRgVgVhngVhnhngVhngVgVgVgVgVgVhngVhnhngRhngRgRhngRhnhngVhngVgVjz", +"i.hFi.i.hFhHi.hFi.hFi.i.hFi.hFhFi.hFi.i.hFhHi.hFi.hFi.i.hFi.hFhFi.hFi.i.hFhHi.hFi.hFi.i.hFi.hFhFi.hFi.i.hFhHi.hFi.hFi.i.hFi.hFhFi.hFi.i.hFhHi.hFi.hFi.i.hFi.hFhFi.hFi.i.hFhHjvjKjvjvjLjMiUjMiUiUjviUjvjLiUisi2isj#isi2iUiUi2iUiUj#i2j#i2hFisi2jai2iUisiHiii2iii.iUiUi2isi.j#iij#iii.i2i2iUi2hFhFj#iii.i2i.isj#iUi.hFiiiHiWisishHisiUi.iii.hFishFishFi.iUisiiisi.iHiri.j#irhHiUhFhFhFiiisiri.isiajfi6iYi6i5i4iNd2hYiYjbd2d2d2d2jfjfjfjfi6jfd2d2jfd2d2iYjNd2i4i6jOiKixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixiKjPhRi7ibhDibibi4gRhnhngRhngRgRgVgRgVgVhngVhnhngRhngRgRhngRhnhngVhngVgVgRgVgRgRhngRhnhnhDhnhDhDgVhDgVgVhngVhnhngRhngRgRhngRhnhngVhngVgVgRgVgRgRhngRhnhngRhngRgRgVgRgVgVhngVhnhngRhngRgRjz", +"i.hHi.hFi.i.hFi.hFi.i.hHhFi.hHisi.hHi.hFi.i.hFi.hFi.i.hHhFi.hHisi.hHi.hFi.i.hFi.hFi.i.hHhFi.hHisi.hHi.hFi.i.hFi.hFi.i.hHhFi.hHisi.hHi.hFi.i.hFi.hFi.i.hHhFi.hHisi.hHi.hFi.i.jMi2jvjvjvisiUi2jQjvi2iUi2jvjvi2jvi2iUj#j#isisi.isiUiUj#iUisj#j#iUi.jaiUiUisiUi.isi2i.isiVjaiUi2isi2iiiii.i.i.i.isisi2isi.i.i.iiiUi.iUi.i.hHhFj#hHi2iiisi.isisiViWiViiiHiHiririshHjQi.hFi.iiiHishFisjaisi.hHhFhFi.ia.qjf.qjfjf.qd2jfiYiNiYjfjf.qjfd2jf.qjfiYiYiNi4jfd2d2d2d2d2.qiYiKixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixiKh0h0i6hRi5hYhYhDhnhngVhngVgVhDgVhDhDgRhDgRgRgVgRgVgVhDgVhDhDhnhDhnhnhnhnhnhngVhngVgVhngVhnhnhnhnhnhngVhngVgVhngVhnhnhnhnhnhngVhngVgVhDgVhDhDhnhDhnhngVhngVgVhDgVhDhDgRhDgRgRgVgRgVgVjR", +"hFishFishFisi.i.hFishFi.isi.i.i.hFishFishFisi.i.hFishFi.isi.i.i.hFishFishFisi.i.hFishFi.isi.i.i.hFishFishFisi.i.hFishFi.isi.i.i.hFishFishFisi.i.hFishFi.isi.i.i.hFishFishFisjvi2i2jKi2jvjvi2i2i2jAjvjAjvjKjvjvi2iUjSiUi2jTiHi2i.iUjviUiUisjvj#i2isjvhFiUiUi2isi2i2jQiiisjai2iUi2iUjKi.iiiUiUiUi.iUi2iri2isiUi.iii.i2i.i.isi2j#i.iWiUisisisisi.iVi.iUisiHi.isisisiUisiUisiiisiHishFisirisjQiiiijUiY.2iN.2dGjbdGi6jf.qi5iYiYjf.qjfi6i5i6i5jfiYiN.qiYd2.qd2d2i6jfixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixi4h0hDh0hYiNiShngRgRhngRhnhnhnhnhnhnhnhnhnhnhnhnhnhngVhngVgVhDgVhDhDgRhDgRgRhngRhnhngVhngVgVgVgVgVgVhngVhnhnhDhnhDhDgRhDgRgRhngRhnhnhnhnhnhngRhngRgRhngRhnhnhnhnhnhnhnhnhnhnhnhnhnhnhS", +"i.i.i.i.hFi.hFhFisi.hFhFi.hFishHi.i.i.i.hFi.hFhFisi.hFhFi.hFishHi.i.i.i.hFi.hFhFisi.hFhFi.hFishHi.i.i.i.hFi.hFhFisi.hFhFi.hFishHi.i.i.i.hFi.hFhFisi.hFhFi.hFishHi.i.i.i.hFi.jQiUjVjvi2i2i2i2jvjKi2i2i2jMiUi2iUiUjvjviUiUi2iUjTisiHj#i.iUiUi2iUiUjvj#iWisiVi.iUiri2i2i.iHisishFi.i2iUiUiUi.i.ish6iUiii.i2iUi2iUhFiUi.iii.iUhFi2isi2j#iViHiWi.hFisj#i.i.i.j#i.iUhFisisisiUi.i.i.hFisiHirhFisirishad2jf.qiYiY.qiYiYhYdGd2.2jWjf.qjX.q.2.qd2jf.qjfd2iYiN.qiNjfjfd2ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixi6d2d2d2i6i6h0hDgVgVhngVhnhngVhngVgVhDgVhDhDhnhDhnhnhnhnhnhngVhngVgVhngVhnhnhDhnhDhDhnhDhnhnhDhnhDhDhnhDhnhngVhngVgVgVgVgVgVhDgVhDhDhDhDhDhDgVhDgVgVhngVhnhngVhngVgVhDgVhDhDhnhDhnhnjz", +"iUhFi.hFisisi.isi.hFi.isi.ishFi.iUhFi.hFisisi.isi.hFi.isi.ishFi.iUhFi.hFisisi.isi.hFi.isi.ishFi.iUhFi.hFisisi.isi.hFi.isi.ishFi.iUhFi.hFisisi.isi.hFi.isi.ishFi.iUhFi.hFisisjvjLjLjQi2jSjMjvjKiUjvjvi2jvjvjSjvjYi2iUi2jvi2jvjvjLjYisi2isi2jvi2iUiUiUiUjTiHjTiUi2i2jai2jajQjQisi2iHi.iVi2i2i2i2isiUi.iUi.iUi2i2isi.hFi.iUiiiUiUi2i.hFj#hFjTiUi.jvi.hFisisi2j#iWiijviiishFisishFjQisi.i.i2isishFha.qjfjZ.qd2.2d2jf.qjfjfiYi6i5iYi6i5jXjfiY.qhY.qi6i6jfi5iY.qjfi4ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixiYh0hYi4h7h7i4gVhnhngVhngVgVhDgVhDhDhnhDhnhngVhngVgVhDgVhDhDhnhDhnhnhDhnhDhDgVhDgVgVgVgVgVgVhngVhnhngVhngVgVhngVhnhnhDhnhDhDhnhDhnhngVhngVgVhngVhnhngVhngVgVhDgVhDhDhnhDhnhngVhngVgVjR", +"ishFisisi.i.i.hFi.isi.ishFi.i.i.ishFisisi.i.i.hFi.isi.ishFi.i.i.ishFisisi.i.i.hFi.isi.ishFi.i.i.ishFisisi.i.i.hFi.isi.ishFi.i.i.ishFisisi.i.i.hFi.isi.ishFi.i.i.ishFisisi.i.jMi2jvjLi2jLiUi2i2jMjvi2jKjvjKi2jviUjLjYjMjviUi2isi2jviUjTjvj#iHi2hFi2iUi2iUi2iUiViHishFjvj#iUi2isiUisi2i2j#iUiVi2i2i2iUjQisisiUiUi.i2i2i2isisi.j#iiiUi.i2i.i2i2i2i.iii2isiUisisisj#j#i.iUisi.isisiri.jQiijQiWi.i.ia.qdGdGjfjfd2i6.q.q.qiY.qdGjfdGd2jfdG.qdG.2.2.2.2d2d2d2jf.2jf.2ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixd2hYhYhYiYi4i5hnhRhRhnhRhnhnhDhnhDhDhnhDhnhnhnhnhnhnhDhnhDhDgVhDgVgVhngVhnhnhDhnhDhDhnhDhnhnhDhnhDhDhDhDhDhDhDhDhDhDhnhDhnhnhDhnhDhDhnhDhnhnhRhnhRhRhnhRhnhnhDhnhDhDhnhDhnhnhnhnhnhnjR", +"i.isi.ishFisisisisi.iUi.ishFisiUi.isi.ishFisisisisi.iUi.ishFisiUi.isi.ishFisisisisi.iUi.ishFisiUi.isi.ishFisisisisi.iUi.ishFisiUi.isi.ishFisisisisi.iUi.ishFisiUi.isi.ishFisj0j0j0i2jvjvjLjKjvjYjvjYiUjYi2jvjvjKjvi2jYi2jYiUjvjvjvjvjvjviUjvjTjTjTiHjMi2iUiUjTiUj#jLj#i2iij#iUiUiUi.iUiUi2i.jvi2i2i2i2jQi.isisisisiUj1i2i2iUjviUiiiUi.i2iUi.i2i2isj#jviii2iWisiUi.jvi.iUisjTiUiUisisiUi.jQi.i2hId2.q.2jf.q.qdGjfjfdGd2j2j2jXaeiYiYd2iYd2d2jfjXjXjX.q.qd2d2jfjXixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixh7hYi4h0h0h0hYhnhnhnhRhnhRhRhnhRhnhngVhngVgVhngVhnhnhnhnhnhnhDhnhDhDhnhDhnhnhRhnhRhRhnhRhnhnhnhnhnhnhnhnhnhnhnhnhnhnhDhnhDhDgVhDgVgVhngVhnhnhnhnhnhnhRhnhRhRhnhRhnhngVhngVgVhngVhnhnjz", +"hFi.iUi.isi.i.hFisi.isi.i.iUi.i.hFi.iUi.isi.i.hFisi.isi.i.iUi.i.hFi.iUi.isi.i.hFisi.isi.i.iUi.i.hFi.iUi.isi.i.hFisi.isi.i.iUi.i.hFi.iUi.isi.i.hFisi.isi.i.iUi.i.hFi.iUi.isi.jLjvj0i2jYi2iUjYjvjvjQi2jYjvjYi2i2jvjKisjvi2jvj3i2i2iUi2jvjvjvi2i2iUjTj#iUj#j#jYi2iUisi2iUj#i2iHiHjaiUi2jvi2iUjKisisisiHi2iUi2i.i.i2jTisjKiii.i2i2i2iUisiUi.jKiiiiiUi2i2jvi.i.iUiii.hFisiUisjvj#j#iiiUi.isish6jai.hadGjf.2i6jf.qjfjWdGjfjW.qiYjWjX.q.2dGjfdGjWdGjW.q.q.q.UiYiY.2d2ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixh0i4d2i4iShYhYhDhDhDgVhDgVgVhDgVhDhDhRhDhRhRhRhRhRhRhDhRhDhDgVhDgVgVhDgVhDhDhnhDhnhnhDhnhDhDhDhDhDhDhRhDhRhRhnhRhnhnhDhnhDhDhnhDhnhnhDhnhDhDhDhDhDhDgVhDgVgVhDgVhDhDhRhDhRhRhRhRhRhRjR", +"i.isisi.i.isiUisiUisi.isiUi.isisi.isisi.i.isiUisiUisi.isiUi.isisi.isisi.i.isiUisiUisi.isiUi.isisi.isisi.i.isiUisiUisi.isiUi.isisi.isisi.i.isiUisiUisi.isiUi.isisi.isisi.i.isjYjLj4jLjvj0jYjMj0jYjLjLjvi2jLjvjvjvjYjvi2jvjKi2jvjvjMjYjvjKi2isjYisjYjvi2jvjviHjTjviUiUiUjviUi2jYjTjYi2i.iUjYisiUjQi2i2jQi2iVi2i2iUjKisi.iUjKiUisi2i2i2i2i2i.jQjAjTi2iUi.isi2i2isisiiiUisishHisi2isjAjTj#i2iUisj#iaiYjfjfjZjf.U.qaej5j2j2jfaed2j5d2jXjX.qjfjXdGi4dGjWi6dGiNjf.qiNixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixi5iNjfjfi5i6i4hDhnhnhDhnhDhDhnhDhnhnhnhnhnhnhDhnhDhDhnhDhnhnhDhnhDhDhRhDhRhRhRhRhRhRhnhRhnhnhnhnhnhnhDhnhDhDgVhDgVgVhngVhnhnhRhnhRhRhDhRhDhDhnhDhnhnhDhnhDhDhnhDhnhnhnhnhnhnhDhnhDhDjz", +"iUi.isiUi2i.isi.i.iUisi.isi.i.iUiUi.isiUi2i.isi.i.iUisi.isi.i.iUiUi.isiUi2i.isi.i.iUisi.isi.i.iUiUi.isiUi2i.isi.i.iUisi.isi.i.iUiUi.isiUi2i.isi.i.iUisi.isi.i.iUiUi.isiUi2i.j0jrjrjYjvjvjvjLjYj3j3jMjvjYjYi2jvi2i2jYjvi2jYi2jvjYjYi2jvjVjLjMjvi2isjYjvi2jvi2i2jvjviHisjYj4i2iUi2i2jYi2j#i2iVi2iUiUisi2jKjvi2j#iHiVi2i2i2jQi.iiiUiUiUisi2i2isisisisjAiUi.i2i2i.i2iUj#isjAi2isisisisjvjTiVisi.i2gIdG.qj2dGdGjwjf.q.q.q.q.qjf.qi4jfdGjW.2.2.2j5.qaeaedGdGaedGd2jfixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixhYhYjfhYj5j5i4hRhnhnh0hnh0h0hRh0hRhRhnhRhnhnhnhnhnhnhRhnhRhRhDhRhDhDhnhDhnhnhnhnhnhnhDhnhDhDhDhDhDhDhRhDhRhRhRhRhRhRhnhRhnhnhDhnhDhDhRhDhRhRhnhRhnhnh0hnh0h0hRh0hRhRhnhRhnhnhnhnhnhnj6", +"isisi.isisisiUisiUi.iUi2isiUisi.isisi.isisisiUisiUi.iUi2isiUisi.isisi.isisisiUisiUi.iUi2isiUisi.isisi.isisisiUisiUi.iUi2isiUisi.isisi.isisisiUisiUi.iUi2isiUisi.isisi.isisisj7j0jvj4j4jLjvjYjLjvj0j0jMjYjKjYj4jLj4jvjYj4jYjvjYi2jvjKi2jvjvj0jYjYjvjvjAi2jvjYjvi2jvjTiHi2jTjviUiUiUi2i2i2jvjvjvi.iUiUisi2iUjQjMi2jMiUjYi2i2i2jQi.iUjQiUjKisiUiUisiUi2jvjviUjAi2iii2i2i2jTisisjYi2iUisiUi2jTi2jvg#dG.2.qj2e0.qjfjfjfjWjZ.2.q.qjf.qjfjfjWd2iYjWjX.qjfi4dGjfd2.Ud2ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixi4iYd2iShYhYhYhnhDhDhnhDhnhnhDhnhDhDh0hDh0h0hDh0hDhDhDhDhDhDhRhDhRhRhDhRhDhDhDhDhDhDhDhDhDhDhnhDhnhnhDhnhDhDhnhDhnhnhRhnhRhRhnhRhnhnhnhnhnhnhDhnhDhDhnhDhnhnhDhnhDhDh0hDh0h0hDh0hDhDjR", +"isi2isi.i2isi.isiUisi.isi.isi2isisi2isi.i2isi.isiUisi.isi.isi2isisi2isi.i2isi.isiUisi.isi.isi2isisi2isi.i2isi.isiUisi.isi.isi2isisi2isi.i2isi.isiUisi.isi.isi2isisi2isi.i2isjvjvjYjvjvj4i2jvj8jvjTj8jvi2j9j0jMjvjvj#jLj0i2jvj4jvjKj4jYisj4jKi2jvjYj0i2jvi2jvjYjvjvjYjvi2jYjTiHisiUjviUjviUiUj4jTjTk.i2i2jriUi.i2iUjQiHjviHj#jri2iUjvjQisjMisiUiUiUi2i.jvi.isjMiUisi2i2i2i2i2j#jTjvisiHisisi.jYha.qjfj2.U.q.U.qj2j2.qj5d2.q.2.q.q.2j2dG.Uk#dG.2.2dGjW.qdGjfjfjWixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixj2.qjf.qd2d2jfhDhRhRhDhRhDhDhnhDhnhnhDhnhDhDhDhDhDhDhnhDhnhnhDhnhDhDhRhDhRhRhnhRhnhnhDhnhDhDh0hDh0h0hDh0hDhDhDhDhDhDhDhDhDhDh0hDh0h0hDh0hDhDhRhDhRhRhDhRhDhDhnhDhnhnhDhnhDhDhDhDhDhDjz", +"iUi.i2isi.i2isi2i.iUi2iUisi2i.isiUi.i2isi.i2isi2i.iUi2iUisi2i.isiUi.i2isi.i2isi2i.iUi2iUisi2i.isiUi.i2isi.i2isi2i.iUi2iUisi2i.isiUi.i2isi.i2isi2i.iUi2iUisi2i.isiUi.i2isi.i2jvjVjLjVj4jYj3jVk.jLjVjLjLjvjLj4jVjVjMj3jLjvjLj4i2jvj3jMjVjYjMjLjvj4i2j3jYjYjYjLjvjvjYi2j4jSi2jYjvjviijYi2iUiUi2j#iUjYjrjvjviHk.i2i2i2jKjriUi2i2i2j#i2i2i2jQisjviUi2iUi2i2iUisi2i2isjAiUi2i2i2i2isi2j#jAjLjvi2jMishIayi4ay.qj2.U.qdG.qj2.qj2.qdG.2.2jWj2jWj5j2.q.qiYd2.2.qayka.q.Uixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixi4iYjfi4d2d2jfhDhRhRhnhRhnhnh0hnh0h0hRh0hRhRhDhRhDhDh0hDh0h0hnh0hnhnhDhnhDhDh0hDh0h0hDh0hDhDh0hDh0h0hnh0hnhnh0hnh0h0hDh0hDhDhnhDhnhnhDhnhDhDhRhDhRhRhnhRhnhnh0hnh0h0hRh0hRhRhDhRhDhDj6", +"isisisi2iUisiUisi2isisiUi.iUisi2isisisi2iUisiUisi2isisiUi.iUisi2isisisi2iUisiUisi2isisiUi.iUisi2isisisi2iUisiUisi2isisiUi.iUisi2isisisi2iUisiUisi2isisiUi.iUisi2isisisi2iUisjYjSjVjVjvjYjvj3jvjVj4j0jLj4jLjLj4jvjVj0jvjvi2j3jvjYjLjTjvj9jYjYjYjvjYjKjvjYi2jVjvjLjYjvisj4iUjvjYjvjvjTjvjvj#jYi2i2i2iUi2jYjvjvjMisj4iUi2i2i2jKjQi2i2j#i2jriUjQjKisjvjTiUjAi.i2iUi2jQisjQjMjvi2i2isi2i2isjvi.jAisgI.Uay.Uay.Ud2j2dG.2.2.q.q.qae.qae.2dG.UjW.2j5.qj2.UdGk#.2.2dGjWixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixix.qjfd2j2i6d2.qhDh0h0hDh0hDhDhRhDhRhRhDhRhDhDh0hDh0h0hRh0hRhRh0hRh0h0hDh0hDhDhDhDhDhDhRhDhRhRhDhRhDhDh0hDh0h0hDh0hDhDhRhDhRhRh0hRh0h0hDh0hDhDh0hDh0h0hDh0hDhDhRhDhRhRhDhRhDhDh0hDh0h0j6", +"i2i2iUi2isisi2isi2isi2isi2i2i.isi2i2iUi2isisi2isi2isi2isi2i2i.isi2i2iUi2isisi2isi2isi2isi2i2i.isi2i2iUi2isisi2isi2isi2isi2i2i.isi2i2iUi2isisi2isi2isi2isi2i2i.isi2i2iUi2isisj4jVjVjVjvjvj4j4jTj3j4jVj4jVj4jLjLjLj4j0j8j3jLjLjvjvjLjYjYj4jvjvj4jYjvjKjLjvj4j4jvjYjYjLjYjLiUiUi2jvj4jvjYjvjYjvj#jvj4i2jvi2i2jTi2i2jvjvjri2jrjriUjKjvjvjYjMjvjvi2i2i2jvi2iUjMjMi.i2iUi2j1i2jTjTjLjKi2i2i2isi2i2i.hI.U.2.2.2.2.q.2iY.q.2j2dGj2j2e0.Uj2dGk#jfjZ.2.2j2.UdGj2d2d2aydGixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixd2d2jfd2i5dGi4hRhnhnhDhnhDhDh0hDh0h0hRh0hRhRhDhRhDhDhDhDhDhDhDhDhDhDhnhDhnhnhRhnhRhRh0hRh0h0hDh0hDhDhRhDhRhRhDhRhDhDh0hDh0h0hDh0hDhDhRhDhRhRhnhRhnhnhDhnhDhDh0hDh0h0hRh0hRhRhDhRhDhDjR", +"i2isisisi2i2isisi2iUisiUisisi2isi2isisisi2i2isisi2iUisiUisisi2isi2isisisi2i2isisi2iUisiUisisi2isi2isisisi2i2isisi2iUisiUisisi2isi2isisisi2i2isisi2iUisiUisisi2isi2isisisi2i2jvjLjVjVjLjVjSjvj4jvj4jvj0jvkbj4j0j4jVjYjLjYjvjvkbjYjVjKjvjvj4i2jLjvjVjVjvjvjvjvj4jvj4jvjSjvj4jvjviUj4jYisjYjvjvjYjvjvjTjvi2i2jLiUjTjvjvjTiUisi2jri2i2iUjQjQjMjvj#jvjri2iUi2isjQjMiUi2isi2iUi2i2isjTjMjAisjAisi2i2kcayayk#ay.qaybP.Uay.2.UjfdG.Ujf.qdG.UdGj2.qayj2k#.2.2.U.q.qaed2ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixj2jfjfjfd2jWjfhDhRhRh0hRh0h0hDh0hDhDh0hDh0h0hDh0hDhDh0hDh0h0h0h0h0h0h0h0h0h0hDh0hDhDhDhDhDhDh0hDh0h0h0h0h0h0hDh0hDhDhRhDhRhRh0hRh0h0hDh0hDhDhRhDhRhRh0hRh0h0hDh0hDhDh0hDh0h0hDh0hDhDj6", +"i2iUi2i2isisi2isi2isi2jvisi2iUisi2iUi2i2isisi2isi2isi2jvisi2iUisi2iUi2i2isisi2isi2isi2jvisi2iUisi2iUi2i2isisi2isi2isi2jvisi2iUisi2iUi2i2isisi2isi2isi2jvisi2iUisi2iUi2i2isisj4jSj9jvj9kbjSj4jVjSjVjYjVj8jvj0kbj0jVj0jVjVjvj8j4jvjVjYjVj4jvjvj4jvj4jLjVjVjvj4jvjvjYjYj4j3jvjSj4jvjLjvj4jVjvjYjYjvjvjYjvj4jTjvi2jvi2iUjvj4jTj4jMisj4i2i2jvi2jQjMjQi2j#i2jLi2jviUisjvjAi2jYisi2i2i2i2isjMjYiUi2jAjU.Ujfayd2.2.2jf.Ub1k#.2.q.U.2j2bPkd.qb1.q.2.Ud2.Uj2dG.2.q.qj5.qixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixked2d2hYjfjwj2hDh0h0hDh0hDhDhRhDhRhRh0hRh0h0hDh0hDhDh0hDh0h0hDh0hDhDhDhDhDhDh0hDh0h0hYh0hYhYhDhYhDhDh0hDh0h0hDh0hDhDh0hDh0h0hDh0hDhDhDhDhDhDh0hDh0h0hDh0hDhDhRhDhRhRh0hRh0h0hDh0hDhDj6", +"i2isi2iUi2i2isi2isi2i2isisi2isjvi2isi2iUi2i2isi2isi2i2isisi2isjvi2isi2iUi2i2isi2isi2i2isisi2isjvi2isi2iUi2i2isi2isi2i2isisi2isjvi2isi2iUi2i2isi2isi2i2isisi2isjvi2isi2iUi2i2jVjVjVj9j9jvjvj4jSjVjVjYjVjVjVj4jLj4j0jVjVjvjvjvjVjVj4jLj4j3jVjvjYjLjvj4jYjLjLj4j0jSjLjYjLj1jYj4j0jYjYjLjLjvjvi2jvjYjYjLjYjvjvjvjvjviUi2i2iUiUjrjTjYjvjvi2iUi2jrjQjrjvj4jvi2j#i2iUjLjrjQi2jvjLjMiUi2i2i2i2isisi2iskffs.UeB.U.Uay.qayk#.2ay.2b1.U.2.U.Uayj2k#k#.Uj2j2ae.Uaej2j2ay.2ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixd2d2.2i6.q.qjXhYh0h0hDh0hDhDhYhDhYhYhDhYhDhDhDhDhDhDhYhDhYhYh0hYh0h0h0h0h0h0hDh0hDhDh0hDh0h0h0h0h0h0hRh0hRhRh0hRh0h0h0h0h0h0hDh0hDhDhYhDhYhYh0hYh0h0hDh0hDhDhYhDhYhYhDhYhDhDhDhDhDhDj6", +"isjvisjvisjvi2i2isjviUi2jvi2i2i2isjvisjvisjvi2i2isjviUi2jvi2i2i2isjvisjvisjvi2i2isjviUi2jvi2i2i2isjvisjvisjvi2i2isjviUi2jvi2i2i2isjvisjvisjvi2i2isjviUi2jvi2i2i2isjvisjvisjvj9jVjSkgjVj9kgj4j4j4jVjVjVjVj4khjVj4jvkgjYjVjVj4j4jvjvjVjLjVjvjVjVjVjKkhjvjYjVj4j4j4jVjVjYj0jKjVjYj4jvj3jYjLjSj4jMjLjvjVjvjVjSjYjvjvjTjViUi2jvjrjLi2jvjvi2jvjvjvi2jriUjvi2j4jYjYjYi2i2jvisjLjQjYi2jMisjviUi2jvjvjLgI.2kd.qfs.UdG.U.2.2k#.2k#k#.2.Ub1dG.2jf.2.2.2.2.U.U.Uj2bP.UjZk#ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixj2jf.2jf.q.2d2h0hDhDh0hDh0h0h0h0h0h0h0h0h0h0hYh0hYhYhDhYhDhDhYhDhYhYhDhYhDhDh0hDh0h0hDh0hDhDhDhDhDhDh0hDh0h0hYh0hYhYhDhYhDhDh0hDh0h0h0h0h0h0hDh0hDhDh0hDh0h0h0h0h0h0h0h0h0h0hYh0hYhYjR", +"i2i2jvi2isi2isiUjvjvisisjvisjvisi2i2jvi2isi2isiUjvjvisisjvisjvisi2i2jvi2isi2isiUjvjvisisjvisjvisi2i2jvi2isi2isiUjvjvisisjvisjvisi2i2jvi2isi2isiUjvjvisisjvisjvisi2i2jvi2isi2jSjYkbkhjSjSjVjSkgjVj4j4jVkbjSjVjSjvkhkhjvjvjVjvjVj0j4jvjSj4jvjLjTj4kbjVjKjKjSjvj3jvj4j4j4j4jvjVjvjvjVj3jvjvjYjvjSjvj4jLjSj4jvj4jvjYjYjvjTjTjvisjViUjrjrjLj4jTjvjTi2jLjvi2iUi2i2jvjQjYjMjvjri2jri2iUjYjLiUisjYisjvhIj2.Uay.2.Uay.Uayay.Uayfs.Uaykday.U.U.U.2fsayfsj2.2.2j2.2.q.qdGixixixixkikjkkkljxkmknixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixix.q.U.U.U.U.qjfhYhRhRhYhRhYhYhDhYhDhDhYhDhYhYh0hYh0h0h0h0h0h0hRh0hRhRh0hRh0h0hYh0hYhYh0hYh0h0hYh0hYhYh0hYh0h0hDh0hDhDhDhDhDhDhYhDhYhYhYhYhYhYhRhYhRhRhYhRhYhYhDhYhDhDhYhDhYhYh0hYh0h0j6", +"jviUi2isjvjvi2jvi2isi2jvi2jvisjvjviUi2isjvjvi2jvi2isi2jvi2jvisjvjviUi2isjvjvi2jvi2isi2jvi2jvisjvjviUi2isjvjvi2jvi2isi2jvi2jvisjvjviUi2isjvjvi2jvi2isi2jvi2jvisjvjviUi2isjvjvjVkgj9jSj4jVkbjVjVjVjVjVj4jVjVkgjVjVjVjSjVkhj4jVjSj9jVj3j4j0jVkhjVjVj4jTj8jVj4j0j3jVjSjYj4jLj4jVjVjVjLjKjLjVjYjVjSjYjYjvj4jvjYjVj4jYjYjvjYjvjYjvjvjVjviUjriUjvjvjvj4i2jTjrjvjrjLjKjvi2jKjMj#j3jvi2jLjri2i2jYjMjYi2gIbP.Uayayj2kdaykdkd.Ufsaej5.2.2.Ujfk#.2k#.U.2.Ujfjfayjf.2j2bPj5ixkokpkqj2k#jW.2.q.qbPkrksktixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixix.2.qjfj2kukvkwkxiwioinjckykzhYhDhYhYh0hYh0h0hDh0hDhDhYhDhYhYh0hYh0h0hYh0hYhYhRhYhRhRhDhRhDhDh0hDh0h0hDh0hDhDh0hDh0h0hYh0hYhYh0hYh0h0hDh0hDhDhYhDhYhYhDhYhDhDhYhDhYhYh0hYh0h0hDh0hDhDj6", +"jvisjvjvi2i2i2isi2jvi2jvisi2i2i2jvisjvjvi2i2i2isi2jvi2jvisi2i2i2jvisjvjvi2i2i2isi2jvi2jvisi2i2i2jvisjvjvi2i2i2isi2jvi2jvisi2i2i2jvisjvjvi2i2i2isi2jvi2jvisi2i2i2jvisjvjvi2i2kAjSjVkgj4kgjYj4j4kbjVjSkgjVjLj4j8jvjVkbjYjVj4jVjLj4jVjvjVjVj4j4j4jvjVjVj8jVjvj8j4j4jSjKkhjSjvj4jVjvjVjVj4jSjYjLi2jYjVjvjVjYj3j4j4jvj4jYj4jYjvjYj4jYjvkBkCkDj4jrjviUjvjYj4jvjvjrj4jrjLiUjvi2jvjYjYj#i2jLjvjLjQi2jvkfb1.U.U.UdGkad2ayk#kd.Ufs.UeB.Ukaayfsk#.Ufs.U.U.U.U.U.2bPfsayfskEkFbPay.Ufsk#.2ay.2.Uj2.UkGknixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixbP.2e0kkiwixixixixixixixixiwjxkHhYhYhYhYhYhYhRhYhRhRhYhRhYhYhDhYhDhDh0hDh0h0hYh0hYhYh0hYh0h0hYh0hYhYhYhYhYhYhYhYhYhYh0hYh0h0hYh0hYhYh0hYh0h0hYh0hYhYh0hYh0h0hYh0hYhYhYhYhYhYhRhYhRhRj6", +"jvjvi2jvisjvjvjvjvi2jvi2jvisjvjvjvjvi2jvisjvjvjvjvi2jvi2jvisjvjvjvjvi2jvisjvjvjvjvi2jvi2jvisjvjvjvjvi2jvisjvjvjvjvi2jvi2jvisjvjvjvjvi2jvisjvjvjvjvi2jvi2jvisjvjvjvjvi2jvisjvkhkhkhj9j9j9kgjVkhjVkgkbjVkbjSjVkgkgkIj4jVjVjVjSjVj0khjVjVj9jvjVjVjVjVjLjSj4jVjvj4j9jVjVjVjVjvjSj3j3j0j4j4jVjVjvj3jSjVjVj4jYjvjYjSjSjSjMjvj4jYjvkJkKkLixixixi2jvjVjvjvjrjLjvjYjTjVjLjvjLi2jvisjLjvjvjvjMjYjrjLi2jYig.2b1.U.2.2.U.U.U.Uaykakdkday.Ufs.Uj2ay.qj2j5k#.U.U.U.Uayj2fsk#kMkd.qb1.q.q.UdGbPbPayay.2j2kNkOixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixd2kPkQixixixixixixixixixixixixkRkSh0hDh0hDhDh0hDh0h0h0h0h0h0hYh0hYhYh0hYh0h0hYh0hYhYh0hYh0h0h0h0h0h0hRh0hRhRh0hRh0h0hYh0hYhYhDhYhDhDhYhDhYhYh0hYh0h0hYh0hYhYh0hYh0h0hDh0hDhDh0hDh0h0j6", +"isi2jvi2jvi2i2isjvi2jvi2i2jvjvi2isi2jvi2jvi2i2isjvi2jvi2i2jvjvi2isi2jvi2jvi2i2isjvi2jvi2i2jvjvi2isi2jvi2jvi2i2isjvi2jvi2i2jvjvi2isi2jvi2jvi2i2isjvi2jvi2i2jvjvi2isi2jvi2jvi2kAjVkhj4kAj9j9kAkgj9j0j4jVj9kbjVjVjVkgjLjVj4jLj9jVjVjSjvj0jVjVj4j4j0jVj4jVjvjVjVjVj4jTj8j9jVjVjLjLjvj3i2jVj4jYjVj9jLj0jLjSjYjYjYjYjVjSjYjVkTkUkVkoixixixixixjYjYi2jvjVj4jLi2i2jvjvjvjTk.jLi2jLjrjrjKjvjvj3jMi2jvjLgIfsb1fsdG.2b1.2.U.U.Uayayay.Uk#fsfs.Ufs.U.2.UbPkdkdfs.U.Ufs.Ufsfsfsj2.Uk#j2.Uj2bPbPfsk#.UbPjWkWkxixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixkXjFixixixixixixixixixixixixixixiJkYhYhYhYhYhYhYhYhYhYhYhYhYhDhYhDhDhYhDhYhYh0hYh0h0hYh0hYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYh0hYh0h0hYh0hYhYhYhYhYhYhDhYhDhDhYhDhYhYhYhYhYhYhYhYhYhYj6", +"jvjvjvisi2jvjvjvjvjvi2jvjvi2jvjvjvjvjvisi2jvjvjvjvjvi2jvjvi2jvjvjvjvjvisi2jvjvjvjvjvi2jvjvi2jvjvjvjvjvisi2jvjvjvjvjvi2jvjvi2jvjvjvjvjvisi2jvjvjvjvjvi2jvjvi2jvjvjvjvjvisi2jvkZj9k0k0kAkgjVkAj9kAkgkgkgjYj9jVjVjVkbjVjVkgjSj4kIj9kbjVjVj4jVj0khjSj9jVj4jVj4j4jVjVjVjVjVjvj4j4jVj4jVjSjLjVjvj4j3j4kAjSjVjYjLjSjVjvk1k2k3k4ixixixixixixixixjYjYjvi2j4jvi2jvjVjLjvjvjYjYjvjvi2jLjvjSjvjLjrj4jvjYjKig.UbP.Uayfsfsb1ay.2b1bP.U.U.q.Uj2k#k#ay.Uay.Uay.U.2jffs.2.U.U.U.UbPbPfskdbPfsb1fs.Uj2.U.Uayk#.Uk5k6ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixjIixixixixixixixixixixixixixixixixkRkHhYh0h0hYh0hYhYh0hYh0h0hYh0hYhYhYhYhYhYhYhYhYhYh0hYh0h0h0h0h0h0hYh0hYhYhDhYhDhDhYhDhYhYhYhYhYhYhYhYhYhYh0hYh0h0hYh0hYhYhYhYhYhYh0hYh0h0hYh0hYhYj6", +"jvi2jvjvj4i2jvi2i2jvjvi2jvi2i2jvjvi2jvjvj4i2jvi2i2jvjvi2jvi2i2jvjvi2jvjvj4i2jvi2i2jvjvi2jvi2i2jvjvi2jvjvj4i2jvi2i2jvjvi2jvi2i2jvjvi2jvjvj4i2jvi2i2jvjvi2jvi2i2jvjvi2jvjvj4i2khj9j9k0k0kAjVj4kAkhkhkAj9khkZj4kgj4j4jVjVjVjVjVj9kgkgj4jVjVj9kbjVjvjvjVjVj4j9j4kbjVjVj4j0khkbj8jVj8j4j9jSjvjVjLj4j3j3j4jVjVjVk7k8k9l.ixixixixixixixixixixixkAjYjYjYjYjYjYjvj4jri2jvjvjLjvjvj4k.jvk.jrjLjLi2jvi2jMigbP.2bPbPfsaybPayfs.2.2.Uayay.U.U.U.Ufskdfs.Ufs.U.Ufsfsfs.Uj2b1k#ay.UbPfsfs.2bPfsfs.U.UbP.Uay.UeBl#ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixlahYh0h0h0h0h0h0hYh0hYhYhYhYhYhYh0hYh0h0h0h0h0h0hYh0hYhYhYhYhYhYhYhYhYhYhYhYhYhYh0hYh0h0hYh0hYhYhYhYhYhYhYhYhYhYiYhYiYiYhYiYhYhYh0hYh0h0h0h0h0h0j6", +"jvjvi2jvjvjvjvjvjvjvjvjLjvjvjvi2jvjvi2jvjvjvjvjvjvjvjvjLjvjvjvi2jvjvi2jvjvjvjvjvjvjvjvjLjvjvjvi2jvjvi2jvjvjvjvjvjvjvjvjLjvjvjvi2jvjvi2jvjvjvjvjvjvjvjvjLjvjvjvi2jvjvi2jvjvjvk0kgj9k0kAj9k0k0jVkAkgkhkAkAj9kAkgkgkgkgjVj9jVjVkbjVkgkgj4jVjSjVkbjVjVkhjVjSjVjVj9j4jVjVj4jVjVjVjVjVj9jVj4j4jSj0jVjLj3j3jLlblcldixixixixixixixixixixixixixixjSjVjYjYjYjYj4j4jYjvj4i2jvjVjLjLjvjvj4j4j4jVjVjLjLjLjvgI.2ay.2b1bPfsbPbPfs.2ayayb1b1.2bP.U.U.2ayfs.Ukdkdkd.U.U.Uayfsj2fskdfs.U.Ufs.U.Uj2j2kdayfs.UbP.U.Ulelfixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixiwlgi4i4hYi4hYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYh0hYh0h0hYh0hYhYhYhYhYhYh0hYh0h0hYh0hYhYhYhYhYhYhYhYhYhYi4hYi4i4hYi4hYhYj6", +"jvj4jvjvj4jvi2jvjvjvi2jvjvjvj4jvjvj4jvjvj4jvi2jvjvjvi2jvjvjvj4jvjvj4jvjvj4jvi2jvjvjvi2jvjvjvj4jvjvj4jvjvj4jvi2jvjvjvi2jvjvjvj4jvjvj4jvjvj4jvi2jvjvjvi2jvjvjvj4jvjvj4jvjvj4jvkAk0k0jVj9kAj9j9lhk0khkAjVjVj9khkAj9j9jSj9jVjVjVkAjVjVj9jVjSk0kIj4jVjVj4jVjVjVjVkbjVlikAj9j9kAjVj4jSj0jSj4jVj9j9kAjVljlkllixixixixixixixixixixixixixixixixixkbjSjMjSjvjVjYjVjYjYlmjvjvj4j4j4j4jvjrjLjLjvjMjVjLjLj8igayfsayayb1ayb1b1bPaybPayfsfs.2b1bPayayeeg3.UgOayfsfskd.UayeBayfsfslnfseB.Ufsay.U.U.UbPkd.U.U.UbPfsloixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixlphYhYhYhYhYhYh0hYh0h0hYh0hYhYhYhYhYhYh0hYh0h0hYh0hYhYiYhYiYiYhYiYhYhYhYhYhYhYhYhYhYhYiYhYiYiYhYiYhYhYhYhYhYhYhYhYhYhYh0hYh0h0hYh0hYhYhYhYhYhYj6", +"jvi2j4jvi2jLjvj4i2jvj4jvjvj4jvjvjvi2j4jvi2jLjvj4i2jvj4jvjvj4jvjvjvi2j4jvi2jLjvj4i2jvj4jvjvj4jvjvjvi2j4jvi2jLjvj4i2jvj4jvjvj4jvjvjvi2j4jvi2jLjvj4i2jvj4jvjvj4jvjvjvi2j4jvi2jLkZk0kAkhlhk0k0k0k0j9kZkAkgjVj4j9kAlhkAk0j9j9kgk0jVjVkgkbj9kbjVj9kgk0j4kgkbjVjVj9jVj0khj4kgj9j8jVkbj9j4khj9j0j9lqlrlsixixixixixixixixixixixixixixixixixixixixjYj4jVjVj4jVjVjvjYj4jVjYjvjYj4jVk.jLjvj4jrjvj4jLj4j4jVltlubPluayayfsb1ay.2lubPeBfsbPayfsb1kdbPk#k#ayay.Ukaayayg3fskdayay.Uj2.U.qj2.U.2kd.U.U.U.U.UbPkd.Ub1lvixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixlwhYhYhYhYhYhYiYhYiYiYhYiYhYhYhYhYhYhYiYhYiYiYhYiYhYhYi4hYi4i4h0i4h0h0iYh0iYiYhYiYhYhYh0hYh0h0hYh0hYhYhYhYhYhYhYhYhYhYiYhYiYiYhYiYhYhYhYhYhYhYj6", +"jvjvjvjLjvjvjvjvj4jvjvjvjvjvjvj4jvjvjvjLjvjvjvjvj4jvjvjvjvjvjvj4jvjvjvjLjvjvjvjvj4jvjvjvjvjvjvj4jvjvjvjLjvjvjvjvj4jvjvjvjvjvjvj4jvjvjvjLjvjvjvjvj4jvjvjvjvjvjvj4jvjvjvjLjvjvkAkgk0k0k0k0kZkhjVk0kAkhj9k0j9jVjVk0k0khj9j9j9khjSk0jVkIjVj9jVkbkbjVk0kgjVjVjVkgj9j9kbjVj0kgjSj9j9jVj9jVlxlylzixixixixixixixixixixixixixixixixixixixixixixixjvjYjYjYj9jSjVjSjSjVjvkAjYjYjYjLi2jVj4jvjVjVjvjvjvjvjvigb1lnb1lulnayeBbPayayb1b1bPfsfsbPfsbPayb1ayk#.2fsay.Ulufsfs.U.Ukdk#fs.Uayay.2ayfsfseB.2ayfs.UaybP.UlAixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixlBhYhYiYhYiYiYhYiYhYhYiYhYiYiYhYiYhYhYhYhYhYhYhYhYhYhYhYhYhYhYi4hYi4i4hYi4hYhYhYhYhYhYi4hYi4i4hYi4hYhYiYhYiYiYhYiYhYhYhYhYhYhYhYhYhYhYiYhYiYiYj6", +"j4jLjvjVjvjvj4jvjLjvj4jvjVjLjvjvj4jLjvjVjvjvj4jvjLjvj4jvjVjLjvjvj4jLjvjVjvjvj4jvjLjvj4jvjVjLjvjvj4jLjvjVjvjvj4jvjLjvj4jvjVjLjvjvj4jLjvjVjvjvj4jvjLjvj4jvjVjLjvjvj4jLjvjVjvjvlhlhkgk0j9kZk0k0kZkZkAkgkAkgkAj9j9k0kgkhkAk0kgj9jVkgj9kgjVk0jVjVkAkbj9kgj9kgk0jVkIkbkbj9kbkgjVjSj9lClDlEixixixixixixixixixixixixixixixixixixixixixixixixixixjVjYj4jYj9jVj4jVkbjSjSjVkbj9j4jVj4j4j8jYjLjVjvjvjVjLjvlFayay.2bP.2.2.2bPfsk#fsb1b1fsfsfslubPlnbPayayayb1bP.Uay.U.Ug3.Ufslnkdaykd.U.U.U.Ufskd.Ukdb1b1fsfs.UlGixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixiohYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYhYiYhYiYiYhYiYhYhYhYhYhYhYhYhYhYhYiYhYiYiYhYiYhYhYhYhYhYhYhYhYhYhYhYhYhYhYjfhYjfjfhYjfhYhYhYhYhYhYj6", +"jLjvjvjvjvj4jvjvjVjvjvjvjvjvj4jvjLjvjvjvjvj4jvjvjVjvjvjvjvjvj4jvjLjvjvjvjvj4jvjvjVjvjvjvjvjvj4jvjLjvjvjvjvj4jvjvjVjvjvjvjvjvj4jvjLjvjvjvjvj4jvjvjVjvjvjvjvjvj4jvjLjvjvjvjvj4kZk0k0kgkgkgkgj9k0k0k0kZkhjVk0k0j9kAk0kZjVk0k0jVkAkAkgj9khj9kgjVkgjVlhjVjVjVjVjVk0jVkgjVkgjVlHlIlJlKixixixixixixixixixixixixixixixixixixixixixixixixixixixixjVj4jSjYkAjVj0jYjYjYkbjVjSjSjVjvjYj9jYjVjVjvjvjvjvj4j4lLluluayeBbPlnfDaylufslubPbPfsb1b1bPfsfslufslufseBbPb1eBb1bPay.Ufs.Ug3kdlnfsfsfs.U.U.U.UfsfseBkdayeBlGixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixioiYiYhYiYhYhYi4hYi4i4jfi4jfjfiYjfiYiYhYiYhYhYhYhYhYhYi4hYi4i4jfi4jfjfhYjfhYhYhYhYhYhYiYhYiYiYhYiYhYhYhYhYhYhYiYhYiYiYhYiYhYhYiYhYiYiYhYiYhYhYj6", +"jVjvj4jVjvjvj4jvjVjvj4jVjvjVjvjvjVjvj4jVjvjvj4jvjVjvj4jVjvjVjvjvjVjvj4jVjvjvj4jvjVjvj4jVjvjVjvjvjVjvj4jVjvjvj4jvjVjvj4jVjvjVjvjvjVjvj4jVjvjvj4jvjVjvj4jVjvjVjvjvjVjvj4jVjvjvk0k0k0kZk0lMkgk0lNkglOk0k0lhkZkhlPkhlhj9k0lhk0kAk0k0k0kAlhk0j9khk0lik0kglhk0jVk0jVjVk0lQlRlSkoixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixjVjVkAjVj9jSkbkAjVjVjYjYkgjVjVkbjSjVj4jVjVjYj4kbjvjVjvhIk#fslu.Ufsayb1ayfsg3.2fslueBfsfseBb1fD.2bPlubPlufsbP.2b1b1ayb1eBayfsay.2fskdk#fskdeB.Ug3g3.2lnfsfslTixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixlBiYiYhYiYhYhYi4hYi4i4hYi4hYhYhYhYhYhYiYhYiYiYjfiYjfjfhYjfhYhYi4hYi4i4hYi4hYhYiYhYiYiYhYiYhYhYhYhYhYhYiYhYiYiYhYiYhYhYhYhYhYhYiYhYiYiYhYiYhYhYj6", +"j4jvjLjvj4jLjvj4jvjLj4jvjvj4jvjVj4jvjLjvj4jLjvj4jvjLj4jvjvj4jvjVj4jvjLjvj4jLjvj4jvjLj4jvjvj4jvjVj4jvjLjvj4jLjvj4jvjLj4jvjvj4jvjVj4jvjLjvj4jLjvj4jvjLj4jvjvj4jvjVj4jvjLjvj4jLk0k0k0lhlPkZkZk0kgk0k0kAkgk0k0k0k0k0kZkgk0j9j9j9k0lhk0jVlhkhk0j9kAj9kgkgk0kgjVkAlUlVlWlXixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixkbj9j4kAjVlYjVjSjSj9j3kAkAjYj4jSjVjSj3jVjVj9kAjYjYj4jvigeBkdfPk#k#luayluayk#lnb1bPfsfsfslueBfseBeBbPfsbPbPfsbPeBeBfslnkdfs.2fs.U.UfP.UfPfsfseBayfsayayfsfslZixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixjchYhYhYhYhYhYjfhYjfjfi4jfi4i4iYi4iYiYhYiYhYhYi4hYi4i4jfi4jfjfhYjfhYhYiYhYiYiYi4iYi4i4hYi4hYhYjfhYjfjfiYjfiYiYhYiYhYhYjfhYjfjfhYjfhYhYhYhYhYhYj6", +"jvjVjvjVjvjVjVj4jvjVjvj4jVjLj4jvjvjVjvjVjvjVjVj4jvjVjvj4jVjLj4jvjvjVjvjVjvjVjVj4jvjVjvj4jVjLj4jvjvjVjvjVjvjVjVj4jvjVjvj4jVjLj4jvjvjVjvjVjvjVjVj4jvjVjvj4jVjLj4jvjvjVjvjVjvjVl0k0kgl1kglPl1lhlhkZk0k0lhk0k0lOlOk0kZk0kZk0k0kAkAj9k0k0k0kgkhlhk0j9j9lOj9l2l3l4k6ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixjVkAkbkAj3khj4j9j9j9kAjSjVkAjYkAj3kbjVjVjSkAjSkAjVj4j8kfkdeBkdeBfsfsfsfsfseBfsfsfs.2fsbPbPfsbPfsay.2aykdfsfsfsfsfsayeBfsb1eBb1eBay.Uay.Ufs.2fskdkday.Ukdlul5ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixl6iYiYjfiYjfjfhYjfhYhYjfhYjfjfhYjfhYhYjfhYjfjfhYjfhYhYhYhYhYhYi4hYi4i4jfi4jfjfhYjfhYhYiYhYiYiYhYiYhYhYhYhYhYhYi4hYi4i4i4i4i4i4iYi4iYiYjfiYjfjfj6", +"jvj4jVj4jvjLjvjvjVjVjvjvjVjvjVjvjvj4jVj4jvjLjvjvjVjVjvjvjVjvjVjvjvj4jVj4jvjLjvjvjVjVjvjvjVjvjVjvjvj4jVj4jvjLjvjvjVjVjvjvjVjvjVjvjvj4jVj4jvjLjvjvjVjVjvjvjVjvjVjvjvj4jVj4jvjLk0k0lMl0kgkgk0kgl1k0k0k0k0lMkgkgkgj9k0lOkZkZlhk0k0khkAj9kglhkZkgkhk0lhl7l8l9ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixjVjVjVjSkAkbjVj3jVj4khjVjVj9j9kAjVjVjVjYkbjVjSjSj4jSkAigfsfsfskdfsfsfslulufslulufslueBlnayf4eBfslululufDbPayf4bPb1bPbPfsayfDfsf4fDfDfDf4fs.UfPfsayfPkdeBm.lfixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixiwlgjfjfjfjfjfjfi4jfi4i4hYi4hYhYi4hYi4i4jfi4jfjfiYjfiYiYjfiYjfjfjfjfjfjfhYjfhYhYhYhYhYhYjfhYjfjfjfjfjfjfhYjfhYhYjfhYjfjfhYjfhYhYjfhYjfjfjfjfjfjfj6", +"jVjvj4jvjVjVjVjVjLjvj4jVjLjVjvjVjVjvj4jvjVjVjVjVjLjvj4jVjLjVjvjVjVjvj4jvjVjVjVjVjLjvj4jVjLjVjvjVjVjvj4jvjVjVjVjVjLjvj4jVjLjVjvjVjVjvj4jvjVjVjVjVjLjvj4jVjLjVjvjVjVjvj4jvjVjVl1l1lhk0k0k0k0k0k0l1k0k0lhkZlhk0k0lMkgkgjVlOlhk0khkAlhkhkAkhk0lOm#mambixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixjVkhkhkAlYjVj9kbj9kAjVjVjVj9kAjSkAkbkbjVj4jVj4jVjVjVjVkcfPayfDeBmceBeBfseBfsfPfsfsfsk#aybPayb1ayk#fslubPbPlubP.UfsfDlulufDfseBfsln.Ue0b1b1fDbPfsg3fsfs.Umdixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixjxjfi4i4hYi4hYhYjfhYjfjfiYjfiYiYjfiYjfjfhYjfhYhYhYhYhYhYi4hYi4i4hYi4hYhYiYhYiYiYjfiYjfjfiYjfiYiYhYiYhYhYjfhYjfjfhYjfhYhYjfhYjfjfi4jfi4i4hYi4hYhYj6", +"jVjvjVjVjLjVjLjvjLjVjLjVjvjVjLj4jVjvjVjVjLjVjLjvjLjVjLjVjvjVjLj4jVjvjVjVjLjVjLjvjLjVjLjVjvjVjLj4jVjvjVjVjLjVjLjvjLjVjLjVjvjVjLj4jVjvjVjVjLjVjLjvjLjVjLjVjvjVjLj4jVjvjVjVjLjVltkglhl1lhl1k0k0lhlMk0kgl1k0lhlhl0kZkglMkAk0kAkgj9lhk0kZk0memfmgixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixkIkAjVj9jVkAj9jVjVj9lYkAkAjVkhjVjVkAjVjSj9kAj4kAjYjVj4kfeBbPfseB.UbP.Ufsfsfsk#eBf4g3fslulululnfseBfsfsfDlufseBlueBfseBkdkdfDlufDluayeBfs.Uf4k#fDlueBlumhk6ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixjIixixixixixixixixixixixixixixixixkRkujfjfjfhYjfhYhYjfhYjfjfhYjfhYhYi4hYi4i4jfi4jfjfiYjfiYiYjfiYjfjfjfjfjfjfjfjfjfjfi4jfi4i4jfi4jfjfi4jfi4i4jfi4jfjfiYjfiYiYjfiYjfjfjfjfjfjfhYjfhYhYj6", +"jVjVjLjVjvjVjVjVjVjvjVjVjVjvjVjVjVjVjLjVjvjVjVjVjVjvjVjVjVjvjVjVjVjVjLjVjvjVjVjVjVjvjVjVjVjvjVjVjVjVjLjVjvjVjVjVjVjvjVjVjVjvjVjVjVjVjLjVjvjVjVjVjVjvjVjVjVjvjVjVjVjVjLjVjvjVl0l0l0k0lhl1l1k0l0k0k0lMkglPkgk0l1l1l0k0lhk0lMkglOkhmimjmkixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixjSjVkbkbjVj9jVkhkAjVk0j9kbkAjVjVj4jVkhkhjVjVj9kAkAjVkhhIeBfseBmlfPbPfs.U.UfDfseBfsfsfsluk#lulufsfsfslnayaykdb1lufsluayfDeBb1eB.2b1fDbPfsfsayeBkdkdfDmmmnixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixmojFixixixixixixixixixixixixixixiJmphYi4hYhYjfhYjfjfiYjfiYiYjfiYjfjfhYjfhYhYjfhYjfjfi4jfi4i4iYi4iYiYhYiYhYhYi4hYi4i4jfi4jfjfhYjfhYhYjfhYjfjfi4jfi4i4jfi4jfjfi4jfi4i4hYi4hYhYjfhYjfjfj6", +"jvjLjVjVjVjLjVjvjVj4jVjVjLjVjVjLjvjLjVjVjVjLjVjvjVj4jVjVjLjVjVjLjvjLjVjVjVjLjVjvjVj4jVjVjLjVjVjLjvjLjVjVjVjLjVjvjVj4jVjVjLjVjVjLjvjLjVjVjVjLjVjvjVj4jVjVjLjVjVjLjvjLjVjVjVjLl1l1l0l0lPlhlNl1l1lhkgk0k0l0lMk0k0k0l1lhlhk0k0mqmrkRixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixkAjSj9khk0j9kAkAjSkhkhjVjVlYj9kbkAj9jVjVkhj4kAj9lYkbkAmsg3fsfDbPb1fsbPfsfseBfDfDfDmceBeBeBmlfPfsfsfslufseBeBf4fDfPfslumtlufsfDaykdf4kdfslulueBfDfsmukOixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfsmvkQixixixixixixixixixixixixkRmwjfjfjfjfjfjfjfjfjfjfjfjfjfhYjfhYhYjfhYjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfi4jfi4i4jfi4jfjfjfjfjfjfhYjfhYhYjfhYjfjfjfjfjfjfjfjfjfjfj6", +"jVjVjVjvjLjVjVjVjVjVjLjVjVj4jVjVjVjVjVjvjLjVjVjVjVjVjLjVjVj4jVjVjVjVjVjvjLjVjVjVjVjVjLjVjVj4jVjVjVjVjVjvjLjVjVjVjVjVjLjVjVj4jVjVjVjVjVjvjLjVjVjVjVjVjLjVjVj4jVjVjVjVjVjvjLjVl1lOlOl1lhl1l0mslhmsl1l1l1l0lhk0k0lhlPk0kgl1mxkoixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixjVkAkgj9kAjVjSkbj9kAkbjVk0khkhjVjSj9kbj4jVjVj9j9jVkAj9lLayfsayeBfDfDeBfDfseBeBfseBe0fDe0fskdeBk#fsfDlueBfsbPluk#ayfsaymymzfslueBfsfDf4eBfskdfDfslGmAixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfsg3mBkkiwixixixixixixixixiwjxkujfjfjfjfjfjfjfjfjfjfi4jfi4i4jfi4jfjfjfjfjfjfjfjfjfjfiYjfiYiYi4iYi4i4jfi4jfjfhYjfhYhYjfhYjfjfjfjfjfjfjfjfjfjfi4jfi4i4jfi4jfjfjfjfjfjfjfjfjfjfjfjfjfjfj6", +"jVjLjVjVjVjVjVjLjLjVjVjLjVjLj4jVjVjLjVjVjVjVjVjLjLjVjVjLjVjLj4jVjVjLjVjVjVjVjVjLjLjVjVjLjVjLj4jVjVjLjVjVjVjVjVjLjLjVjVjLjVjLj4jVjVjLjVjVjVjVjVjLjLjVjVjLjVjLj4jVjVjLjVjVjVjVlhlNlNl1l1lhk0k0lPlhlhltlhl1l1k0l1k0k0lMlMkgixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixk0kAkbjVjVkgkgj9kAlYkbkbkbj9kbkhkhkAjVlYlYkAkAj9jVj4kAhIeBfseBfsfsfsfsfsfskdkdfslufPayfsfsfafDeBeBf4eBf4fDlululueBfsfsixkomCmDlulufslufDfDf4mEmFktixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfDfDfsg3mGmHkwmIiwiolBjcmJmKjf.qjfjfi4jfi4i4i4i4i4i4jfi4jfjfjfjfjfjfi4jfi4i4i4i4i4i4jfi4jfjfjfjfjfjfjfjfjfjfjfjfjfjfi4jfi4i4jfi4jfjfjfjfjfjfjfjfjfjf.qjf.q.qjf.qjfjfi4jfi4i4i4i4i4i4j6", +"jVjVjLjVjVjVjVjVjVjVjVjVjVjVjVjLjVjVjLjVjVjVjVjVjVjVjVjVjVjVjVjLjVjVjLjVjVjVjVjVjVjVjVjVjVjVjVjLjVjVjLjVjVjVjVjVjVjVjVjVjVjVjVjLjVjVjLjVjVjVjVjVjVjVjVjVjVjVjVjLjVjVjLjVjVjVl1lPlPmsl1lhl1l1lOlhl1l0lhlPlhl1l1l1k0l1lhk0ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixkgkAkbkbkbkgkAkAkgkgkAjSkbk0j9kAjVjVkAj9j9kbkAj9kAkAjVhmmceBfsfseBlufsfsfDbPfDfDfseBmleBfsfsfafDfDmcfsfseBk#f4fslulufsixixixixmLkjkkmMkkksmAixixixixixixixixixixixixixixixixixixixixmNmOkwkwmOmPixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfsfsfsfsmQeefsi4jfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfi4jfi4i4jfi4jfjfjfjfjfjfi4jfi4i4jfi4jfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfj6", +"jVj9jVjVj9jVjLjVjVjVjLjVjVjVj9jVjVj9jVjVj9jVjLjVjVjVjLjVjVjVj9jVjVj9jVjVj9jVjLjVjVjVjLjVjVjVj9jVjVj9jVjVj9jVjLjVjVjVjLjVjVjVj9jVjVj9jVjVj9jVjLjVjVjVjLjVjVjVj9jVjVj9jVjVj9jVl0l1l1lOl0l1lhlhmsl1l1lhlOk0lPl0lhlhlhl1lhl0ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixk0kgjVjVjVj9jVk0kbkbk0kgkgj9j9j9mRjVj9kAjVjVlYk0j9j9mRfPgOfDfsfDfseBfseBeBeefsayfsfDkdeBeBmlmlfPfPfsfPfDfDeBeBfsfPfPluixixixixixixixixixixixixixixixixixixixixixixixixixixixixktmSmTjoayayg3aymBmTmUktixixixixixixixixixixixixixixixixixixixixixixixixixixixixg3luf4lufsfsfDjfjfjfjfjfjfjfi4jfi4i4jfi4jfjfjfjfjfjfi4jfi4i4jfi4jfjfjfjfjfjfi4jfi4i4jfi4jfjf.qjf.q.qjf.qjfjfjfjfjfjfjfjfjfjf.qjf.q.qjf.qjfjfjfjfjfjfjfjfjfjfi4jfi4i4jfi4jfjfjfjfjfjfj6", +"jVjLj9jVjSj9jVj9jVjVj9jVjVj9jVjVjVjLj9jVjSj9jVj9jVjVj9jVjVj9jVjVjVjLj9jVjSj9jVj9jVjVj9jVjVj9jVjVjVjLj9jVjSj9jVj9jVjVj9jVjVj9jVjVjVjLj9jVjSj9jVj9jVjVj9jVjVj9jVjVjVjLj9jVjSj9l1mskclOmsl1lPltmslhl1lhmslOk0lhlPlhlPl1lhlhixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixjVkAk0kgkbk0j9jVkbkAkgkbkgkAkgkAlYj9kbkAkAkhmRkAkAkgk0hIlufslufDfDfDfseBfslueBg3fsfsg3fDeBlueBlumlg3fPayfseBfsfPlueBluixixixixixixixixixixixixixixixixixixixixixixixixixixixmxmVf4fPfsmllufPlufsg3eBlGmxixixixixixixixixixixixixixixixixixixixixixixixixixixixmWmWg3fsf4fsfDjfjfjfjfjfjfjf.qjf.q.qjf.qjfjfjfjfjfjf.qjf.q.qjf.qjfjfjfjfjfjf.qjf.q.qjf.qjfjf.qjf.q.qi4.qi4i4.qi4.q.qjf.qjfjfd2jfd2d2jfd2jfjfjfjfjfjfjfjfjfjf.qjf.q.qjf.qjfjfjfjfjfjfmX", +"jVjVjVj9jVjVjVjVj9jVjVjVjVjVjVj9jVjVjVj9jVjVjVjVj9jVjVjVjVjVjVj9jVjVjVj9jVjVjVjVj9jVjVjVjVjVjVj9jVjVjVj9jVjVjVjVj9jVjVjVjVjVjVj9jVjVjVj9jVjVjVjVj9jVjVjVjVjVjVj9jVjVjVj9jVjVl1ltl1l1l1l1l1l0lOltlPl0lhmsl0lOk0l1ltl0lPlhixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixjVkbkbkblhkgkgkgkgk0j9lhkbjVkAkgj9kAkAjVk0k0kbkAkbkhjVhIfsmYeBlufPfDfPfseBeBfseBeBlufPfsfDfsfDeBfDlumleBfsfsfDfDfDeBeBixixixixixixixixixixixixixixixixixixixixixixixixixixmZg#fPfPmcg3fDfsfPlululufseBjUm0ixixixixixixixixixixixixixixixixixixixixixixixixixixfPhxlulueBfsfsjf.q.qjf.qjfjfjfjfjfjfjfjfjfjf.qjf.q.qjf.qjfjf.qjf.q.qjf.qjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjf.qjf.q.qjf.qjfjf.qjf.q.qjf.qjfjfjfjfjfjfjfjfjfjf.qjf.q.qj6", +"kgj9jVk0jVjVj9jVj9jVj9jVk0j9jVjVkgj9jVk0jVjVj9jVj9jVj9jVk0j9jVjVkgj9jVk0jVjVj9jVj9jVj9jVk0j9jVjVkgj9jVk0jVjVj9jVj9jVj9jVk0j9jVjVkgj9jVk0jVjVj9jVj9jVj9jVk0j9jVjVkgj9jVk0jVjVl0msl0l1lhl1msmsl1l1m1ltl1l0l1l1l0l1msl1lhl1ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixkbjVj9kblNk0kAk0k0kgkgk0lNlhkAk0kAj9j9kAkgk0j9kbk0kAkbigfDeBfseBfsfseBfslueBfDfseBg3eBfsmlfsfPfDfDfDfDfseBfsfPfDfDfPfDixixixixixixixixixixixixixixixixixixixixixixixixixm2luf4fsfsfsfsf4eBf4fsf4lululufPfsm3ixixixixixixixixixixixixixixixixixixixixixixixixixfsfsfsfsfsfDfsjfjfjfjfjfjfjf.Ujf.U.Ujf.Ujfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjf.qjf.q.qjf.qjfjfjfjfjfjfjfjfjfjf.qjf.q.qjf.qjfjfjfjfjfjfjfjfjfjfjfjfjfjf.Ujf.U.Ujf.Ujfjfjfjfjfjfj6", +"j9jVjVjVjVkgjVjVk0jVjVjVjVjVj9jVj9jVjVjVjVkgjVjVk0jVjVjVjVjVj9jVj9jVjVjVjVkgjVjVk0jVjVjVjVjVj9jVj9jVjVjVjVkgjVjVk0jVjVjVjVjVj9jVj9jVjVjVjVkgjVjVk0jVjVjVjVjVj9jVj9jVjVjVjVkgl1kcigltl0ltltl1msl1msl1l0lOlPm1lhl1msl1lOl1ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixk0k0kgkbl1k0jVkbkbkbkgkgkgkgk0j9kblNkbkAk0kgj9jVkgj9kAhalufPg3fPfPfPg#lululug3fsfsfDfsfseBg3lufPfsg#fPfPg#eBiaeBeBfPfDixixixixixixixixixixixixixixixixixixixixixixixixkRm4mcfPf4fDlufsfsfsfDeBfPfPhxfPlulum5m6ixixixixixixixixixixixixixixixixixixixixixixixixfPmleBmlfsfDfDjfjfjf.qjf.q.qjf.qjfjf.2jf.2.2jf.2jfjf.qjf.q.q.U.q.U.U.q.U.q.qjf.qjfjfjfjfjfjf.qjf.q.q.U.q.U.Ujf.Ujfjfjfjfjfjf.2jf.2.2jf.2jfjfjfjfjfjf.qjf.q.qjf.qjfjf.2jf.2.2jf.2jfjfmX", +"k0jVj9kgjVjVkgjVkgjVj9k0jVkgjVjVk0jVj9kgjVjVkgjVkgjVj9k0jVkgjVjVk0jVj9kgjVjVkgjVkgjVj9k0jVkgjVjVk0jVj9kgjVjVkgjVkgjVj9k0jVkgjVjVk0jVj9kgjVjVkgjVkgjVj9k0jVkgjVjVk0jVj9kgjVjVmsl1msl1l1igmskclPltkcl1msm1l1l0l1l0l0lhmsmsixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixk0k0lhk0lNkglhl1k0k0kbkbl0k0kglhkgk0k0k0k0kbkglMkgk0jVkcaybPlufDfseBfseBeBfPluf4f4fPfPfDg3fsg#fseBlufslufsfDeBfseBlueBixixixixixixixixixixixixixixixixixixixixixixixixm7fsfsluf4f4luf4fPlufsfseBfDmcf4mcf4fPm8ixixixixixixixixixixixixixixixixixixixixixixixixfsfDfsfseBfsmljf.2.2jf.2jfjfjfjfjfjf.qjf.q.qjf.qjfjf.qjf.q.qjf.qjfjfjfjfjfjf.2jf.2.2.U.2.U.Ujf.Ujfjf.qjf.q.qjf.qjfjf.qjf.q.qjf.qjfjfjfjfjfjf.2jf.2.2jf.2jfjfjfjfjfjf.qjf.q.qjf.qjfjfmX", +"j9jVj9jVj9j9jVj9jVjVj9jVjVkgjVk0j9jVj9jVj9j9jVj9jVjVj9jVjVkgjVk0j9jVj9jVj9j9jVj9jVjVj9jVjVkgjVk0j9jVj9jVj9j9jVj9jVjVj9jVjVkgjVk0j9jVj9jVj9j9jVj9jVjVj9jVjVkgjVk0j9jVj9jVj9j9l1ltmsl1msl1l1msl0igltl1lhl1l1mslOmsl1l1ltlPixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixk0lhkAkAkgkglNkgkglNk0l1lhkbkAkgkgkgkAkgk0kblhkbkbk0kghIg3fsg#ayfslufDlulueBg3eBeBg3lufslufPfsg3g3eBmleBfsf4fsfPfPfPfPixixixixixixixixixixixixixixixixixixixixixixixm9fDlufslufsfsgIeBfPg#fsg#lulufseBfPmlf4fPn.ixixixixixixixixixixixixixixixixixixixixixixixfsfPfDfsfDeBfs.U.q.qjf.qjfjf.Ujf.U.Ujf.Ujfjfjfjfjfjf.Ujf.U.Ujf.Ujfjf.qjf.q.qjf.qjfjf.qjf.q.q.2.q.2.2jf.2jfjf.2jf.2.2.q.2.q.qjf.qjfjf.Ujf.U.U.q.U.q.qjf.qjfjf.Ujf.U.Ujf.Ujfjfjfjfjfjfn#", +"jVk0jVk0jVk0kgj9jVk0jVj9k0j9j9jVjVk0jVk0jVk0kgj9jVk0jVj9k0j9j9jVjVk0jVk0jVk0kgj9jVk0jVj9k0j9j9jVjVk0jVk0jVk0kgj9jVk0jVj9k0j9j9jVjVk0jVk0jVk0kgj9jVk0jVj9k0j9j9jVjVk0jVk0jVk0m1msltiglthmmshIhIl1mshImshIl1mshImsl1kcl1msixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixlOl1l0lhk0lOkAlNlhlhlhkgk0l1kbl1kAlMkgkgkgl1kglhk0mRmRhIfsg3lug3fDfseBfsfsg3fsg3g3fsmleBfslufslufDfseBg3g3fPg3fDfDfDfPixixixixixixixixixixixixixixixixixixixixixixixnalug3g3fPfPfPeBfPfsbPfPf4g#fPfsfDfsfsmlfDnbixixixixixixixixixixixixixixixixixixixixixixixg3lufDfPeBfDfsjfjfjf.qjf.q.qjf.qjfjf.qjf.q.q.U.q.U.Ujf.Ujfjf.Ujf.U.Ujf.Ujfjf.2jf.2.2jf.2jfjfjfjfjfjf.qjf.q.q.U.q.U.Ujf.Ujfjf.qjf.q.qjf.qjfjfjfjfjfjf.qjf.q.qjf.qjfjf.qjf.q.q.U.q.U.Uj6", +"jVkgkgj9jVj9jVjVk0k0jVjVkgjVk0jVjVkgkgj9jVj9jVjVk0k0jVjVkgjVk0jVjVkgkgj9jVj9jVjVk0k0jVjVkgjVk0jVjVkgkgj9jVj9jVjVk0k0jVjVkgjVk0jVjVkgkgj9jVj9jVjVk0k0jVjVkgjVk0jVjVkgkgj9jVj9l1msigkcmslthIlhigl1kcmsmsmsltl0ltlhmshIl1l1ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixk0k0khkgl1nck0k0k0kAl0lhk0lNlNlhk0lNkgkblMk0kgkgkgjVl1hxfsfslufseBg3eBg3lufPlufPfDfsg3g3lug#g3fDfPfPfPfPmleBfPeBeBeBfsixixixixixixixixixixixixixixixixixixixixixixndnefDmlfPhxfPmlfPmllug3fsfPf4fDg3fPfPfsfDfsnfndixixixixixixixixixixixixixixixixixixixixixixfDfPg#g#fPeBfP.Ujfjf.Ujf.U.Ujf.Ujfjf.Ujf.U.U.2.U.2.2.q.2.q.qjf.qjfjf.qjf.q.q.U.q.U.U.q.U.q.q.U.q.U.U.2.U.2.2jf.2jfjfjfjfjfjf.Ujf.U.U.U.U.U.Ujf.Ujfjf.Ujf.U.Ujf.Ujfjf.Ujf.U.U.2.U.2.2mX", +"k0jVj9jVk0k0kgk0j9jVkgk0j9k0jVkgk0jVj9jVk0k0kgk0j9jVkgk0j9k0jVkgk0jVj9jVk0k0kgk0j9jVkgk0j9k0jVkgk0jVj9jVk0k0kgk0j9jVkgk0j9k0jVkgk0jVj9jVk0k0kgk0j9jVkgk0j9k0jVkgk0jVj9jVk0k0hIgImsl1mslLhIigl1hImsmshIl1hIkcm1igmsltl0msixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixk0lOlOlPlNkglNl1lhl1k0k0k0kglhkglMl1l0k0kAkgk0k0k0k0k0hIg#fsfPmlf4g3g3lug3eBg#fsbPfseBfDfDg3fsmYlululufsfsmlfDfPmlg#mlixixixixixixixixixixixixixixixixixixixixixixngg3lululufsfsf4iafsg3g#mlfDf4fPfPfPfPfPfDg#fsnhixixixixixixixixixixixixixixixixixixixixixixfPeBfsg3fsf4fPjf.2.2jf.2jfjf.Ujf.U.U.q.U.q.qjf.qjfjf.Ujf.U.U.2.U.2.2.U.2.U.Ujf.Ujfjfjfjfjfjf.qjf.q.qjf.qjfjf.2jf.2.2.U.2.U.U.q.U.q.qjf.qjfjf.2jf.2.2jf.2jfjf.Ujf.U.U.q.U.q.qjf.qjfjfn#", +"k0jVk0k0j9kgj9jVj9k0j9kgjVkgj9j9k0jVk0k0j9kgj9jVj9k0j9kgjVkgj9j9k0jVk0k0j9kgj9jVj9k0j9kgjVkgj9j9k0jVk0k0j9kgj9jVj9k0j9kgjVkgj9j9k0jVk0k0j9kgj9jVj9k0j9kgjVkgj9j9k0jVk0k0j9kghIltmsgIigmshImshImsigltigmsmshIhIl1l0igl1jUixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixnik0kAlhk0lPlhlNk0lNj9lhl1k0l0k0k0lhkAkglhl1kAk0kbk0kAigfPeBeBfPeBfs.Ulug3lufsg3fDfPgff4mlfPg3fDg3fPg3g#fPfDfPfDfPfPfPixixixixixixixixixixixixixixixixixixixixixixnjg3hxlulufslueBfPeBfPg#fPg3fDlug3eBfsg3fPlug#nkixixixixixixixixixixixixixixixixixixixixixixhIfPfsfPfDlufD.q.U.U.2.U.2.2.U.2.U.U.2.U.2.2jf.2jfjf.Ujf.U.Ujf.Ujfjf.qjf.q.q.U.q.U.U.2.U.2.2.U.2.U.U.U.U.U.U.U.U.U.U.q.U.q.q.U.q.U.U.q.U.q.q.U.q.U.U.2.U.2.2.U.2.U.U.2.U.2.2jf.2jfjfn#", +"kgk0j9k0jVk0k0k0k0jVk0kgk0jVk0k0kgk0j9k0jVk0k0k0k0jVk0kgk0jVk0k0kgk0j9k0jVk0k0k0k0jVk0kgk0jVk0k0kgk0j9k0jVk0k0k0k0jVk0kgk0jVk0k0kgk0j9k0jVk0k0k0k0jVk0kgk0jVk0k0kgk0j9k0jVk0ighIkchIhxhIgIl1igmskchxl0igl1msigigigkchIjUixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixkglhncnclOkAk0l0lhkgl1lhl0l1lhk0kAk0l0lhlhkgk0l0k0k0l0hIfPmlfPfDg#fPeBfseBfPbPg3lug3eBg3lufslufsfsbPg3eBeBlueBlufsfPg3ixixixixixixixixixixixixixixixixixixixixixixnlg3eBeBmlf4mlmlmlfPf4fPfPfPg3fPfDfPlulugIeBmlnlixixixixixixixixixixixixixixixixixixixixixixfsfslFfsfPfPeB.2.q.q.U.q.U.U.q.U.q.qjf.qjfjf.2jf.2.2.2.2.2.2.U.2.U.Ujf.Ujfjf.Ujf.U.U.q.U.q.q.q.q.q.qjf.qjfjf.qjf.q.q.U.q.U.Ujf.Ujfjf.2jf.2.2.q.2.q.q.U.q.U.U.q.U.q.qjf.qjfjf.2jf.2.2n#", +"jVj9k0kgk0j9kgjVkgj9k0kgj9k0kgj9jVj9k0kgk0j9kgjVkgj9k0kgj9k0kgj9jVj9k0kgk0j9kgjVkgj9k0kgj9k0kgj9jVj9k0kgk0j9kgjVkgj9k0kgj9k0kgj9jVj9k0kgk0j9kgjVkgj9k0kgj9k0kgj9jVj9k0kgk0j9g#igighIhxhIl0ighImsl1hImshIhxmshImshImshImsixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixlPlNlNk0l0lNl1l0kgk0l0k0k0lNlhlOk0lhk0lhl0kAlPlNlNl1l0g#nmfsfPeBfsfPeBeBeBfDfDmlfPfPg3g3g3gfg#eBfDfDfDg3g3fPfPfPhxg3mlixixixixixixixixixixixixixixixixixixixixixixmOfPfPg#g#fPg#g#g3fPfPfPfsfDeBf4fPf4fPfPmleBf4mOixixixixixixixixixixixixixixixixixixixixixixg3ayfPnnfPfPfP.U.U.Ujf.Ujfjf.2jf.2.2.U.2.U.U.U.U.U.U.U.U.U.Ujf.Ujfjf.Ujf.U.U.2.U.2.2.U.2.U.U.U.U.U.U.U.U.U.U.2.U.2.2.U.2.U.U.q.U.q.q.U.q.U.U.U.U.U.Ujf.Ujfjf.2jf.2.2.U.2.U.U.U.U.U.Un#", +"kgk0kgjVj9k0k0kgk0k0j9k0k0kgk0k0kgk0kgjVj9k0k0kgk0k0j9k0k0kgk0k0kgk0kgjVj9k0k0kgk0k0j9k0k0kgk0k0kgk0kgjVj9k0k0kgk0k0j9k0k0kgk0k0kgk0kgjVj9k0k0kgk0k0j9k0k0kgk0k0kgk0kgjVj9k0ighIighImslLhIiakcighagIhIigmshIighIigigltgIixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixk0l1l0lhlPlhlNl1lhk0nck0l1lhlOlOlNlhl1kgk0k0kAlhk0lhlhigfseBfsfDfPfPfPgIfDfPfPeBgff4fPf4lug3g3fsg3gflugffDbPfPeBfDmlg3ixixixixixixixixixixixixixixixixixixixixixixmbf4gIeBfPfPfPg#lug#lumlfPmleBfPfDmlfPg3fPg#fPmbixixixixixixixixixixixixixixixixixixixixixixf4gIfPfPfPmllu.2.q.q.U.q.U.U.2.U.2.2.2.2.2.2.U.2.U.U.2.U.2.2.U.2.U.U.U.U.U.U.U.U.U.U.2.U.2.2.q.2.q.q.U.q.U.Ujf.Ujfjf.2jf.2.2.U.2.U.U.2.U.2.2.q.2.q.q.U.q.U.U.2.U.2.2.2.2.2.2.U.2.U.Un#", +"k0kgk0k0k0kgk0j9kgk0kgj9k0j9kgk0k0kgk0k0k0kgk0j9kgk0kgj9k0j9kgk0k0kgk0k0k0kgk0j9kgk0kgj9k0j9kgk0k0kgk0k0k0kgk0j9kgk0kgj9k0j9kgk0k0kgk0k0k0kgk0j9kgk0kgj9k0j9kgk0k0kgk0k0k0kghIhag#fPg#mshIhIhIkckciahIigighIighIhIighIltixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixl1l0nclhl0mslNlhlPlNl0ncnck0l1lOlOlhlhlNlNl1l0lhk0kAlhhIfDf4fDfsfsmlmlmlmlg3fPg3f4g#lufPfPf4fPg3g3fDg3gfgffPfDfPfPfPluixixixixixixixixixixixixixixixixixixixixixixixnofPfPfPfPfPfDfPlug#g#mlialuluf4mlf4f4lumlnpixixixixixixixixixixixixixixixixixixixixixixixfPfPfPgIfPfPml.U.2.2.U.2.U.U.U.U.U.U.q.U.q.q.q.q.q.q.U.q.U.U.2.U.2.2.2.2.2.2.q.2.q.q.U.q.U.U.2.U.2.2.U.2.U.U.U.U.U.U.2.U.2.2.U.2.U.U.U.U.U.U.2.U.2.2.U.2.U.U.U.U.U.U.q.U.q.q.q.q.q.qn#", +"k0kgj9k0kgk0k0kgk0kgk0k0kgk0k0j9k0kgj9k0kgk0k0kgk0kgk0k0kgk0k0j9k0kgj9k0kgk0k0kgk0kgk0k0kgk0k0j9k0kgj9k0kgk0k0kgk0kgk0k0kgk0k0j9k0kgj9k0kgk0k0kgk0kgk0k0kgk0k0j9k0kgj9k0kgk0fPg#fPhIfPfPhIighImsighIhIhxm1iggIhamsighIhIixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixl0lPl1l0l1l0l1lhltltlPlNnclOlhk0l0k0lPlNlhlOlPlhl0l1k0hIeBeBf4f4fDfPeBfPfPeBfPfPfPfPfDfPfsfsf4f4fPfPlug3g#fsfDfDmlfPfPixixixixixixixixixixixixixixixixixixixixixixixnqfPg#fDgIhafPfPfPfPfPg#g3fPg#g3fPfPfPmlfPnqixixixixixixixixixixixixixixixixixixixixixixixlufPlugIfPfPfP.q.U.U.2.U.2.2.U.2.U.U.U.U.U.U.U.U.U.U.U.U.U.U.U.U.U.U.U.U.U.U.U.U.U.U.2.U.2.2.2.2.2.2.U.2.U.U.2.U.2.2.U.2.U.U.2.U.2.2.q.2.q.q.U.q.U.U.2.U.2.2.U.2.U.U.U.U.U.U.U.U.U.Un#", +"k0lhk0kglhk0j9k0k0k0j9k0kgk0lhk0k0lhk0kglhk0j9k0k0k0j9k0kgk0lhk0k0lhk0kglhk0j9k0k0k0j9k0kgk0lhk0k0lhk0kglhk0j9k0k0k0j9k0kgk0lhk0k0lhk0kglhk0j9k0k0k0j9k0kgk0lhk0k0lhk0kglhk0hafPfPhIhIfPg#iafPigighahIhIfPkchIhxiaighmigixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixl1l0lhlhlhl1k0kcl0l1msl0l0lPlPlNmslOlhk0k0lOlNl1lNlhmsfPeBfPmlfPf4fDf4fDfDfDfPfsmlfPmlfPfPiagfg#hxnmfPfPfPfPg3fsfPg#f4ixixixixixixixixixixixixixixixixixixixixixixixnrnsfPg3g#fDg#fPfPfPf4fPfPfDfPg#fPiaialunsnrixixixixixixixixixixixixixixixixixixixixixixixhxhxg#mYfPhIfP.U.U.U.U.U.U.U.q.U.q.q.U.q.U.U.U.U.U.U.2.U.2.2.U.2.U.U.U.U.U.U.2.U.2.2.U.2.U.Uay.Uayay.Uay.U.U.2.U.2.2.U.2.U.Uay.Uayay.Uay.U.U.U.U.U.U.U.U.U.U.q.U.q.q.U.q.U.U.U.U.U.Un#", +"k0kglhk0kglhkglhkgk0lhk0k0lhkgkgk0kglhk0kglhkglhkgk0lhk0k0lhkgkgk0kglhk0kglhkglhkgk0lhk0k0lhkgkgk0kglhk0kglhkglhkgk0lhk0k0lhkgkgk0kglhk0kglhkglhkgk0lhk0k0lhkgkgk0kglhk0kglhiagIfPigfDhIfPigg#g#igg#igighIhaiahIiaigfPhIixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixl0msl1l0lhl1ntlhl1l1l0l0l0lhl0lPlhlhl0l1l0lOlhk0lPlhl0hIg3fsnumlmlfPf4fDf4g3fDfPhxfsg3mlfPmlfPfPiafPhxmlf4g3mlhxlug3g3iJixixixixixixixixixixixixixixixixixixixixixixixnvfPeBfPfPmlg#f4hIfPfPfPeBfPfPmlg#fPg3nwixixixixixixixixixixixixixixixixixixixixixixixiJfPfPfPmlfPlufP.2.U.U.2.U.2.2ay.2ayay.Uay.U.U.2.U.2.2ay.2ayay.2ay.2.2.U.2.U.Uay.Uayay.2ay.2.2ay.2ayay.2ay.2.2ay.2ayay.Uay.U.U.2.U.2.2.2.2.2.2.U.2.U.U.2.U.2.2ay.2ayay.Uay.U.U.2.U.2.2n#", +"kgk0kglhk0k0k0k0lhk0kgk0kgk0k0lhkgk0kglhk0k0k0k0lhk0kgk0kgk0k0lhkgk0kglhk0k0k0k0lhk0kgk0kgk0k0lhkgk0kglhk0k0k0k0lhk0kgk0kgk0k0lhkgk0kglhk0k0k0k0lhk0kgk0kgk0k0lhkgk0kglhk0k0fPiafPigg#hxiaighIgIg#hIg#igfPighIighahIiahxixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixlhkcl1ncl0l0msl0l0l1lhl1l0ncl0l0lhlPlPlhl1ncl0l1l0lOk0gff4fPfDg3iamlfPmllueBfDfDfDeBg#eBfPnmfPfPfDfDfDfPeBfsfPmlmlg3fPnxiJixixixixixixixixixixixixixixixixixixixixixixk4nyhxfPhxhxeBmYg#fPg#hafPhIeBhxfPfPnzk4ixixixixixixixixixixixixixixixixixixixixixixiJnxfPhxfPhxgIfPfP.Ufsfs.Ufs.U.U.U.U.U.U.2.U.2.2ay.2ayay.Uay.U.Uay.Uayay.2ay.2.2.U.2.U.U.U.U.U.U.U.U.U.U.U.U.U.U.2.U.2.2.U.2.U.Uay.Uayay.Uay.U.Ufs.Ufsfs.Ufs.U.U.U.U.U.U.2.U.2.2ay.2ayayn#", +"l0lhk0l0k0kglhk0lhkglhk0l0k0kgk0l0lhk0l0k0kglhk0lhkglhk0l0k0kgk0l0lhk0l0k0kglhk0lhkglhk0l0k0kgk0l0lhk0l0k0kglhk0lhkglhk0l0k0kgk0l0lhk0l0k0kglhk0lhkglhk0l0k0kgk0l0lhk0l0k0kghafPhag#fPhxfPhxhIigg#gIfPfPfPg#fPfPigighaigixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixlPlPntncmsl0msl1m1l0l0l1msl1l1msl1lhlhlPl0l0lhl1msl1nAfPfPeBf4fDf4fDg#fseBeBmlf4fDfPfDfPhIeBigfPfPfDfPmlfPeBg#fPfPhxfPlufPg3fPfPf4gffDfDfPg3fDfsfPg#hxfPfPhxfPfPigfPigfPiaf4g#f4f4fPf4fPfPg#fPg#gIiBhafPfPfPfPfPfPigg#fPfPfPfPfPfPfPfPfPfPg3fPfDfDfPfPmlhxmlg3mllufPmlgIfDf4.U.2.2.U.2.U.Ufs.Ufsfs.Ufs.U.U.2.U.2.2.U.2.U.U.U.U.U.U.2.U.2.2.U.2.U.Ufs.Ufsfs.Ufs.U.U.U.U.U.U.U.U.U.Uay.Uayay.2ay.2.2.U.2.U.U.2.U.2.2.U.2.U.Ufs.Ufsfs.Ufs.U.U.2.U.2.2n#", +"k0k0kgk0k0l0k0kgl0k0kgk0k0k0lhkgk0k0kgk0k0l0k0kgl0k0kgk0k0k0lhkgk0k0kgk0k0l0k0kgl0k0kgk0k0k0lhkgk0k0kgk0k0l0k0kgl0k0kgk0k0k0lhkgk0k0kgk0k0l0k0kgl0k0kgk0k0k0lhkgk0k0kgk0k0l0hxgIfPgIhafPhxfPfPg#igigighIfPg#hxg#igighIfPixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixl1m1l0l1hIl1l0ncncl1l1l1l0l0l0lhl1jUl1l0m1l0lhlhlhlhmshImYfPg3gImlfPfPmlg3eBiaeBeBmlfDfDfDfPfsiahxhahxgIfPfPgIfPfPgfmlfPfPhxg3gImlfPhIfDfPfPhafPfPfPlufPg3iahxg#fDiafDigfPeBhxg#g#g#fDhxfPigg3iggIg#hafPfPfDg#fPfPfPfPmlfPialufPhxg#hxhxhxfPhxfPhxg3f4fDfPigfPhxgIfPhag3fDfD.U.U.Uay.Uayay.Uay.U.Ufs.Ufsfs.Ufs.U.Uay.Uayayfsayfsfsayfsayay.Uay.U.U.2.U.2.2ay.2ayayfsayfsfs.2fs.2.2.U.2.U.Ufs.Ufsfs.Ufs.U.U.U.U.U.Uay.Uayay.Uay.U.Ufs.Ufsfs.Ufs.U.Un#", +"l0k0l0l0k0kgl0k0l0k0l0l0k0l0k0k0l0k0l0l0k0kgl0k0l0k0l0l0k0l0k0k0l0k0l0l0k0kgl0k0l0k0l0l0k0l0k0k0l0k0l0l0k0kgl0k0l0k0l0l0k0l0k0k0l0k0l0l0k0kgl0k0l0k0l0l0k0l0k0k0l0k0l0l0k0kgmlfPfPhxfPg3fPfPhxiafPmlgIfPhxigfPhIhxhIigfPixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixl1jUkcl0m1l0hIl1l1hIncncm1l1m1msl0l0msl1msncm1hIl0l1lhhIlueBmYg3eBg3f4g3fDfPeBfPfPgIgIfPfPf4fPfPfDg3fPg3hxfPfPmlfPfPfPfPg3fPg3fDfPg3lug3g3hIfsfPhxfDfPfPg3fPg3fPg#hxfDhxfPfDmYfPg#fPg#g#hxfDfPg3fPeBfPg#g#hafDigfPfPigighIfPhIg3iag3fDg3hxfPhxf4mlg3fPiag3fDhxfPfDfPfPfPmlha.2fsfs.Ufs.U.U.U.U.U.Uay.Uayay.Uay.U.Uay.Uayay.Uay.U.U.U.U.U.Ufs.Ufsfsfsfsfsfs.Ufs.U.Uay.Uayay.Uay.U.Uay.Uayay.Uay.U.U.2.U.2.2fs.2fsfs.Ufs.U.U.U.U.U.Uay.Uayay.Uay.U.Un#", +"lhkglhk0l0k0k0lhk0k0lhkgk0l0k0l0lhkglhk0l0k0k0lhk0k0lhkgk0l0k0l0lhkglhk0l0k0k0lhk0k0lhkgk0l0k0l0lhkglhk0l0k0k0lhk0k0lhkgk0l0k0l0lhkglhk0l0k0k0lhk0k0lhkgk0l0k0l0lhkglhk0l0k0fPfPg3hxgIiahxfPhag3gIg#iafPfPigigfPigighag#ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixhIl1hIl1l1l0l0l0l0m1l1l1l1l0l1l0m1l0lPlhncncl1l0l1msl0g#fPf4fPg3g3g3fPg3fsfDg3fDfDfDg3eBeBgIfPfDfPfDigfDeBfPeBigighxfPfPfPfDfPeBeBhxfDhxmlfPfPhIhIgffPfPfPgIfPfPfPfPgIfPf4fDmlfDfPigg3hahafPhafDfPfPg3hxfPg#g#g#g#hafDfPfPgIfPhIhIg#fPiafPigfPgIigfPhxhxhxg3g#fPfPgIfPgImlgIfsfsfs.Ufs.U.Ufs.Ufsfs.2fs.2.2.U.2.U.Ufs.Ufsfs.Ufs.U.Uay.Uayay.Uay.U.U.U.U.U.Ufs.Ufsfs.Ufs.U.Ufs.Ufsfsayfsayay.Uay.U.Ufs.Ufsfsfsfsfsfs.Ufs.U.Ufs.Ufsfs.2fs.2.2.U.2.U.Un#", +"k0l0k0l0k0l0l0lhk0l0k0l0l0lhlhk0k0l0k0l0k0l0l0lhk0l0k0l0l0lhlhk0k0l0k0l0k0l0l0lhk0l0k0l0l0lhlhk0k0l0k0l0k0l0l0lhk0l0k0l0l0lhlhk0k0l0k0l0k0l0l0lhk0l0k0l0l0lhlhk0k0l0k0l0k0l0fPfPfPfPfPfPfPf4f4hxfPfPfPg#fPgIgIfPigfPhxfPixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixl1hIhImsl1hIl1kcm1msnBl0l1hIl1hIkckcm1l0l0hIl0l1l1msmshafsfPf4fPlug3hxeBeBfPeBfDfPf4mlfDeBeBeBeBg3f4eBfDfDfPfDfPfPmlgImlmlfPmlfPgIeBfPfPfPg3fPg3g3fPfDiahxfPfPfPg3mlg3hahIfPhafPfPfPigfDfPhxfPhIhxfDfPfDfDfPfPgIhafPigg#fDfDfPfDeBfPg#g#g#fPiaigigf4fPmlmlhxmlhxfPfDigfDfPfP.U.U.Uay.Uayay.Uay.U.Ufs.Ufsfsfsfsfsfs.Ufs.U.Ufs.Ufsfs.Ufs.U.Ufs.Ufsfs.Ufs.U.U.U.U.U.U.U.U.U.Ufs.Ufsfs.2fs.2.2fs.2fsfs.Ufs.U.U.U.U.U.Uay.Uayay.Uay.U.Ufs.Ufsfsfsfsfsfsn#", +"k0l0l0l0k0lhk0k0l0l0k0k0l0k0l0k0k0l0l0l0k0lhk0k0l0l0k0k0l0k0l0k0k0l0l0l0k0lhk0k0l0l0k0k0l0k0l0k0k0l0l0l0k0lhk0k0l0l0k0k0l0k0l0k0k0l0l0l0k0lhk0k0l0l0k0k0l0k0l0k0k0l0l0l0k0lhg3hxg3fPfPfPhxhxfPfPmlfPfPg3gIhahafPfPgIhxigixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixhIltl0l0hIhIl1l1l1l1m1l0msm1m1l1l1msl1l1kcm1l0l0ltk0hIhxeBeBeBfsg3g3g3fPg3hxg3fPfPgIg#fPmlfPfPfPfPiag#gImleBfPfDfDfDeBeBfPigfPmlfPfPfPhIhxeBhxfPfPhxg3fPgIgfhafDfPnCg#g#fDfPhahIhag#hxhxhxfPhxfPg#iag#g#fPiafPg#hIighahaighahaigfPfPigigigg#hIg#iaiafPfPeBgIfDmlhxhxhxhagIfPfs.U.Ufs.Ufsfs.Ufs.U.UeB.UeBeBfseBfsfsayfsayay.Uay.U.Uay.UayayeBayeBeBfseBfsfsfsfsfsfsfsfsfsfs.Ufs.U.U.U.U.U.Ufs.Ufsfsfsfsfsfs.Ufs.U.Ufs.Ufsfs.Ufs.U.UeB.UeBeBfseBfsfsn#", +"l1k0lhk0l0l1l0l1lhk0l0l0lhl0k0l0l1k0lhk0l0l1l0l1lhk0l0l0lhl0k0l0l1k0lhk0l0l1l0l1lhk0l0l0lhl0k0l0l1k0lhk0l0l1l0l1lhk0l0l0lhl0k0l0l1k0lhk0l0l1l0l1lhk0l0l0lhl0k0l0l1k0lhk0l0l1fDmlfPhxfPg3fPg3fPfPhxfPf4hxf4mlg#luhxhahxgIixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixkchIhIigltl0jUhImshIhIhIl1l0msl0msighIl1l1ncl1m1msm1l1fPfPfPfDfPfDfPfPmlfPg3fPg3eBeBg3fPfPeBf4fPmleBmYeBeBg#fPfPmlg#fPigg#fPhIigigfPfPmlfPfPg#fPhxfPfPfDfPfPfDfPgIg3fPfPhxfPfPg3fPhag3gIhahxigfPighxhxgIhag#hafDfPfDeBg3fPg#fPg#igfDfPfPfPigfPhIighIfPg#jUighaighxfPhxfPfPhx.Ufsfs.Ufs.U.Ufs.Ufsfsayfsayay.Uay.U.Ufs.Ufsfsfsfsfsfsfsfsfsfs.Ufs.U.U.U.U.U.Uay.Uayay.Uay.U.Ufs.UfsfseBfseBeBfseBfsfs.Ufs.U.Ufs.Ufsfs.Ufs.U.Ufs.Ufsfsayfsayay.Uay.U.Un#", +"l0k0l1l0lhl0k0k0lhl0lhl0k0l0lhl0l0k0l1l0lhl0k0k0lhl0lhl0k0l0lhl0l0k0l1l0lhl0k0k0lhl0lhl0k0l0lhl0l0k0l1l0lhl0k0k0lhl0lhl0k0l0lhl0l0k0l1l0lhl0k0k0lhl0lhl0k0l0lhl0l0k0l1l0lhl0fPfPeBfsf4fPmlfPfDg3g3fPfPhxfPf4mlhxgIg3fPfPixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixmskchImshIhIhajUhIjUltl1hIl1hIl1hIhIl1l0mshImsl1l1l1msfPg#eBg3fDeBeBlneBg3f4mlfPmlfPfPfPfPfPfPgIfPmllufPhxfPgIfPgIfPgImlfPfPg3fPg#hxfPhxfPfPhxg#gIhIhahxhxhxfPhxfPgIfPhIfPfPfPfPfPg#fPgIhaiafPg#g#hxg3hxmYgIfPhagIhahIigigigfPighxigfPhafPiggIiggIhxmshxiahIg3hIfPg3hxhxhxfPayeBeBfseBfsfsfsfsfsfsfsfsfsfs.Ufs.U.UeB.UeBeB.UeB.U.Uay.UayayfsayfsfsfsfsfsfsfsfsfsfsfsfsfsfsfsfsfsfsayfsayayfsayfsfsayfsayayeBayeBeBfseBfsfsfsfsfsfsfsfsfsfs.Ufs.U.UnD", +"l0l0lhl1k0l0l0l1l0k0l1l0l0k0l0l1l0l0lhl1k0l0l0l1l0k0l1l0l0k0l0l1l0l0lhl1k0l0l0l1l0k0l1l0l0k0l0l1l0l0lhl1k0l0l0l1l0k0l1l0l0k0l0l1l0l0lhl1k0l0l0l1l0k0l1l0l0k0l0l1l0l0lhl1k0l0fslufPfPlueBmlg3f4fPg3g3fPfPfPhxfPfPgIfPfDfPixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixl1jUhIighIl1l1hIhIl0g#hIhIhImsmsl1l1m1kcm1msmshIl1nchIgIfPf4fPmlfPfPlufsg3fDeBg#gfg3g3fDg3fPg3gIgIfPfPg3g3fPfDg3g3fPg3hagff4g#f4fDfPeBfPg#hxigfPmlg#g3hIfPhxiafPhxfPg3g3g3gIgfhxhxhxfPfPfPgIfPgIhag#hxfPg#hIfPg#gIg#iag#fDfPfDigigfPhag#g#iggIfPhxhIg3hIhIfPhIg3fPhIfPigigfPfsayayeBayeBeBayeBayay.Uay.U.Ufs.Ufsfsfsfsfsfsfsfsfsfs.Ufs.U.UeB.UeBeB.UeB.U.Ufs.Ufsfs.Ufs.U.Uay.UayayeBayeBeB.UeB.U.Ufs.UfsfsayfsayayeBayeBeBayeBayay.Uay.U.Ufs.Ufsfsn#", +"k0l0l1l0l0lhl0k0l0l0l1l0lhl1l0lhk0l0l1l0l0lhl0k0l0l0l1l0lhl1l0lhk0l0l1l0l0lhl0k0l0l0l1l0lhl1l0lhk0l0l1l0l0lhl0k0l0l0l1l0lhl1l0lhk0l0l1l0l0lhl0k0l0l0l1l0lhl1l0lhk0l0l1l0l0lhfPfsfsf4fsg3fPfDeBeBfPfPfPfPeBfPg3g3fPfPf4fPixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixhImshImshIiakchImshIjUhIhIhIhIhIl1l1l1l1hIhIigm1l0ighIgIg3fDfPeBbPf4mclulug3fDfDfPhafPg#g#fPg#hxfPfPfPgfg#gIfPfPfPhxfPfPiafPgIeBmlfPfPfPfPhIighIjUeBg#fPfPhIg#fPhxiahxg#g#hagIhIg#gIiahxiafDfPg3fPgIg#hxhxhIigfPfPiahahahIgIhafPighIiggIhahxgIhahafPfPfPg#hxigiahxigiafPfPg#fseBeB.UeB.U.Ufs.UfsfseBfseBeBeBeBeBeBfseBfsfs.Ufs.U.Ufs.UfsfsfsfsfsfseBfseBeBfseBfsfseBfseBeBfseBfsfsfsfsfsfsayfsayayfsayfsfseBfseBeB.UeB.U.Ufs.UfsfseBfseBeBeBeBeBeBn#", +"l0l1l0k0lhl0l1l0l1l0k0l0l1l0l0l0l0l1l0k0lhl0l1l0l1l0k0l0l1l0l0l0l0l1l0k0lhl0l1l0l1l0k0l0l1l0l0l0l0l1l0k0lhl0l1l0l1l0k0l0l1l0l0l0l0l1l0k0lhl0l1l0l1l0k0l0l1l0l0l0l0l1l0k0lhl0fsfDf4fPfPg3fDeBg3fDfseBeBmlfPfDlug#fPhxfPfPixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixmsl1igkchxjUl0hIlLhIhIkchIigjUhIltjUigjUmsl1l1kchIhIm1fPlufPlueBfPfDfPhafPg#iafPg#eBfPfPf4eBfPlufPfPg3fPgIeBfPeBg3mlfDmlfPfPfPhafPgIg#gImlfPfPg3g#ighxgImlfPfPgIfPhIfPhafPhxhxfPgIgIfPgIhxhxhxgIhxhafPhagIhahag#jUhxiahxgIg#hafPg#hafPhIigfPigfPg3hxg#fPfPhIfPg#hIigigmsfPiafsayayfsayfsfsfsfsfsfsfsfsfsfsfsfsfsfsfsfsfsfsfsfsfsfseBfseBeBeBeBeBeBfseBfsfsayfsayayfsayfsfs.Ufs.U.Ufs.UfsfseBfseBeBfseBfsfsayfsayayfsayfsfsfsfsfsfsfsfsfsfsfsfsfsfsn#", +"l1l0l0l1l1l0l0lhl0l1l0l0l0k0l0l1l1l0l0l1l1l0l0lhl0l1l0l0l0k0l0l1l1l0l0l1l1l0l0lhl0l1l0l0l0k0l0l1l1l0l0l1l1l0l0lhl0l1l0l0l0k0l0l1l1l0l0l1l1l0l0lhl0l1l0l0l0k0l0l1l1l0l0l1l1l0g3fPg3fsfseBf4fPg3lulufDg3eBfDf4fPfDfPlug3g3ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixigiahImsl1ighIjUial0hIigigkchIhIhIhIjUltjUigl1hIhIl1kcg#g#mlg#lulufDfPfDfDlufPg3mlg#g3fPfPfDfDfPfPfPfPg#g#fPfPfPhagIfPfPfPmlfPhxfPhafPhahafPg#fPighIfPfPfPhIhIg#g#mlg#iaiahxhxhxfPg#mlg#g#gIhIhxhxfPhxfPgIeBhaiafPhafPiaiaigg#hxhxgIhahahag#hIg#hIiggIhahaighafPfPhIighIhIigeBfsfseBfseBeBeBeBeBeBayeBayayfsayfsfseBfseBeBfseBfsfsfsfsfsfsayfsayayeBayeBeBfseBfsfseBfseBeBeBeBeBeBfseBfsfsfsfsfsfseBfseBeBfseBfsfseBfseBeBeBeBeBeBayeBayayfsayfsfsnD", +"l0l0l0l0l0l1l1l0l1l0l1l1l0l1l0l0l0l0l0l0l0l1l1l0l1l0l1l1l0l1l0l0l0l0l0l0l0l1l1l0l1l0l1l1l0l1l0l0l0l0l0l0l0l1l1l0l1l0l1l1l0l1l0l0l0l0l0l0l0l1l1l0l1l0l1l1l0l1l0l0l0l0l0l0l0l1lug3lufsfslufseBf4fPg3f4eBg3g3fDmlmlfPfsf4fDixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixhahxighIigigkcl1hIhIiajUhIhIkcl1hIhIighIhIhIfPhIhIigmshIg#eBeBfDg#lufPfPfPg#eBfDg#fPfPhxlulufDfDfPhaf4gfg#lufPfPfPfPgIfPgffPfPmlfPhxfPfPfPgfgIhag#fPigfPfPfPigighIhamlg#g#fPgIhIhxiafPiag#g#gIgIhIgIiafPfPg#gIhahahagIhahxhxigigfPhIgIgIhafPhIhIfPg#ighIhahagIgIhafPigigigigfseBeBfseBfsfsfsfsfsfseBfseBeBfseBfsfseBfseBeBeBeBeBeBfseBfsfseBfseBeBfseBfsfsfsfsfsfseBfseBeBfseBfsfseBfseBeBfseBfsfsfsfsfsfseBfseBeBfseBfsfsfsfsfsfseBfseBeBfseBfsfsnD", +"l0msl1l0msl0l0l0l1l0l0l0l0l0msl1l0msl1l0msl0l0l0l1l0l0l0l0l0msl1l0msl1l0msl0l0l0l1l0l0l0l0l0msl1l0msl1l0msl0l0l0l1l0l0l0l0l0msl1l0msl1l0msl0l0l0l1l0l0l0l0l0msl1l0msl1l0msl0mlfsfPf4g3eBg3g3fseBeBmlf4fDfsg3g3g3lufDfPf4ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixgIhIiajUhIiahIkchIhIgIhIhIhxhxiagIhIl1l1l1hIjUg#jUjUigfPmlfPfDfDmlg#f4iaialufPeBfDfPf4fPfPfPmlg#fPfPfPfDfDfPfDlufPg#fPfPiahIhahIhxfPhxfPfPiafPg#hagIfPfPfPmlhIhIigg#igigiggIfPfPfPhIhxhxg#fPfPgImlg#gIhahahaiafPfPfPgIfPfPiggIhIg#hxhIigigighafPhahafPg#g#hIhIhIighakchIhIg#fseBeBfseBfsfsfsfsfsfsfsfsfsfsfsfsfsfsfsfsfsfsfsfsfsfseBfseBeBfseBfsfsfsfsfsfsg3fsg3g3fsg3fsfsfsfsfsfseBfseBeBg3eBg3g3fsg3fsfseBfseBeBfseBfsfsfsfsfsfsfsfsfsfsfsfsfsfsn#", +"l1l0msl0l0l1l0hIl0l1hIl1l0msl0l0l1l0msl0l0l1l0hIl0l1hIl1l0msl0l0l1l0msl0l0l1l0hIl0l1hIl1l0msl0l0l1l0msl0l0l1l0hIl0l1hIl1l0msl0l0l1l0msl0l0l1l0hIl0l1hIl1l0msl0l0l1l0msl0l0l1fseBfsfseBfPg3fslng3fsfseBf4f4fPg3fsg3fDfsfPixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixiahxgIg#iahxhIhIhIgIkchIighIhaiaiahIhIgIl1hIhIl1hIhIg#iafPmlfPeBfDfPmlg#fDfPg#hIigfPfPfDfPfDfPg3fPfPfDfPfDiafDfPfPfPg3fPhxfPiagIgIgImlfDmlg3fPg#fPfPg#gIfPhxfPigiahIfPfPhIfPhIigmlhIfPkchxhxhxfPfPg#mlg#gIhxhahIhahxhIfPfPgIfPgIgIhag#hxigiaighIighaijhafPigfPigigiggIhaigg#fseBeBfseBfsfsg3fsg3g3eBg3eBeBfseBfsfsfPfsfPfPfsfPfsfseBfseBeBfPeBfPfPfsfPfsfseBfseBeBfseBfsfsg3fsg3g3fsg3fsfsfsfsfsfsfsfsfsfseBfseBeBfseBfsfsg3fsg3g3eBg3eBeBfseBfsfsnE", +"l0l0l0msl1l0l1l0msl0l0l1l0l1l0msl0l0l0msl1l0l1l0msl0l0l1l0l1l0msl0l0l0msl1l0l1l0msl0l0l1l0l1l0msl0l0l0msl1l0l1l0msl0l0l1l0l1l0msl0l0l0msl1l0l1l0msl0l0l1l0l1l0msl0l0l0msl1l0fseB.UeBfsfseBfsmlfsg3g3g3fsfDf4mleBf4f4g3luixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixigiahxhIhIhIgIighahxiagIhIhIhIgIkciahxjUgIhIhIighIhIhIg#fPg#g#fPhaeBfPfPeBfPmlfPfPg3g3g3fDfPfDfPgIfPmlg#g3lufDfDfPg#gIg#fPiag#hxg#gIhIfPfPiafPhIhIhxhIfPgIg#gIg#hamlfPfPfPfPighIhIg#hag#fPiahIhxhIfPg#hxfPfPhIhIhIfPhaiag#hxhafPgIgIgIhaiahxg#fPfPiaighagIhIhaiam1hxhIigfPhIfsfPfPfsfPfsfseBfseBeBfseBfsfsg3fsg3g3eBg3eBeBg3eBg3g3fsg3fsfsfsfsfsfseBfseBeBfseBfsfseBfseBeBfseBfsfseBfseBeBg3eBg3g3fsg3fsfsfPfsfPfPfsfPfsfseBfseBeBfseBfsfsg3fsg3g3nD", +"hImsl1hIl1l0msl1l1l0hIl0hIl1l0l1hImsl1hIl1l0msl1l1l0hIl0hIl1l0l1hImsl1hIl1l0msl1l1l0hIl0hIl1l0l1hImsl1hIl1l0msl1l1l0hIl0hIl1l0l1hImsl1hIl1l0msl1l1l0hIl0hIl1l0l1hImsl1hIl1l0fsfsfs.UfsfsfsfseBfsbPbPfsfsfseBfDfseBfsg3g3ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixigiggIhxiggIgIgIfPgIhafPhxgIhxgIkckchIighIg#hIhIgIighImleBeBfsg#eBmlg#fPmleBfPfPg#g#g#fPfPlugIfPfDfDfDfDg#lug#fPfDgIfPhxhIfPgIfPmlfPfPfPhxfPhaf4fPfPfPfPigfPgIhIhIhahIfPg#fPg#fPigigigmlgIg#gIg#gIhahahxiag#hxfPg#kcg#hxighxhafPgIhagIgIg#hxhafPfPhahxgImshaighagIhIhIhIg#fPeBfsfsfsfsfsfsfPfsfPfPeBfPeBeBfseBfsfsfsfsfsfseBfseBeBfseBfsfseBfseBeBfPeBfPfPfsfPfsfseBfseBeBfseBfsfsfPfsfPfPfsfPfsfseBfseBeBfseBfsfsfsfsfsfsfPfsfPfPeBfPeBeBfseBfsfsn#", +"l1l0l0l0l1hIl0l0hIl1l0l1l1l0hIl0l1l0l0l0l1hIl0l0hIl1l0l1l1l0hIl0l1l0l0l0l1hIl0l0hIl1l0l1l1l0hIl0l1l0l0l0l1hIl0l0hIl1l0l1l1l0hIl0l1l0l0l0l1hIl0l0hIl1l0l1l1l0hIl0l1l0l0l0l1hIfslu.UfsfsfsfseBfsfseBeBfsf4lu.Ug3eBeBeBfDeBixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixgIiagIigfDgIigigigiafPgIhahaiaiaigiahIkcg#hIkchIhIhIgIfPfPfPg3hxfPfPg#fPfPfPg#fPfPfDfPmlfPg#lug#hxg#hxfPiafPiafDfPg#fPfDfPigg#hIgIg#hafPhxhxhIfPfPhIfPighIfPhxfPfPhIgIhIhIfPhIfPfPhIfPhIfPhIg#haigmshIighxfPhIiaiafPg#mlfPiafPhIg#jUg#mshIfPmshahahafPg#iahaiamsigiahIiag#g#eBeBeBfPeBfPfPfsfPfsfsfPfsfPfPfsfPfsfsg3fsg3g3fPg3fPfPfPfPfPfPeBfPeBeBfseBfsfsg3fsg3g3fPg3fPfPfsfPfsfseBfseBeBfPeBfPfPeBfPeBeBeBeBeBeBfPeBfPfPfsfPfsfsfPfsfPfPfsfPfsfsnD", +"hIl1hIhIl0l0hIl1hIl0hIhIl0hIl1l0hIl1hIhIl0l0hIl1hIl0hIhIl0hIl1l0hIl1hIhIl0l0hIl1hIl0hIhIl0hIl1l0hIl1hIhIl0l0hIl1hIl0hIhIl0hIl1l0hIl1hIhIl0l0hIl1hIl0hIhIl0hIl1l0hIl1hIhIl0l0fs.UfseB.U.Ufsk#fseB.UfseB.UeBfsfsfsfsg3eBfsixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixhahIg3hIfPhafPgIhIfPighIiagIg#hxjUg#gIlLgIhIg#fPhIhxhIg#fDf4fDg3f4eBmleBfPg#mlfPfPfPigigfPmlg#lufPg3fPg3hxfPiamlfPgIfDiafPfDigfPfPfPf4fPfPhahxgIgIgIgIfPfPfPfDg3g#hxfPhxfPgIg#mlfPfPfPg#fPigfPgIigfPmlfPfPhIfPhIhIhahIhIhImlhIgIhIhxfPiaiahxiagIfPighahIhIfPhIigfPhxgIhxhahIfsfPfPfsfPfsfseBfseBeBfPeBfPfPfsfPfsfseBfseBeBfseBfsfsfsfsfsfsfPfsfPfPfPfPfPfPfsfPfsfsg3fsg3g3fsg3fsfsfPfsfPfPfsfPfsfsfsfsfsfsfPfsfPfPfsfPfsfseBfseBeBfPeBfPfPfsfPfsfsnD", +"hIl0msl1hIl1l0hIl0l1hIl0l1hIl0hIhIl0msl1hIl1l0hIl0l1hIl0l1hIl0hIhIl0msl1hIl1l0hIl0l1hIl0l1hIl0hIhIl0msl1hIl1l0hIl0l1hIl0l1hIl0hIhIl0msl1hIl1l0hIl0l1hIl0l1hIl0hIhIl0msl1hIl1ay.UfsfseBfsfsfsfsfs.UlnfseBeBeBfseBfsfsfsg3ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfPgIg#gIgIgIg#gIhahaiggIgIiagIhIiahaiahIhIiggIhIiggIkchxfDg3g#fDfDfPeBfPfPg#g#g#g#g#fPfPfPgIfPg#g#g#fPg#g3fPg3gIighxiaiaiafPiag3g3igfDgIhxg#iahahafPfPfPfPhIfPiaiafPigfPfPfPfPhahahIg#hIhIighIfPfPfPhahIigg#hIg#hIhIfPiaiahaiahIhIg#ighIiahIiahahag#hIhIiagIhIiaiahahImsighIfPfPfPfsfPfsfsfPfsfPfPfsfPfsfseBfseBeBfPeBfPfPeBfPeBeBfPeBfPfPeBfPeBeBeBeBeBeBfPeBfPfPeBfPeBeBfPeBfPfPg3fPg3g3eBg3eBeBfPeBfPfPfPfPfPfPfsfPfsfsfPfsfPfPfsfPfsfseBfseBeBnE", +"l0hIl0hIl1hIhIhIl0hIl1hIhIl1hIl1l0hIl0hIl1hIhIhIl0hIl1hIhIl1hIl1l0hIl0hIl1hIhIhIl0hIl1hIhIl1hIl1l0hIl0hIl1hIhIhIl0hIl1hIhIl1hIl1l0hIl0hIl1hIhIhIl0hIl1hIhIl1hIl1l0hIl0hIl1hI.U.Ufs.2fsfsfsayayeBfs.UeB.U.UfseBfseB.UeB.UixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixigfDfPfDigmlgIfPfPfPfPhahIfPiaighxfPiafPhahxhIgIgIhxiafPfsfDmlfDeBf4fPf4f4fPf4fPg#lueBfPmlfPfPfPeBfPg#g#g#fPg#fPfPfDgIfPmliaf4iafPg3fPfPfPfPfDfPfDgImlhagIhxgIhxg#fPg#hIhIhxhIfPfPgIhIgImlhImlhIigfPfPigfPg#ighIhIigigfPhxfPhxiag#fPg#g#g#hxhIhIhIfPmsfPf4hIgIhIhxg#hIg#g#hxeBfsfsfPfsfPfPeBfPeBeBfPeBfPfPfPfPfPfPfsfPfsfsfPfsfPfPfsfPfsfsfPfsfPfPfsfPfsfseBfseBeBeBeBeBeBfPeBfPfPfsfPfsfsfPfsfPfPeBfPeBeBfseBfsfsfPfsfPfPeBfPeBeBfPeBfPfPfPfPfPfPn#", +"l1hIhIhIl0msl0l1hIhIl1l0hIl1hIl0l1hIhIhIl0msl0l1hIhIl1l0hIl1hIl0l1hIhIhIl0msl0l1hIhIl1l0hIl1hIl0l1hIhIhIl0msl0l1hIhIl1l0hIl1hIl0l1hIhIhIl0msl0l1hIhIl1l0hIl1hIl0l1hIhIhIl0mseBfs.U.Ufsfsfsfs.2eBayfsfs.UfsbPfsfsfseBfseBixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixighIhIfPfDfPgIigfPgIiahIigg3hagIgIgIhIigfPiagIhaighIfPfPg3fDfPfseBeBeBg#fPfPfPfPigigfPfPeBg#g#fPfPfPfPigfPfPg#fPfPg#lug3fDigfDhxiaiaiahaigg3igfPfDgIg#iagIfPhafPhxhIiagIfPiahIhIhIiafPfPhIhahIfPg#iag#iafPfPhIhIhIhIhIhIigkckchIhxhIjUhIigfPhIfPhahIhxiag#hag#ighIhIhIhIhaiafPeBeBfPeBfPfPfsfPfsfsfPfsfPfPfPfPfPfPg3fPg3g3eBg3eBeBg3eBg3g3fPg3fPfPfPfPfPfPfPfPfPfPfPfPfPfPfsfPfsfseBfseBeBfPeBfPfPfPfPfPfPeBfPeBeBfPeBfPfPfsfPfsfsfPfsfPfPfPfPfPfPnD", +"hIl1hIl1hIhIhIhIhIl0hIhImshIl1hIhIl1hIl1hIhIhIhIhIl0hIhImshIl1hIhIl1hIl1hIhIhIhIhIl0hIhImshIl1hIhIl1hIl1hIhIhIhIhIl0hIhImshIl1hIhIl1hIl1hIhIhIhIhIl0hIhImshIl1hIhIl1hIl1hIhI.U.UbPeBbP.U.2.U.2ayfsfsayfsay.U.UfsfsfsfsfsixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfPmlfPg3fPhIg#fPfPfDmlfPfPigg3hafPfPfPgIgIhIgIfPgIg#fPmYg#fPg3fPfPfDfDfPg#g#g#fDfDf4fseBfPg3mlg#eBmlfPmlfPfPfPg#fPg#fPfPhIfPgIiggIfPiamlfPiafPfPgIfPiafPfDiamlhagIhxfPfPfPiahIfDmlhIhxighIfPhIfPhIg#iaighIg#hIfPhIigg#gIigiamliaigfPhxhxhxjUhxhIighIhxighIhIhahImsfPhIgIhakcfsfPfPfsfPfsfsfPfsfPfPg3fPg3g3fsg3fsfsfPfsfPfPfPfPfPfPfPfPfPfPeBfPeBeBfseBfsfsg3fsg3g3fsg3fsfsfPfsfPfPfPfPfPfPfPfPfPfPfsfPfsfsfPfsfPfPfsfPfsfsfPfsfPfPg3fPg3g3fsg3fsfsnE", +"hIl0hIhIhIhIl1l1hIhIl1hIl0hImshIhIl0hIhIhIhIl1l1hIhIl1hIl0hImshIhIl0hIhIhIhIl1l1hIhIl1hIl0hImshIhIl0hIhIhIhIl1l1hIhIl1hIl0hImshIhIl0hIhIhIhIl1l1hIhIl1hIl0hImshIhIl0hIhIhIhIk#bP.2bPaybPaybP.U.U.Ufs.2fs.Uayfsfs.U.Uk#.UixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfPmlmlfPmlfPfPiafPnFgIfPgIhafPgIiag3ighafPgIhxgIiagIhImlfDeBg3fPlug3g3fPeBf4g3g#g3g#g3fPfPfPfPigfPeBeBg#fPfPfPfPgIhxfPfPg#g#g3hIfPgIfPigiahxgIiagIhahagIgIigiagIiagIfPhaigfPgIfPhahIhaighIfPhxfPiahIfPhIg#ighxhIighIhIhIhIhIg#hIhIlLhxhIhxhIhahIhaiamsigiahIhxhIiahahIhahIigfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPeBfPeBeBfPeBfPfPfsfPfsfsg3fsg3g3fPg3fPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPg3fPg3g3fPg3fPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPeBfPeBeBnE", +"hIhIhIhIl1hIhIhIhIl1hIhIhIl1hIhIhIhIhIhIl1hIhIhIhIl1hIhIhIl1hIhIhIhIhIhIl1hIhIhIhIl1hIhIhIl1hIhIhIhIhIhIl1hIhIhIhIl1hIhIhIl1hIhIhIhIhIhIl1hIhIhIhIl1hIhIhIl1hIhIhIhIhIhIl1hI.U.U.Uayay.2bP.2aebP.U.2.Uk#fsfs.2eBeBfsfs.UixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfPeBfPfPfPfPfPmlg3hIfPg3fPfPfPfPfPgIiaiaiaigfPfDgIigfPfPf4f4fDg3g3g#eBfDfDg3fPfDfPeBeBg#fDfPfDhxigfPfPeBg#fPg#fPfPfPg3igg#fPg#fDgfg#g3fPfPfPgIiafPiag3hafPgIiggIgIfDiag#g#hag#iggIigiagIg#igiaighIg#fPfPg#hIhahIigg#fPg#fPhIfPhIhIhIhIg#g#kchIhxiahIiahIhIfPhIgIhakciahIjUg#fPg3g3fPg3fPfPfPfPfPfPeBfPeBeBfPeBfPfPfPfPfPfPfPfPfPfPeBfPeBeBfPeBfPfPeBfPeBeBfPeBfPfPeBfPeBeBg3eBg3g3fPg3fPfPeBfPeBeBfPeBfPfPg3fPg3g3fPg3fPfPfPfPfPfPeBfPeBeBfPeBfPfPnE", +"l1hIhIhIhIhIhIl1hIhIhIhIhIhIhImsl1hIhIhIhIhIhIl1hIhIhIhIhIhIhImsl1hIhIhIhIhIhIl1hIhIhIhIhIhIhImsl1hIhIhIhIhIhIl1hIhIhIhIhIhIhImsl1hIhIhIhIhIhIl1hIhIhIhIhIhIhImsl1hIhIhIhIhI.U.U.U.U.qj2.Uk#.2.2.U.UbP.U.q.Ufs.2.2ayayfsixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixeBeBeBigfPfPmleBfPfPhxhIhIg3nFfPfPfPfPigiafPfPfPiafPfDfDfPeBfPg3g3fPfPeBeBg3g3fPfPfPfDfDfDfPg#fPfPfPfPmlfPfPg3g#g#fPfPfPg#hxigg3fPfPg#fPfPg#gIigigeBiag#g#hag#fPgIgIgIiaiaiagIhahIfPigighIfPhIg#fPhIg#fPfPhIhIhIg#iahIhIhIhIhIfPhIhIhIhIhImsighIhIighxhIiag#hIiaigighIhIhxhIfPfPfPeBfPeBeBfPeBfPfPfPfPfPfPfPfPfPfPfPfPfPfPeBfPeBeBfPeBfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPg3fPg3g3fPg3fPfPfPfPfPfPeBfPeBeBfPeBfPfPfPfPfPfPfPfPfPfPnE", +"hIhIhIl1hIhIhIhIighIl1hIhIhIhIhIhIhIhIl1hIhIhIhIighIl1hIhIhIhIhIhIhIhIl1hIhIhIhIighIl1hIhIhIhIhIhIhIhIl1hIhIhIhIighIl1hIhIhIhIhIhIhIhIl1hIhIhIhIighIl1hIhIhIhIhIhIhIhIl1hIhI.2ay.2.Uay.U.U.q.Uk#.2bP.2aybP.U.Uayk#fsfsfsixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfPfPmlfPfseBg#fPfPmlfPfPfPfPiafPg#fPfDfPfPfPgIfPiafPg#fPfPfPfPg3fPg3f4g#g3fPg#fPg3fPfPfPf4fsfDg3g#g#fPiahxfPfPfPeBeBg#fPfPfPfPfPigigg#gIfPg#g#fPg#fPigfPhxiaiafPiahaighagIgIgIiaiagIiagIgIgIigiagIhIg#hIighIhIfPiahIfPhIigiahIigiahIfPhIhIhIhIg#haigg#ighIhIhIhahIhahImsighIfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPg#fPg#g#fPg#fPfPfPfPfPfPg3fPg3g3fPg3fPfPeBfPeBeBfPeBfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPnE", +"hIhIhIhIighIhIhIhIhIhIhIhIl1hIhIhIhIhIhIighIhIhIhIhIhIhIhIl1hIhIhIhIhIhIighIhIhIhIhIhIhIhIl1hIhIhIhIhIhIighIhIhIhIhIhIhIhIl1hIhIhIhIhIhIighIhIhIhIhIhIhIhIl1hIhIhIhIhIhIighIj2j2j2.q.q.2.Uay.2.U.Uk#.U.2.2.UbP.U.U.U.U.UixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfPfsg3fPfPg3eBfPeBfPeBfPfPfPgImlhxg3g#g3fPfDfPgIfPfPfPhxfPfPfDfPfPg3fPg3g3fsf4eBg3g3fDfPfPfPfPfDfDg3g#fPg#fPfPfPhxigfPg#g#eBg#fPfPgIfPigigfPfPg#gIhaigfPhIgIhIiaiagIiahahagIgIiggIhIfPiaiagIhaigiggIgIhIhIg#hIg#fPhIfPfPfPhIhIg#hIighIhIhIfPhIhIhahIighIhIighIhxhxhahIhIhahIfPfPfPg#fPg#g#fPg#fPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPeBfPeBeBfPeBfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPg#fPg#g#fPg#fPfPfPfPfPfPfPfPfPfPnE", +"hIhIhIhIhIhIhIhIighIhIighIhIhIhIhIhIhIhIhIhIhIhIighIhIighIhIhIhIhIhIhIhIhIhIhIhIighIhIighIhIhIhIhIhIhIhIhIhIhIhIighIhIighIhIhIhIhIhIhIhIhIhIhIhIighIhIighIhIhIhIhIhIhIhIhIhI.q.q.q.2k#j2.q.2.U.U.U.U.q.q.Uk#bPbPb1.U.U.2ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfPeBeBg3eBmlfPfPeBfPeBeBfPfPmlfPfPfPg3g3lufPfPlufDfDfPfPf4g#fPfPgffPfPfPfPf4g3g3f4fDg3g#fPg3g3g3fPfPfPfPfPfPg3g#fPfPhxigmlfPeBfPg#fPfPfPigmlfPigfPg#gIg#fPfPgIgIigiafPiag#fPgIhagIiggIgIiaiagIgIhIfPhagIgIiahIhIhIhIighIfPhxhIhIg#hIighIhIfPighIhIhIhIhIhIhIiglLhIighIhIhIhIfPfPfPfPfPfPfPfPfPfPfPg#fPg#g#fPg#fPfPfPfPfPfPg#fPg#g#fPg#fPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPg#fPg#g#fPg#fPfPnE", +"hIfPhIhIgIhIhIhIhIhIhIhIhIhIfPhIhIfPhIhIgIhIhIhIhIhIhIhIhIhIfPhIhIfPhIhIgIhIhIhIhIhIhIhIhIhIfPhIhIfPhIhIgIhIhIhIhIhIhIhIhIhIfPhIhIfPhIhIgIhIhIhIhIhIhIhIhIhIfPhIhIfPhIhIgIhI.U.U.U.Uj2.qj2j2jf.2.2.q.U.U.q.Uk#j2ayaybPaeixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfsfPeBeBeBeBg3mleBeBfsfPfPfseBeBeBfPfPfPfPfPfPfPg3g#eBfDfPg3g3g3f4fPfPgIhafPfPg3fPfPf4fDfPhxg3hxgIfPfPfPfPfPfPg3g#g#fPfPfPhIhxiaigg#igg#g#fPfPg#hIigfPg#g#gIgIhaigfPgIgIigiaiafPhahagIigiggIhIhIgIiagIhahahIgIiagIhIighIiaighIhIhIial1hIhIhIhIhxhIhIfPfPg#l1hImsighIl1kckcjUfPg#g#fPg#fPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPhIfPhIhIfPhIfPfPfPfPfPfPfPfPfPfPhIfPhIhIfPhIfPfPg#fPg#g#fPg#fPfPfPfPfPfPfPfPfPfPfPfPfPfPnE", +"hIhIgIhIhIighIfPhIhIfPhIhIgIhIhIhIhIgIhIhIighIfPhIhIfPhIhIgIhIhIhIhIgIhIhIighIfPhIhIfPhIhIgIhIhIhIhIgIhIhIighIfPhIhIfPhIhIgIhIhIhIhIgIhIhIighIfPhIhIfPhIhIgIhIhIhIhIgIhIhIigjfjf.U.U.q.U.qj2.2j2.2.qdG.U.U.2.q.U.q.2j2fsixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixeBeBlufseBeBfseBeBfPmleBmleBfDeBeBfseBfPfPmleBfPg3mlfPg3fPg3fPg3g3g3f4fDfsfPhahagIfPfPfPf4fDfPg3g3g3fPfPg3g#fPhIigg#fPg#fPfPfPhxigigfPg#fPfPg#g#fPfPfPigg#igg#igkfhahIfPgIfPgIgIfPhIfPhIhxgIhxfPgIhIfPiagIhxhahIhIgIhIhIg#igiaighIhIg#fPhIhIhIl1ighIhIhIfPhIfPhahIhIighIlLhxfPfPfPfPfPfPfPhIfPhIhIfPhIfPfPfPfPfPfPhIfPhIhIfPhIfPfPfPfPfPfPhIfPhIhIfPhIfPfPg#fPg#g#fPg#fPfPhIfPhIhIfPhIfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPhIfPhIhIfPhIfPfPfPfPfPfPnG", +"hIhIhIighIhIhIhIfPhIhIhIhIhIhIfPhIhIhIighIhIhIhIfPhIhIhIhIhIhIfPhIhIhIighIhIhIhIfPhIhIhIhIhIhIfPhIhIhIighIhIhIhIfPhIhIhIhIhIhIfPhIhIhIighIhIhIhIfPhIhIhIhIhIhIfPhIhIhIighIhI.qj2jfdGjfjfjfae.Uj2.q.Uj2.2ay.U.UbPjf.Uj2j2ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfDfsfsg3fsmllueBg3eBeBfPeBfPfPmlfPeBeBg#fPfPeBfPfDmlfPmlfsiafsfPfPlufPfPg3fPf4f4fDfPfPfPg3fPg3f4fPg3g3gfg3g3fPfPfPfDfPfDfPg#g#fPnFhxhIfPfPg#fPhIhIfPigfPigfPgIg#gIfPfDg#fPhxighIhIiahIiagIhIhahxhIgIhIigiaiahahahafPhagIgIgIhIg#hIigighIiahxhIhIfPg#hIhIighIhIg#jUhIhIhIhIhIfPhIhIfPhIfPfPfPfPfPfPfPfPfPfPiafPiaiafPiafPfPhIfPhIhIfPhIfPfPfPfPfPfPfPfPfPfPfPfPfPfPg#fPg#g#fPg#fPfPfPfPfPfPg#fPg#g#fPg#fPfPhIfPhIhIfPhIfPfPfPfPfPfPfPfPfPfPiafPiaianE", +"fPighIfPhIhIfPhIighIfPhIfPighIhIfPighIfPhIhIfPhIighIfPhIfPighIhIfPighIfPhIhIfPhIighIfPhIfPighIhIfPighIfPhIhIfPhIighIfPhIfPighIhIfPighIfPhIhIfPhIighIfPhIfPighIhIfPighIfPhIhI.q.qjfjfjfjfjfjfdGjf.q.U.qj2.qk#.U.U.2ae.q.2ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfDfDfslufPmlmcluaylufPfPfsfPfseBfPfPeBeBfPeBfsfPeBeBfPlueBmlfsfsfsfPfPg3fPlug3f4f4fPfPfDfPfDfPfPg3g3fPfPfPeBg#fPfPgIfPfPiafDhxg#g3g3g3fPfPmligfPfPg#g#fPfPigfPhIg#gIg#fPg#fPhafPigigighxgIiagIg#gIgIhagIigiagIfPg#hIiakchxgIhIgIhIhIhIigiafPhIfPfPhIfPigl1hIighIighIhIhIg#g#fPfPfPfPfPfPfPhIfPhIhIfPhIfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPfPhIfPhIhIfPhIfPfPfPfPfPfPfPfPfPfPhIfPhIhIfPhIfPfPfPfPfPfPfPfPfPfPfPfPfPfPhIfPhIhIfPhIfPfPfPfPfPfPnE", +"ighIhIhIhIfPhIhIfPhIhIhIhIhIfPhIighIhIhIhIfPhIhIfPhIhIhIhIhIfPhIighIhIhIhIfPhIhIfPhIhIhIhIhIfPhIighIhIhIhIfPhIhIfPhIhIhIhIhIfPhIighIhIhIhIfPhIhIfPhIhIhIhIhIfPhIighIhIhIhIfPjf.Ui4jf.U.qj2.qjfjfdGjfae.U.q.qj2k#dG.2ay.UixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfseBfsfDfsg3fDg3g3fPaylumlfPeBeBeBfPeBfPfsmlfPeBg#eBfDeBfPfPmlfPg3fPfPfPfPfPfPfPfPg3f4f4fPg#fPfPfPfPfPfPfPfDiafDg#g3fPfPfPgIfDiaigfDhIg3fPfPg#fPfPhIfPighIg#igg#fPhIigg#hIg#g#g#g#msighIhIhIhIhagIhIhIhxhxighIgIighIfPgIiakcfPhIhIm1hImshag#hIhIhIhIfPfPfPhIg#l1ighIhIigfPfPfPfPfPhIfPhIhIfPhIfPfPhIfPhIhIfPhIfPfPhIfPhIhIhIhIhIhIhIhIhIhIfPhIfPfPfPfPfPfPiafPiaiahIiahIhIfPhIfPfPfPfPfPfPhIfPhIhIfPhIfPfPfPfPfPfPhIfPhIhIfPhIfPfPhIfPhIhIfPhIfPfPnG", +"fPhIfPfPhIhIfPhIfPhIfPfPhIfPhIhIfPhIfPfPhIhIfPhIfPhIfPfPhIfPhIhIfPhIfPfPhIhIfPhIfPhIfPfPhIfPhIhIfPhIfPfPhIhIfPhIfPhIfPfPhIfPhIhIfPhIfPfPhIhIfPhIfPhIfPfPhIfPhIhIfPhIfPfPhIhIjf.qjfjfi4i4jfjf.qj2jfjfdG.qjfae.qaej2j2dGjfixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixg3f4ayf4ayfsfsfsg3bPg3g3fsfsfsfsg3eBfsfPg3fPfsfsfPfseBfPfPeBfPfsfPfsfseBf4g#fPeBfDmlfPgIfPf4g#fPfDfPfPfPfPfPhxf4f4hxfPhIhxfPgIfPfPfDfPfDg#hIfPgIgIhxgIfPfPfPg#eBg#igfPighIigg#fPg#fPg#g#fPigfPfPigiafPiaiahIfPhIkchIhIkchIgIhIfPhakcgIkchIgIiaighIighIhIkcfPhIhIfPg#ighIhIhIfPhIhIfPhIfPfPfPfPfPfPhIfPhIhIfPhIfPfPfPfPfPfPfPfPfPfPfPfPfPfPhIfPhIhIhIhIhIhIfPhIfPfPiafPiaiafPiafPfPhIfPhIhIfPhIfPfPfPfPfPfPhIfPhIhIfPhIfPfPfPfPfPfPhIfPhIhIfPhIfPfPnE", +"fPhIhIhIfPhIhIfPhIhIfPhIhIfPhIfPfPhIhIhIfPhIhIfPhIhIfPhIhIfPhIfPfPhIhIhIfPhIhIfPhIhIfPhIhIfPhIfPfPhIhIhIfPhIhIfPhIhIfPhIhIfPhIfPfPhIhIhIfPhIhIfPhIhIfPhIhIfPhIfPfPhIhIhIfPhI.qj5jfjfjfjfjfjf.UjfiY.qj2dGdGjf.Ujf.q.q.U.qixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfsg3fseBfsfsayfsfslufDeBeBfsg3mleBlueBlug3g3fPeBg3eBfPeBg#fsnFfPfPfPfsfDfPmliafsfDeBfPfPfPfPfPiaiafDfPfDfDgIfPgIgIfPiaf4fDg3g#eBg3gIfPgIfPfPg#ighIg#gIfPfPiaigg#g#g#fPg#fPfPfPigighIfPighIgIhIfPfPhIfPhIigiahIhahIhafPgIighIgIhIhIg#hxhagIhIighIhIhIhIhIhIighIfPhxhIhIl1ighIhIhIhIfPhIfPfPhIfPhIhIfPhIfPfPfPfPfPfPhIfPhIhIfPhIfPfPhIfPhIhIfPhIfPfPfPfPfPfPhIfPhIhIfPhIfPfPhIfPhIhIfPhIfPfPfPfPfPfPhIfPhIhIhIhIhIhIfPhIfPfPhIfPhIhIfPhIfPfPfPfPfPfPnG", +"hIfPhIfPhIfPfPfPhIfPhIfPfPhIfPhIhIfPhIfPhIfPfPfPhIfPhIfPfPhIfPhIhIfPhIfPhIfPfPfPhIfPhIfPfPhIfPhIhIfPhIfPhIfPfPfPhIfPhIfPfPhIfPhIhIfPhIfPhIfPfPfPhIfPhIfPfPhIfPhIhIfPhIfPhIfPi4i4jfiYj2jfjfjf.qjfjfiY.qiY.qjfdGjfjf.q.U.qixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfDk#ayayg3bPg3ayayfDfsfPlufsfsfDmlayfsaylufsg3g3g3fsfsg3aymlfsg#eBfPfPeBfPmlfDhxfPfsg3fDfDfPg3fPg3f4fPf4fDfPfDgIgIg3fPfPf4g#g3g#hxfPgIfPfPfDfPf4f4hxg3hIgIfPgIfPfPfPfPhIhIighIigiggIg#igfPhIfPhIigfPigigigg#ighIhIgIhxhIigiggIigiahxiahaiahxhahIhIgImsgIighIighIhxfPhIfPfPg#fPfPfPhIfPhIhIfPhIfPfPhIfPhIhIhIhIhIhIfPhIfPfPhIfPhIhIfPhIfPfPhIfPhIhIfPhIfPfPfPfPfPfPfPfPfPfPhIfPhIhIfPhIfPfPhIfPhIhIfPhIfPfPfPfPfPfPhIfPhIhIfPhIfPfPhIfPhIhIhIhIhIhInE", +"hIfPfPfPhIg#hIhIfPfPhIhIfPhIfPhIhIfPfPfPhIg#hIhIfPfPhIhIfPhIfPhIhIfPfPfPhIg#hIhIfPfPhIhIfPhIfPhIhIfPfPfPhIg#hIhIfPfPhIhIfPhIfPhIhIfPfPfPhIg#hIhIfPfPhIhIfPhIfPhIhIfPfPfPhIg#jfjfi4i4jfj5jfj2jfjfjfjfjfi4j5j5j2j2jfdGjfjfixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfsayfsfsk#ayg3g3eBg3fsf4fDaylufsg3eBfPg3ayfslumlfsg3fPfPfseBeBfsfsfPfPeBeBfPeBfPfPfPfPiag3g3g3eBfPfPfPgIfPfPfPfDfPhafPfPg3gIfPfPg#g#g#hIgIeBgIfPfPgIfDg#hxg#hIfPfPgIfPhIigg#hIhIhIg#igfPfPigigfPg#hIg#hIigighIhIhahIhIhIighIhIhIkckckchIgIiahaiahahaigighImshIighahIhIhIhIhxhIfPfPhIfPhIhIfPhIfPfPhIfPhIhIhIhIhIhIfPhIfPfPfPfPfPfPhIfPhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIfPhIfPfPfPfPfPfPhIfPhIhIhIhIhIhIfPhIfPfPhIfPhIhIfPhIfPfPhIfPhIhIhIhIhIhInE", +"fPhIfPhIfPfPfPfPfPhIfPfPg#fPhIfPfPhIfPhIfPfPfPfPfPhIfPfPg#fPhIfPfPhIfPhIfPfPfPfPfPhIfPfPg#fPhIfPfPhIfPhIfPfPfPfPfPhIfPfPg#fPhIfPfPhIfPhIfPfPfPfPfPhIfPfPg#fPhIfPfPhIfPhIfPfPiYjfjfjfjfi4jfi4iYjfjfjfjfjf.qi4.qjfjfj5j5jfixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixbP.UbPayayfsayayfseBfsbPeBfs.UfsfsfsfseBeBfPeBfseBayeBeBeBfPfsg3fPmlfPeBeBfPeBfPeBfPfseBeBhxfsg#g3fPfPg3fPigfPfPg3fPfPfPhafDfPgIfPfPiaf4f4g#g#gIgIfPfPfPfPg#g3hIhxfPgIfPfPfPigfPfPhIfPighIighIhIg#g#g#ighIg#hIfPgIigiafPighIgIhIigfPhxgIgIm1gIhIighahxhxhIkchIhImsiahIighIhIfPhIhIfPhIfPfPhIfPhIhIfPhIfPfPfPfPfPfPhIfPhIhIhIhIhIhIhIhIhIhIfPhIfPfPfPfPfPfPhIfPhIhIfPhIfPfPhIfPhIhIhIhIhIhIhIhIhIhIfPhIfPfPhIfPhIhIfPhIfPfPhIfPhIhIfPhIfPfPfPfPfPfPnG", +"fPhIfPfPfPfPhIhIfPfPiafPhIfPg#fPfPhIfPfPfPfPhIhIfPfPiafPhIfPg#fPfPhIfPfPfPfPhIhIfPfPiafPhIfPg#fPfPhIfPfPfPfPhIhIfPfPiafPhIfPg#fPfPhIfPfPfPfPhIhIfPfPiafPhIfPg#fPfPhIfPfPfPfPjfd2iNjfi4jfjfjfj2i4i4j5iYj5jfd2.2jfi4jf.qj5ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixbPbPfsg3.Uay.UayfDayeBfseBg3fseBeBfsfDfPfseBfseBeBeBeBeBg3eBeBfsfPfsfseBmleBfsfPeBeBeBeBfDfPmlfPfPg3fPg#fPfDmlgIfPgIfPlumlg#fPhafPfPfPgIg#fPhxg#hxhIhxgIgIgIg#hIiahxhxhIgIfPiafPhxhIhxighIg#igg#fPfPfPigfPigfPhIkfl1mshIhIhIhIhahIighxhIhxhIhIkchIigmsighIhIhxhIm1mshIhIhIighIhIhIhIhIhIhIhIhIhIhIhIhIhIhIfPhIfPfPhIfPhIhIfPhIfPfPigfPigighIighIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIighIigighIighIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIfPhIfPfPnG", +"fPfPfPfPhIfPfPfPfPhIfPfPfPhIfPfPfPfPfPfPhIfPfPfPfPhIfPfPfPhIfPfPfPfPfPfPhIfPfPfPfPhIfPfPfPhIfPfPfPfPfPfPhIfPfPfPfPhIfPfPfPhIfPfPfPfPfPfPhIfPfPfPfPhIfPfPfPhIfPfPfPfPfPfPhIfPi6i6i5dGjfiNjfjfi4jfi4i4i4jf.qjfjfjfjfjf.qi4ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfsfs.U.U.UlubPayayfsfsayayfsaybPg3eBayfseBfDfsayeBg3fseBmlfsmleBeBfsfPfPfPeBeBmleBfseBfsfPeBfPfPfPeBmlfPfPlufsfPfDfPmlgIhxfsg#fPfPhafDfPfPfPfPf4f4g#fPhIgIgIgIfPgIfPfPfDfDigg3gIgIgIg#hIg#ighIfPhIg#igfPfPhIgIhIigg#igg#fPhafPhahagIhIiaiaighahxigkchIhIhIgIhIgIhahIigkcm1mshIgIgIhIgIhIhIighIigigfPigfPfPhIfPhIhIhIhIhIhIhIhIhIhIfPhIfPfPhIfPhIhIfPhIfPfPhIfPhIhIfPhIfPfPigfPigighIighIhIfPhIfPfPhIfPhIhIgIhIgIgIhIgIhIhIighIigigfPigfPfPhIfPhIhInG", +"hIfPfPfPfPg#fPhIfPfPfPfPg#fPfPiahIfPfPfPfPg#fPhIfPfPfPfPg#fPfPiahIfPfPfPfPg#fPhIfPfPfPfPg#fPfPiahIfPfPfPfPg#fPhIfPfPfPfPg#fPfPiahIfPfPfPfPg#fPhIfPfPfPfPg#fPfPiahIfPfPfPfPg#jfd2i6jfhYi6i5iYiNiNjfjfjfi4jfjfjfiYiNjfd2jfixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfsfsfsfs.UbPbPeBe0bPayayfDayayfsbPfseBfsfseBfsaylnfsayfDfDmcg3fsfseBfsfPfPeBfseBfPfPmlmlfPeBeBfPfseBfPmlmlfPg3fPiafPfPfPfPfPgIhxlug3mlfDfPfPfPgIgIfPfPfDfPhxg#fPgIgIgIfPg#iahxhIhIhxgIgIgIighIfPfPigg#igigighahIigg#hIhIhIgIhIighahIhaighIhIighahIhxkckcm1ighIhIighIhIkckchIhIhIhIfPhIfPfPhIfPhIhIhIhIhIhIhIhIhIhIhIhIhIhIfPhIfPfPhIfPhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIighIigighIighIhIhIhIhIhIfPhIfPfPhIfPhIhIhIhIhIhIhIhIhIhInG", +"fPfPfPhIg#fPfPfPfPfPiafPfPfPfPfPfPfPfPhIg#fPfPfPfPfPiafPfPfPfPfPfPfPfPhIg#fPfPfPfPfPiafPfPfPfPfPfPfPfPhIg#fPfPfPfPfPiafPfPfPfPfPfPfPfPhIg#fPfPfPfPfPiafPfPfPfPfPfPfPfPhIg#fPhYiYi4jfjfi6jfiYi6iYhYjfiNi4jfjfi4.qjfjfj2jfixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixe0fsfsmcbPfsfs.2bPbP.UbPayayayfDayluayfsbPeBg3ayeBfsayfDlug3fPfsg3eBlufPeBeBg3g3eBmlfPfPlulufPfsfPeBfPfPmleBgIf4eBg3fPfPfDfDfPmlgIfPfPfPfPlug#gIfPmlgIiafPfPg#hxhIhxgIiaiagIgIfPiahxg#hxgIgIgIg#gIhIigiafPhIhIigigg#igg#igighIighImsigmsg#ighIhxiahxiahxhIkckchIkcmshImsighIhIigighIighIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIighIigighIighIhIfPhIfPfPhIfPhIhIhIhIhIhIhIhIhIhIighIigighIighIhIhIhIhIhIhIhIhIhIhIhIhIhInG", +"fPfPfPfPfPfPfPg#fPfPfPfPfPiafPfPfPfPfPfPfPfPfPg#fPfPfPfPfPiafPfPfPfPfPfPfPfPfPg#fPfPfPfPfPiafPfPfPfPfPfPfPfPfPg#fPfPfPfPfPiafPfPfPfPfPfPfPfPfPg#fPfPfPfPfPiafPfPfPfPfPfPfPfPi6hYhYhYd2jfi4jfi6i6i6iYi6iNiNd2jfjfjfi4i4i4ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixix.Ufs.UfseBfsfsfsbPfs.2.U.UbPeB.Uay.UayaylufsfseBbPeBayfsfsfDfPfDfDfsg3eBeBfslufsfseBg3fPfPfPg3mlfPeBg#eBeBeBeBfPfPfPfPiaiafPg#fPfPfPgIfPfPfPfPg#fPfPgIfDgIfPgIg#g#hxg#hxhIgIgIgIiahIfPg#g#hxhIgIgIiagIhIhIgIighafDhIgIigighahIhIhIgIhImsmshIhIhIhahaighIhIigkchxkchIkchImsgIhIhIhIhIhIhIhIhIhIhIhIighIigighIighIhIhIhIhIhIhIhIhIhIhIhIhIhIgIhIgIgIhIgIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIighIigighIighIhInG", +"fPfPfPfPfPfPfPfPfPfPfPfPfPfPfPg#fPfPfPfPfPfPfPfPfPfPfPfPfPfPfPg#fPfPfPfPfPfPfPfPfPfPfPfPfPfPfPg#fPfPfPfPfPfPfPfPfPfPfPfPfPfPfPg#fPfPfPfPfPfPfPfPfPfPfPfPfPfPfPg#fPfPfPfPfPfPhYi6hYhYiYiYhYjfi4jfi6d2iYiYi5iYd2jfjfi4jfjfixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfs.U.2.2.2.UfseBfsfsbPfs.2.UbPbP.UbPayayayk#fslufseBbPeBfsf4fDfDeBfDlug3g3mlfPeBlulueBg3g3fPfseBfPfPeBeBeBfPeBeBeBfPfPgIhxfPg3g3iafPfDgIgImlfPfPfPfPfPg#fDfPfPfPgIiafPfPigfPhxiagIgIfPgIfPg#hxighIhxgIgIgIfPg#hIighIhxhIigigg#haigg#igighIigigmshIhIhahIhIhIigighIhxhIigighIighIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIighIigighIighIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhInG", +"fPeBfPfPg3fPg#fPfPfPg#fPfPfPeBfPfPeBfPfPg3fPg#fPfPfPg#fPfPfPeBfPfPeBfPfPg3fPg#fPfPfPg#fPfPfPeBfPfPeBfPfPg3fPg#fPfPfPg#fPfPfPeBfPfPeBfPfPg3fPg#fPfPfPg#fPfPfPeBfPfPeBfPfPg3fPi4i4iYi4i6hYhYi6i6iNiNhYi4iYhYi5jfjfjfiYjfi4ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixix.U.Ufsfsfs.U.U.Ufs.U.Ufsfs.UbPbP.2.UfseBeBbPayfsayayayeBfDfsfseBfsfDfDfPfPfDg3fsg3g3lulufPfPeBfPfDfPfPeBfPfPeBeBeBnmeBfPfPfPfPfPfPiafPg3nFfPgImlfPgIg3g#nHnInJnKnLnlknnMgIiafPhxighIgIgIgIfPiaiaigg#ighIhIgIgIg#hIhIighIhIigighIl1igl1hIhIg#hIighIhIhIhIigmshamsighIkfhIhIhxhIhIhIhIhIhIhIighIigighIighIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIl1hIl1l1hIl1hIhIhIhIhIhIhIhIhIhIl1hIl1l1hIl1hIhIhIhIhIhIhIhIhIhIighIigighIighIhIhIhIhIhInG", +"fPfPeBfPfPg3fPeBfPfPeBfPfPeBfPfPfPfPeBfPfPg3fPeBfPfPeBfPfPeBfPfPfPfPeBfPfPg3fPeBfPfPeBfPfPeBfPfPfPfPeBfPfPg3fPeBfPfPeBfPfPeBfPfPfPfPeBfPfPg3fPeBfPfPeBfPfPeBfPfPfPfPeBfPfPg3h0iNi4i4iNiYhYhYiYhYiNi4iYi4i4iYi6hYiYiYjfjfixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixix.qfs.U.Uay.2.Ufs.2.U.Ufs.Ufsj2bPfsay.2.UbP.UlneBay.UfsfseBlufDfsfseBfsfsfDfPfPhxmlg3mleBlufsfPfDfDeBfPfPfsmleBfPfPfPfPeBfPeBfPmlfPfPg3nNlGnOlvnPnQnRmgk4ixixixixixixixixfPhxgIgIfPhahxhIiggIigiaiahIigg#hxfPhIhIiggIhIhIg#igg#fPigl1igighahIhIl1lLhIighIighIigg#hahaighIighxhIhIhIhIhIhIhIl1hIl1l1hIl1hIhIhIhIhIhIl1hIl1l1hIl1hIhIhIhIhIhIl1hIl1l1hIl1hIhImshImsmshImshIhIl1hIl1l1hIl1hIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIl1hIl1l1hIl1hIhIhIhIhIhInG", +"fPfPfPg3fPfPfPfPeBfPfPfPfPfPfPeBfPfPfPg3fPfPfPfPeBfPfPfPfPfPfPeBfPfPfPg3fPfPfPfPeBfPfPfPfPfPfPeBfPfPfPg3fPfPfPfPeBfPfPfPfPfPfPeBfPfPfPg3fPfPfPfPeBfPfPfPfPfPfPeBfPfPfPg3fPfPhYhYiYiNh0h0jfi4i4hYhYi6iYiNiYi4i4jfhYd2i6i6ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixix.Ujf.q.U.U.Ufsfsfs.U.U.U.2.Ufsj2bPbPfsfs.U.U.2fseB.UbPayfslufDeBfDfsf4g3f4f4fsfsfsfDfDg3eBfPeBmlfPeBeBnFeBfPeBeBg3mlfPnSnTnUnVman.nWmPixixixixixixixixixixixixixixixixixfPgIgIgIgIiahIg#hxhahxigiafPhxgIg#g#igighIgIhxgIhIgIhIg#g#fPighIigighIhIhIg#g#hIgIhImsighIg#hmhIhIkchIl1l1hIl1hIhIhIhIhIhIhIhIhIhImshImsmshImshIhIl1hIl1l1hIl1hIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhImshImsmshImshIhIl1hIl1l1hIl1hIhIhIhIhIhIhIhIhIhImshImsmsnG", +"fsg3fPfsfPfPeBfPg3fPeBfPfsg3fPfPfsg3fPfsfPfPeBfPg3fPeBfPfsg3fPfPfsg3fPfsfPfPeBfPg3fPeBfPfsg3fPfPfsg3fPfsfPfPeBfPg3fPeBfPfsg3fPfPfsg3fPfsfPfPeBfPg3fPeBfPfsg3fPfPfsg3fPfsfPfPhYhYh7hYh0h0h4h0iNhYhYhYhYh0iYiYiYd2iNhYiNi6ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixix.U.Uj2.2.U.U.2.U.2ay.U.2jf.2bP.2bPfs.Ufsay.2.U.2.2.2.UfsfsfsbPfsfDfDfDlueBfseBfsfslufslufPg3f4g3fsfPnsnXnYnZn0n1njjFndixixixixixixixixixixixixixixixixixixixixixixixixixiafPfPgIgIgIgIfPiag#hxighxhxhxgIgIfPhIfPfPigg#ighxgIkcgIiaiaiaighIighIigigl1iglLhIhIighIkfigmshaighIhIhIhIhIhIhIhIl0hIl0l0hIl0hIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIl1hIl1l1hIl1hIhIhIhIhIhIhIhIhIhIl1hIl1l1hIl1hIhIhIhIhIhIhIhIhIhIhIhIhIhIl0hIl0l0hIl0hIhIhIhIhIhInG", +"g3fPfPfPfPfsfPfPfsfPfPfPfPfPeBfPg3fPfPfPfPfsfPfPfsfPfPfPfPfPeBfPg3fPfPfPfPfsfPfPfsfPfPfPfPfPeBfPg3fPfPfPfPfsfPfPfsfPfPfPfPfPeBfPg3fPfPfPfPfsfPfPfsfPfPfPfPfPeBfPg3fPfPfPfPfsh0hYh7h7hYhYhYhYh0h0iNiNhYi4hYhYi6iYhYiNiYjfixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixix.UdG.UdGd2.2ae.2.Ufs.Ufs.Ufs.Ufs.2.U.U.U.q.UbPfsfsfs.2fseBeBfsf4g3fPg3fDeBeBfDg3g3eBn2n3n4n5n6n7kRnrixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixigiahIfPg#iggIigighIhIhIgIhIjUhxiggIiagIgIiafPigg#hIgIhxgIjUgIhIhIhIhIhIhIigigigighIg#mskchIm1igigighIhIhIl1hIl1l1hIl1hIhIl0hIl0l0hIl0hIhIl1hIl1l1l0l1l0l0l1l0l1l1hIl1hIhIhIhIhIhImshImsmsl0msl0l0hIl0hIhIhIhIhIhIl0hIl0l0hIl0hIhIhIhIhIhIl1hIl1l1hIl1hIhIl0hIl0l0hIl0hIhInG", +"fsfPeBfsfPfPeBfPfsfPeBfsfPfsfPfPfsfPeBfsfPfPeBfPfsfPeBfsfPfsfPfPfsfPeBfsfPfPeBfPfsfPeBfsfPfsfPfPfsfPeBfsfPfPeBfPfsfPeBfsfPfsfPfPfsfPeBfsfPfPeBfPfsfPeBfsfPfsfPfPfsfPeBfsfPfPhRiYhDh0h7h0hRh4hYhYi4h0iNhYiNi4h0i4hYi6iNiSixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixix.2.Ujf.UdG.Ujf.2.2.q.2.U.2fs.Ud2fs.U.2.U.2.Uj2.2fsdGfsfseBfseBfsg3n8n9o.o#oamUobjhlfixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixgIgImlfPfPgIfPhagIgIgIfPgIiafPiaighafPigigiaighIhIighIfPhIgIgIgIgIgIhaighIfPhIhIigigigg#hIg#ighIhIm1hIl0l0hIl0hIhIhIhIhIhIl1hIl1l1hIl1hIhImshImsmshImshIhIhIhIhIhIl1hIl1l1l0l1l0l0hIl0hIhImshImsmshImshIhIl1hIl1l1hIl1hIhIhIhIhIhIl0hIl0l0hIl0hIhIhIhIhIhIl1hIl1l1hIl1hIhInG", +"eBfPg3fPeBg3fPeBfPfPeBfPfPeBfPfseBfPg3fPeBg3fPeBfPfPeBfPfPeBfPfseBfPg3fPeBg3fPeBfPfPeBfPfPeBfPfseBfPg3fPeBg3fPeBfPfPeBfPfPeBfPfseBfPg3fPeBg3fPeBfPfPeBfPfPeBfPfseBfPg3fPeBg3iSh0h0h0hRh0h0hYhYh0hYhYh0iNiYhYi4i5i6i6hYhYixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixjf.2.q.2.U.Uae.UbP.Uae.2.U.q.U.U.Uay.U.U.2.2.U.2.UocodoeofogohmOoikoixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfDfDfPfPgIg#iagIhafPhafPfPigfPiggIfPhaighIhIhxgIgIkcgIhIhIhIiahIgIhIgIhIhImshIighIhxhIigighIg#l1kckcl0l1l1hIl1hIhIl0hIl0l0hIl0hIhIhIhIhIhIl0hIl0l0hIl0hIhIl1hIl1l1hIl1hIhImshImsmsl0msl0l0hIl0hIhIl0hIl0l0msl0msmshImshIhIl0hIl0l0l1l0l1l1hIl1hIhIl0hIl0l0hIl0hIhIhIhIhIhIoj", +"fPfsfPfsfPfsfseBfPfsfPeBfsg3eBfPfPfsfPfsfPfsfseBfPfsfPeBfsg3eBfPfPfsfPfsfPfsfseBfPfsfPeBfsg3eBfPfPfsfPfsfPfsfseBfPfsfPeBfsg3eBfPfPfsfPfsfPfsfseBfPfsfPeBfsg3eBfPfPfsfPfsfPfshRhRhRh4hYhRhDh0hYh0hRh7hYh7iSiShYh0iNhYiYhYixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixdGd2d2.2.2jf.2aedG.q.qbP.2d2.2.U.U.2.2.Ufsd2fs.2ioixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixhIighagIgIfPg#gIfPhIg3hIigfDiggIgIiggIgIgIhxiahIgIgIgIgIiagIg#ighIg#hIhIhIiakchIighIighIigigigigigg#hIhIhIl1hIl1l1msl1msmsl1msl1l1l0l1l0l0hIl0hIhIl0hIl0l0hIl0hIhIl0hIl0l0hIl0hIhIhIhIhIhImshImsmsl0msl0l0hIl0hIhIl1hIl1l1hIl1hIhIhIhIhIhIl1hIl1l1msl1msmsl1msl1l1l0l1l0l0nG", +"g3eBfseBfPeBfPfPfsfsfPfPfsfPfsfPg3eBfseBfPeBfPfPfsfsfPfPfsfPfsfPg3eBfseBfPeBfPfPfsfsfPfPfsfPfsfPg3eBfseBfPeBfPfPfsfsfPfPfsfPfsfPg3eBfseBfPeBfPfPfsfsfPfPfsfPfsfPg3eBfseBfPeBh0h0hDhRhRhRh0hYhDiNhYhRhDh0h7h7i6hYh4hYh0iNixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixix.Uae.Uj2d2d2.2.2.q.2dG.U.q.U.2.2.2.U.U.2.2.2fs.UixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixgIfPgIhIfPhaiggIfPfPfPfPiahIiaigigigighIiaighahahxhIhIhIhIhIhIhIigg#hIhIighIgIgIial1hIhIjUjUjUjUhIigl0hIhIl0hIl0l0hIl0hIhIl0hIl0l0l1l0l1l1msl1msmshImshIhIl1hIl1l1l0l1l0l0l1l0l1l1l0l1l0l0l0l0l0l0hIl0hIhIhIhIhIhIl0hIl0l0l0l0l0l0hIl0hIhIl0hIl0l0hIl0hIhIl0hIl0l0l1l0l1l1nG", +"fsfPeBfPfsfseBfseBfPeBfsg3fsfPfsfsfPeBfPfsfseBfseBfPeBfsg3fsfPfsfsfPeBfPfsfseBfseBfPeBfsg3fsfPfsfsfPeBfPfsfseBfseBfPeBfsg3fsfPfsfsfPeBfPfsfseBfseBfPeBfsg3fsfPfsfsfPeBfPfsfsh2hDhRh0hRhRh4hRibh4h0h0hRh0hYh7hYh0h0i6h0iSixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixix.qjf.qd2ae.Uj5d2.q.2jfjf.2.U.q.U.qd2j5.2.2.U.U.2ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfDfPfPhxg3gIfPigg#igg#gIfPighIgIgIg#hafDfPgIhxiagIhahxhahxfPhxgIgIiggIigigjUfPg#jUighIigkcg#hIighIjUhIl0l0hIl0hIhIl0hIl0l0msl0msmshImshIhIl0hIl0l0l1l0l1l1l0l1l0l0hIl0hIhIhIhIhIhIl1hIl1l1hIl1hIhIl1hIl1l1l0l1l0l0l1l0l1l1hIl1hIhIl0hIl0l0hIl0hIhIl0hIl0l0msl0msmshImshIhIoj", +"fsfPfsfseBeBg3fPeBfsg3fsfPeBeBeBfsfPfsfseBeBg3fPeBfsg3fsfPeBeBeBfsfPfsfseBeBg3fPeBfsg3fsfPeBeBeBfsfPfsfseBeBg3fPeBfsg3fsfPeBeBeBfsfPfsfseBeBg3fPeBfsg3fsfPeBeBeBfsfPfsfseBeBh2hRhnhDh0hDh0hRhYh4h0hYh4h0h0hYhYh0h7h0h4hYixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixix.q.q.q.qaed2.qdG.2j5j2.q.2.2dG.2ae.qjfbPjf.2.q.UixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixigeBfPfPfPg#fPfPgIg#ignFfPfPmlgIiggIighaigmsigigighIhIhIhIiaiajUg#hIiahIkcigkcighIjUigjUhIkchIl1hIhIl1l0l0l1l0l1l1l0l1l0l0l0l0l0l0hIl0hIhIl0hIl0l0hIl0hIhIl1hIl1l1l0l1l0l0l1l0l1l1l0l1l0l0l0l0l0l0l0l0l0l0l1l0l1l1l0l1l0l0l1l0l1l1l0l1l0l0l1l0l1l1l0l1l0l0l0l0l0l0hIl0hIhIoj", +"eBfseBfsfPfsfsfsfsg3fseBfsfPfsfseBfseBfsfPfsfsfsfsg3fseBfsfPfsfseBfseBfsfPfsfsfsfsg3fseBfsfPfsfseBfseBfsfPfsfsfsfsg3fseBfsfPfsfseBfseBfsfPfsfsfsfsg3fseBfsfPfsfseBfseBfsfPfshRhRhRh4h4hnhDh0h0hDhRhDhRh4hYh0hDh7h7hYhYh7ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixix.qjfd2d2d2.q.qaed2.Ud2jfjf.qjf.q.q.2dGdGdGj2.q.2ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfPfPeBigfPigmlfPmlfPhIfPfPg#iggIgIg#fPgIgIhaiggIithxgIhxhxgIhag#g#hxhxigighIkchIhIighIhxighIgIigigmsl1msmsl0msl0l0l1l0l1l1hIl1hIhIl1hIl1l1l1l1l1l1l0l1l0l0msl0msmsl0msl0l0msl0msmsl1msl1l1hIl1hIhIl1hIl1l1l0l1l0l0hIl0hIhIl1hIl1l1msl1msmsl0msl0l0l1l0l1l1hIl1hIhIl1hIl1l1nG", +"fPeBfseBfseBeBfPfseBfseBeBfseBg3fPeBfseBfseBeBfPfseBfseBeBfseBg3fPeBfseBfseBeBfPfseBfseBeBfseBg3fPeBfseBfseBeBfPfseBfseBeBfseBg3fPeBfseBfseBeBfPfseBfseBeBfseBg3fPeBfseBfseBhRhDhRh0h4hRhRh2hRh0h0hRhDhRh4h0h0h0hDh0hDhRixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixd2jfj5dGd2.q.q.q.2.qae.U.Uj2jfjf.q.q.q.UdG.UjwaeixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfPg#fPfPhIigigigigfPg#fPg3fPg3igigigg#gIgIg#gIhIhIigmsigiahIhIgIhIhIiagIhaiaighIhIgIhIhahxhIighIhIhIl0l0l0hIl0hIhIl0hIl0l0l0l0l0l0l0l0l0l0l0l0l0l0hIl0hIhIl0hIl0l0l1l0l1l1l0l1l0l0l0l0l0l0l0l0l0l0l1l0l1l1l0l1l0l0msl0msmsl0msl0l0l0l0l0l0hIl0hIhIl0hIl0l0l0l0l0l0l0l0l0l0oj", +"eBfsfsg3eBfsfsfsfsfsg3fsfseBfsfseBfsfsg3eBfsfsfsfsfsg3fsfseBfsfseBfsfsg3eBfsfsfsfsfsg3fsfseBfsfseBfsfsg3eBfsfsfsfsfsg3fsfseBfsfseBfsfsg3eBfsfsfsfsfsg3fsfseBfsfseBfsfsg3eBfshDh4hnhDhRhDh0h2hRh2hRhDhDh0hRhDhRhYh4h0h0hDixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixiYjfd2jfd2jfj2i4.q.qd2.qjfdGdG.qae.qd2.q.q.q.2dGixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixmlfDfPg3g#fPhIfPfPgIigfPhag#gIg#fPfPhagIigmligfPgIgIhIgIhaiggIigiagIhIfPiahxg#hxighIigkchIl1igkciahIl0l1l1l0l1l0l0l1l0l1l1l1l1l1l1l0l1l0l0l1l0l1l1l0l1l0l0l0l0l0l0l0l0l0l0l1l0l1l1msl1msmsl0msl0l0hIl0hIhIl1hIl1l1l0l1l0l0l0l0l0l0l1l0l1l1l0l1l0l0l1l0l1l1l1l1l1l1l0l1l0l0nG", +"fseBfsfsayeBfseBeBfsfseBfsg3eBfsfseBfsfsayeBfseBeBfsfseBfsg3eBfsfseBfsfsayeBfseBeBfsfseBfsg3eBfsfseBfsfsayeBfseBeBfsfseBfsg3eBfsfseBfsfsayeBfseBeBfsfseBfsg3eBfsfseBfsfsayeBhRhnh4hDhDhRhnhRhDhRhRh4hRhnhnhRhDhRhRhRh0hRixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixd2jfi4jfjfd2d2j5jwj2iYd2d2.q.qdGjfjfdGj2j2.2.q.2ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfPfPfDfPfPg#hxfPhIfPg#igigfPigfPfPgIfPhagIigitigigg#hahIhIighaighIhIhIhIiahxgIhahaiahIhIhIiahIkcl1igl0l1l1lhl1lhlhl0lhl0l0l1l0l1l1l1l1l1l1l0l1l0l0l1l0l1l1l1l1l1l1msl1msmsl0msl0l0l0l0l0l0l0l0l0l0l0l0l0l0l1l0l1l1l0l1l0l0l0l0l0l0l1l0l1l1lhl1lhlhl0lhl0l0l1l0l1l1l1l1l1l1oj", +"fsfseBfsfsfsfsfsayeBfsayfsfsfseBfsfseBfsfsfsfsfsayeBfsayfsfsfseBfsfseBfsfsfsfsfsayeBfsayfsfsfseBfsfseBfsfsfsfsfsayeBfsayfsfsfseBfsfseBfsfsfsfsfsayeBfsayfsfsfseBfsfseBfsfsfshDhDhDhDh4h2hDhDhnhRhDh0h2h4hRh2hDhDhDhRhYh0ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixjfd2i4i4i4i6jfjfd2jfd2j5d2d2.q.q.2.qd2jfiYjf.q.qixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixhxfPfPfPfPfPfPfPfPhafPfPhIigfPfPfPfPg#g#fPgIfPg#gIigmlfPgIg#fPgIfPigighIighIhxhIgIhIiahxhahxigigigigl1l0l0l1l0l1l1l0l1l0l0lhl0lhlhl0lhl0l0l0l0l0l0lhl0lhlhl0lhl0l0l0l0l0l0l1l0l1l1l1l1l1l1l0l1l0l0l1l0l1l1l0l1l0l0l1l0l1l1l1l1l1l1l0l1l0l0l1l0l1l1l0l1l0l0lhl0lhlhl0lhl0l0oj", +"fs.UfseBayfseBfsfsfseBfseBfs.Ufsfs.UfseBayfseBfsfsfseBfseBfs.Ufsfs.UfseBayfseBfsfsfseBfseBfs.Ufsfs.UfseBayfseBfsfsfseBfseBfs.Ufsfs.UfseBayfseBfsfsfseBfseBfs.Ufsfs.UfseBayfshyhnhnhnhDh4hDhRhDhDhDhDhnhyhDhRh4h0h0h4hRhDixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixi4i5jfd2jfjwiYjfjfjfi4jfdGi4d2j2i4d2.q.q.q.qj5jwixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfDg#hxfPfPhxf4fPgIg#fPg#fPg#fPfPfPigfPmlfPg#fPgIhIhxigigmsighIg#g#g#gIigigmsiahIkchIhIkcighakchIjUhIl0lhlhl0lhl0l0l1l0l1l1l0l1l0l0l0l0l0l0l1l0l1l1l0l1l0l0l0l0l0l0l1l0l1l1l0l1l0l0k0l0k0k0l0k0l0l0l0l0l0l0l0l0l0l0k0l0k0k0l0k0l0l0lhl0lhlhl0lhl0l0l1l0l1l1l0l1l0l0l0l0l0l0nG", +"fseBayfseBayeB.UeBfs.UfsfsayeBfsfseBayfseBayeB.UeBfs.UfsfsayeBfsfseBayfseBayeB.UeBfs.UfsfsayeBfsfseBayfseBayeB.UeBfs.UfsfsayeBfsfseBayfseBayeB.UeBfs.UfsfsayeBfsfseBayfseBaygVgVhngRhnhnhDhDh4hDhDhRhnhnhyhDhDhRh4hnhRh0ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixiYjfhYiNjfi6iYjfi4i4jfjfhYjfjfd2jfiYiYi6.qjfjf.qixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixf4fPfDfDeBfPfPfPigf4igmlfPg#fPfPfPigfPg#fPigigfPigfPhIfPfPigfPigfPigfPhIighaigiggIhxighxhxiagIhaiaial0l0l0l1l0l1l1k0l1k0k0l0k0l0l0l0l0l0l0k0l0k0k0l1k0l1l1l0l1l0l0k0l0k0k0l1k0l1l1lhl1lhlhl1lhl1l1k0l1k0k0l0k0l0l0l1l0l1l1l0l1l0l0l0l0l0l0l1l0l1l1k0l1k0k0l0k0l0l0l0l0l0l0ok", +"fsfseBayfsfsfsfs.UfsfsfseBfsfs.UfsfseBayfsfsfsfs.UfsfsfseBfsfs.UfsfseBayfsfsfsfs.UfsfsfseBfsfs.UfsfseBayfsfsfsfs.UfsfsfseBfsfs.UfsfseBayfsfsfsfs.UfsfsfseBfsfs.UfsfseBayfsfshnhDgVhKhnhnhRgRhnhDhnhRh4hnh4hbhnhDh0h0hDhDixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixd2jbjfi4i5i5d2d2j5d2d2hYi4i4jfi4jfd2d2jfi6d2.q.qixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfPfPfPfPfPfPhag3fPhafPhxfPf4hIgIfPg#fPfPfPigigighaighaigg#fPfPhaigighafPgIg#fPhafPhaiggIigiahIhIhIkcl0k0k0l0k0l0l0l0l0l0l0l0l0l0l0lhl0lhlhl0lhl0l0k0l0k0k0l0k0l0l0l0l0l0l0lhl0lhlhl0lhl0l0lhl0lhlhl1lhl1l1l0l1l0l0lhl0lhlhl0lhl0l0k0l0k0k0l0k0l0l0l0l0l0l0l0l0l0l0lhl0lhlhoj", +".Uayfs.UfseBayfsayfs.Ufs.UayeBfs.Uayfs.UfseBayfsayfs.Ufs.UayeBfs.Uayfs.UfseBayfsayfs.Ufs.UayeBfs.Uayfs.UfseBayfsayfs.Ufs.UayeBfs.Uayfs.UfseBayfsayfs.Ufs.UayeBfs.Uayfs.UfseBhnhnhRhKgVgVhhhnhKgRhnhDhnhDh4h4h2hDhnhDhDhDixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixd2jfi4i4i4hYjfhYi4i4j5i4i4i4jfhYjfjfjfd2jfd2iYi4ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfPfPfPfPfPfPgIg3g#g3fPhxfPfPfPf4gIfPfPfPfPfPhIigigigigfPg#g#g#fPhIigfPgIgImsigighIgIiggIigigighImsgIlhl1l1l0l1l0l0k0l0k0k0l0k0l0l0l1l0l1l1l0l1l0l0l0l0l0l0l1l0l1l1l0l1l0l0k0l0k0k0l0k0l0l0l0l0l0l0l0l0l0l0k0l0k0k0l0k0l0l0lhl0lhlhl1lhl1l1l0l1l0l0k0l0k0k0l0k0l0l0l1l0l1l1oj", +"ayfseBfsay.UfseB.Ufsfsayfsfs.UfsayfseBfsay.UfseB.Ufsfsayfsfs.UfsayfseBfsay.UfseB.Ufsfsayfsfs.UfsayfseBfsay.UfseB.Ufsfsayfsfs.UfsayfseBfsay.UfseB.Ufsfsayfsfs.UfsayfseBfsay.UgVhngRgRhnhnhnhngVgVgVhKgVhnhDh0hDh4gVhDh4hDixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixhYiYd2jfhYi4jwi4i4jfhYd2iNjfjfd2i4d2i4jfi6i5jfiYixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfPg3hag3g3fPfPmlgIhxgIhafDhahxfPiggIigfPfPg#g3fPg#hIigigfPigfPhIigighIgIhaiggIg#igmsg#hIighIigiggIgIl0lhlhk0lhk0k0l0k0l0l0k0l0k0k0l0k0l0l0lhl0lhlhk0lhk0k0k0k0k0k0l0k0l0l0l0l0l0l0lhl0lhlhk0lhk0k0l1k0l1l1l0l1l0l0k0l0k0k0l0k0l0l0lhl0lhlhk0lhk0k0l0k0l0l0k0l0k0k0l0k0l0l0ol", +".Ufs.U.UfseB.Ufs.Ufs.U.Ufs.Uayfs.Ufs.U.UfseB.Ufs.Ufs.U.Ufs.Uayfs.Ufs.U.UfseB.Ufs.Ufs.U.Ufs.Uayfs.Ufs.U.UfseB.Ufs.Ufs.U.Ufs.Uayfs.Ufs.U.UfseB.Ufs.Ufs.U.Ufs.Uayfs.Ufs.U.UfseBglhKhhgVgRgVhngVhnhRgVgRgVhbhDgVhnhRhDhRgVhDixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixjfhYhYiNhYd2h0hYi4iSi4i4i5d2iNi4jfjfhYd2hYi4i5i4ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixhxfPfPg3g3fPhxfPfPmlfPf4fPfPeBg#hxhxfPmlhxigfPigigfPigfPfPigmliggIgIgIfPgIhxfPhIigg#igfPhahIfPhIgIhIl0k0k0l0k0l0l0lhl0lhlhk0lhk0k0l0k0l0l0lhl0lhlhl0lhl0l0l0l0l0l0k0l0k0k0k0k0k0k0l0k0l0l0lhl0lhlhl0lhl0l0k0l0k0k0l0k0l0l0l0l0l0l0k0l0k0k0l0k0l0l0lhl0lhlhk0lhk0k0l0k0l0l0ol", +".Ufsayfs.Uayfs.Ufsay.Ufsfs.Ufs.U.Ufsayfs.Uayfs.Ufsay.Ufsfs.Ufs.U.Ufsayfs.Uayfs.Ufsay.Ufsfs.Ufs.U.Ufsayfs.Uayfs.Ufsay.Ufsfs.Ufs.U.Ufsayfs.Uayfs.Ufsay.Ufsfs.Ufs.U.Ufsayfs.UaygVgVgRhhhhgVgVhnhbgVhKhnhnhKgVgVhbhDhDhDhRhDixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixh7iYi4iNhYhYhYd2d2i4d2hYhYjfi4iNi5iYd2iYi4i4hYi4ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfseBeBfPfPhxfPfPfPfPhxfPfPhxf4hxfPg3gIfPfPfPfPfDgIhIfPighIhxfPhIfPigighIhIhahIfPhIfPhaigigmsg#msigigk0k0k0l0k0l0l0k0l0k0k0l0k0l0l0l0l0l0l0k0l0k0k0lhk0lhlhk0lhk0k0l0k0l0l0lhl0lhlhk0lhk0k0l0k0l0l0k0l0k0k0lhk0lhlhl0lhl0l0k0l0k0k0k0k0k0k0l0k0l0l0k0l0k0k0l0k0l0l0l0l0l0l0ok", +"fs.Ufs.2fs.U.U.Ufs.2ay.U.2ay.Uayfs.Ufs.2fs.U.U.Ufs.2ay.U.2ay.Uayfs.Ufs.2fs.U.U.Ufs.2ay.U.2ay.Uayfs.Ufs.2fs.U.U.Ufs.2ay.U.2ay.Uayfs.Ufs.2fs.U.U.Ufs.2ay.U.2ay.Uayfs.Ufs.2fs.UglgVglg4hnglhhhbhbhhhngRhnhKhKgVgVgVhKgRhDhDixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixi4h7h7iNjfiSiYhYiSi4i4i4i4hYi4hYjfhYiNi4jfhYiYhYixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfPfPfPluhxf4mlfPg3fPfPfPfPfPfPfPfPgIfPfPfPfPfPhxfPgIfDgIfPgIfPhxigfPg#igigfPhIg#gIhIfPkciggIiggIhmfPlhl0l0k0l0k0k0lhk0lhlhk0lhk0k0k0k0k0k0l0k0l0l0k0l0k0k0l0k0l0l0k0l0k0k0l0k0l0l0l0l0l0l0lhl0lhlhk0lhk0k0l0k0l0l0k0l0k0k0lhk0lhlhl0lhl0l0k0l0k0k0lhk0lhlhk0lhk0k0k0k0k0k0oj", +"ay.U.U.Ufsayfsay.U.Ufsfs.Ufs.Ufsay.U.U.Ufsayfsay.U.Ufsfs.Ufs.Ufsay.U.U.Ufsayfsay.U.Ufsfs.Ufs.Ufsay.U.U.Ufsayfsay.U.Ufsfs.Ufs.Ufsay.U.U.Ufsayfsay.U.Ufsfs.Ufs.Ufsay.U.U.UfsaygVglglglhnhngVhngVhKhbhnhngVgRgVhRhnhhgVgVhKixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixhYiNiNd2hYh7jfjfiNjfhYiNi4i4i4hYi4i4i4i4hYiNd2jfixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfPfPfPfPfPfPg3eBf4fPfPg3g3hanFhxomfPhxiag#hxgIgIfPfPfPmligigigfPhxg#fPhIfPg#igigg#hIg#hxlLkckchxgIigk0lhlhk0lhk0k0l0k0l0l0kgl0kgkgk0kgk0k0lhk0lhlhl0lhl0l0k0l0k0k0kgk0kgkgk0kgk0k0k0k0k0k0k0k0k0k0l0k0l0l0l0l0l0l0k0l0k0k0k0k0k0k0lhk0lhlhk0lhk0k0l0k0l0l0kgl0kgkgk0kgk0k0ol", +".2ay.Ufs.U.2.U.2.Ufs.U.2ay.2fs.U.2ay.Ufs.U.2.U.2.Ufs.U.2ay.2fs.U.2ay.Ufs.U.2.U.2.Ufs.U.2ay.2fs.U.2ay.Ufs.U.2.U.2.Ufs.U.2ay.2fs.U.2ay.Ufs.U.2.U.2.Ufs.U.2ay.2fs.U.2ay.Ufs.U.2higlglgVhhglhngRglgVgRhhglgVhbgRgRgRgVhRgVgVixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixh0hYi4hYhYhYi6h7i4hYhYi4hYhYi4hYi4h0i4hYhYi4hYiSixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfPeBfPg3g3fPfsfPhxg3mlhxmlfPgIfPfPg#hxfPf4fPgIfPfDgIfPfPfPgIfPfDfPigfDigfPg#ighIg#ighIighIfPhIfPfPkcl0k0k0l0k0l0l0k0l0k0k0lhk0lhlhl0lhl0l0k0l0k0k0k0k0k0k0k0k0k0k0lhk0lhlhl0lhl0l0k0l0k0k0l0k0l0l0k0l0k0k0kgk0kgkgk0kgk0k0l0k0l0l0k0l0k0k0l0k0l0l0k0l0k0k0lhk0lhlhl0lhl0l0ok", +".Ufs.2.U.U.Uayfs.U.Uay.Ufs.Uay.U.Ufs.2.U.U.Uayfs.U.Uay.Ufs.Uay.U.Ufs.2.U.U.Uayfs.U.Uay.Ufs.Uay.U.Ufs.2.U.U.Uayfs.U.Uay.Ufs.Uay.U.Ufs.2.U.U.Uayfs.U.Uay.Ufs.Uay.U.Ufs.2.U.U.UgugVguglguhhgRhhhbgVgVhng4gVgVhbgRgVgRgVhnhKixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixhYh0h0i5hYhYi4hYi4i6i4hYiNjfjbiNiYi4icd2hYhYjbhYixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixg3fsf4eBf4g#fDfPfPg3fPfPfDmlf4hxfPfPfPfPfPhxfPmlhxhxgIfPhag#fPiahIhxigighIhahIfPhxhIigfPigigighIighIk0kgkgk0kgk0k0k0k0k0k0k0k0k0k0lhk0lhlhkglhkgkgl0kgl0l0k0l0k0k0k0k0k0k0k0k0k0k0k0k0k0k0kgk0kgkgk0kgk0k0lhk0lhlhk0lhk0k0k0k0k0k0kgk0kgkgk0kgk0k0k0k0k0k0k0k0k0k0lhk0lhlhok", +".U.U.U.2fs.2.U.2.Uay.2.U.2ay.U.2.U.U.U.2fs.2.U.2.Uay.2.U.2ay.U.2.U.U.U.2fs.2.U.2.Uay.2.U.2ay.U.2.U.U.U.2fs.2.U.2.Uay.2.U.2ay.U.2.U.U.U.2fs.2.U.2.Uay.2.U.2ay.U.2.U.U.U.2fs.2glglglgVgRguglhhglglglgVglgRhngRgVgVgVhbhngRixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixh0hYh7h7h7h0h0hYhYhYhYi6h7hYiNiSjfhYhYhYiNi4i4hYixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixg3fPg3g3lug3fsfPfPfPfPfPfPfPg3eBeBfPfDfPfPfPfPhxg3omfPmlfPfPgIgIhafPfPigfPhIigg#igfPigfPfPfPigigighak0lhlhkglhkgkgk0kgk0k0l0k0l0l0k0l0k0k0k0k0k0k0k0k0k0k0lhk0lhlhkglhkgkglhkglhlhk0lhk0k0lhk0lhlhk0lhk0k0kgk0kgkgl0kgl0l0k0l0k0k0lhk0lhlhkglhkgkgk0kgk0k0l0k0l0l0k0l0k0k0ok", +"ay.U.2.U.U.U.Uay.U.U.2.U.U.2.Uayay.U.2.U.U.U.Uay.U.U.2.U.U.2.Uayay.U.2.U.U.U.Uay.U.U.2.U.U.2.Uayay.U.2.U.U.U.Uay.U.U.2.U.U.2.Uayay.U.2.U.U.U.Uay.U.U.2.U.U.2.Uayay.U.2.U.U.UgRglglglongVglguglhhhhglhhglgVgVgVglgVgVhbhhixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixh0h0h0iSh7hYh4iNiNh4hYhYiYi6i6h7hYiNiNhYhYiYi4i4ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixeBeBfPfPfPg3g3g3g3mlg#fsfDfPfPlug3fPmlhxhxfPfPfPhafPhxfPmliafPgIgIhag#hxhIfPhxigighafPgIfPfPfPigighIk0kgkglhkglhlhk0lhk0k0kgk0kgkgkgkgkgkgkgkgkgkglhkglhlhk0lhk0k0k0k0k0k0kgk0kgkgk0kgk0k0kgk0kgkgk0kgk0k0k0k0k0k0lhk0lhlhk0lhk0k0kgk0kgkglhkglhlhk0lhk0k0kgk0kgkgkgkgkgkgok", +".U.2.Uay.U.2.2.U.q.Uay.U.2.U.U.2.U.2.Uay.U.2.2.U.q.Uay.U.2.U.U.2.U.2.Uay.U.2.2.U.q.Uay.U.2.U.U.2.U.2.Uay.U.2.2.U.q.Uay.U.2.U.U.2.U.2.Uay.U.2.2.U.q.Uay.U.2.U.U.2.U.2.Uay.U.2gvhifQgRgRglglguglguglglhhgRhhhbglhbglgVhnglixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixh7h4hYjgh0h0hYibhYh0iNh4hYhYhYi4hYi6hYhYh4iNiYhYixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfslug3fPfsfDfPfPlulug3f4fPoofPoofDfPfPlufPmliamlfPfPfPfPfPfPfPfPmlfDhxfPhafPhafPhxiaigigiahIiahIfPfPk0k0k0kgk0kgkgk0kgk0k0k0k0k0k0k0k0k0k0k0k0k0k0k0k0k0k0j9k0j9j9kgj9kgkgk0kgk0k0lhk0lhlhk0lhk0k0lhk0lhlhk0lhk0k0kgk0kgkgk0kgk0k0k0k0k0k0kgk0kgkgk0kgk0k0k0k0k0k0k0k0k0k0ok", +".2.U.2.2.q.U.U.U.U.2.U.U.2ay.U.2.2.U.2.2.q.U.U.U.U.2.U.U.2ay.U.2.2.U.2.2.q.U.U.U.U.2.U.U.2ay.U.2.2.U.2.2.q.U.U.U.U.2.U.U.2ay.U.2.2.U.2.2.q.U.U.U.U.2.U.U.2ay.U.2.2.U.2.2.q.UguhhhhgRgRgRglgRglglglhhgRgvhhglhhglhbglgVgVixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixhRh0h7h0jghYh0hYhYi4ibh7h7h0hYhYhYh0hYi6i6hYiNiNixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfPlulug3f4fPfPfPfPfDfPg3g3mlg3mlfPfPfPfPfDfPfsg3g3mliafPgIgIfPgIgIiahIiagIfPfPgIgIfPhxigigfPhxhIhIhxkgk0k0j9k0j9j9kgj9kgkgk0kgk0k0k0k0k0k0kgk0kgkgk0kgk0k0k0k0k0k0lhk0lhlhkglhkgkgk0kgk0k0j9k0j9j9kgj9kgkgk0kgk0k0k0k0k0k0kgk0kgkgk0kgk0k0j9k0j9j9kgj9kgkgk0kgk0k0k0k0k0k0ok", +".U.U.U.U.U.2.2.U.q.U.2.q.U.2.U.U.U.U.U.U.U.2.2.U.q.U.2.q.U.2.U.U.U.U.U.U.U.2.2.U.q.U.2.q.U.2.U.U.U.U.U.U.U.2.2.U.q.U.2.q.U.2.U.U.U.U.U.U.U.2.2.U.q.U.2.q.U.2.U.U.U.U.U.U.U.2fkfkfQgugvglgRgRgugRglglgugVglguglglglglhbhbixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixhYhYhDhDh4hRh4h0h0hYhYhYibhYh0h0iNh4hYhYhYh7hYhYixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfPfseBfPlulufsg3eBfDfDfPfPg3mllumleBf4g#fPfPfPfPlufPmlmlhxfPfPfPfPg3fPfPhxiamliafPhag#fPiafPmlfPfPigk0kgkgk0kgk0k0kgk0kgkgj9kgj9j9k0j9k0k0kgk0kgkgj9kgj9j9k0j9k0k0kgk0kgkgk0kgk0k0k0k0k0k0kgk0kgkgk0kgk0k0kgk0kgkgk0kgk0k0k0k0k0k0kgk0kgkgk0kgk0k0kgk0kgkgj9kgj9j9k0j9k0k0ok", +".Ujf.2.U.q.U.U.2.2.U.U.U.U.2jf.2.Ujf.2.U.q.U.U.2.2.U.U.U.U.2jf.2.Ujf.2.U.q.U.U.2.2.U.U.U.U.2jf.2.Ujf.2.U.q.U.U.2.2.U.U.U.U.2jf.2.Ujf.2.U.q.U.U.2.2.U.U.U.U.2jf.2.Ujf.2.U.q.UguglglgugugufQgRglgRhiggglglglglgVgRgRhiglglixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixh0hRh0h0h0hYhDh0h0h0hRhYhYhRhYhYiSh7h0hYhYi4hYhYixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixeBeBfPfsfPfPlulug3mlf4mlfPfDfDfPfPfPmlf4fPg#fPfPfPgIfPgIigfPgIhxhxhafPfPfPhxomiahIhahahIhxhahIfPfPhak0j9j9k0j9k0k0k0k0k0k0k0k0k0k0kgk0kgkgk0kgk0k0k0k0k0k0j9k0j9j9k0j9k0k0kgk0kgkgjVkgjVjVk0jVk0k0k0k0k0k0kgk0kgkgjVkgjVjVk0jVk0k0j9k0j9j9k0j9k0k0k0k0k0k0k0k0k0k0kgk0kgkgok", +".q.U.q.U.U.q.Ujf.U.2jf.q.U.q.U.U.q.U.q.U.U.q.Ujf.U.2jf.q.U.q.U.U.q.U.q.U.U.q.Ujf.U.2jf.q.U.q.U.U.q.U.q.U.U.q.Ujf.U.2jf.q.U.q.U.U.q.U.q.U.U.q.Ujf.U.2jf.q.U.q.U.U.q.U.q.U.U.qfQgvgugufQggfkguguhhgvgRguguglggglgugVgvgRgRixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixh0h4h0hDh0h4hRh0hDhRh0h0hDh0i4hYh0h0ibhRh0hDhYhYixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfsfsf4eBfPfPfsfPmllufPfsmlfPfPfPfPfPfPfPmlg3fPfPfPfPfPgIgIfPigg3mlgIhxhIhxfPgIhxg3omigmlfPfPfPgIfPg#k0kgkgk0kgk0k0j9k0j9j9j9j9j9j9k0j9k0k0jVk0jVjVk0jVk0k0kgk0kgkgjVkgjVjVk0jVk0k0j9k0j9j9k0j9k0k0jVk0jVjVk0jVk0k0k0k0k0k0k0k0k0k0kgk0kgkgk0kgk0k0j9k0j9j9j9j9j9j9k0j9k0k0ok", +".U.U.U.q.q.U.2.Ujf.U.U.q.U.2.U.q.U.U.U.q.q.U.2.Ujf.U.U.q.U.2.U.q.U.U.U.q.q.U.2.Ujf.U.U.q.U.2.U.q.U.U.U.q.q.U.2.Ujf.U.U.q.U.2.U.q.U.U.U.q.q.U.2.Ujf.U.U.q.U.2.U.q.U.U.U.q.q.UfufQgvguglgggRgugugufkguhhgvguguglgRguglglgRixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixhRh0h4hRh4hRh0h0h0ibhYhDh0h7h0hYh4h0h0hYh0h7hYhYixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixeBeBfsf4fDfDfDfDfPfPfPmlfPfsgIg3fsfsfPfPfPfPfPfPgIg3gIfPgffDfPfPgIfPighxhxgIfPfPfPfPfPhIgImlgIhxgIigk0jVjVk0jVk0k0kgk0kgkgk0kgk0k0j9k0j9j9j9j9j9j9jVj9jVjVk0jVk0k0k0k0k0k0j9k0j9j9kgj9kgkgj9kgj9j9k0j9k0k0kgk0kgkgj9kgj9j9k0j9k0k0jVk0jVjVk0jVk0k0kgk0kgkgk0kgk0k0j9k0j9j9ok", +"jf.q.2jf.2.U.q.2.q.Ujf.Ujf.q.U.2jf.q.2jf.2.U.q.2.q.Ujf.Ujf.q.U.2jf.q.2jf.2.U.q.2.q.Ujf.Ujf.q.U.2jf.q.2jf.2.U.q.2.q.Ujf.Ujf.q.U.2jf.q.2jf.2.U.q.2.q.Ujf.Ujf.q.U.2jf.q.2jf.2.UfQfkgugufkfQfkgggugvfXgufugugvguglglguglglglixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixopoqorosjPjPjOotououovowoxoyozoAoBi0h4h0hYibhRibixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixeBfsg3fsfsfseBfsfseBlufPg3eBeBlug3eBmlfPfPfPfPmlgIg3gIfPf4fDfDfPfPluhxomomhIhxhxgIfPgIfPfPgIfPgImsgIj9k0k0k0k0k0k0jVk0jVjVkgjVkgkgk0kgk0k0k0k0k0k0kgk0kgkgk0kgk0k0kgk0kgkgjVkgjVjVk0jVk0k0kgk0kgkgk0kgk0k0jVk0jVjVk0jVk0k0j9k0j9j9k0j9k0k0k0k0k0k0jVk0jVjVkgjVkgkgk0kgk0k0ok", +".q.U.U.U.qjf.2.Ujf.q.U.q.2.Ujf.U.q.U.U.U.qjf.2.Ujf.q.U.q.2.Ujf.U.q.U.U.U.qjf.2.Ujf.q.U.q.2.Ujf.U.q.U.U.U.qjf.2.Ujf.q.U.q.2.Ujf.U.q.U.U.U.qjf.2.Ujf.q.U.q.2.Ujf.U.q.U.U.U.qjfglgufbfkgufkfQgufkglgvgugugufkgugugugvgvhiglixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixkoiwoCnWnkjcixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfDeBfDfPeBg3fsf4fDfPfDhxfPfPfPeBmllufPlug3mlfPfPfPfPg3gImlgIhagIhxfPhIfPfPfPluhxg3hIgIhahxhahxfPg3hIkgj9j9jVj9jVjVk0jVk0k0jVk0jVjVkgjVkgkgj9kgj9j9jVj9jVjVjVjVjVjVkgjVkgkgk0kgk0k0j9k0j9j9jVj9jVjVk0jVk0k0kgk0kgkgjVkgjVjVkgjVkgkgj9kgj9j9jVj9jVjVk0jVk0k0jVk0jVjVkgjVkgkgok", +"jf.qjfjf.U.Ujf.2jf.2jfjf.Ujf.q.2jf.qjfjf.U.Ujf.2jf.2jfjf.Ujf.q.2jf.qjfjf.U.Ujf.2jf.2jfjf.Ujf.q.2jf.qjfjf.U.Ujf.2jf.2jfjf.Ujf.q.2jf.qjfjf.U.Ujf.2jf.2jfjf.Ujf.q.2jf.qjfjf.U.UgWgvfkfkfbfkfkfufkfQgvgggvfXgRgufkgugugugvglixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixmlfsfDkdfslufPfPeBfsf4fseBfsfPfsfPfPlumlmlfPigfPfPfPfPeBfPfPlufPfPfPfDfPfPfPfPhxgIomfPmlhxgIfPgIfPhxk0jVjVk0jVk0k0j9k0j9j9jVj9jVjVk0jVk0k0j9k0j9j9k0j9k0k0k0k0k0k0jVk0jVjVjVjVjVjVk0jVk0k0j9k0j9j9k0j9k0k0jVk0jVjVk0jVk0k0k0k0k0k0jVk0jVjVk0jVk0k0j9k0j9j9jVj9jVjVk0jVk0k0ok", +"jf.U.q.qjf.q.Ujf.2.qjf.U.2jf.Ujfjf.U.q.qjf.q.Ujf.2.qjf.U.2jf.Ujfjf.U.q.qjf.q.Ujf.2.qjf.U.2jf.Ujfjf.U.q.qjf.q.Ujf.2.qjf.U.2jf.Ujfjf.U.q.qjf.q.Ujf.2.qjf.U.2jf.Ujfjf.U.q.qjf.qfufkfkfkfkglglfQgufkfQfufQgvgvgvgugugvgugufQixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixbPfsbPayfsmleBfDfDg3fPfsfDmlfDfPfPeBfPfPfPfPg3mlfPigfPfPfPfPeBfPmlmlfPighIhagIfPfPfPfPg3fPhIfPhIhxhxjVjVjVkgjVkgkgjVkgjVjVk0jVk0k0kgk0kgkgjVkgjVjVj9jVj9j9jVj9jVjVkgjVkgkgj9kgj9j9jVj9jVjVj9jVj9j9jVj9jVjVj9jVj9j9kgj9kgkgjVkgjVjVjVjVjVjVkgjVkgkgjVkgjVjVk0jVk0k0kgk0kgkgok", +".2jf.Ujf.2jfjfjf.Ujf.qjfjf.qjf.q.2jf.Ujf.2jfjfjf.Ujf.qjfjf.qjf.q.2jf.Ujf.2jfjfjf.Ujf.qjfjf.qjf.q.2jf.Ujf.2jfjfjf.Ujf.qjfjf.qjf.q.2jf.Ujf.2jfjfjf.Ujf.qjfjf.qjf.q.2jf.Ujf.2jffufkfkfbfkfkfkfkfuggfkgvfkgugvfkgvfkgufXgggvixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfDfsfDfsmlaylufseBfDg3fDfsfsfsfsfsfPeBfPfPg3eBfPlufPlufPfDfDfDfPfPfPfPmlmlf4gIfPfPhxfPhxgImlfPhIhImlj9k0k0jVk0jVjVj9jVj9j9jVj9jVjVjVjVjVjVkgjVkgkgjVkgjVjVk0jVk0k0jVk0jVjVk0jVk0k0j9k0j9j9j9j9j9j9jVj9jVjVk0jVk0k0jVk0jVjVj9jVj9j9k0j9k0k0jVk0jVjVj9jVj9j9jVj9jVjVjVjVjVjVok", +".qjfjfjf.U.q.U.qjfjf.2.Ujf.qjf.U.qjfjfjf.U.q.U.qjfjf.2.Ujf.qjf.U.qjfjfjf.U.q.U.qjfjf.2.Ujf.qjf.U.qjfjfjf.U.q.U.qjfjf.2.Ujf.qjf.U.qjfjfjf.U.q.U.qjfjf.2.Ujf.qjf.U.qjfjfjf.U.qfQfkfufufkfkfkfkfkfQfkfQfQfkfkgugufkfkgvfkoDixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfsfDeBfDg3fDfsbPfsfsfsf4fDfDfDfPf4fPf4fPmlfPfPfPg3fPfPmlfPfPfPigfPeBfPfPfPfPg3fPitighahxhxhxhxhxmlfPjVj9j9jVj9jVjVkgjVkgkgjVkgjVjVjVjVjVjVj9jVj9j9j9j9j9j9j9j9j9j9jVj9jVjVjVjVjVjVjVjVjVjVjVjVjVjVkgjVkgkgj9kgj9j9jVj9jVjVjVjVjVjVj9jVj9j9jVj9jVjVkgjVkgkgjVkgjVjVjVjVjVjVok", +"jf.qjf.qjfjfjfjfjf.Ujfjf.qjf.qjfjf.qjf.qjfjfjfjfjf.Ujfjf.qjf.qjfjf.qjf.qjfjfjfjfjf.Ujfjf.qjf.qjfjf.qjf.qjfjfjfjfjf.Ujfjf.qjf.qjfjf.qjf.qjfjfjfjfjf.Ujfjf.qjf.qjfjf.qjf.qjfjffbgWfkfQfkfufufkfkfufkfkfufkfXfboEoFoGoHjmlXixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfsbPfsfsk#fDbPfsg3fslumllufDfDfDfDfDfPf4fsfsfDfseBfPfPfPg3lufPlulufPfPfPfPfPmlgIfPfPgIfPgIeBgIfPfPhxkgjVjVkgjVkgkgjVkgjVjVj9jVj9j9k0j9k0k0jVk0jVjVjVjVjVjVjVjVjVjVj9jVj9j9k0j9k0k0jVk0jVjVk0jVk0k0jVk0jVjVjVjVjVjVjVjVjVjVkgjVkgkgjVkgjVjVkgjVkgkgjVkgjVjVj9jVj9j9k0j9k0k0ok", +"jf.qjfjfjfjf.q.qjfjf.qjf.2jf.qjfjf.qjfjfjfjf.q.qjfjf.qjf.2jf.qjfjf.qjfjfjfjf.q.qjfjf.qjf.2jf.qjfjf.qjfjfjfjf.q.qjfjf.qjf.2jf.qjfjf.qjfjfjfjf.q.qjfjf.qjf.2jf.qjfjf.qjfjfjfjffbfkfbgWe2fkf5gufWfkfkoIoJoKoLoMkoixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfs.UfsbPfsfsfsfseBfDfseBmcfsbPf4fDfDlufDg3fPg3mlmlfPfPfPfPlufsfPmlmlfPfPighagIfPfPfPfPfPfPgIgIhIhxhxjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVj9jVj9j9jVj9jVjVj9jVj9j9j9j9j9j9jVj9jVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVj9jVj9j9jVj9jVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVj9jVj9j9ok", +"jfjfjfjf.qjfjfjfjf.qjfjfjf.qjfjfjfjfjfjf.qjfjfjfjf.qjfjfjf.qjfjfjfjfjfjf.qjfjfjfjf.qjfjfjf.qjfjfjfjfjfjf.qjfjfjfjf.qjfjfjf.qjfjfjfjfjfjf.qjfjfjfjf.qjfjfjf.qjfjfjfjfjfjf.qjffufufufbfbfkoNoOoPjnoQixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfDfsfsfsayfsbPfsfDfsfDlug3eBfsbPmclufsfDfDfDfsfPfsmlfDlumlfDfPfDfDfPfPmlmlfPfDhIfPfPfPfDfPfPfPfPfPitjVj9j9jVj9jVjVjVjVjVjVj9jVj9j9jVj9jVjVjVjVjVjVjVjVjVjVj9jVj9j9jVj9jVjVj9jVj9j9jVj9jVjVj9jVj9j9j9j9j9j9jVj9jVjVj9jVj9j9jVj9jVjVj9jVj9j9jVj9jVjVjVjVjVjVj9jVj9j9jVj9jVjVok", +".qjfjfjfjfjfjf.qjfjfjfjfjfi4jf.q.qjfjfjfjfjfjf.qjfjfjfjfjfi4jf.q.qjfjfjfjfjfjf.qjfjfjfjfjfi4jf.q.qjfjfjfjfjfjf.qjfjfjfjfjfi4jf.q.qjfjfjfjfjfjf.qjfjfjfjfjfi4jf.q.qjfjfjfjfjfe2fuoRoSoToUkoixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixbPeBfseBfDlufsfseBfsfsfDfseBeBfsfseBlulufPfDfDfDfDg3fPfslufPmlfPfPfPlufPfPeBmlfPfPgIhIfDfPfPfPmlfPgIjVjVjVj9jVj9j9jVj9jVjVjVjVjVjVjSjVjSjSjVjSjVjVj9jVj9j9jVj9jVjVjVjVjVjVjVjVjVjVjVjVjVjVjSjVjSjSjVjSjVjVjVjVjVjVj9jVj9j9jVj9jVjVjVjVjVjVj9jVj9j9jVj9jVjVjVjVjVjVjSjVjSjSok", +"jfjfjf.qjfjfd2jfi4jf.qjfjfjfjfjfjfjfjf.qjfjfd2jfi4jf.qjfjfjfjfjfjfjfjf.qjfjfd2jfi4jf.qjfjfjfjfjfjfjfjf.qjfjfd2jfi4jf.qjfjfjfjfjfjfjfjf.qjfjfd2jfi4jf.qjfjfjfjfjfjfjfjf.qoVoWoXoMixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixfsfsfsfsfsfsfDluayayfsbPf4eBfDeBmleBfDfseBlufPfsfDfDfDfsfDg3f4eBfsf4fPfDfDfsfPeBmlfPfPfPg#iggIgIfDfPjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjLjVjLjLjVjLjVjVjVjVjVjVj9jVj9j9jVj9jVjVj9jVj9j9jVj9jVjVjLjVjLjLjVjLjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVok", +"iYjfjfjfi4jfjfjfjfjfjfjfjf.qjfi4iYjfjfjfi4jfjfjfjfjfjfjfjf.qjfi4iYjfjfjfi4jfjfjfjfjfjfjfjf.qjfi4iYjfjfjfi4jfjfjfjfjfjfjfjf.qjfi4iYjfjfjfi4jfjfjfjfjfjfjfjf.qjfi4oYoZo0o1ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixkso2o3ayfsfsfsfDfDfseBfsfsfsf4fsfsfseBfDfsg3f4fsmllumlfDfDmlfDfPgImlfPmlhxhxfPfPfPg3fPfPfPigfPigigfPjLjVjVjLjVjLjLjVjLjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVj9jVj9j9jVj9jVjVjVjVjVjVjLjVjLjLjVjLjVjVjVjVjVjVjVjVjVjVjLjVjLjLjVjLjVjVjLjVjLjLjVjLjVjVjVjVjVjVjVjVjVjVok", +"jfjfjfjfjfjfjfjfi4jfjfi4jfjfjfjfjfjfjfjfjfjfjfjfi4jfjfi4jfjfjfjfjfjfjfjfjfjfjfjfi4jfjfi4jfjfjfjfjfjfjfjfjfjfjfjfi4jfjfi4jfjfjfjfjfjfjfjfjfjfjfjfi4jfjfi4kuo4o5oQixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixlKo6o7o2o8fs.2mcfsfsfDlufsfsfsb1fsfsfsfDlueBfsfslulufPfDf4fDfPfsg3g3mlmlhxmlfPfPlufPfPeBmlmlmlfPjVjVjVjVjVjVjVjVjVjVjVjLjVjLjLjVjLjVjVjVjVjVjVjLjVjLjLjVjLjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjLjVjLjLjVjLjVjVok", +"jfi4jfjfi4jfjfjfjfjfjfjfjfjfi4jfjfi4jfjfi4jfjfjfjfjfjfjfjfjfi4jfjfi4jfjfi4jfjfjfjfjfjfjfjfjfi4jfjfi4jfjfi4jfjfjfjfjfjfjfjfjfi4jfjfi4jfjfi4jfjfjfjfoVo9p.p#ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixlKpapbpcpdfsfsfDfseBeBfseBbPfsfsfsfDfDmlg3fPg3eBg3lumlfDfDfsfPfPmlmlfPg3hxhxigfPhxfPfPg#jVjLjLjVjLjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjVjLjVjLjLjVjLjVjVjVjVjVjVjvjVjvjvjVjvjVjVjVjVjVjVjVjVjVjVjvjVjvjvjVjvjVjVjLjVjLjLjVjLjVjVjVjVjVjVjVjVjVjVjVjVjVjVok", +"i4jfi4jfjfi4jfhYjfjfhYi4jfi4jfjfi4jfi4jfjfi4jfhYjfjfhYi4jfi4jfjfi4jfi4jfjfi4jfhYjfjfhYi4jfi4jfjfi4jfi4jfjfi4jfhYjfjfhYi4jfi4jfjfi4jfi4jfjfj2pepfpgixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixl.obphpifsfDfsf4eBeBfsfseBfsfDfsfDf4fPg3g3fslueBlufPfPfDf4fPfsmlfPfPluhxfPfPg3f4jVj4j4jVj4jVjVjLjVjLjLjLjLjLjLjVjLjVjVjvjVjvjvjVjvjVjVjVjVjVjVjvjVjvjvjVjvjVjVjLjVjLjLjVjLjVjVjvjVjvjvjVjvjVjVjVjVjVjVjVjVjVjVj4jVj4j4jVj4jVjVjLjVjLjLjLjLjLjLjVjLjVjVpj", +"jfjfjfi4i4jfiYjfi4jfjfiYjfiYjfi4jfjfjfi4i4jfiYjfi4jfjfiYjfiYjfi4jfjfjfi4i4jfiYjfi4jfjfiYjfiYjfi4jfjfjfi4i4jfiYjfi4jfjfiYjfiYjfi4jfjfpkplohiwixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixnrpmo#pnfsfseBf4luf4eBfseBfDfsmllufDf4fsg3lumlfDfPfDfPfDfPfPfDfsg3fPfPfPjVjvjvjVjvjVjVj4jVj4j4jVj4jVjVjLjVjLjLj4jLj4j4jLj4jLjLjVjLjVjVjVjVjVjVjLjVjLjLjVjLjVjVjLjVjLjLjVjLjVjVj4jVj4j4jLj4jLjLjVjLjVjVjvjVjvjvjVjvjVjVj4jVj4j4jVj4jVjVjLjVjLjLpj", +"hYi4iYhYjfjfi4jfi4jfhYjfhYi4jfjfhYi4iYhYjfjfi4jfi4jfhYjfhYi4jfjfhYi4iYhYjfjfi4jfi4jfhYjfhYi4jfjfhYi4iYhYjfjfi4jfi4jfhYjfhYi4jfpopppqprixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixlKjIpsm8fsfDfsmlfsg3fsfsfsfsfsfDfsfPmlmlfPfPfPhxfDmlfPg3fPg3g3g#fPjLjVjVjVjVjVjVjvjVjvjvj4jvj4j4jVj4jVjVjVjVjVjVjVjVjVjVjVjVjVjVj4jVj4j4jvj4jvjvjVjvjVjVj4jVj4j4jVj4jVjVjvjVjvjvjVjvjVjVjLjVjLjLjVjLjVjVjVjVjVjVjvjVjvjvj4jvj4j4jVj4jVjVok", +"i4jfjfjfi4hYjfjfhYiYjfi4iYjfhYjfi4jfjfjfi4hYjfjfhYiYjfi4iYjfhYjfi4jfjfjfi4hYjfjfhYiYjfi4iYjfhYjfi4jfjfjfi4hYjfjfhYiYjfi4ptovpuiwixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixllpvnUpweBfsfPeBfDeBg3fPfDhxeBfDfPmllumlfPfDhxfPfDgIf4mlfPj4jLjLjvjLjvjvjVjvjVjVjvjVjvjvjVjvjVjVjLjVjLjLjvjLjvjvjvjvjvjvjVjvjVjVjVjVjVjVjLjVjLjLjvjLjvjvjVjvjVjVj4jVj4j4jvj4jvjvj4jvj4j4jLj4jLjLjvjLjvjvjVjvjVjVjvjVjvjvjVjvjVjVpj", +"hYiYhYhYjfjfhYiYhYjfhYhYjfhYiYjfhYiYhYhYjfjfhYiYhYjfhYhYjfhYiYjfhYiYhYhYjfjfhYiYhYjfhYhYjfhYiYjfhYiYhYhYjfjfhYiYhYjfowpxllixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixpyiKjmpzpAoHpBpCpDpEpFpGpHpIpJpKpLpMpNpNpOpPpQpRpSpTpUpVpQpNpKpWoSpXpYpZp0p1p2p3oCktixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixktmxphp4eBf4fsf4eBf4fDfsfDmleBfPf4luf4fsmlfDfsfDfDfPjVjvjvjVjvjVjVj4jVj4j4jvj4jvjvjVjvjVjVjLjVjLjLjVjLjVjVjVjVjVjVjvjVjvjvjvjvjvjvjVjvjVjVjLjVjLjLjVjLjVjVjvjVjvjvjVjvjVjVjVjVjVjVjvjVjvjvjVjvjVjVj4jVj4j4jvj4jvjvjVjvjVjVpj", +"hYjfi4iYhYi4jfhYjfi4hYjfiYhYjfhYhYjfi4iYhYi4jfhYjfi4hYjfiYhYjfhYhYjfi4iYhYi4jfhYjfi4hYjfiYhYjfhYhYjfi4iYhYi4jfhYp5p6ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixlXp7pzp1pDp8p9q.q#qaqbfbfkgWgWfuftfkfXfufkfkfufkfkfkfkfufkfkfkfQfkfQfQgvfQgvfXgugugugugvgugvglguglglguguoDiDqcqdqeqfpzqgixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixlBqhnzeBfsfsf4g3g3eBfPeBfDmlfDf4f4fPfsfPfPfPjvjvjvjVjvjVjVjvjVjvjvjVjvjVjVj4jVj4j4jvj4jvjvjLjvjLjLjvjLjvjvj4jvj4j4jLj4jLjLjvjLjvjvj4jvj4j4jvj4jvjvjLjvjLjLj4jLj4j4jvj4jvjvjvjvjvjvjVjvjVjVjvjVjvjvjVjvjVjVj4jVj4j4pj", +"jfhYjfhYiYhYhYhYjfhYiYhYhYi4hYiYjfhYjfhYiYhYhYhYjfhYiYhYhYi4hYiYjfhYjfhYiYhYhYhYjfhYiYhYhYi4hYiYjfhYjfhYiYhYhYhYjfozpxp#ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixmPqiqfqjp9pSqkfbeTe2e2fbf6e2e2fufbfbfbfbfufbfkftfte2fbfkfufbfufQfufkfkfkfkfufQfkfkfufufQfkgvfQgvfXgvgugugufkgugugufQfXglgugugRgufkgvgvqlpMqmqnqomNixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixmbm0qpqqfsfsfsg3fsfsfPfsfPf4f4f4mlmlfsj4jVjVjvjVjvjvjLjvjLjLjvjLjvjvjvjvjvjvjVjvjVjVjvjVjvjvjVjvjVjVjvjVjvjvjVjvjVjVj4jVj4j4jLj4jLjLjvjLjvjvjVjvjVjVjvjVjvjvj4jvj4j4jVj4jVjVjvjVjvjvjLjvjLjLjvjLjvjvjvjvjvjvok", +"iYhYhYhYjfi4jfiYhYhYiYjfhYiYhYjfiYhYhYhYjfi4jfiYhYhYiYjfhYiYhYjfiYhYhYhYjfi4jfiYhYhYiYjfhYiYhYjfiYhYhYhYjfi4jfiYhYhYiYqriviKixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixnMqsqtquqvqwfbfbfbeTdmeoeTfbeLeTfufue2fbfbeLgWfkfke2fbfbfbfufufkfufkfbfufXfkfugafQfkgWfkfkfkfQfQfugufufkfkfkgvgvgvfQfkguguguggglfkgvgggugugvguguguglgvfQglhhgvqxqyqzqAnMixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixldohqBmKf4f4fsg3fslufPfPfPfPf4f4jvj4j4jvj4jvjvj4jvj4j4jvj4jvjvjvjvjvjvjLjvjLjLj4jLj4j4jLj4jLjLjvjLjvjvjvjvjvjvjvjvjvjvjvjvjvjvj4jvj4j4j4j4j4j4jvj4jvjvjvjvjvjvj4jvj4j4jvj4jvjvj4jvj4j4jvj4jvjvjvjvjvjvpj", +"hYiYhYiYhYhYhYhYhYjfhYhYi4hYiYhYhYiYhYiYhYhYhYhYhYjfhYhYi4hYiYhYhYiYhYiYhYhYhYhYhYjfhYhYi4hYiYhYhYiYhYiYhYhYhYhYhYjfhYhYi4j5qCkRixixixixixixixixixixixixixixixixixixixixixixixixixixqDqEqFqGgadmeLdmeLdmdmd3dmeLeLfbeTeoeLeLeTeLeLfbeLfbfbe2fbeLeLfkeTfbfbfbe2fbfkfke1fkfbfbfbfkfkfQfkfkftfkfbfXfQfkfufQfugvgvfQfQgufQgufkgufkfkggfkfQglfXguguguguguglgvgQgvgug4guhiqHoGqIqgixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixqJqKqLqMf4g3g3g3fDfPfseBfPjVjvjvj4jvj4j4jvj4jvjvjLjvjLjLjVjLjVjVjvjVjvjvjvjvjvjvjvjvjvjvj4jvj4j4jVj4jVjVjLjVjLjLjVjLjVjVjvjVjvjvjvjvjvjvjvjvjvjvjVjvjVjVjvjVjvjvj4jvj4j4jvj4jvjvjLjvjLjLjVjLjVjVpj", +"hYiYhYhYi4hYiYiYhYhYi4hYiYhYi4hYhYiYhYhYi4hYiYiYhYhYi4hYiYhYi4hYhYiYhYhYi4hYiYiYhYhYi4hYiYhYi4hYhYiYhYhYi4hYiYiYhYhYi4hYiYhYi4hYqNqOkoixixixixixixixixixixixixixixixixixixixoQqPqQqRd3c3dmeLdmeLeLeTdmeLeod3dmeTdmeLeLfbe2eTfbeLeLfbfueLfue2e2fbe2eLe2fte2eTfbfufkfbfbfkfkeLfbfkfbfkfufkfufkfufkfkfkfufQfXfugvfQgvfkfXgufXfQfXfkgugufkglfQfXfQglgugufkguggglglgvgvhhgugVg4glqSqmoLllixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixk4kOqTqMf4g3g3g3fPlujvjvjvjvjvjvjvjvjvjvjvjvjvjvjvj4jvj4j4jvj4jvjvj4jvj4j4jLj4jLjLjvjLjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjLjvjLjLjvjLjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvj4jvj4j4pj", +"hYhYhYhYiYhYhYhYhYiYhYhYhYiYhYhYhYhYhYhYiYhYhYhYhYiYhYhYhYiYhYhYhYhYhYhYiYhYhYhYhYiYhYhYhYiYhYhYhYhYhYhYiYhYhYhYhYiYhYhYhYiYhYhYhYhYqUqVioixixixixixixixixixixixixixixoUqWqXdzdzdzdmdmc3bEdmd3dmdzeLeLdmdmeLdmdmdmeTeLe2eLfbe2eTeoeLeoeLeTeLfufufufbfbe2eLfkfbeLfbfcfbfbfbfkfufufuftfXfbfkfkfbfkfkgWfkfufQfQfugugufufugvfQgvfXfQgufkgufkggglfkfkfQggfQgugugufkglgvgvgugvglguguguguglqYpZqZq0ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixk4kOnZq1f4f4eBjvjLjLjvjLjvjvjLjvjLjLj4jLj4j4jvj4jvjvjvjvjvjvjvjvjvjvj4jvj4j4jvj4jvjvj4jvj4j4jvj4jvjvj4jvj4j4jLj4jLjLjvjLjvjvj4jvj4j4jvj4jvjvjLjvjLjLjvjLjvjvjLjvjLjLj4jLj4j4jvj4jvjvpj", +"iYhYh0hYhYi4hYiYhYhYhYhYi4h0hYiYiYhYh0hYhYi4hYiYhYhYhYhYi4h0hYiYiYhYh0hYhYi4hYiYhYhYhYhYi4h0hYiYiYhYh0hYhYi4hYiYhYhYhYhYi4h0hYiYiYhYh0hYq2q3oCixixixixixixixixixjCq4q5hYhYi4dzcqdzd3dmdmeLdmc3dmeodmdmd3dmeLeLdmdmdmd3c3eodme2fbfbeLfceoeLeLeLfbfufue2eTfufbftfbfkeLeLfbfbfufugWfbfbeLfufkfbfbfbgafufkfkftfkfQfbfufXfufkgufkgvgvgvfQfQgufkfkfQfkfkfQfXguhhfQguguguglglfkgvgQgvgugugRglglglq6q7oHmPixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixktq8q9r.jvjvjvj4jvj4j4jvj4jvjvjvjvjvjvi2jvi2i2jvi2jvjvj4jvj4j4jvj4jvjvjvjvjvjvjvjvjvjvjvjvjvjvi2jvi2i2jvi2jvjvjvjvjvjvjLjvjLjLjvjLjvjvjvjvjvjvj4jvj4j4jvj4jvjvjvjvjvjvi2jvi2i2pj", +"hYhYhYiYi4hYh0hYh0hYiYhYh0hYhYhYhYhYhYiYi4hYh0hYh0hYiYhYh0hYhYhYhYhYhYiYi4hYh0hYh0hYiYhYh0hYhYhYhYhYhYiYi4hYh0hYh0hYiYhYh0hYhYhYhYhYhYiYi4hYjDr#raixixixixprm0rbhYhYhYiYi4hYbEdmbEdzc3bEd3dmdzdmbEdzc3dmdmdmeLdmdmeLeTc3dmd3dmdmeLeLfbe2eTfbdmfbeLeLeTfbfbe2fue2e2e2fue2fkeLfbfbfbfufkftfufbfufbfbfkfbfbfkfuflgvfufQfQfkfXfugugvfkfkgvfXfQfQgvgufkgggugufQfXhifXgugugufkglglgvglgugvhhgugugvglgViNrcrdiJixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixqJrerfrgjvjLjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvi2jvi2i2i2i2i2i2jvi2jvjvj4jvj4j4jvj4jvjvj4jvj4j4jvj4jvjvi2jvi2i2jvi2jvjvjLjvjLjLjvjLjvjvjvjvjvjvjvjvjvjvjvjvjvjvpj", +"h0hYhYh0h0hYhYhYhYh0hYhYhYiYhYh0h0hYhYh0h0hYhYhYhYh0hYhYhYiYhYh0h0hYhYh0h0hYhYhYhYh0hYhYhYiYhYh0h0hYhYh0h0hYhYhYhYh0hYhYhYiYhYh0h0hYhYh0h0hYhYhYhYrhrirjrkrlhYh0h0hYhYh0h0hYcqdmdmbEdzc3d3dzbEdzdzdmdmc3dmd3c3dmdmeLeLeLdmeLc3dmdmeLeLdmfYeLfbe2fbeLeLeLeLeTeLfbfbe2fbe2e2eLfueLeLfbfkfkfbfbftfufufufufbfbfufkfufuflfkfkfXfXfkfQgugvfQfkfkfQgufQfQgvfkgufkgggugugufXgufQfQgQguglglgvgvgugRgRglhnhYh0hYh0rml#jFixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixk6rnrorpi2i2i2i2i2jvi2jvjvjvjvjvjvi2jvi2i2jvi2jvjvjvjvjvjvj4jvj4j4jvj4jvjvjvjvjvjvi2jvi2i2i2i2i2i2jvi2jvjvjvjvjvjvi2jvi2i2jvi2jvjvi2jvi2i2i2i2i2i2jvi2jvjvjvjvjvjvpj", +"hYhYhYhYhYhYh0hYh0hYh0h0hYh0hYhYhYhYhYhYhYhYh0hYh0hYh0h0hYh0hYhYhYhYhYhYhYhYh0hYh0hYh0h0hYh0hYhYhYhYhYhYhYhYh0hYh0hYh0h0hYh0hYhYhYhYhYhYhYhYh0hYh0hYh0h0hYh0hYhYhYhYhYhYhYhYbEcqcqbEdmdmdzbEdzc3bEd3dmdmeLdmbEbEcqdzd3dmeLeLdmeLc3dmd3eLdmdmdmeLdmeoe2fbeLeLeLeLfbfue2e2fufbeLeLeLfbfbeLfbfbfbfkftfueLe1fufbfkfkfkfkfufufkflfufQfkfXfufugugvflfQfQfufXfQfkgufkgufkfkfXfQhigufkgufQfkglglgvgvgvhyhDhDh0hYhYhRh4rmrqnrixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixmPrrrsjvjvjvi2jvi2i2jvi2jvjvjvjvjvjvi2jvi2i2jvi2jvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvi2jvi2i2jvi2jvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvi2jvi2i2jvi2jvjvpj", +"hYh0hYhYh0hYhYhYh0hYhYhYhYhYh0h0hYh0hYhYh0hYhYhYh0hYhYhYhYhYh0h0hYh0hYhYh0hYhYhYh0hYhYhYhYhYh0h0hYh0hYhYh0hYhYhYh0hYhYhYhYhYh0h0hYh0hYhYh0hYhYhYh0hYhYhYhYhYh0h0hYh0hYhYh0hYdzdzdzcqcqeudmdmbEbEeSbEd3d3dmdzdmdmdmdmcqd3dmeLdmeLfbdmdmdmc3dmeLeLeLdmeLeoeTeTeLeoe2eLeLeLdmfufbfbfbfbe2eLeLfvfcfbfbfbfbfbftfufufvfXgafkfbfkfkf6flfkfXfXfXfkgufufkfQgvfQfXfQfQfkgugugPfkfQgufQfQgugugugufkglglgvhnh0h4iSiSh0hDh0hYhYj5rtruprixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixrvrwrxjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvjvi2jvi2i2jvi2jvjvjvjvjvjvisjvisisjvisjvjvjvjvjvjvjvjvjvjvi2jvi2i2jvi2jvjvi2jvi2i2jvi2jvjvjvjvjvjvjvjvjvjvjvjvjvjvpj", +"h0hYh0hYhYh0hYhDhYh0hDh0hYh0hYhYh0hYh0hYhYh0hYhDhYh0hDh0hYh0hYhYh0hYh0hYhYh0hYhDhYh0hDh0hYh0hYhYh0hYh0hYhYh0hYhDhYh0hDh0hYh0hYhYh0hYh0hYhYh0hYhDhYh0hDh0hYh0hYhYh0hYh0hYhYh0dzbrdzbrdzdzcqcqbEdmbEdzc3dzdzd3bEdzdmdmdmdmbEc3d3dmdzdmdmdmdmdmc3dmeLd3eLeLeLfce2eTeoeLe2eLeLeLeLfbe2e2eTfbeLftfbeLfbfcfbfbfbfbftfue1fbfkfQfkfkfbfkflgWflfufXfXfkfkfufkfQggfQfkfXfQgvfkgufkfkgggugvfQfQgvgugufQfkhDh4h4h0ibiSiSh0hDhYh7hYhYryrzrAixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixrBrCrDi2jvi2jvjvisjvisisjvisjvjvi2jvi2i2isi2isisjvisjvjvi2jvi2i2jvi2jvjvi2jvi2i2jvi2jvjvjvjvjvjvjvjvjvjvi2jvi2i2jvi2jvjvi2jvi2i2i2i2i2i2jvi2jvjvrE", +"hYhYhYh0h0hYh0hYh0hYhYh0hYh0hYh0hYhYhYh0h0hYh0hYh0hYhYh0hYh0hYh0hYhYhYh0h0hYh0hYh0hYhYh0hYh0hYh0hYhYhYh0h0hYh0hYh0hYhYh0hYh0hYh0hYhYhYh0h0hYh0hYh0hYhYh0hYh0hYh0hYhYhYh0h0hYeucqbEbEdzdzdzcqdzcqdmcqdmbEdmdzbEc3d3d3dmdmdmdmdmc3bEc3dmdzdmdmdmeTdmdmc3eLeLdmeLfYeLfbe2eofbeLeLe2fbfueTfcfbe2fbfteLfueLfbfbfbfbfkfbfuftfufufbfXfkfufQfuflfkfufXfkfXfXfkfkfQfkgvfkfQgufQgvfXfkfkgufkfkgugugufQgugVhYhDhYh4hYhDh4h0hDhDh0h0h0hYi4rFl#mbixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixktrGrHrIi2i2i2i2i2i2i2i2i2i2jvi2jvjvjvjvjvjvi2jvi2i2jvi2jvjvi2jvi2i2jvi2jvjvi2jvi2i2i2i2i2i2jvi2jvjvisjvisisjvisjvjvi2jvi2i2jvi2jvjvi2jvi2i2pj", +"hDh0h0hDh0hYh0h0h0hYhRhYhDh0hYh0hDh0h0hDh0hYh0h0h0hYhRhYhDh0hYh0hDh0h0hDh0hYh0h0h0hYhRhYhDh0hYh0hDh0h0hDh0hYh0h0h0hYhRhYhDh0hYh0hDh0h0hDh0hYh0h0h0hYhRhYhDh0hYh0hDh0h0hDh0hYbrdHcqbEcqdzdzdzdzbEeucqd3bEdmdmdmbEcqbEbEbEbEdmdzdzdmc3d3cqd3dmdmdmfbdmeLc3c3c3dmdmeLdmdmdme2e2fbeLeoeLeLeLfcfcfue2eTfteLeLeLfbfbfbfbe2fuftftftfufbe2fbfufkfufufue2flfbfQfkfkfkguflfufkfXfkfXfXfXfkgufkfQgugvguhigVhDhDhnhYhRh0h7h4h0hDh4hDhYhYhDh4hYrJrKp.q0ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixldrLrMjvjvjvjvi2jvi2i2jvi2jvjvi2jvi2i2isi2isisjvisjvjvi2jvi2i2jvi2jvjvisjvisisjvisjvjvi2jvi2i2jvi2jvjvjvjvjvjvisjvisisi2isi2i2jvi2jvjvpj", +"h0hYhYhYh0hDhYhYhDh0hYh0h0hYhRhYh0hYhYhYh0hDhYhYhDh0hYh0h0hYhRhYh0hYhYhYh0hDhYhYhDh0hYh0h0hYhRhYh0hYhYhYh0hDhYhYhDh0hYh0h0hYhRhYh0hYhYhYh0hDhYhYhDh0hYh0h0hYhRhYh0hYhYhYh0hDbEbrdIcqcqcqcqeubrdzbEdzcqdmcqbEcqdmbEbEdmbEcqd3bEdmdzdmdmdmbEdmc3d3dzdzdmeLeTdmc3c3dmc3d3eLdmfYeTe2dmeofbeLe2eLfceTfbfue2fbeLeTeLfbe2fbfbfkfkfbfte1fufufbfXfkfbfkfufbflgvflflfXfkfkgufQgvgvfQfufQfXguguguguguggguhnhDh4hDhRhRhDhYhRh4h4hRh4h0iShDh0hDhYhYhYkYrNrOixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixmgrPi2i2isi2isisiUisiUiUi2iUi2i2jvi2jvjvi2jvi2i2isi2isisjvisjvjvi2jvi2i2isi2isisi2isi2i2i2i2i2i2iUi2iUiUjviUjvjvisjvisisi2isi2i2pj", +"hDh0hDhDhYhYhDh0hDhYhDhDhYhDh0hYhDh0hDhDhYhYhDh0hDhYhDhDhYhDh0hYhDh0hDhDhYhYhDh0hDhYhDhDhYhDh0hYhDh0hDhDhYhYhDh0hDhYhDhDhYhDh0hYhDh0hDhDhYhYhDh0hDhYhDhDhYhDh0hYhDh0hDhDhYhY#9brbEdzrQdVbE#9bEcqbrdzdzbrdzcqdIcqbEcqbEbEdzbEbEc3d3bEdzdmdmdmbEdmcqc3dzeLeLdmeLeTdmc3dmd3eLdmdmfYdmeTdmc3dmeLeLe2fbdmfbeLfbe2eLfbe2fue2eLfbfbfufkfbe1fue2fufbfufbfkfkfbe2fkftfQfkfufkfQfufQgvfbfkfkgugufkgufufQgVhDhDhDgVhDhDhZhDh0hRhnh4h4hDh4h0h0h0h0hRh0hYrRl6oUixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixl.rSrTrUi2i2jvi2jvjvjvjvjvjvisjvisisisisisisjvisjvjvi2jvi2i2jvi2jvjviUjviUiUjviUjvjvjvjvjvjvisjvisisjvisjvjvi2jvi2i2iUi2iUiUjviUjvjvpj", +"hRhYh0h0hRh0hYhRhYh0hRhYh0hDhYhDhRhYh0h0hRh0hYhRhYh0hRhYh0hDhYhDhRhYh0h0hRh0hYhRhYh0hRhYh0hDhYhDhRhYh0h0hRh0hYhRhYh0hRhYh0hDhYhDhRhYh0h0hRh0hYhRhYh0hRhYh0hDhYhDhRhYh0h0hRh0azdHazazbrbEbEbEbrdIcqbEcqbEbrdzbrdzbEcqcqdmdmdmc3bEcqdzcqdzd3dmdmdmbEc3c3cqcqdmd3eLeLdmeTdmdmc3dmeLeLdmfYeLfceTe2eLe2eLeLfbfbfYe2fYe2ftfueLfteLfbfufbfbfkfbfufufbfufbfbfkfkfkfkgvfbfbfXfQfufXfkgvfQfkflfufQfQfkguglhRhDhRhDhDh4gVh4hDhDhDhYhYhYhRh4h0i4h0h0h0hYh7hYh4rVpqk4ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixkorWrXrYi2isi2isisi2isi2i2iUi2iUiUi2iUi2i2i2i2i2i2isi2isisi2isi2i2isi2isisi2isi2i2i2i2i2i2isi2isisiUisiUiUi2iUi2i2isi2isisjvisjvjvi2jvi2i2rE", +"h0hDhYhDh0hDhDhRhYhDh0hRhDh0hRh0h0hDhYhDh0hDhDhRhYhDh0hRhDh0hRh0h0hDhYhDh0hDhDhRhYhDh0hRhDh0hRh0h0hDhYhDh0hDhDhRhYhDh0hRhDh0hRh0h0hDhYhDh0hDhDhRhYhDh0hRhDh0hRh0h0hDhYhDh0hD#9dHdHbrdHazazrQbrbEbEbEdHbEbEbrbrdzdzbrdzcqbEd3d3dmbEbEdzcqd3bEdzbEdmdmdmbEc3d3d3c3eLdzdmeTdmc3dmc3c3d3eLdmdmdmeLfcdmdmeLdmeLeofbfYfbeLfbfte2eLeLfufbe2fbe2fbftfuftfue2fufbfkfufufkf6fbfXfbfufkftfkfQfufQfbfbfXfXhohbhRhbhRhDhnhDhDhDhRhDhRhRh2hDh0h4h4h4h0hDhDhDhYhYh7hYrZr0iwixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixkDr1r2isiUisisjvisjvjvisjvisisjvisjvjvisjvisisjvisjvjvi2jvi2i2i2i2i2i2isi2isisjvisjvjviUjviUiUi2iUi2i2jvi2jvjvi2jvi2i2i2i2i2i2iUi2iUiUisiUisispj", +"h0hDhDhRhYh0h0h0hDhDh0h0hDh0hDhYh0hDhDhRhYh0h0h0hDhDh0h0hDh0hDhYh0hDhDhRhYh0h0h0hDhDh0h0hDh0hDhYh0hDhDhRhYh0h0h0hDhDh0h0hDh0hDhYh0hDhDhRhYh0h0h0hDhDh0h0hDh0hDhYh0hDhDhRhYh0azbEdVrQdHdHdVdHbrbrbrbEbEdHcqcqcqcqbrbrdzdzdzdzbEcqd3dmdmbEdzcqcqbEbEdzdmdzdmdmc3dmd3cqdmeLdzdmfbeLc3dmc3c3eLeLfYfbdmdmfcc3eoe2e2eLdmfbfcfYeTfbeLfueLeLeLeTfbfbfbfuftfuftfufvfue2fbfufkfke2fkflflfQfkfufkfQfkfQfugRgVhnhnhbhDhDhDh0hDhDhDh4h4h4hRhDhDhYhYh4h4h0h0h4h0hDhYhYhYr3r4iKixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixrvrwr5i2isisisisisisi2isi2i2i2i2i2i2i2i2i2i2isi2isisiUisiUiUisiUisisisisisisi2isi2i2i2i2i2i2isi2isisisisisisi2isi2i2isi2isisi2isi2i2isi2isisisisisispj", +"hDh0hRh0hDhDhDhDhRhYhRhDh0hDh0hDhDh0hRh0hDhDhDhDhRhYhRhDh0hDh0hDhDh0hRh0hDhDhDhDhRhYhRhDh0hDh0hDhDh0hRh0hDhDhDhDhRhYhRhDh0hDh0hDhDh0hRh0hDhDhDhDhRhYhRhDh0hDh0hDhDh0hRh0hDhDaz#1azazbEdVazdVbrbrdVbrbrbrbrrQbrdVbEcqcqbrdzdzcqbrdzcqd3cqdmbEc3bEbEc3cqbEdmdmdzdmdmc3bEc3d3d3d3eLeLfbeLc3eLc3d3dmeLeodmfceLdmeLeLeLe2eLfYdmfYe2eLeTfteLfteLe2e2fbfkfbfkfte1fufbe2fufbfufkfufbfufbfbfkfQfkfkfkfkgRhyhDgVhDgVhRhRhnhRhDhRhDhnhDhDhDhDhDh4hDhRhDhRh4h4h0h0h0hYhYhDr6r7rjixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixldlcr8jlisi2isi2i2jvi2jvjvisjvisisisisisisisisisisi2isi2i2jvi2jvjvi2jvi2i2i2i2i2i2isi2isisisisisisiUisiUiUi2iUi2i2isi2isisi2isi2i2isi2isisi2isi2i2jvi2jvjvrE", +"hDh0hDhDh0hRh0h0hRhDh0hDh0hDh0hRhDh0hDhDh0hRh0h0hRhDh0hDh0hDh0hRhDh0hDhDh0hRh0h0hRhDh0hDh0hDh0hRhDh0hDhDh0hRh0h0hRhDh0hDh0hDh0hRhDh0hDhDh0hRh0h0hRhDh0hDh0hDh0hRhDh0hDhDh0hRr9dHaz#9rQazrQbEbrdHdVdHbrdVbrrQrQbEbEdIbEbEbEc3cqdzdzdzbEdzcqd3bEdmbEbEbEcqd3cqd3dmdmdmdmdmdmd3d3dmd3eLdmeLdmeLdmc3c3c3dmeLfYdme2fcdmeLeLe2eLeLeLdmfcfbeLeTfbeLfbeLfbe2e2e2fbfufbe1fufue2fufbfufbfkfbfbfufbfXfkfXglhnhnhDhRhKgVgVhnhDhnhDhRhDhYhRh4h4h4hDh4hRhDhDhYhDh4hDh0i4h0i4hYhYhYo4s.ixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixk4s#rHsaisiUisisisisisisi2isi2i2isi2isisi2isi2i2i2i2i2i2isi2isisiUisiUiUisiUisisisisisisisisisisi2isi2i2isi2isisiUisiUiUisiUisisiUisiUiUisiUisisisisisisi2isi2i2rE", +"hDhDhRhDh0hDhDhDhDh0hnhDhDh0hDhnhDhDhRhDh0hDhDhDhDh0hnhDhDh0hDhnhDhDhRhDh0hDhDhDhDh0hnhDhDh0hDhnhDhDhRhDh0hDhDhDhDh0hnhDhDh0hDhnhDhDhRhDh0hDhDhDhDh0hnhDhDh0hDhnhDhDhRhDh0hD#9#9#9azazaz#9azrQbE#9dVdH#9azdVbrbrbrbEbEdIdIcqbrbEbrdzbrbEdzdzbEbEbEdmdmbEbEcqd3bEbEdmdzdmdmdmc3c3c3dmdmdzeLeLdmdmdmc3dmdmeLeLfYfYfYdmeLdmeLeoeLeLfbdmfbeLfbeTeLfufueLdme2eLfbe2fufte1fue2fvfke2e2fbfufufbfbflftglglhnhngVgVhnhDhKhKhnhnhRhnhDhDh0hDh4hDhnh2hDhDhDhDhYhYh4h4h0hDh4h0hYh0hYsbnkixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixscsdlHi2isisi2isi2i2i2i2i2i2isi2isisiUisiUiUisiUisisi2isi2i2i.i2i.i.i2i.i2i2iUi2iUiUi2iUi2i2i2i2i2i2isi2isisi2isi2i2isi2isisi2isi2i2isi2isisi2isi2i2i2i2i2i2isi2isisse", +"h0hRhnhDhDh0hRh0hDhRhDhRh0hnhDh0h0hRhnhDhDh0hRh0hDhRhDhRh0hnhDh0h0hRhnhDhDh0hRh0hDhRhDhRh0hnhDh0h0hRhnhDhDh0hRh0hDhRhDhRh0hnhDh0h0hRhnhDhDh0hRh0hDhRhDhRh0hnhDh0h0hRhnhDhDh0#1#9#9#9azazdHr9azazazrQbE#9#9dHdHbrbrbrrQbEdzbrdIcqcqbEbEbrbEdzdzcqbEdUd3cqcqdmbEcqc3bEcqdzdmdmdmdzdmdmbEcqc3d3dzd3dmdmdmdmeMeLeLd3dmdmdmdmfYeLdmeofbeLeLfbdmfceTe2eLeTeLeLfue2e2fbfbfbfufbe1ftfvfbe2gafufkfQfufbglhKgVhngRgRhnhbhDhDhDgVhnhDgVhRhRhRh4hYhDh2h4h4hRhRhRh4hYhRhRh4h0h0h4h0hDhYhYowsfixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixsgshsiisisi2isi2i2isi2isisi.isi.i.i.i.i.i.isi.isisi2isi2i2isi2isisisisisisisisisisisisisisi.isi.i.isi.isisisisisisi2isi2i2isi2isisisisisisi2isi2i2isi2isisi.isi.i.i.i.i.i.rE", +"hDhDhDh0h0hDhnhDhnhDh0hDhnhRhDhDhDhDhDh0h0hDhnhDhnhDh0hDhnhRhDhDhDhDhDh0h0hDhnhDhnhDh0hDhnhRhDhDhDhDhDh0h0hDhnhDhnhDh0hDhnhRhDhDhDhDhDh0h0hDhnhDhnhDh0hDhnhRhDhDhDhDhDh0h0hD#9az.F#1sj#9#9skdHr9#9#9azrQazbrdVbr#9dVdHazbrrQbrbrdIdIbEbEcqcqbEcqdHdzdzbEeueubEd3bEbEcqbEcqd3bEdmdmdmdmbEdmd3c3d3eLeLd3dmeLeLc3dmd3eLeLdmdmdmfYeLc3eLdmeLe2eLfYfbeLeTfbfteTeLfue2e2e2e2fbfkfbe2fbe2fvfufkfbfufkgugRgVgVgRhngVhngVgVhnhyhnhDgRhDgVhbhZhDhDhDh0hDhRhnhDh4hDhDhDhDhRh4iSh0h0hYi4h0i4slsmixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixnrsnsoj2.qisi2i2isi2isisisisisisiUisiUiUisiUisisiUisiUiUisiUisisi.isi.i.i.i.i.i.iUi.iUiUi2iUi2i2isi2isisi2isi2i2isi2isisi.isi.i.isi.isisi2isi2i2isi2isisisisisisiUisiUiUisiUisisse", +"hnhRhDhnhnhRhDh0hRhnhDhRhDh0hRhnhnhRhDhnhnhRhDh0hRhnhDhRhDh0hRhnhnhRhDhnhnhRhDh0hRhnhDhRhDh0hRhnhnhRhDhnhnhRhDh0hRhnhDhRhDh0hRhnhnhRhDhnhnhRhDh0hRhnhDhRhDh0hRhnhnhRhDhnhnhR#1azaz#1#1#9#9#9#9#9#9r9dHazazrQazbrbrdVdVdHbrazazbrbrbrdzbEbEdHbEeucqbEdzdzbEdzcqbEbEdmdmbEcqbEcqbEd3dmdmdmdmdmbEc3c3d3eLd3dzdmeLdmeLc3c3c3eLeLdmfYfYeLfcdmdmeLe2eLeLfcfcfYeTfbeLe2fbeLfue2e2fbfkfbfbe2e1fuftfXfkfkhbgRhbgVgVgRgVhngVhbhbhnhKh4gVhDhDgVhnhRhRhDhRh0h0h4h4h4hnh4hRhDhDh0hYh4h0iSh0i4i4hYrmobixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixk6o0spmc.U.2.U.Ui.isisi.isi.i.i.i.i.i.i2i.i2i2iUi2iUiUi.iUi.i.isi.isisiUisiUiUi2iUi2i2i.i2i.i.isi.isisi.isi.i.i.i.i.i.iUi.iUiUisiUisisi.isi.i.isi.isisi.isi.i.i.i.i.i.i2i.i2i2iUi2iUiUrE", +"hDhDhRhDhDhnhnhDhnhRhnhnhDhnhDhRhDhDhRhDhDhnhnhDhnhRhnhnhDhnhDhRhDhDhRhDhDhnhnhDhnhRhnhnhDhnhDhRhDhDhRhDhDhnhnhDhnhRhnhnhDhnhDhRhDhDhRhDhDhnhnhDhnhRhnhnhDhnhDhRhDhDhRhDhDhn.M.Maz#9azaz#1#9#9sj#9#9skazdHr9#9#9az#9rQbrdVdV#9dHazbrrQbrdzbrdVdVdIbrbrcqdzbEbEdzbEbEd3dmbEdmbEbEbEbEd3dmbEdmdmdmdzdmd3d3c3d3dUeLdmdmdmdmc3c3eLeLdmfYdmdmfYeLeLeLeLfYeLeLfcfYeLeTfteTeLeLfue2eLfufue2fbfbftfbe2glglglgRgRhnhKgRgVgVglgVgVhnhngVhyhnhDgVgVhDgVhbhnhnhDhDh0h4h4hnh4hRhRhDhYhRhRh4h4h4h0iSi4sbjCixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixq0sqoWmmj2.Uay.q.q.2dGiUi.i.iUi.iUiUisiUisisi.isi.i.isi.isisi.isi.i.i.i.i.i.isi.isisi.isi.i.isi.isisiUisiUiUi.iUi.i.iUi.iUiUi.iUi.i.isi.isisiUisiUiUi.iUi.i.iUi.iUiUisiUisisi.isi.i.isi.isisrE", +"hDhnhnhDhnhDhRhDhnhDhRhDhRhDhnhnhDhnhnhDhnhDhRhDhnhDhRhDhRhDhnhnhDhnhnhDhnhDhRhDhnhDhRhDhRhDhnhnhDhnhnhDhnhDhRhDhnhDhRhDhRhDhnhnhDhnhnhDhnhDhRhDhnhDhRhDhRhDhnhnhDhnhnhDhnhD.F.F.F#9.F#9azaz.F#9az#9#9#9az#9azazazaz#9rQbEbEdVdVbE#9brbrazbrbrbEbEbrbEbrcqbEbEbEbrbEbEd3bEbEdmdmdmc3bEd3bEcqdmbEdzdmdmdmbEdmd3dmdmdmeLdmeLeLc3c3dmeLeLeLdmfYeLfcfceLeLeLeLeLdmfYfbeTeTeTdmeTeLeLfce2fke2fufkeLglgVgRgRgVhbhhgRhbhbhKgVgRhnhnhbhnhRgVhDhDh2hDhnhnhngVhRhDh0hYh2h4h0h2h2hRhRhRhYhYhYhRh4h0iSh0ppmIixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixjIsrss.U.2.2.2.U.U.Uj2j2ayisi.i.isi.isisiUisiUiUisiUisisisisisisiUisiUiUisiUisisi.isi.i.iUi.iUiUisiUisishFishFhFishFisisisisisisi.isi.i.i.i.i.i.isi.isisi.isi.i.isi.isisiUisiUiUisiUisisisisisisse", +"hnhRhnhDhRhnhDgVhRhngVhnhDhnhRhDhnhRhnhDhRhnhDgVhRhngVhnhDhnhRhDhnhRhnhDhRhnhDgVhRhngVhnhDhnhRhDhnhRhnhDhRhnhDgVhRhngVhnhDhnhRhDhnhRhnhDhRhnhDgVhRhngVhnhDhnhRhDhnhRhnhDhRhn.F.F.F#9.F.F.M#1#9az#9.Faz#9#9#9az#9azskazaz#1azrQbE#9dHbE#9az#9azbrbrrQbEbEdVbEbrcqbrdzbrbrdzbEdIbEd3bEc3dmbEbEd3bEbEdmdzdmdmdmbEc3d3d3dmdmdmdmdmeLc3c3dmd3eLeLdmfYdmdmfceTeLe2eLeLfYdmfbeLeTeTeLe2eLe2dmfcfue2e2gugRg4gVglgRgVhbglgRhnhbhbhngVgRhnhnhnhRgVhDhDhnhDgVgVhnhnhnhRhDh0hDh2hRhnh2h2hDhRh4hDhYhYiSh4h0h4pllBixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixmgstsujf.2j2.q.q.U.2.2.2.qj2j2.2isi.i.iUi.iUiUi.iUi.i.i.i.i.i.isi.isishFishFhFiUhFiUiUi.iUi.i.hFi.hFhFishFisisi.isi.i.iUi.iUiUi.iUi.i.isi.isisiUisiUiUisiUisisi.isi.i.iUi.iUiUi.iUi.i.i.i.i.i.isi.isisrE", +"hDhDhDhnhnhDhnhDhnhDhDhnhRhnhDhnhDhDhDhnhnhDhnhDhnhDhDhnhRhnhDhnhDhDhDhnhnhDhnhDhnhDhDhnhRhnhDhnhDhDhDhnhnhDhnhDhnhDhDhnhRhnhDhnhDhDhDhnhnhDhnhDhnhDhDhnhRhnhDhnhDhDhDhnhnhD#1.F#1#1.F.F.F#9#9#1#9#9az#9az.F#9az#9#9azazdHazazazrQazbrdVbr#9#9dHbrbrbEbEbEbrbrbEdVbEbEbrcqbrdHdzbEbEbEbEdmbEdmbEbEcqd3bEd3dmdmdmdmdmbEcqc3d3dmdmdmeTdmc3dmd3dmeLdmfYdmdmfcdmeLdmeLe2eLfYfbeLeLfYeLeTeLdmfue2e2fkglglglgRglglglgVglglhbhbhbgVhKgVhngVgVhngVgVgVhyhDhDhnhnhngVgVhRh4hRh0hDhRhnh2h4hRh0hRhYhYhDhRh4h4j5o9svixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixmPswqNjfjfjfjf.2jf.2.U.U.q.U.2.2j2dG.2ishFhFishFisisi.isi.i.isi.isisi.isi.i.i.i.i.i.i.i.i.i.isi.isisisisisisi.isi.i.isi.isisi.isi.i.isi.isisi.isi.i.i.i.i.i.isi.isishFishFhFishFisisi.isi.i.isi.isisi.isi.i.rE", +"gVhnhngVhnhDhnhnhnhDgVhDgVhnhRhngVhnhngVhnhDhnhnhnhDgVhDgVhnhRhngVhnhngVhnhDhnhnhnhDgVhDgVhnhRhngVhnhngVhnhDhnhnhnhDgVhDgVhnhRhngVhnhngVhnhDhnhnhnhDgVhDgVhnhRhngVhnhngVhnhD.F.M.F.F#1.F.F.F.F.M#9#9#9sj#9azaz.Faz#9rQ#9azdHdH#9r9azrQazrQrQbr#9bEbrdVazazbEbrdIbrdIbrdIbrbrdObEbEbEbEdIbEbEcqbEc3bEbEbEd3d3d3dmdzdzc3bEbEcqc3dzdmdmdUdmdUdmdmc3dmd3eLdmdmdmfYdmeLdmeLeLeLeLeLdmfbeLeTe2dmdmfufkglglgQgRglglglg4gVglgVhbhbhbhbhnhDgVgRgVgVgVgVhnhRhDhnhnhnh2hDhDhnhRhDhRhDhDhRh2h0hDh2hDhDhYhDh4hRiSh4sxsylfixixixixixixixixixixixixixixixixixixixixixixixixixixixixixixl.szrKnmjfiY.Ujfjf.qjfd2.2jf.2j2.q.Uj2.q.UdGi.iUiUisiUisishFishFhFi.hFi.i.isi.isisisisisisi.isi.i.iUi.iUiUi.iUi.i.hFi.hFhFishFisisi.isi.i.isi.isishFishFhFishFisisi.isi.i.iUi.iUiUisiUisishFishFhFi.hFi.i.isi.isisrE", +"hnhDhDhDhngVhnhDgVhnhDhnhnhDgVhDhnhDhDhDhngVhnhDgVhnhDhnhnhDgVhDhnhDhDhDhngVhnhDgVhnhDhnhnhDgVhDhnhDhDhDhngVhnhDgVhnhDhnhnhDgVhDhnhDhDhDhngVhnhDgVhnhDhnhnhDgVhDhnhDhDhDhngV.FsA.M.M#9.M.F#1.F.Fsk.F#9#9.M#9az#9#9#9az#1az#9#9az#9dHazaz#1brazrQ#9dVbrdVdVbrazbEbrdzrQbrdVbEbEbrbrbrdObEbEdzbEbEbEbEbEdmbEdmbEd3bEbEd3dmdmdmbEbEc3c3d3dmdUdmdmdmdmc3dmeLeLeLdmdmfYfYdmeLeLeTeLeLfYfYfbfcfYeTeofugVgVglglgRglhbglgRgVgVgVgVgVhbhbhbhbgVhKgVhDhngVhnhngVhnhyhDhnhnhDh4gVhnhnhRhRhDhDh4hRh4h4h2hRh0h0hRhDhYh4sBn1koixixixixixixixixixixixixixixixixixixixixixixixixixkonksCsD.qjfiYd2jf.qjfjf.2jfjfjf.qjf.2.2.2.U.2.q.qi.i.i.hFi.hFhFishFisishFishFhFi.hFi.i.i.i.i.i.hFi.hFhFi.hFi.i.i.i.i.i.isi.isisi.isi.i.hFi.hFhFishFisisi.isi.i.hFi.hFhFi.hFi.i.i.i.i.i.hFi.hFhFishFisishFishFhFi.hFi.i.rE", +"gVhngVgVhDhDgVhngVhngVgVhDgVhnhngVhngVgVhDhDgVhngVhngVgVhDgVhnhngVhngVgVhDhDgVhngVhngVgVhDgVhnhngVhngVgVhDhDgVhngVhngVgVhDgVhnhngVhngVgVhDhDgVhngVhngVgVhDgVhnhngVhngVgVhDhD.M.F.F.F.M.M.M#1.M.F#1.Fsk.F.F#9.M#9.Fbr#1#1#9.F#1az#9az#9r9dHaz#1azazaz#9dVdV#9dVdHbrazbrrQbEbrdVbEbEbEbrbEdzazbEbEbEdIbEdzdmbEbEc3bEcqbEbEbEc3dmdmc3bEc3d3c3d3dUdmdmdmdmc3c3d3eLeLdmdmfYeLfcdmeLeLeLe2fbdmfYeLeTgvgvgugRgugVglfQglhbhbgQg4gVguhNgVglhbhnglhbgVg4gVgVgVglhnhngVhnhyhDhnhDgVhnhRhnhRhRhRhDh4h4hnh4h4hRhDhRhDhYhDiSrbsEixixixixixixixixixixixixixixixixixixixixixixknsFsGjfiYjfi5jfi5jfjfd2d2d2.q.qjfjfjf.q.2jfjf.qjfj2.UishFhFishFisisi.isi.i.hFi.hFhFishFisisi.isi.i.isi.isisisisisishFishFhFhHhFhHhHishHisisi.isi.i.isi.isishFishFhFishFisisisisisishFishFhFishFisisi.isi.i.hFi.hFhFishFisisrE", +"gVhDhnhngVhnhDgVhnhngVhDhngVhDgRgVhDhnhngVhnhDgVhnhngVhDhngVhDgRgVhDhnhngVhnhDgVhnhngVhDhngVhDgRgVhDhnhngVhnhDgVhnhngVhDhngVhDgRgVhDhnhngVhnhDgVhnhngVhDhngVhDgRgVhDhnhngVhn.F.M.9sHsH.F.F.Fsk.M.F#1.F#1#1.F.F.F.M.F#9azazsjaz#1az#9az#9#9dHr9dH#9#9azazbEbErQdVdV#9dHbrbrazrQbEdIdIbEdIbEc3brbEbEbEbEbEbEbEd3c3bEbEcqbEbEcqd3c3bEdmdmc3dmc3d3c3dzdmdmeLdmdmdmc3c3eLeLdmfYfceLfcc3c3dmeLeLeLfbfuglgvglgvgugRglgVglglglgRhbgRglgRgVglgVglglhbgRhbgVhngVhnhnhngVhnhyhDhyhDhDh2gVh2hDhDhRhyhRh0hDh4h4hnh4h0h0hRhDhYh4sIinixixixixixixixixixixixixixixixixixiKr0sJiYi6iYjfd2jfd2.qjfi5jfjfjfjfjf.qd2jf.q.Ujfjf.q.2.qjfjfhHhFhFi.hFi.i.hFi.hFhFishFisisi.isi.i.hHi.hHhHi.hHi.i.hFi.hFhFi.hFi.i.i.i.i.i.hFi.hFhFi.hFi.i.hFi.hFhFi.hFi.i.i.i.i.i.hHi.hHhHhFhHhFhFi.hFi.i.hFi.hFhFishFisisi.isi.i.rE", +"hngVhngRhngRgVgVhDgRhngVgRhngVhnhngVhngRhngRgVgVhDgRhngVgRhngVhnhngVhngRhngRgVgVhDgRhngVgRhngVhnhngVhngRhngRgVgVhDgRhngVgRhngVhnhngVhngRhngRgVgVhDgRhngVgRhngVhnhngVhngRhngR.9.M.MsH.MsHsHsAsA.F.M.M.M.F#1sH.F.F.F#9.F.M.F#9#1az#1#9.Faz#9#9#9dHdHsKdH#9#9rQrQbEdV#9#9bEbrazbrazbrrQdzdIdIbrbrbEbrbrbEbEazbEbEbEbEbrdmbEbEbEbEcqbEdzdmdzdmdzc3cqc3d3eLdmd3dUdUdmc3dmeLdmdmd3fYdmfcdmfcc3eLeLeLfufkglfkglgvglgvgugVglgVglglglglhbgRgVg4gVglglglhbhbhnhbhnhngRgRhnhnhDhnhRhDhnhDhnhDgVhnhDhRhDhDhYh4hDh2h0hRh0hRh0h0hDptsLioixixixixixixixixixixixixiwmUsMiYhYi4hYiSi6iYi6hYd2d2d2jfjfjfjfjfiYiYjfd2jf.qjf.qjfjfjfjfjfi.isisi.isi.i.i.i.i.i.hFi.hFhFhFhFhFhFi.hFi.i.hFi.hFhFishFisishFishFhFi.hFi.i.i.i.i.i.i.i.i.i.hHi.hHhHishHisishFishFhFi.hFi.i.isi.isisi.isi.i.i.i.i.i.hFi.hFhFhFhFhFhFrE", +"hngVgVgVhDhnhnhngVgVhnhngVhngVhDhngVgVgVhDhnhnhngVgVhnhngVhngVhDhngVgVgVhDhnhnhngVgVhnhngVhngVhDhngVgVgVhDhnhnhngVgVhnhngVhngVhDhngVgVgVhDhnhnhngVgVhnhngVhngVhDhngVgVgVhDhnsH.F.M.M.M.M.M.MsHsHsA.F.F.M.Msk.F#1.F#1.F.F.F.F#9#9#9sjsj#1#9azaz#1#1#9azdHazdHazazrQbrbEdVdVbEbEdVazbrbEbrbEbEbEbEbrdVbEbEbrbrbEbEdIbEbEbEc3dmbEcqbEbEbEbEdzdzdzdmbEc3cqc3dmdmdzd3dUdmdmc3c3dmdmeLdmfYdmfYdmfcdmfbgugugufkglglglgugugugugVguhNglglglgVgRg4gVgVgVgVglglhbhbgRhbgVgVgRhnhnhnhnhRhRhnhnhDh2hDhnh2hRhRhDhyh0h4hDhRh0h4h0h0h0h0rZsNlKixixixixixixixk4losOsPhYiNiYiYhYd2i4i4i4hYi5i6i5iYd2jfd2jfi4aei5i5iYjfjfd2jfjfjf.U.qjfhHi.i.hFi.hFhFi.hFi.i.hHi.hHhHhFhHhFhFi.hFi.i.i.i.i.i.i.i.i.i.hHi.hHhHhFhHhFhFhHhFhHhHhFhHhFhFi.hFi.i.i.i.i.i.hFi.hFhFhHhFhHhHi.hHi.i.hFi.hFhFi.hFi.i.hHi.hHhHhFhHhFhFrE", +"glhngVhngVgRgVgRgVhngVgRhngRhngVglhngVhngVgRgVgRgVhngVgRhngRhngVglhngVhngVgRgVgRgVhngVgRhngRhngVglhngVhngVgRgVgRgVhngVgRhngRhngVglhngVhngVgRgVgRgVhngVgRhngRhngVglhngVhngVgRsH.r.rsH.F.MsA.MsH.F.MsH.M.F.F.Msk.M.M.FsjsH.F.FsQ#9.F#9#1#1#1#1sj.F#1az#1#9azdVskr9#9azrQazrQ#9rQdVdVdHdVazbEbErQbrdHbEdVbEbrbrbrbEbEbEbEbEdIbEbEdzc3bEbEcqd3dzd3c3c3dzc3c3bEc3d3d3dmdmdUdmdUdmdmc3eLdmeLsRdmdmdmfugufkguguguglglfkfQglgugvgugRglglhiglglhbglgVglg4gVgVgVglgRhnhngVhnhngRhngVglgVhnhnhRhyhnh2hnhbgVhnhRhnhRhDhDhDh4h4h0h4hRhDh0rmsSixixixixsTsUsVhYhYhYhYhYiShYhYhYiYi6hYhYhYi6i6iYiYjfiYd2i4d2jf.q.UjfiYjfiYjfjfd2jfjfi.hFhFi.hFi.i.hHi.hHhHi.hHi.i.i.i.i.i.hFi.hFhFhFhFhFhFhHhFhHhHi.hHi.i.i.i.i.i.i.i.i.i.i.i.i.i.hFi.hFhFhHhFhHhHhFhHhFhFi.hFi.i.hFi.hFhFi.hFi.i.hHi.hHhHi.hHi.i.i.i.i.i.rE", +"gRhngRgVhngVhnhnhngVhngVhngVhngVgRhngRgVhngVhnhnhngVhngVhngVhngVgRhngRgVhngVhnhnhngVhngVhngVhngVgRhngRgVhngVhnhnhngVhngVhngVhngVgRhngRgVhngVhnhnhngVhngVhngVhngVgRhngRgVhngVsWsA.9.r.9sH.M.F.MsA.M.MsH.M.FsAsA.F.M.M#1.F#1sj#1.F.F.F#9.F#9#1#9az#9#1.F#9#9sj#9azdVdHazazazrQrQbE#9dV#9dVbrdVbrbEazdzbrbEbEbrbrbEbrbEazbEdHbEd3dIbEbEbrc3dmbEd3cqdzc3dmc3dmc3dmbEcqc3d3dmdmdUdmdmdmc3d3c3eLeLeLftfQfQfkgugvguguguglfQgvgggugugugugRgVglhiglhhhhhngVgRglgVglhngVgRhbhbgVhbhDgRgRhnglhngVhyhDhyhDh2h2h4hnh2hRhDhDhYhDh4hnh4hnh4hRh0l6p3sXqUhYiSh0hYh0hYhYhYhYhYhYiShYiNhYiYhYi4i5i4i5hYi4jfiYiYiYi4d2jfi5.UjfiYhYjfjfd2hFg1g1hFg1hFhFhFhFhFhFhFhFhFhFi.hFi.i.hHi.hHhHi.hHi.i.i.i.i.i.hFi.hFhFhFhFhFhFhHhFhHhHhHhHhHhHhFhHhFhFi.hFi.i.hFi.hFhFhFhFhFhFg1hFg1g1hFg1hFhFhFhFhFhFhFhFhFhFi.hFi.i.rE", +"gVgVhngRhngRgVglgRhnglgVgRhngRglgVgVhngRhngRgVglgRhnglgVgRhngRglgVgVhngRhngRgVglgRhnglgVgRhngRglgVgVhngRhngRgVglgRhnglgVgRhngRglgVgVhngRhngRgVglgRhnglgVgRhngRglgVgVhngRhngR.r.9.9sAsA.9.rsH.9sY.9sA.MsAsA.MsHsHsH.F.F.M.M.F.F#1sH.Fsk.F.F.F#9#9#9azsZ#9#1#9#9#1#9az#9skaz#9azazazrQbrdVdVdVbrdVbrazbrbrbEbEbEbEbEbEbEbrbEbEazbEbEdIbEdzdmc3bEcqcqbEd3bEc3bEc3cqc3bEc3d3dmdmdzd3eodUeuc3c3c3d3fugPfkgggvfkfXfkfkgvguguggfQglgvgugvgugRguguhiglglglglglgRgRgVglgVglhbhngRgRhbgVhnhngVgVhnhnhyhDhnhDhnhDgVhnhnhRhRhRh4hRhDh4h4hnh4h0h4h0hDhYhYhYhYhYhYi4h0hYh0ibhYhYiShYi4iYiYhYi6i5hYhYi6hYi5hYiYd2i4d2jfjf.Ui5jfjfiYhFi.i.g1i.g1g1i.g1i.i.i.i.i.i.hFi.hFhFhFhFhFhFhHhFhHhHi.hHi.i.g1i.g1g1i.g1i.i.hFi.hFhFi.hFi.i.i.i.i.i.hHi.hHhHi.hHi.i.hFi.hFhFi.hFi.i.g1i.g1g1i.g1i.i.i.i.i.i.hFi.hFhFrE", +"hngVglgVgVhngVhngVgVglgVhnglgVhnhngVglgVgVhngVhngVgVglgVhnglgVhnhngVglgVgVhngVhngVgVglgVhnglgVhnhngVglgVgVhngVhngVgVglgVhnglgVhnhngVglgVgVhngVhngVgVglgVhnglgVhnhngVglgVgVhn.r.r.r.9sWsAsAsH.9sHsH.MsY.9sAsA.M.9sH.FsA.F.FsY.M.Fsk#1sj#1.F.F.FsQ#9#9.Fsj#9.F#9#9az.F#1#9azazaz#9azbrrQazazrQdVdVbr#9dVbrdIbEbEdIbEbr#9dIbEbrbrbrdObEbEbEdIdmc3bEbrc3bEbEcqd3bEdzd3c3cqdmbEbEcqdzd3dmeudmfcdmc3fbfkgugufQfufkfufkfkgvguguglglggglglgugugvgugugugRgRglhhhbhbhhgVgVgVgRgVglgRgRhbhnhnhDgRhnhngVhnhyhRgVhyhDh2h4h2hDhRhRhDhRhRhDhDhDhRh2h4hRh4hYhYiShDhRiSiShYh0h0h0hYhYhYi6iShYiYh0hYiNi4i4jfi5i6i5i5iYiYhYi4i4i4jfjf.qhFg1g1i.g1i.i.hFi.hFhFg1hFg1g1g1g1g1g1hHg1hHhHi.hHi.i.hFi.hFhFhFhFhFhFhHhFhHhHhFhHhFhFg1hFg1g1hFg1hFhFhHhFhHhHi.hHi.i.hFi.hFhFg1hFg1g1i.g1i.i.hFi.hFhFg1hFg1g1g1g1g1g1rE", +"gVglgVhnhnglglgVglgRhngVglgVgRgRgVglgVhnhnglglgVglgRhngVglgVgRgRgVglgVhnhnglglgVglgRhngVglgVgRgRgVglgVhnhnglglgVglgRhngVglgVgRgRgVglgVhnhnglglgVglgRhngVglgVgRgRgVglgVhnhngl.r.r.9.r.r.r.9sWsA.r.r.rsH.9sHsA.M.FsA.M.MsH.FsA.FsA.M.M.F#1.F#1.Fsj.F.F.F.M#9#1#9#1#1#1#9#9az#9#9#9sk#9dH#9brrQazrQdVdVdV#9dV#9azbr#9bEdzdIazbrbEbrbrbEbrazbEbEbEd3dzc3dmbEc3bEcqd3bEbEdzdmdmcqdzc3d3cqeLdmdmdUdmfbfufufkfkgugufkgugvfkfQfugvguglgufkfQglgvgggugRgugugugVglglgQglglgRgRgVgRgVgVhngVgRgRhbgVhKgRgVgRhnhnhyhDhyhDhnhDhnh2h2hDhRhDhRhDh4h4h4hnh4iSh0h0hDhYhYhYhRh0iNhYh0hYh0h7hYhYiSd2i4iYiYhYiNi4i6d2iSiNi6i6dGiYd2iY.qi4hFi.i.hHi.hHhHhFhHhFhFhFhFhFhFhHhFhHhHhFhHhFhFhFhFhFhFg1hFg1g1g1g1g1g1hFg1hFhFi.hFi.i.hFi.hFhFi.hFi.i.hFi.hFhFg1hFg1g1hFg1hFhFi.hFi.i.hHi.hHhHhFhHhFhFhFhFhFhFhHhFhHhHrE", +"glgVgRglglgVgRhngVglgVgVglhngVglglgVgRglglgVgRhngVglgVgVglhngVglglgVgRglglgVgRhngVglgVgVglhngVglglgVgRglglgVgRhngVglgVgVglhngVglglgVgRglglgVgRhngVglgVgVglhngVglglgVgRglglgV.rsW.r.r.r.r.9.MsA.rsA.rsA.9sH.MsH.M.F.M.M.M.F.F.9sHsH.F.F.M.F.M.F#1#1.F.F.F.F.F.M#9#9#1s0#9#9.Faz.FrQdVazdHskaz#9azazrQbE#9#9#9dV#9dVazbEbrazbEdVbEbEbrbEbrbrdHbEbEbEbEbEbEc3dmbEcqcqbEcqdzdzc3c3dmc3bEbEc3d3dmdmeLfufbfufkfkgugugugufufkfXgvgufufkfkguguglglgvgggugugugugRgRgugRglglhhhbgVgVgRgVhKhKhbhnhbgRgVhnhnhnhKhDgRhRgVhygVh4h2hnh4hnh4hnhRhRhDhYhDhDh2h4h2h4hRh0hRhRhYiSiNiNi4iNh0h0hYhYhYhYhYhYiNi4iYi4d2d2jfi4i5i5d2jfjfjfd2g1hFhFg1hFg1g1g1g1g1g1i.g1i.i.hFi.hFhFg1hFg1g1hFg1hFhFhFhFhFhFi.hFi.i.g1i.g1g1hFg1hFhFg1hFg1g1g1g1g1g1hFg1hFhFhHhFhHhHg1hHg1g1hFg1hFhFg1hFg1g1g1g1g1g1i.g1i.i.hFi.hFhFs1", +"gRgVgVgRgVglglgVglgVglglgVglgVgVgRgVgVgRgVglglgVglgVglglgVglgVgVgRgVgVgRgVglglgVglgVglglgVglgVgVgRgVgVgRgVglglgVglgVglglgVglgVgVgRgVgVgRgVglglgVglgVglglgVglgVgVgRgVgVgRgVgldzsWsWs2s2.r.r.r.9.r.r.9sW.rsAsH.r.rsA.9sAsY.M.M.F.MsHsHsA.M.FsY.M.M.MsH#1sj.F.F.F.F.M#9#1#1#9.F.F#1#1#9#9#9azdVr9dH#9azrQrQazrQdVdVdVbrbrbrazdHbrbEdIbEbrdVbEbrbEbEbEbEazbEbEbEdzc3bEdObEbEcqd3cqc3cqefcqcqbEc3d3eTfufkfbfbfXfkfugugufkfQgufkfQgvfQfugvguguglgufkfQfQgvgugugugRgugVgRglglglhbglgRgRgRgRglhKhnhbgVhbhnhngRgVhnhKhnhnhnhDhDhDhnh4hnh4hDhRhDhDhRhDiSh4h4h0h0iSh0h0h0hYiShYibiShYhYhYhYhYiShYhYiSiNi4iYi4hYi4i4jfi6iSiYiYhahFg1g1hFg1hFhFhHhFhHhHg1hHg1g1hFg1hFhFg1hFg1g1g1g1g1g1hFg1hFhFg1hFg1g1hFg1hFhFhFhFhFhFg1hFg1g1hFg1hFhFg1hFg1g1hFg1hFhFhFhFhFhFg1hFg1g1hFg1hFhFhHhFhHhHg1hHg1g1hFg1hFhFs1", +"gRglglgVglgVgVgRglgVgVgRgVglglglgRglglgVglgVgVgRglgVgVgRgVglglglgRglglgVglgVgVgRglgVgVgRgVglglglgRglglgVglgVgVgRglgVgVgRgVglglglgRglglgVglgVgVgRglgVgVgRgVglglglgRglglgVglgVgVsjs3.rsWs2.rsA.r.r.r.9.9.9sA.9sAsAsAsH.r.9.MsYsA.M.MsA.F.F.MsH.F.F.Fsk.M#1#1sj.F.F#9.F.F#9.M#9#1sjsZsj#1#9#1sj#9dV#9#9#9az#9azrQbrbEbrdV#9dVdVdVazbrazazbrdVbEbEcqc3brbEazbEbEdIbEbEc3c3c3bEcqbEbEcqd3c3c3c3cqbEeTfufkfkfQfufQfbfufufkfufkgugufkfkgggvgvgugugvguguguglfQgvgugugugRgVgugRgRg4glhhgVgVhhgRgRhKgVgVhnhhgRgVhDhnhngVgVhnhKhnhDhnhyh4hnhDhnhnhnhnhRh4h0h0hRiShnh4iSh4h0h0hYhRhYhYiNi4hYiNhYhYh7hYhYiSiNi5i4i4iYi4jfi4i4fPhHhHg1g1hHg1hHhHhFhHhFhFhFhFhFhFg1hFg1g1hFg1hFhFhFhFhFhFg1hFg1g1hFg1hFhFg1hFg1g1g1g1g1g1hHg1hHhHhFhHhFhFg1hFg1g1g1g1g1g1hHg1hHhHg1hHg1g1hHg1hHhHhFhHhFhFhFhFhFhFg1hFg1g1s4", +"glgVglgRgVglgVgugVglglglgVglgVgVglgVglgRgVglgVgugVglglglgVglgVgVglgVglgRgVglgVgugVglglglgVglgVgVglgVglgRgVglgVgugVglglglgVglgVgVglgVglgRgVglgVgugVglglglgVglgVgVglgVglgRgVglgVgu#1sWs5s3sW.rs2.r.r.r.r.9.9.9s6.rsWsWsAsA.rsH.9.M.rsA.M.FsA.F.9sH.MsA.F.F.M.M#1#1sH.F#1.F.F.F.M#9#1.FsZsZ.F#9#9#1#9az#9skaz#9#1azrQ#9bEbEdVdVbrdVazazbrrQbrdHdVbEbrbrbEc3bEbEazbEbEdIbEbrc3c3bEbEbEdzbEcqc3bEc3eLfue2fkfkfkfQfufufbfkfufXfufufkgufkfkgggvgvgvgufkgugugugufkgggvgugvgugVguguglglglglglgRgVg4gVgRhNglhnglgVgVgVhbhnhUgRhDhnhnhnhDhnhDh2hDhnhnh4hnhRhDhDh0hYiSh4hDh4h0h4h0hDhYhYhYiSiSh0iSiYhYhYh7hYhYiSi5hYi4iYi4fDjag1hFg1g1hFg1hFhFg1hFg1g1g1g1g1g1hFg1hFhFgAhFgAgAhFgAhFhFg1hFg1g1g1g1g1g1hFg1hFhFg1hFg1g1hFg1hFhFg1hFg1g1hHg1hHhHhFhHhFhFhFhFhFhFg1hFg1g1hFg1hFhFg1hFg1g1g1g1g1g1hFg1hFhFg0", +"gVgRgVglglgVglgVglglgVglgVglglglgVgRgVglglgVglgVglglgVglgVglglglgVgRgVglglgVglgVglglgVglgVglglglgVgRgVglglgVglgVglglgVglgVglglglgVgRgVglglgVglgVglglgVglgVglglglgVgRgVglglgVglgVgu.Ms5s5.r.r.r.rsW.rsW.r.r.9.9.r.9.9sAsAsA.9sH.9.MsH.M.MsYsAsA.MsHsH.F.F.F.M.F.M.M.F#1sH#1.F.F.F#9#9#1sZsj.FsZ#9.Faz#9#9#9sk#9dHazaz#1azbErQbrdVdVdH#9azbrrQazdzazbEdVdIbEbrbrbEbEbEbEbEd3dzc3c3dOdObEbEcqdzd3dmfbfufbfufkfkfkfufkfkfufufufkfkfugufkgugPgufkgvfQfkgvguguguglguggguglgugRgugugugVglgRgVgVhnhhhhgVg4gVhKgRhKglgVhbhnhnhnhDhDhUhKhngVhyhDhnhDhnhDh2hRhRhnhyhRhDhRiSh4hYh4h0h0hDhYhYhRhYhYiShYh0hYhYhYhYhYhYjfi4fsjThHgAhHgAgAg1gAg1g1g1g1g1g1hFg1hFhFg1hFg1g1g1g1g1g1g1g1g1g1hFg1hFhFhFhFhFhFg1hFg1g1g1g1g1g1g1g1g1g1hFg1hFhFg1hFg1g1g1g1g1g1hHg1hHhHgAhHgAgAg1gAg1g1g1g1g1g1hFg1hFhFg1hFg1g1s7", +"guglglguglgVglglglgVglglguglgVglguglglguglgVglglglgVglglguglgVglguglglguglgVglglglgVglglguglgVglguglglguglgVglglglgVglglguglgVglguglglguglgVglglglgVglglguglgVglguglglguglgVglglglgusAs5s3s5.rsW.rsWs2sWs2sW.r.r.r.r.MsAsAsAsA.rsH.9sAsAsA.MsAsA.MsHsA.9.F.F.F.MsY.M#1.M#1#1#1.F#9sQ.F.M.F.F#9#9sZ#9.F#1#9#9#9#9#9#9#9#9#9#9az#9rQdV#9dVbrbrbrdHbrrQdHdIdIdIbEbrbrbrdHbEdHdHd3dIbEbrc3bEbEbEcqdmfteTfbfbfbfbfbe2fufkfQfufufufXfufkfufkgugufQgufkggfkgufuguguglgPglgggQfQgvgugugugVglguglglgVglgRhhhnglhngRgRgVgVgVhngVhnhnhnhnhngVhngVhnhDhDhDh2hDhZh2hnhDh3hRhDhDh4hDh4hnh0h0hYh0hDhYhYhYhYiNh0hYh0i4hYhYjfjYg1g1hFg1hFhFhHhFhHhHgAhHgAgAg1gAg1g1hFg1hFhFhFhFhFhFg1hFg1g1hFg1hFhFg1hFg1g1gAg1gAgAhFgAhFhFg1hFg1g1hHg1hHhHgAhHgAgAhFgAhFhFg1hFg1g1hFg1hFhFhHhFhHhHgAhHgAgAg1gAg1g1hFg1hFhFs8", +"glglgVglglguglgVguglgVglglgVgugVglglgVglglguglgVguglgVglglgVgugVglglgVglglguglgVguglgVglglgVgugVglglgVglglguglgVguglgVglglgVgugVglglgVglglguglgVguglgVglglgVgugVglglgVglglguglgVguglfQ.r.r.rs5s5.r.rsW.rsW.rsAs2.r.rsH.r.r.9sAsAsAsA.9.r.rsHsA.9.r.M.F.M.M.FsH.MsH.F.M.F.M#9.M#1sHsH#1.F.F.F.M#1#9sj#1sZ.Faz#1#9#9#9#9dVdVaz#9#1azaz#9brdVbrdVbrbrbrbrdHbEbrdVdVbEbEbrbrbEdObEazbEbEeuc3bEc3bEdmfbfbfvfufbfufbfbfufufkfbfufQfufbfufXfkfkfufkgufQgPgggufkfQgvfugufkglglglfkglfQgvgvgRgugugVgRgRhhgRhbhhglg4hngVgRgRgRgVhnhngVhnhnhnhnhUhDhnhRhnhDhDhDhDh4iShnhnhnhRhRhDhRiShRiSh0h0h0h0hYhYhYiShYiNiNh0i4.qjvgAg1g1g1g1g1g1gAg1gAgAhFgAhFhFgAhFgAgAg1gAg1g1g1g1g1g1gAg1gAgAg1gAg1g1g1g1g1g1hFg1hFhFg1hFg1g1gAg1gAgAhFgAhFhFg1hFg1g1gAg1gAgAg1gAg1g1g1g1g1g1gAg1gAgAhFgAhFhFgAhFgAgAg1gAg1g1s9", +"guglguguhhgVguglguglgugugVguglglguglguguhhgVguglguglgugugVguglglguglguguhhgVguglguglgugugVguglglguglguguhhgVguglguglgugugVguglglguglguguhhgVguglguglgugugVguglglguglguguhhgVguglguglgufbsA.r.r.r.rt..rsW.rsWsW.rs6sA.r.r.r.9.r.r.9s6.rsWsA.9.rsH.rsH.rsA.MsA.M.MsH.MsHsA.F.M.Msk.M.F.F.F.F.F.F.F#9.M#9.FsZ#1.Faz#1az#9#1#9.M#9dVaz#1azrQaz#9#9brdVdVbr#9az#9dHbEazdVbEbrbEbrbEbrbEbEbEdIbEdzc3eLfce2fbfufbe1fbftfbfbfue2fufkfQfufkfbfufbfufkftfkfugufkfkfkfkfkfQfkguglguguglfkglfQgggvgugRgugugVglguglhhhnhhg4hnhngVgRhbhbhhhbhnhnhbhnhUhDgRhnhRhnhyhDhnhnhDh4iSh0hnh0hDhRhDh4hDiSh4h0h4h0hDhYhYhRhYiNiNj9hFg1hFhFgAhFgAgAhFgAhFhFg1hFg1g1gAg1gAgAhFgAhFhFg1hFg1g1g1g1g1g1hFg1hFhFgAhFgAgAgAgAgAgAhegAheheg1heg1g1hFg1hFhFgAhFgAgAg1gAg1g1hFg1hFhFgAhFgAgAhFgAhFhFg1hFg1g1gAg1gAgAhFgAhFhFt#", +"gugVglglguglglglglglgugVglgugVgugugVglglguglglglglglgugVglgugVgugugVglglguglglglglglgugVglgugVgugugVglglguglglglglglgugVglgugVgugugVglglguglglglglglgugVglgugVgugugVglglguglglglglglgugVdm.r.r.r.r.r.r.r.r.rt..rsWsW.rsWsA.r.r.r.r.9.r.r.9sAsWsA.r.rsH.MsYsY.9.M.MsA.MsHsH.FsA.F.M.M.F.M.Fsj#1.F.F.F.F#9#9#1.Fsj#1#9#9#1#1rQ#9#1#9r9dVazazazrQbEdVdVdVdVdVdVbrazbrbrbEbEbEbEbrbEbrbEbrbEbEbEbEdmeLfbfbdmfbe2fufbeTeTfvfbfbfbfke2fufkfufkfkfufbfufufufkgugugufQfkgggvfQfkfkglgugufkggglfQglguglgugVgugVgRgRhbhhhbgRgRgVg4gVhKgRhnhngVhnhnhnhnhUhDhnhnhyhnhnhyhDh4iShDh4h0h0hRhDhRhDiSiSiSiSiSh0h0h0h0hYk0g1gAg1gAgAgAgAgAgAg1gAg1g1gAg1gAgAhFgAhFhFg1hFg1g1gAg1gAgAg1gAg1g1g1g1g1g1g1g1g1g1g1g1g1g1gAg1gAgAg1gAg1g1gAg1gAgAg1gAg1g1g1g1g1g1gAg1gAgAgAgAgAgAg1gAg1g1gAg1gAgAhFgAhFhFg1hFg1ta#h", +"glguglguglguguglhhguglguguglguglglguglguglguguglhhguglguguglguglglguglguglguguglhhguglguguglguglglguglguglguguglhhguglguguglguglglguglguglguguglhhguglguguglguglglguglguglguguglhhguglguguc3.r.r.r.r.r.r.r.r.r.r.rsWs3sWsWs2s2sA.r.rs3.r.9.r.rsAsAsWsA.r.rsA.rsYsA.MsA.M.F.9sH.FsH.r.F.M.M#1#1.F.F.F.F#9#9.M#9.F#9.Fsj#1#1.F#9#1#1#9#9#9sk#9az#9az#9bEdVdV#9#9dVazbrdHbr#9dIbEdIbEbrbEbrbEdHdHdmeMeLe2eLdme2e2e2fbfvfbeTeTfbfbfbe2fue2fufkfufkfufufkfufufufkfQgufkggfkgggvfufkguglgugufQfQgvgvgugugvgugVgRgRgRgRgRglhnhbhbgRgRgVgRgRgVhyhnhnhnhnhnhZhnhnhngVgVhyhnhDh4hDhDhDhRhRhRhDhYiSiShnhDh4h0h0l1gAgAg1gAg1g1hFg1hFhFg1hFg1g1g1g1g1g1gAg1gAgAgAgAgAgAg1gAg1g1gAg1gAgAhFgAhFhFgAhFgAgAg1gAg1g1g1g1g1g1g1g1g1g1gAg1gAgAhFgAhFhFgAhFgAgAg1gAg1g1hFg1hFhFg1hFg1g1g1g1g1g1gAg1gAgAgAgAgAtbQt", +"glguguguhhglglglguguglglguglguhhglguguguhhglglglguguglglguglguhhglguguguhhglglglguguglglguglguhhglguguguhhglglglguguglglguglguhhglguguguhhglglglguguglglguglguhhglguguguhhglglglguguglglgugldH.r.r.r.r.r.r.r.r.rs5.rs3.rs5.r.r.rs2sW.r.r.r.r.r.MsW.rsAsAsAsA.9sHsAsAsYsA.M.M.M.M.9sH.F.F.F.F.M.F#1#1sj.F.F#1#9.F.M#9.F#9sZsZ#9az.F#1#1#1#9#9dV#9#9azazazbrbE#9dI#9dVdVazbrbrbrdHdIbEdIbEbEbEbrdzdmeLeLeMeLeLeLfbe2fbe2fbe2fbfvfvfbfbfbe2fufkfufQfufkfufufufufkfkfQgugufkggggggfQfufkguglguguglglgQguglgugRgVgVgugRgRhngRhhhnhngVgRhNhKhngRgRhygVhbhnhnhnhDh4hnhRgVhnhnhnhDhDhDh4h2hRh0hRh0hYhDhDhDhxg1gAgAgAgAgAgAg1gAg1g1gAg1gAgAg1gAg1g1fjg1fjfjgAfjgAgAg1gAg1g1g1g1g1g1g1g1g1g1gAg1gAgAgAgAgAgAgAgAgAgAgAgAgAgAg1gAg1g1g1g1g1g1gAg1gAgAgAgAgAgAg1gAg1g1gAg1gAgAg1gAg1g1fjg1fjfjgAfjgAtcQt", +"fQglguglgufQgufQglglguguglguglgufQglguglgufQgufQglglguguglguglgufQglguglgufQgufQglglguguglguglgufQglguglgufQgufQglglguguglguglgufQglguglgufQgufQglglguguglguglgufQglguglgufQgufQglglguguglgugu#9.r.r.r.r.r.r.r.r.r.r.r.rt..rsW.rs3sWs2sAs2.r.r.r.rtd.M.rs6sA.9.rsA.9sAsHsA.r.rsA.M.M.MsH.M.F.rsA.F.M#1.F#1.F#1.F.F.F.F.F.F.F.F.FsZ#9.F#9#9#9#9#1dVdV#1#9#1az#9rQbEdV#9dVbrbrbrdHdHbrdHdIdVdIbrdzfbdmeoe2eoeLeLeLeLeLfbe2e2e2eTfufbeTfbfbfbfufke2fbfufQfufbf5fkfkf5fugufufQfkfQfkfkggfQfkgugugugugugggvgggQgvglgugugVhigRglhngQhhgVhnglhngRgRgRhnhnhyhnhngRhngRhUhDhngVhyhDhDhDh4h4iSh4h0h2hYhDhDayhPfjgAgAg1gAg1g1gAg1gAgAg1gAg1g1gAg1gAgAg1gAg1g1heg1hehegAhegAgAgAgAgAgAgAgAgAgAg1gAg1g1g1g1g1g1g1g1g1g1g1g1g1g1gAg1gAgAfjgAfjfjgAfjgAgAg1gAg1g1gAg1gAgAg1gAg1g1gAg1gAgAg1gAg1g1heg1heteQt", +"guglfQguglguglglglguglguglguglguguglfQguglguglglglguglguglguglguguglfQguglguglglglguglguglguglguguglfQguglguglglglguglguglguglguguglfQguglguglglglguglguglguglguguglfQguglguglglglguglguglguglfXsk.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5s3.rsWs3.rs2s2sA.r.rs3.r.9.M.9sAsAsA.9sAsHsA.rsY.r.MsA.M.F.M.F.M.F.FsA.Fsk#1#1.F.F.F.F.F.F.F#9.F.Fsj.FsZsZ#9#9#1#9#1sK#9sk#1br#1azazrQbEbr#9brdVbraz#9brazdHazbEeLeLdmfbdmdmc3eLeLeLe2eLfbfbfbe2e2fbfvfbfufbfbfbfue2fkfufkfQfQfbfufufkfufkfkfkgugPgvgvgugvfQguguguglglguglgQgQglgQgugugRgRhigRhhhnhnhhgVgVgRgRhDgRhngVhngVhnhDhnhnhnhZh4hDhKhDhRhDh4hDh4hDh4hDjfhFgAg1gAgAg1gAg1g1fjg1fjfjgAfjgAgAgAgAgAgAgAgAgAgAg1gAg1g1fjg1fjfjg1fjg1g1g1g1g1g1gAg1gAgAgAgAgAgAgAgAgAgAfjgAfjfjgAfjgAgAg1gAg1g1gAg1gAgAg1gAg1g1fjg1fjfjgAfjgAgAgAgAgAgAgAgAgAgAg1gAg1tfQt", +"guguglfQglfQgufQguglfkguguglgufkguguglfQglfQgufQguglfkguguglgufkguguglfQglfQgufQguglfkguguglgufkguguglfQglfQgufQguglfkguguglgufkguguglfQglfQgufQguglfkguguglgufkguguglfQglfQgufQguglfkguguglgufkfXsY.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt..rt.s5s3.r.r.rsWsW.r.r.r.r.M.rs6.rsWsAsA.9sHsHsAsA.M.M.MsH.MsH.MsHsA.F.F.M.Fsk#1#1sH.F.F#9.F#9.M#9.FsZsZ#9azaz#9#9#9#1dV.M#1az#1azrQbrbEdVdVbE#9#9brazbr#9dUeMd3eLdmeoeLdmdmeLe2eLeTeTeLdmfbdme2fbfue2fbfvfvfvfbfbfue2fufkfQfufbf5fbfbfufkfkfkgugugPfkglfkgufkgufkguguguglglglglgugRgugRgRgugRgugRhhhhhnhngVgVhUhKgRhnhnhhhnhngRhnhZhDhDhnhRhRgVhDhDh4hDiNiifjg1fjg1g1gAg1gAgAg1gAg1g1fjg1fjfjg1fjg1g1g1g1g1g1gAg1gAgAgAgAgAgAgAgAgAgAg1gAg1g1fCg1fCfCg1fCg1g1gAg1gAgAg1gAg1g1g1g1g1g1fjg1fjfjg1fjg1g1gAg1gAgAg1gAg1g1fjg1fjfjg1fjg1g1g1g1g1g1gAg1gAtgQt", +"glglfkguguglguglgugufQguglfkguglglglfkguguglguglgugufQguglfkguglglglfkguguglguglgugufQguglfkguglglglfkguguglguglgugufQguglfkguglglglfkguguglguglgugufQguglfkguglglglfkguguglguglgugufQguglfkguglglfk.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt.s5s5sW.r.rs2s6sAs2.r.rtd.9.M.rsWsAsA.r.9sHsAsAsH.r.M.M.FsA.MsH.M.F.F.M.M#9#1.M.F#1sH.F#1.F.F#9.F.FsZ.F.FsZ.F#1az#9#9#9#9#9#1az#1#1br#9#9br#9dVdHbrazdzdmdmdmeLd3d3d3dmdmdmeoe2e2eLeLeLeLfbfbdme2e2e2eTfvfufbfbfufbfufufkfQfQfkfbfufufufkfkfkgufkfQgQgggggvfQfkguguguglgRgRgQgugRglgRgRgVgugugRhhhhgVhbhnhhglhnhngRgRgRgVgVhnhngRh0h4hnhDhDhKhnhyhYjvgAgAg1gAg1g1gAg1gAgAfjgAfjfjg1fjg1g1gAg1gAgAfjgAfjfjfCfjfCfCfjfCfjfjg1fjg1g1gAg1gAgAgAgAgAgAfjgAfjfjgAfjgAgAfCgAfCfCgAfCgAgAgAgAgAgAg1gAg1g1gAg1gAgAfjgAfjfjg1fjg1g1gAg1gAgAfjgAfjfjfCfjthabQt", +"gufQguglglfQfkgufkguglgufkgugufQgufQguglglfQfkgufkguglgufkgugufQgufQguglglfQfkgufkguglgufkgugufQgufQguglglfQfkgufkguglgufkgugufQgufQguglglfQfkgufkguglgufkgugufQgufQguglglfQfkgufkguglgufkgugufQgufQeL.9.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt.s5.rsW.rs2.rs2.r.r.r.9.r.9.r.9sWsAsA.rsH.rsH.r.M.M.M.F.M.FsH.FsA.F.F.Msk#1.F#1.F.F.F.F.F.F.F#9.MsZsj#1.F#9az#9#1#9#9skdV#1#9brrQbrbEdVbr#9dHdzdUdUdmdzeLdmeMc3dmdmeTdmdmdme2dmeMeLeLdmeLfbe2fbe2e2fbeTfufbfufbe2e2fufkfufQf5fQfbfufufufkfkfufQgvfkglgvfQgvfugugugugufQglgQglgQguglgVgugVgugRgRhhgVhnhbhngRhngRgRhngRhhhngVhnhDhZhDh4hnhRjVg1gAgAfCgAfCfCgAfCgAgAg1gAg1g1fjg1fjfjgAfjgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAgAfCgAfCfCfCfCfCfCgAfCgAgAg1gAg1g1gAg1gAgAg1gAg1g1gAg1gAgAfCgAfCfCgAfCgAgAg1gAg1g1fjg1fjfjgAfjgAgAgAgAgAgAgAgAtiQtQt", +"fkgufQfkfkguguglgufkgugufQglgufkfkgufQfkfkguguglgufkgugufQglgufkfkgufQfkfkguguglgufkgugufQglgufkfkgufQfkfkguguglgufkgugufQglgufkfkgufQfkfkguguglgufkgugufQglgufkfkgufQfkfkguguglgufkgugufQglgufkfkgufQdz.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5s5.rs5.rsW.r.rs2sA.r.rs3.r.9.MsAsAsA.rsA.rsHsH.rsY.r.MsA.MsA.FsH.F.F.F.F.M.M.F#1.FsH.F.F.F.F.F.M.F#1.FsZ#1#9az#9sj#9#9.F#9#9#1#9#1azrQbrdVbEd3dzd3dmdmc3eLdmdmd3d3eLdmfYeLdme2eoe2eLeLe2eLfbfbe2e2fbfufueTeTfvfbfbfufufkfufQfQfuf5fufbfufufufkfQguglglfkgugvgugugugugugufkglgRgQhhgQgQgugVgugRgRgVhnhhhhgRhnhngRhngRgRgVhngVgVhnhnhnkggAfCgAgAgAgAgAgAfCgAfCfCgAfCgAgAfCgAfCfCfjfCfjfjg1fjg1g1gAg1gAgAfCgAfCfCgAfCgAgAgAgAgAgAg1gAg1g1fjg1fjfjgAfjgAgAfCgAfCfCfCfCfCfCgAfCgAgAgAgAgAgAfCgAfCfCgAfCgAgAfCgAfCfCfjfCfjfjg1fjg1g1gAg1tjQtQt", +"fQgugugugufQfkgufkgufQfkgufkgugufQgugugugufQfkgufkgufQfkgufkgugufQgugugugufQfkgufkgufQfkgufkgugufQgugugugufQfkgufkgufQfkgufkgugufQgugugugufQfkgufkgufQfkgufkgugufQgugugugufQfkgufkgufQfkgufkgugufQgugugudm.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5.rs5s3sW.rs2s2sAs2.r.r.r.r.9.MsAsAsWsA.rsH.rsAsH.rsAsA.M.9.F#1.F.FsA.F.Msk#1#1.F#1.F.F.FsQ#9#9.M.F.Fsj#9sZ.F.Faz#9#1#1#1#9#1#1#9azrQdUd3dzdzdzdmdmdUdmdmdzc3dmeMeLdmeLdmdmdmeoe2eLeMeLfbdmfbfbe2e2fufbeTfvfbfbfufbe2e2fbfufkfQf5fufufufufkfkgufkfQgvgvgvfkguguguguguguglgRgQglgRglgugugugRgRgRgRhnhhhnhbhngRgRhngRgVhnhnhnhDhIgAfCgAfCfCgAfCgAgAgAgAgAgAfjgAfjfjgAfjgAgAfjgAfjfjfCfjfCfCgAfCgAgAfjgAfjfjfCfjfCfCgAfCgAgAfjgAfjfjgAfjgAgAgAgAgAgAfjgAfjfjgAfjgAgAfCgAfCfCgAfCgAgAgAgAgAgAfjgAfjfjgAfjgAgAfjgAfjfjfCfjfCfCgAfCt#QtQt", +"gufkfQgufkguglfQfQgugufQgufQfkfQgufkfQgufkguglfQfQgugufQgufQfkfQgufkfQgufkguglfQfQgugufQgufQfkfQgufkfQgufkguglfQfQgugufQgufQfkfQgufkfQgufkguglfQfQgugufQgufQfkfQgufkfQgufkguglfQfQgugufQgufQfkfQgufkfQgufkbE.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt..r.rs5.r.rsW.rs2.rsA.r.r.r.r.Ms6sA.9sA.rsA.rsHsAsAsY.r.MsH.M.M.MsHsH.F.F.F.Msk#1#1sj#1.F.F.F.F.M.F#9.MsZsZ#1az.F#9br#9#1.M#1#9#1bEcqdzdzc3d3eLdzdzd3dmdmdzdmdmd3eMeLeodmeoe2dme2e2e2eLeLdmfbfbe2e2fbe2fufufbfvfbfbfbfkfbfkfQfQfuf5gufbfufkfufkgugugufQfkfkgvfQgugugvgugugufkgRglhhhhglguguguhNhhgRgRhhhhhhhbhnhUhKhKgRfPgAgAfjgAfjfjfCfjfCfCfjfCfjfjfCfjfCfCfjfCfjfjgAfjgAgAgAgAgAgAfjgAfjfjgAfjgAgAgAgAgAgAfCgAfCfCgAfCgAgAfjgAfjfjfCfjfCfCfjfCfjfjgAfjgAgAfjgAfjfjfCfjfCfCfjfCfjfjfCfjfCfCfjfCfjfjgAfjgAgAgAgAgAgAfjtkQtQtQt", +"fkgufkfQgufkgufugufQfkfkgufkgugufkgufkfQgufkgufugufQfkfkgufkgugufkgufkfQgufkgufugufQfkfkgufkgugufkgufkfQgufkgufugufQfkfkgufkgugufkgufkfQgufkgufugufQfkfkgufkgugufkgufkfQgufkgufugufQfkfkgufkgugufkgufkfQgufk#1.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt..rsWs5.rtl.rs2s2.rsA.r.r.r.M.rs6.9sWsAsA.rsHsA.rsYsYsA.MsH.MsH.MsHsA.F.F.M.M#1.Fsjsj.F.F#9.F#1.M#9.FsZ.F.F#9.F#9#1br#1#9.MbrbEbrcqdzdzc3d3d3dzdmd3d3dUdUdzdmeMdmeLdmeofYe2dmeodme2e2eLeLdmfbe2e2fufue2fueTeTfbfbfbfbe2fufkfQfufufQfufufufufugufkgugufkgufkgugggugggugugufkglgQglglhhgugRgRgugRgugRhhhhhhgRhngR.UgAfCfCfjfCfjfjgAfjgAgAgAgAgAgAfCgAfCfCgAfCgAgAfCgAfCfCfCfCfCfCgAfCgAgAeKgAeKeKgAeKgAgAfjgAfjfjfCfjfCfCgAfCgAgAfCgAfCfCgAfCgAgAfCgAfCfCfjfCfjfjgAfjgAgAgAgAgAgAfCgAfCfCgAfCgAgAfCgAfCfCfCfCfCfCgAtmQtQtQt", +"gufQgufkfkgufQgufkfQgufkgufkfQfkgufQgufkfkgufQgufkfQgufkgufkfQfkgufQgufkfkgufQgufkfQgufkgufkfQfkgufQgufkfkgufQgufkfQgufkgufkfQfkgufQgufkfkgufQgufkfQgufkgufkfQfkgufQgufkfkgufQgufkfQgufkgufkfQfkgufQgufkfkgufX#9.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt.s5.r.r.rsWsWsA.rs2.rs3.r.9.r.9sW.rsAsHsH.rsA.F.rsA.MsA.M.FsH.FsA.M.FsKsk.M.M.F#1#1.F.F.F.F.F#9.F.Fsj.Maz.F#1#1#9#9azdzd3dzbEc3dzdzdmdzdzd3d3d3dmdmdmdmdmc3eMeLdmeoeTdmdme2e2e2eTe2eTfbeTfbe2fbe2fufbfufufbfbfbfufbfue2fufQfufQfbfufXfkfkgugugufQgugugvglgufQhiguguglglgRfQguglglglgRgugVgugRgRglhhhnj5g1fCgAgAfCgAfCfCfCfCfCfCfjfCfjfjeKfjeKeKfjeKfjfjfCfjfCfCgAfCgAgAfCgAfCfCfCfCfCfCfCfCfCfCgAfCgAgAfjgAfjfjfCfjfCfCfjfCfjfjfCfjfCfCgAfCgAgAfCgAfCfCfCfCfCfCfjfCfjfjeKfjeKeKfjeKfjfjfCfjfCfCgAfCgAgAtnfGQtQtQt", +"fufkfkfufQgufkfQfkgufkfQfufkgufQfufkfkfufQgufkfQfkgufkfQfufkgufQfufkfkfufQgufkfQfkgufkfQfufkgufQfufkfkfufQgufkfQfkgufkfQfufkgufQfufkfkfufQgufkfQfkgufkfQfufkgufQfufkfkfufQgufkfQfkgufkfQfufkgufQfufkfkfufQgufkfX.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5.rsWs5sWs6s6.r.rs2.rs3.r.9.9.9s6.M.9sA.r.r.rsH.r.r.MsA.M.F.F.F.MsH.M.F.M.M.M.FsH#1.F.F.F.F#9#9.F#9.F.F#1#1#1azazbEbEdIdzbEbEc3brcqbEc3d3d3d3dmdUdmdUdzdmc3c3c3dmeTdmfYe2e2e2e2eLfbeLe2fbfbfbfbe2fbeTfueTfbfbfuftfbfufkfbfkfQfQfugufkf5fufufufkgvfQgvgvgvgvgvgugufkglgugvgugugRglhhgRglgugRgRgRh0g1fjfCfjfjeKfjeKeKgAeKgAgAfCgAfCfCgAfCgAgAfjgAfjfjeKfjeKeKfjeKfjfjgAfjgAgAfjgAfjfjfjfjfjfjgAfjgAgAfjgAfjfjeKfjeKeKgAeKgAgAfCgAfCfCfjfCfjfjeKfjeKeKgAeKgAgAfCgAfCfCgAfCgAgAfjgAfjfjeKfjeKeKfjeKfjfjtoQtQtQtQt", +"fkfQgufQfkfufQgufufkgufkfQfQfugufkfQgufQfkfufQgufufkgufkfQfQfugufkfQgufQfkfufQgufufkgufkfQfQfugufkfQgufQfkfufQgufufkgufkfQfQfugufkfQgufQfkfufQgufufkgufkfQfQfugufkfQgufQfkfufQgufufkgufkfQfQfugufkfQgufQfkfufQgueL.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs3.r.rsWsW.rsAs2sW.rtd.r.9.r.r.9sAsAsA.r.rsHsH.rsAsAtd.M.FsHsH.F.F.F.F.M.M.F.F.F#1.F#1.F#9#9#9#9.FsjsZ#1#9cqcqbEbEdzbEd3bEbEcqc3cqcqc3d3d3d3d3dmdmdUdmdmc3eLeLeTeMeLeoe2e2e2e2eLfueLeLfbe2dme2fbe2fbfueTfbfvfbfufbe2fkfbfkfufufQf5fWfkfugufkgufkgugvglgugufQfkhigugugugvfkglglgQhhguhhhDiseKgAeKgAgAfjgAfjfjeKfjeKeKfjeKfjfjfCfjfCfCfCfCfCfCfjfCfjfjeKfjeKeKfjeKfjfjfCfjfCfCeKfCeKeKfCeKfCfCfjfCfjfjgAfjgAgAfCgAfCfCeKfCeKeKgAeKgAgAfjgAfjfjeKfjeKeKfjeKfjfjfCfjfCfCfCfCfCfCfjfCfjfjeKfjeKeKhQQtQtQtQt", +"fufQfufufQgufufQfufQfufufQfufkfQfufQfufufQgufufQfufQfufufQfufkfQfufQfufufQgufufQfufQfufufQfufkfQfufQfufufQgufufQfufQfufufQfufkfQfufQfufufQgufufQfufQfufufQfufkfQfufQfufufQgufufQfufQfufufQfufkfQfufQfufufQgufufQfue2.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt..rt.s5.r.rsW.rs2sA.rs3.r.r.r.rs6sAsWsAsAsH.rsH.r.F.rsA.M.M.MsH.MsHsA.F.FsK.M.F#1.F.F.F#1.F.F#9.M.F.M.Fbr#9azbEbEdObEdzbEdzd3cqbrcqdzc3cqdzdzd3dmd3dmd3dmdmeLeLd3eMdmeMeTdme2e2dme2eLeMeLeLeTdme2e2e2e2fbfvfbfvfbfbfue2fkfbfufufufufufufWfkfufufkgufkgvglgvgufkgufQguguglgRgvglgQglgRjMfCfCfjfCfjfjeKfjeKeKfjeKfjfjgAfjgAgAeKgAeKeKfjeKfjfjfCfjfCfCeKfCeKeKgAeKgAgAfCgAfCfCfjfCfjfjfjfjfjfjeKfjeKeKeKeKeKeKfjeKfjfjfCfjfCfCfjfCfjfjeKfjeKeKfjeKfjfjgAfjgAgAeKgAeKeKfjeKfjfjfCfjfCfCeKfCeKtpQtQtQtQtQt", +"fugufkfQfufkfQfkfQfkfugufQfufQfufugufkfQfufkfQfkfQfkfugufQfufQfufugufkfQfufkfQfkfQfkfugufQfufQfufugufkfQfufkfQfkfQfkfugufQfufQfufugufkfQfufkfQfkfQfkfugufQfufQfufugufkfQfufkfQfkfQfkfugufQfufQfufugufkfQfufkfQfkfQfkc3.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5.rs5.r.r.r.rs2sA.r.r.r.r.r.M.9sAsA.rsAsHsH.r.r.F.M.M.M.M.M.M.F.FsHsA.F.Msk.M#1.F.F.F.F.F.F.F#9sZbE#9brbEcqbrbEdObEbEbEdzdzdzcqbEcqdzdmbEdzd3dzd3dUdUdmdmdmdmeTeLeTdmeTdmdme2dme2eLeTeTfbfbfbeTe2fbe2fbfufufbfbfbe2fufufbfbfQfkgvgvfugufkfkfufkgugvgvglgvguhigvguglgugugRgRlheKfjfjeKfjeKeKfCeKfCfCfjfCfjfjeKfjeKeKeKeKeKeKfjeKfjfjeKfjeKeKgAeKgAgAfjgAfjfjeKfjeKeKfCeKfCfCfCfCfCfCfjfCfjfjfCfjfCfCeKfCeKeKfjeKfjfjeKfjeKeKfCeKfCfCfjfCfjfjeKfjeKeKeKeKeKeKfjeKfjfjeKfjeKeKgAeKgAtqQtQtQtQtQt", +"fQfufQfufQfufufkfQfufkfufufkfkfkfQfufQfufQfufufkfQfufkfufufkfkfkfQfufQfufQfufufkfQfufkfufufkfkfkfQfufQfufQfufufkfQfufkfufufkfkfkfQfufQfufQfufufkfQfufkfufufkfkfkfQfufQfufQfufufkfQfufkfufufkfkfkfQfufQfufQfufufkfQfufkbE.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5.rs5.rsWsW.rs6.rs2sA.r.r.9.9.rs6.rsAsAsW.rsH.rsH.rsY.M.MsA.F.M.9sH.MsHsA.M.M.M.F.F.F.F.F.F.F#9#9bE#9bE#9brbEbrbEbEdObEd3bEbEdzbEcqbrcqdzd3bEd3d3dmd3dUdUdzc3dmeMeTd3eTdmdmdme2e2dme2eLeLe2e2fbe2e2e2fbfufbfvfbfbeTfbe2fbfkfkfuf5fufugufkfufkfufufkgugvgvgvglgufufkgufkmsfCfjfCfCeKfCeKeKfjeKfjfjeKfjeKeKfCeKfCfCfjfCfjfjfCfjfCfCfCfCfCfCeKfCeKeKeKeKeKeKfjeKfjfjeKfjeKeKfjeKfjfjeKfjeKeKfjeKfjfjfjfjfjfjfCfjfCfCeKfCeKeKfjeKfjfjeKfjeKeKfCeKfCfCfjfCfjfjfCfjfCfCfCfCfCfCeKfCtrQtQtQtQtQtQt", +"fkfufufufQfkfQfkfufufQfQfufQfufQfkfufufufQfkfQfkfufufQfQfufQfufQfkfufufufQfkfQfkfufufQfQfufQfufQfkfufufufQfkfQfkfufufQfQfufQfufQfkfufufufQfkfQfkfufufQfQfufQfufQfkfufufufQfkfQfkfufufQfQfufQfufQfkfufufufQfkfQfkfufufQfQbr.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt..r.r.rs5sW.rsWsAsWsA.r.r.9.r.Ms6.r.9sAsA.rsH.rsHsAsY.M.MsA.M.M.MsHsHsH.F.M.F.M.F#1sjsH#9az#9az#9bEbEbEazbrbEbrcqbEdObEbEbEdzdzbrcqcqcqc3d3bEd3d3dzd3dmdmdzdmdmdmeTeTeTfbdmdme2e2e2e2eTeTe2fbfbe2e2fbfbe2fbfufufbfbfbfufufkfQfQfuf5fQfWf5fufkfufuguguglglgvgvgufPeKeKeKeKeKfjeKfjfjfjfjfjfjeKfjeKeKeKeKeKeKfCeKfCfCeKfCeKeKfjeKfjfjdkfjdkdkeKdkeKeKfCeKfCfCfjfCfjfjfCfjfCfCdkfCdkdkeKdkeKeKeKeKeKeKeKeKeKeKfjeKfjfjfjfjfjfjeKfjeKeKeKeKeKeKfCeKfCfCeKfCeKeKfjeKfjfjdktsttQtQtQtQtQtQt", +"fbfQfkfQfufbfufbfkfQfufbfkfbfQfufbfQfkfQfufbfufbfkfQfufbfkfbfQfufbfQfkfQfufbfufbfkfQfufbfkfbfQfufbfQfkfQfufbfufbfkfQfufbfkfbfQfufbfQfkfQfufbfufbfkfQfufbfkfbfQfufbfQfkfQfufbfufbfkfQfufbfkfbfQfufbfQfkfQfufbfufbfkfQfufbfksZ.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5s3.rs3sWsWs6s2s2.r.rs3.r.9.r.9sAsAsAsA.9sA.r.r.rsY.MsA.M.FsH.F.F.F.F.F.M.M.M.F#9az.FazazazbEbE#9azbEazbEazbrbEbEc3bEdzd3bEcqcqbrcqcqdmd3dzd3dmdmd3dmc3dmc3eMeTd3eMeTfbdmeoe2eTeLe2eTe2eTfbdmeTe2e2fbfbeTfbfbeLfbfbfufkfufkfufkfkgvfWgvfugufufkfQgugQjffCfCfjfCfjfjeKfjeKeKdkeKdkdkeKdkeKeKfjeKfjfjeKfjeKeKfjeKfjfjeKfjeKeKfCeKfCfCfjfCfjfjeKfjeKeKeKeKeKeKeKeKeKeKfjeKfjfjfjfjfjfjfCfjfCfCfjfCfjfjeKfjeKeKdkeKdkdkeKdkeKeKfjeKfjfjeKfjeKeKfjeKfjfjeKfjeKeKfCtuQtQtQtQtQtQtQt", +"fufQfbfufkfufkfQfkfufkfufQfufkfufufQfbfufkfufkfQfkfufkfufQfufkfufufQfbfufkfufkfQfkfufkfufQfufkfufufQfbfufkfufkfQfkfufkfufQfufkfufufQfbfufkfufkfQfkfufkfufQfufkfufufQfbfufkfufkfQfkfufkfufQfufkfufufQfbfufkfufkfQfkfufkfufQfv#1.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5t..rs5s3.rsWs6sAs2sA.r.9.r.r.rsAsWsA.rsAsHsA.rsH.rsY.rsAsH.MsH.FsA.FsY.F.F.F#1sj#9az#9az#9azbE#9#9br#9azazazbrcqbEc3bEbEbEdzcqbrdzcqdzdmc3d3d3d3dmd3dmdzdzdmeLdmdmeTdmfbfce2e2e2eTe2eTe2dmfbe2e2e2fbfufbfufufufbfkfbfkfkfkfQfugvfugvfkgvfkgufujfgBeKeKdkeKdkdkeKdkeKeKfCeKfCfCeKfCeKeKfCeKfCfCdkfCdkdkeKdkeKeKeKeKeKeKeKeKeKeKfCeKfCfCdkfCdkdkfjdkfjfjfCfjfCfCeKfCeKeKeKeKeKeKeKeKeKeKdkeKdkdkeKdkeKeKfCeKfCfCeKfCeKeKfCeKfCfCdkfCdkdkeKdkeKeKeKeKeKeKtvhrQtQtQtQtQtQtQt", +"fufufkfbfQfbfufbfbfkfbfufbfQfufbfufufkfbfQfbfufbfbfkfbfufbfQfufbfufufkfbfQfbfufbfbfkfbfufbfQfufbfufufkfbfQfbfufbfbfkfbfufbfQfufbfufufkfbfQfbfufbfbfkfbfufbfQfufbfufufkfbfQfbfufbfbfkfbfufbfQfufbfufufkfbfQfbfufbfbfkfbfufbfQfusY.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5.rsWs3sWsW.rs2sWsA.r.r.r.9.9.rsAsAsWsA.9.rsHsAsAsY.M.M.MsA.FsH.F.FsA#1#1#1sj#9#9az.FskbEazazbr#9brrQazbEazbEbEbEdObEbEbEdzdzcqbEcqdzc3bEdzd3dzdzd3dmdmdmdmc3eLdmeTdmfbdme2dmdme2e2eTeLeLfbfbe2e2fbfue2fbfbfufbfbfbfufufufkfQgvgvf5fkf5hBhFfCeKeKfjeKfjfjfCfjfCfCdkfCdkdkfjdkfjfjeKfjeKeKfCeKfCfCdkfCdkdkfCdkfCfCfjfCfjfjeKfjeKeKeKeKeKeKeKeKeKeKfCeKfCfCdkfCdkdkfCdkfCfCeKfCeKeKfjeKfjfjfCfjfCfCdkfCdkdkfjdkfjfjeKfjeKeKfCeKfCfCdkfCdkdkfCdkfCfCtwQtQtQtQtQtQtQtQt", +"fQfkfbfufufkfufQfufufbfufkfbfufkfQfkfbfufufkfufQfufufbfufkfbfufkfQfkfbfufufkfufQfufufbfufkfbfufkfQfkfbfufufkfufQfufufbfufkfbfufkfQfkfbfufufkfufQfufufbfufkfbfufkfQfkfbfufufkfufQfufufbfufkfbfufkfQfkfbfufufkfufQfufufbfufkfbfufl.M.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5s3.rs2sWsW.r.r.rs3.rtd.9.rsA.9sAsAsHsAsAsA.r.r.rsA.M.M.FsH.Fsk#9#9#1#1#1#9.Fsk#9azazazbEazbEbEazbrbEazazbrbEbEbEbEdzd3bEcqcqcqdzc3bEdzd3d3dUdmdmdzdmeLc3eLeLeLeofbdmeoe2e2eTeTeTftfueTfve2fbfue2fbfvfbfbfbftfufkfkfbfufkfugVi.eKdkeKeKdkeKdkdkeKdkeKeKdkeKdkdkfCdkfCfCeKfCeKeKdkeKdkdkfjdkfjfjeKfjeKeKdkeKdkdkdkdkdkdkdkdkdkdkfjdkfjfjeKfjeKeKeKeKeKeKdkeKdkdkeKdkeKeKdkeKdkdkeKdkeKeKdkeKdkdkfCdkfCfCeKfCeKeKdkeKdkdkfjdkfjfjeKfjeKtxtyQtQtQtQtQtQtQtQt", +"fufbfufQfkfbfbfufbfbfkfufbfufufbfufbfufQfkfbfbfufbfbfkfufbfufufbfufbfufQfkfbfbfufbfbfkfufbfufufbfufbfufQfkfbfbfufbfbfkfufbfufufbfufbfufQfkfbfbfufbfbfkfufbfufufbfufbfufQfkfbfbfufbfbfkfufbfufufbfufbfufQfkfbfbfufbfbfkfufbfufufbeLsH.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5s5.rsW.r.rsW.rs2sWsA.r.r.9.r.9s6sAsAsWsAsA.rsH.rsAsY.M.rsA.Msk.M.F.FsK#9#9#1br#9#9sjaz#9azazbE#9azbE#9brazbrazbEbrdObEbEdId3bEbEbEcqdzcqc3d3c3dzd3d3dUdmdzdmc3eLeLeTdmeTeoe2fcdme2e2eTeTeoeTe2e2e2fbfkfbfkfbfkfufufue2fkfkgujTeKfCeKfCfCeKfCeKeKfjeKfjfjeKfjeKeKdkeKdkdkeKdkeKeKfCeKfCfCdkfCdkdkeKdkeKeKeKeKeKeKdkeKdkdkeKdkeKeKeKeKeKeKdkeKdkdkdkdkdkdkeKdkeKeKfCeKfCfCeKfCeKeKfjeKfjfjeKfjeKeKdkeKdkdkeKdkeKeKfCeKfCfCdkfCdkdkeKdktztAQtQtQtQtQtQtQtQtQt", +"fbfufbfbfbfufbfkfufbfufkfbfQfufbfbfufbfbfbfufbfkfufbfufkfbfQfufbfbfufbfbfbfufbfkfufbfufkfbfQfufbfbfufbfbfbfufbfkfufbfufkfbfQfufbfbfufbfbfbfufbfkfufbfufkfbfQfufbfbfufbfbfbfufbfkfufbfufkfbfQfufbfbfufbfbfbfufbfkfufbfufkfbfQfufbfbc3sA.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt..rs5.rsWsWsAs2.r.r.r.r.9.r.r.rsWsAsAsA.rsAsHsAsA.M.F#1sk#1.F.FsK#9#9#9#1#1sj#9#9azazdHazazbEbErQbrbrbrbrbrbEbEbEdzbEd3bEd3cqcqdzcqc3c3d3d3d3dmdzdUdmdmc3dmeTeTdmeTeofYe2e2e2eoe2eTeTeTfbfudmfbe2fbe2fkfbfbfbfbfQlNdkdkeKdkeKeKdkeKdkdkdkdkdkdkeKdkeKeKdkeKdkdkdkdkdkdkeKdkeKeKdkeKdkdkdkdkdkdkfCdkfCfCeKfCeKeKdkeKdkdkeKdkeKeKeKeKeKeKfCeKfCfCdkfCdkdkeKdkeKeKdkeKdkdkdkdkdkdkeKdkeKeKdkeKdkdkdkdkdkdkeKdkeKeKdkeKdkdkdkdktBQtQtQtQtQtQtQtQtQtQt", +"fbfufufbfufbfbfufbfufbfbfufbfufkfbfufufbfufbfbfufbfufbfbfufbfufkfbfufufbfufbfbfufbfufbfbfufbfufkfbfufufbfufbfbfufbfufbfbfufbfufkfbfufufbfufbfbfufbfufbfbfufbfufkfbfufufbfufbfbfufbfufbfbfufbfufkfbfufufbfufbfbfufbfufbfbfufbfufkfbfubE.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt.s5s5sW.r.rsWsWs2sA.r.r.9.9.r.9s6sAsA.r.rsA.r.rsA.F#1.9.F.F#1sksQ#9#9#1#9#9#1#1#9az.F#9azazazbE#9#9br#9brazbrbrbEbrbEbEdIdzd3bEbrbEdzbEdzc3d3d3dzd3dUdmdzc3eLc3dmdmdmdmdmfYe2e2dme2eTeTftfteTfve2e2e2fbfkfQfukcdkeKeKeKeKeKeKdkeKdkdkeKdkeKeKdkeKdkdkeKdkeKeKeKeKeKeKdkeKdkdkeKdkeKeKdkeKdkdkdkdkdkdkeKdkeKeKdkeKdkdkdkdkdkdkdkdkdkdkdkdkdkdkeKdkeKeKeKeKeKeKdkeKdkdkeKdkeKeKdkeKdkdkeKdkeKeKeKeKeKeKdkeKdkdkeKdkeKeKdktCQtQtQtQtQtQtQtQtQtQtQt", +"fbe2fbfue2fbfkfbfbfufkfbfufbe2fbfbe2fbfue2fbfkfbfbfufkfbfufbe2fbfbe2fbfue2fbfkfbfbfufkfbfufbe2fbfbe2fbfue2fbfkfbfbfufkfbfufbe2fbfbe2fbfue2fbfkfbfbfufkfbfufbe2fbfbe2fbfue2fbfkfbfbfufkfbfufbe2fbfbe2fbfue2fbfkfbfbfufkfbfufbe2fbfbe2fbbr.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs3sWs5s3tlsW.rs2s2.r.r.r.9.M.M.9sAsWsAsA.rsA.F.M.M.M.F#1.F#1#9sk#9sK#9#9#1#1az#9#9#9azbEazazazbEaz#9azbrazbEcqbEbEbEdzbEdzdzdzcqbEbEc3dmdzd3eLdzdmdmdmdmc3dmdmeTeLdmfYfbe2e2e2e2eofueTeTfbfufueTe2fkfsdkeKdkdkdldkdldldkdldkdkeKdkeKeKdkeKdkdkdldkdldldkdldkdkdkdkdkdkdkdkdkdkeKdkeKeKeKeKeKeKdkeKdkdkeKdkeKeKeKeKeKeKdkeKdkdkeKdkeKeKdkeKdkdkdldkdldldkdldkdkeKdkeKeKdkeKdkdkdldkdldldkdldkdkdkdkdkdkdkdkdkdktDbHQtQtQtQtQtQtQtQtQtQtQt", +"tEfufbfbfufbfue2fufbe2fbfue2fufufbfufbfbfufbfue2fufbe2fbfue2fufufbfufbfbfufbfue2fufbe2fbfue2fufufbfufbfbfufbfue2fufbe2fbfue2fufufbfufbfbfufbfue2fufbe2fbfue2fufufbfufbfbfufbfue2fufbe2fbfue2fufufbfufbfbfufbfue2fufbe2fbfue2fufufbfufbfb#9.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5.rsWs5sWsW.rs2sWsW.r.r.9.r.M.MsAsAsWsYsAsA.F.9.M.M.F#1.F.F#1#9#9#9sk#9#1#1az#9#9#9azazazbEazaz#9brrQbrbEazcqbEbEbEdzbEbEdIdzdzbrcqdzc3bEc3eLdmc3dmdUdmc3dzdmdmdmeTdmfYdme2dmdme2e2eLeTfteLfu.UdleKdleKeKdkeKdkdkeKdkeKeKdleKdldldkdldkdkeKdkeKeKeKeKeKeKdkeKdkdkeKdkeKeKdleKdldldkdldkdkeKdkeKeKdleKdldleKdleKeKdkeKdkdkdldkdldleKdleKeKdkeKdkdkeKdkeKeKdleKdldldkdldkdkeKdkeKeKeKeKeKeKdkeKdkdkeKdkeKtFtGQtQtQtQtQtQtQtQtQtQtQtQt", +"QtfytHtIfbfbfbfbe2fbfufbfufbfbe2fufbfufbfbfbfbfbe2fbfufbfufbfbe2fufbfufbfbfbfbfbe2fbfufbfufbfbe2fufbfufbfbfbfbfbe2fbfufbfufbfbe2fufbfufbfbfbfbfbe2fbfufbfufbfbe2fufbfufbfbfbfbfbe2fbfufbfufbfbe2fufbfufbfbfbfbfbe2fbfufbfufbfbe2fufbfufbeT.F.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5.r.rsW.rs2s2sA.r.r.r.r.r.r.9sY.F.F.FsA.F.9.M#1.9.9.F#1#1.Fsk.F#9#9#9#1br#9#9sj#9#9azazazbEbEbrrQbEbrbEbrbEbEdObEbEdzdzd3cqc3cqdzcqc3d3c3dzdzd3dmdmdmdmdmeTc3eTdmeTeodmfcdmeoe2eTeThRfjdkdkdkdkdkdkdkdkdkdkdkdkdkeKdkeKeKdkeKdkdkdkdkdkdkdkdkdkdkcVdkcVcVdkcVdkdkdkdkdkdkeKdkeKeKdkeKdkdkdkdkdkdkdldkdldleKdleKeKdkeKdkdkdkdkdkdkdkdkdkdkdkdkdkdkeKdkeKeKdkeKdkdkdkdkdkdkdkdkdkdkcVdkcVcVdkcVtJe6QtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtfotKtLfbfbfbfue2fbeLfbfufbeLfbfbeLfbfufbfbfbfue2fbeLfbfufbeLfbfbeLfbfufbfbfbfue2fbeLfbfufbeLfbfbeLfbfufbfbfbfue2fbeLfbfufbeLfbfbeLfbfufbfbfbfue2fbeLfbfufbeLfbfbeLfbfufbfbfbfue2fbeLfbfufbeLfbfbeLfbfufbfbfbfue2fbeLfbfufbeLfbfbeLfbfu.F.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt.s5s5.r.rsW.rs2.r.r.r.r.rsW.9.r.9.FsH.F.F.M.F.9.M#1#1#1#1.Msk.Fsk#9sk#9br#1sj.F#9azazazaz#9bEazrQaz#9brazazcqbEbEbrdzdzd3bEbEcqdzc3dzdzc3dzeLdmdzdUdmdmeLeLc3eLdmdmdmdmfce2e2hnhHdkdkcVdkcVcVdkcVdkdkdkdkdkdkdkdkdkdkcVdkcVcVeKcVeKeKdkeKdkdkeKdkeKeKdkeKdkdkcVdkcVcVdkcVdkdkeKdkeKeKdkeKdkdkdkdkdkdkeKdkeKeKdkeKdkdkcVdkcVcVdkcVdkdkdkdkdkdkdkdkdkdkcVdkcVcVeKcVeKeKdkeKdkdkeKdkeKeKdktMtNQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQt#htOtPeLfbfufbfbfbe2fbfbfbfufbfbeLfbfueLfbfufbfbfbe2fbfbfbfufbfbeLfbfueLfbfufbfbfbe2fbfbfbfufbfbeLfbfueLfbfufbfbfbe2fbfbfbfufbfbeLfbfueLfbfufbfbfbe2fbfbfbfufbfbeLfbfueLfbfufbfbfbe2fbfbfbfufbfbeLfbfueLfbfufbfbfbe2fbfbfbfufbfbeLfbsH.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5.rs3.rsWsW.rs6sAsW.r.r.r.r.r.9.F.9.FsHsA.M.F.M#1.M#1.F#1#1.FsksQsk#9sk#1#1br#1sj#9azazazazbEbE#9bEbrrQbEbEcqbrcqbEdzdzd3d3cqdzbrcqcqdzbEd3c3d3d3dmdUdmdmeLeLc3dmeTeTdmfki.dldkdkeKdkeKeKdkeKdkdkcVdkcVcVeKcVeKeKdkeKdkdkcVdkcVcVdkcVdkdkdkdkdkdkdldkdldldkdldkdkcVdkcVcVdkcVdkdkdkdkdkdkcVdkcVcVdlcVdldldkdldkdkeKdkeKeKdkeKdkdkcVdkcVcVeKcVeKeKdkeKdkdkcVdkcVcVdkcVdkdkdkdkdkdktQtAQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQte5tRtSeLfbeLfbfbeLfbeLeLfbfueLfbeLfbeLeLfbeLfbfbeLfbeLeLfbfueLfbeLfbeLeLfbeLfbfbeLfbeLeLfbfueLfbeLfbeLeLfbeLfbfbeLfbeLeLfbfueLfbeLfbeLeLfbeLfbfbeLfbeLeLfbfueLfbeLfbeLeLfbeLfbfbeLfbeLeLfbfueLfbeLfbeLeLfbeLfbfbeLfbeLeLfbfueLeL.9.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt..rs3.rsWtlsWs2sWsY.r.r.r.9.r.r.M.9.F.F.MsAsA.9.M.F.9.F#1sk#1.FsQ.F#9#9#1#1#1#9#1sj#9azazazazbErQbEbrefbEazbEbEbrcqbEdzbEdId3cqbrcqcqcqbEd3d3dzd3dmdmdmdmdzdmeLdmfbi2cVdkcVcVcVcVcVcVdkcVdkdkdkdkdkdkdkdkdkdkdldkdldldkdldkdkdkdkdkdkcVdkcVcVdkcVdkdkdkdkdkdkdldkdldldkdldkdkdkdkdkdkdkdkdkdkdkdkdkdkcVdkcVcVcVcVcVcVdkcVdkdkdkdkdkdkdkdkdkdkdldkdldldkdldkdkdkdkdkdkcVdkcVtTtUQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtb4tVtWeLfbeLe2fbfbfbeLfbfbe2fbfbeLfbfbeLfbeLe2fbfbfbeLfbfbe2fbfbeLfbfbeLfbeLe2fbfbfbeLfbfbe2fbfbeLfbfbeLfbeLe2fbfbfbeLfbfbe2fbfbeLfbfbeLfbeLe2fbfbfbeLfbfbe2fbfbeLfbfbeLfbeLe2fbfbfbeLfbfbe2fbfbeLfbfbeLfbeLe2fbfbfbeLfbfbe2dU.9.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5.r.rs5s3s5.r.r.r.r.F.r.r.r.9.r.F.F.F.F.F.F.FsA.F.M#1#1#1#1sk#1.M.F.Fazaz#9az#1sj#9sjsk#9azazaz#9azbEbrbrbrbrbrbEbEdObEbEd3bEd3bEbEcqc3dmc3dzdmdmdzdmdmdmdUeSjSdldkdldkdkdkdkdkdkcVdkcVcVdkcVdkdkcVdkcVcVdkcVdkdkdkdkdkdkcVdkcVcVcVcVcVcVdkcVdkdkcVdkcVcVdkcVdkdkdkdkdkdkcVdkcVcVdkcVdkdkdldkdldldkdldkdkdkdkdkdkcVdkcVcVdkcVdkdkcVdkcVcVdkcVdkdkdkdkdkdkcVdkcVcVcVcVtXfzQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtbYtYtZfbfbeLfbdmfbeLeLfbfbdmfbe2dmfbfbfbfbeLfbdmfbeLeLfbfbdmfbe2dmfbfbfbfbeLfbdmfbeLeLfbfbdmfbe2dmfbfbfbfbeLfbdmfbeLeLfbfbdmfbe2dmfbfbfbfbeLfbdmfbeLeLfbfbdmfbe2dmfbfbfbfbeLfbdmfbeLeLfbfbdmfbe2dmfbfbfbfbeLfbdmfbeLeLfbfbbE.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs3t.sWs3tl.r.r.r.r.r.r.r.r.r.r.F.F.9.9.FsA.F.M.F.9#1.9#1#1.F#1.F.FsKsk#9#1#1#1sj#9.F.FazazbEaz#9azrQrQbrbrbEazcqbEbEbEd3d3dIdzbEbEdzdzdmd3dmdzfcdmdUk0dkdkcVdkcVcVdkcVdkdkdkdkdkdkdkdkdkdkcVdkcVcVdkcVdkdkcVdkcVcVdkcVdkdkdkdkdkdkdkdkdkdkdkdkdkdkdldkdldlcVdlcVcVdkcVdkdkcVdkcVcVdkcVdkdkcVdkcVcVdkcVdkdkdkdkdkdkdkdkdkdkcVdkcVcVdkcVdkdkcVdkcVcVdkcVdkdkdkt0chQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtejt1t2eLeLfbfbfbfbeLeLfbfbeLfbeLfbfbeLeLeLfbfbfbfbeLeLfbfbeLfbeLfbfbeLeLeLfbfbfbfbeLeLfbfbeLfbeLfbfbeLeLeLfbfbfbfbeLeLfbfbeLfbeLfbfbeLeLeLfbfbfbfbeLeLfbfbeLfbeLfbfbeLeLeLfbfbfbfbeLeLfbfbeLfbeLfbfbeLeLeLfbfbfbfbeLeLbE.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt.s5s5tl.r.r.rsH.r.r.r.r.M.M.r.r.9.F.FsA.F.F.F.F#1#1#1#1.F#1.F.FsKaz#9#1#1sjsj#9az#9azazazazbEbErQbrbrazbEcqcqdzbrbEdzd3dzd3cqcqdzc3c3d3d3fPdkdkdkcEdkcEcEcVcEcVcVcEcVcEcEcVcEcVcVdkcVdkdkdkdkdkdkcVdkcVcVcVcVcVcVdkcVdkdkcVdkcVcVdkcVdkdkcEdkcEcEcVcEcVcVdkcVdkdkdkdkdkdkdkdkdkdkcEdkcEcEcVcEcVcVcEcVcEcEcVcEcVcVdkcVdkdkdkdkdkdkcVdkcVcVcVcVcVt3t4QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtt5e#t6eLdmeLdmfbfbeLdmfbdmfbeLdmfbfbfbeLdmeLdmfbfbeLdmfbdmfbeLdmfbfbfbeLdmeLdmfbfbeLdmfbdmfbeLdmfbfbfbeLdmeLdmfbfbeLdmfbdmfbeLdmfbfbfbeLdmeLdmfbfbeLdmfbdmfbeLdmfbfbfbeLdmeLdmfbfbeLdmfbdmfbeLdmfbfbfbeLdmeLdmfbfbeLsk.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt..r.rsW.r.9.M.F.F.rs3.r.r.9.M.r.9.FsH.M.FsAsA.F.F#1.F#1.Fsk#9.F#9azsk#1az#1#1sjazazazazdIbEazbrrQbrrQrQcqazbrcqdzbEdzd3dIbEdzcqdzcqj2cEcVcEcEdkcEdkdkdkdkdkdkdkdkdkdkdkdkdkdkcVdkcVcVcEcVcEcEcVcEcVcVdkcVdkdkcVdkcVcVdkcVdkdkcEdkcEcEdkcEdkdkdkdkdkdkcVdkcVcVcVcVcVcVcEcVcEcEdkcEdkdkdkdkdkdkdkdkdkdkdkdkdkdkcVdkcVcVcEcVcEcEcVcEcVcVdkcVt7tNQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtert8t9fbfbfbeLfbeLfbeLfbeLdmfbdmeLfbeLfbfbfbeLfbeLfbeLfbeLdmfbdmeLfbeLfbfbfbeLfbeLfbeLfbeLdmfbdmeLfbeLfbfbfbeLfbeLfbeLfbeLdmfbdmeLfbeLfbfbfbeLfbeLfbeLfbeLdmfbdmeLfbeLfbfbfbeLfbeLfbeLfbeLdmfbdmeLfbeLfbfbfbeLfbeL#1.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt.sWs3sWsW.r.F.r.FsHs3.r.r.r.M.9.9.9.F.MsA.9.F.M.F.M.F#1#1.F#9sksKsk#9#1#1#9sjrQazrQazazazbEazbErQrQbrbEazazbrbEdObEdzdzd3cqhDdkdkdkdkdkcVdkcVcVcVcVcVcVcEcVcEcEcEcEcEcEcVcEcVcVdkcVdkdkcVdkcVcVdkcVdkdkcEdkcEcEcVcEcVcVcVcVcVcVcVcVcVcVdkcVdkdkcEdkcEcEdkcEdkdkdkdkdkdkcVdkcVcVcVcVcVcVcEcVcEcEcEcEcEcEcVcEcVcVdkcVdkdkcVdkcVcVdku..BQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtu#uaubfbdmeLdmfbdmdmeLeLfbdmfbdmeLdmdmfbdmeLdmfbdmdmeLeLfbdmfbdmeLdmdmfbdmeLdmfbdmdmeLeLfbdmfbdmeLdmdmfbdmeLdmfbdmdmeLeLfbdmfbdmeLdmdmfbdmeLdmfbdmdmeLeLfbdmfbdmeLdmdmfbdmeLdmfbdmdmeLeLfbdmfbdmeLdmdmfbdmeLc3.F.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5.rsW.9.r.r.F.rs3.r.9.9.M.r.r.9.F.FsAsA.F.9.F#1.F#1.Fsk#1.F.F.F#9az#1#1sjskrQ#1azazazazazbEbEbEbrbrbEbrbEbEbrbEdzgleKcEdkcEdkdkcEdkcEcEdkcEdkdkdddkdddddkdddkdkdkdkdkdkcEdkcEcEdkcEdkdkcVdkcVcVdkcVdkdkcEdkcEcEdkcEdkdkdkdkdkdkcVdkcVcVddcVddddcEddcEcEdkcEdkdkcEdkcEcEdkcEdkdkdddkdddddkdddkdkdkdkdkdkcEdkcEcEdkcEdkucudQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQteaueufeLfbdmeLfbfbfbdmeLeLfbeLfbeLeTdmeLfbdmeLfbfbfbdmeLeLfbeLfbeLeTdmeLfbdmeLfbfbfbdmeLeLfbeLfbeLeTdmeLfbdmeLfbfbfbdmeLeLfbeLfbeLeTdmeLfbdmeLfbfbfbdmeLeLfbeLfbeLeTdmeLfbdmeLfbfbfbdmeLeLfbeLfbeLeTdmeLfbdU.9.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt.s5.r.rsW.r.r.9.F.rs3.r.r.r.r.9.9.F.F.F.M.F.F.M.F#1.F.F#1.M.Fskskazazsk#1sjaz#9azskazazaz#9bEbErQbrdzrQbEbEcqeTfjdkdkcVdkcVcVcVcVcVcVcEcVcEcEcEcEcEcEcEcEcEcEcVcEcVcVcEcVcEcEdkcEdkdkcVdkcVcVcEcVcEcEdkcEdkdkcVdkcVcVcEcVcEcEcEcEcEcEcEcEcEcEdkcEdkdkcVdkcVcVcVcVcVcVcEcVcEcEcEcEcEcEcEcEcEcEcVcEcVcVcEcVcEcEdkuguhQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtd6uiujeLdmdmeLdmeLfbfbdmdmeLdmdmfbdmdmeLdmdmeLdmeLfbfbdmdmeLdmdmfbdmdmeLdmdmeLdmeLfbfbdmdmeLdmdmfbdmdmeLdmdmeLdmeLfbfbdmdmeLdmdmfbdmdmeLdmdmeLdmeLfbfbdmdmeLdmdmfbdmdmeLdmdmeLdmeLfbfbdmdmeLdmdmfbdmdmeLdU.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt..r.rsWsW.r.r.FsH.rsH.9.M.r.F.9.9.FsHsAsA.F.9#1.M#1.F.F.F#1.Fsksk#9sk#1#1sj#9sj#9azazazazaz#9bErQbErQeSi.ddcVcVcEcVcEcEcEcEcEcEddcEdddddkdddkdkcVdkcVcVdkcVdkdkcVdkcVcVcEcVcEcEcVcEcVcVdkcVdkdkcEdkcEcEcVcEcVcVcVcVcVcVcEcVcEcEddcEddddcVddcVcVcEcVcEcEcEcEcEcEddcEdddddkdddkdkcVdkcVcVdkcVdkdkcVdkcVukulabQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtumunubdmeTdmdmdmeLdmfbeTdmeLfbdmfbeTdmdmeTdmdmdmeLdmfbeTdmeLfbdmfbeTdmdmeTdmdmdmeLdmfbeTdmeLfbdmfbeTdmdmeTdmdmdmeLdmfbeTdmeLfbdmfbeTdmdmeTdmdmdmeLdmfbeTdmeLfbdmfbeTdmdmeTdmdmdmeLdmfbeTdmeLfbdmfbeTcq.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5.r.r.r.r.r.r.r.F.9sH.r.r.r.9.9.F.F.FsH.F.M.F.M.F.M.F#1.F#1sksk#9.Fazskaz#1sj#9sj#9#9azazazazaz#9c3jrcVcEcVcVddcVdddddkdddkdkcEdkcEcEcVcEcVcVcEcVcEcEcEcEcEcEddcEddddcEddcEcEcEcEcEcEcVcEcVcVcEcVcEcEcEcEcEcEdkcEdkdkdkdkdkdkcEdkcEcEcVcEcVcVddcVdddddkdddkdkcEdkcEcEcVcEcVcVcEcVcEcEcEcEcEcEdduoupaaQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtbbuqureTdmeLdmdmeLdmeLdmdmeLdmdmfbdmeLeTdmeLdmdmeLdmeLdmdmeLdmdmfbdmeLeTdmeLdmdmeLdmeLdmdmeLdmdmfbdmeLeTdmeLdmdmeLdmeLdmdmeLdmdmfbdmeLeTdmeLdmdmeLdmeLdmdmeLdmdmfbdmeLeTdmeLdmdmeLdmeLdmdmeLdmdmfbdH.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt.s5.r.rtl.9.9.r.rsH.r.r.9.r.r.9.9sH.F.FsAsA.M.9.M.F#1#1sk#1.F.Fsk#9azr9#1#1#1#1rQbrazazazlhcEcEcEcEcEcEcEcEcEcEcEcEcEcVcEcVcVcVcVcVcVcEcVcEcEddcEddddcEddcEcEcVcEcVcVdkcVdkdkcEdkcEcEcVcEcVcVcEcVcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcVcEcVcVcVcVcVcVcEcVcEcEddcEddusutabQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtdLdouudmdmfbdmdmdmfbdmeLdmdmdmdmdmdmeLdmdmfbdmdmdmfbdmeLdmdmdmdmdmdmeLdmdmfbdmdmdmfbdmeLdmdmdmdmdmdmeLdmdmfbdmdmdmfbdmeLdmdmdmdmdmdmeLdmdmfbdmdmdmfbdmeLdmdmdmdmdmdmeLdmdmfbdmdmdmfbdmeLdmdmdmdmbr.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5.rs5s5s5.r.M.rsH.F.r.r.r.9.9.9.F#1sH.F.F.FsA.9.M.F.F#1#1sksksksQ.F#9azazaz#1rQrQazfPddddcEddcEcEcEcEcEcEddcEddddcEddcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEddcEddddcEddcEcEcEcEcEcEddcEddddcEddcEcEcEcEcEcEddcEddddcEddcEcEcEcEcEcEcEcEcEcEuvfAuwQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.kuxuyuzdmeTdmdmdmdmdmeLeLdmeTdmdmeTdmeLdmeTdmdmdmdmdmeLeLdmeTdmdmeTdmeLdmeTdmdmdmdmdmeLeLdmeTdmdmeTdmeLdmeTdmdmdmdmdmeLeLdmeTdmdmeTdmeLdmeTdmdmdmdmdmeLeLdmeTdmdmeTdmeLdmeTdmdmdmdmdmeLeLdmeTsj.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5.rs5sWs5.9.9.r.rsH.r.r.r.9.9.9.M.F.9.M.F.FsAsA.9.M#1.F#1sksksk#9.Fsk.Fazaz#1dGcEcEcEcVcEcVcVcEcVcEcEcEcEcEcEcVcEcVcVcEcVcEcEddcEddddcEddcEcEcEcEcEcEddcEddddcVddcVcVcEcVcEcEcVcEcVcVcEcVcEcEcEcEcEcEcEcEcEcEcEcEcEcEcVcEcVcVcEcVcEcEcEcEcEcEcVcEcVcVcEcVcEcEddcEuAfpuBQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtduuCuDdmdmdmeLdmdmdmdmdmeLdmdmdmdmdmdmdmdmdmeLdmdmdmdmdmeLdmdmdmdmdmdmdmdmdmeLdmdmdmdmdmeLdmdmdmdmdmdmdmdmdmeLdmdmdmdmdmeLdmdmdmdmdmdmdmdmdmeLdmdmdmdmdmeLdmdmdmdmdmdmdmdmdmeLdmdmdmdmdmeL.F.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5s5.rsW.r.r.9.F.9.rsH.9.9.r.r.9.F.F.F.M.F.M.M.F.M#1.Fsk.F#1.F.Fazazh0cEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcVcEcVcVcEcVcEcEcEcEcEcEcEcEcEcEcDcEcDcDcEcDcEcEcEcEcEcEcVcEcVcVcEcVcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEfgffabQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtdpc5uEdmc3dmeLdmc3dmdmc3dmeLdmdmdmdmdmdmc3dmeLdmc3dmdmc3dmeLdmdmdmdmdmdmc3dmeLdmc3dmdmc3dmeLdmdmdmdmdmdmc3dmeLdmc3dmdmc3dmeLdmdmdmdmdmdmc3dmeLdmc3dmdmc3dmeLdmdmdmdmdmdmc3dmeLdmc3dmdmd9.F.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5s5s5.rsW.rsW.r.9.r.rsHs3.r.r.M.M.F.F.FsA.F.M.9sK.M.MsK.F#1.F.FfkeKcEcVcEcEcEcEcEcEcVcEcVcVcEcVcEcEcDcEcDcDcEcDcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcVcEcVcVcEcVcEcEcDcEcDcDcEcDcEcEcVcEcVcVcEcVcEcEcEcEcEcEcVcEcVcVcEcVcEcEcDcEcDcDcEuFe7uGQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtabcZuHdmdmdmdmdmdmdmdmc3dmeLdzdmdmdmdmdmdmdmdmdmdmdmdmc3dmeLdzdmdmdmdmdmdmdmdmdmdmdmdmc3dmeLdzdmdmdmdmdmdmdmdmdmdmdmdmc3dmeLdzdmdmdmdmdmdmdmdmdmdmdmdmc3dmeLdzdmdmdmdmdmdmdmdmdmdmdmeu.F.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5s5.r.rsW.r.9.r.F.r.r.rtd.r.r.9.F.F.F.F.M.FsA.F.M.M#1#1eTfjcEcEcEcEcEcDcEcDcDcEcDcEcEcEcEcEcEcEcEcEcEcEcEcEcEcDcEcDcDcVcDcVcVcEcVcEcEcDcEcDcDcEcDcEcEcEcEcEcEcEcEcEcEcEcEcEcEcDcEcDcDcEcDcEcEcEcEcEcEcDcEcDcDcEcDcEcEcEcEcEcEcEcEcEuIuJfMQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtdauKuLc3dmc3c3dmeLc3dmc3dmc3dzdmc3dmdmc3dmc3c3dmeLc3dmc3dmc3dzdmc3dmdmc3dmc3c3dmeLc3dmc3dmc3dzdmc3dmdmc3dmc3c3dmeLc3dmc3dmc3dzdmc3dmdmc3dmc3c3dmeLc3dmc3dmc3dzdmc3dmdmc3dmc3c3dmdzsA.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt..rsW.rsW.9.r.M.F.rsAsA.9sA.r.r.M.F.9.F.MsA.FsAcqhHcEcEcEcEcEcEcEcEcEcEcEcEcEcEcDcEcDcDcCcDcCcCcEcCcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcDcEcDcDcEcDcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcDcEcDcDcCuMuN#zQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtc6uOuPdmdmc3dmdmdmdmdmdmdmdmc3dmdzdmdmdmdmc3dmdmdmdmdmdmdmdmc3dmdzdmdmdmdmc3dmdmdmdmdmdmdmdmc3dmdzdmdmdmdmc3dmdmdmdmdmdmdmdmc3dmdzdmdmdmdmc3dmdmdmdmdmdmdmdmc3dmdzdmdmdmdmc3dmcqsA.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rsW.rsWsW.r.9.FsH.r.r.r.9.r.r.9.F.9.FskjvcEcEcEcCcEcCcCcEcCcEcEcEcEcEcEcEcEcEcEcEcEcEcEcDcEcDcDcEcDcEcEcDcEcDcDcEcDcEcEcEcEcEcEbDcEbDbDcEbDcEcEcEcEcEcEcCcEcCcCcEcCcEcEcEcEcEcEcCcEcCcCcEcCcEcEcEcEcEcEcEcEe8uQttQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtf1csuRdmbEc3dmdmbEdmdmbEdmdmdmdmdzdmbEdmbEc3dmdmbEdmdmbEdmdmdmdmdzdmbEdmbEc3dmdmbEdmdmbEdmdmdmdmdzdmbEdmbEc3dmdmbEdmdmbEdmdmdmdmdzdmbEdmbEc3dmdmbEdmdmbEdmdmdmdmdzdmbEdmbEc3bE.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5.rs5.rsWs3sWsW.r.r.F.r.rs3.r.r.r.rsYk0cDcEcDcDcEcDcEcEcCcEcCcCcEcCcEcEcDcEcDcDcEcDcEcEcEcEcEcEcEcEcEcEbDcEbDbDcEbDcEcEcDcEcDcDcEcDcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcEcDcEcDcDcEcDcEcEcCcEcCcCcEcCcEcEuSezuTQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtuUuVuWdmbEc3dmdmc3dmdzdmdmc3c3dmdmdmdmdmbEc3dmdmc3dmdzdmdmc3c3dmdmdmdmdmbEc3dmdmc3dmdzdmdmc3c3dmdmdmdmdmbEc3dmdmc3dmdzdmdmc3c3dmdmdmdmdmbEc3dmdmc3dmdzdmdmc3c3dmdmdmdmdmsk.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt..rs5.rs5s3.r.r.r.9sH.r.rsA.rjUbDcDbDcDcDcEcDcEcEcEcEcEcEcEcEcEcEbDcEbDbDcDbDcDcDbDcDbDbDcDbDcDcDcEcDcEcEcEcEcEcEcCcEcCcCbDcCbDbDcEbDcEcEcDcEcDcDcEcDcEcEbDcEbDbDcDbDcDcDcEcDcEcEcEcEcEcEcEcEetuX#hQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtahuYuZdmc3bEdmbEdmc3bEdmdmdmbEbEc3bEdmdmc3bEdmbEdmc3bEdmdmdmbEbEc3bEdmdmc3bEdmbEdmc3bEdmdmdmbEbEc3bEdmdmc3bEdmbEdmc3bEdmdmdmbEbEc3bEdmdmc3bEdmbEdmc3bEdmdmdmbEbEc3bEdmaz.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt..rsW.rs3sW.r.F.r.raycEcEcEcEcEcEbDcEbDbDcDbDcDcDbDcDbDbDcEbDcEcEcEcEcEcEcEcEcEcEcEcEcEcEcDcEcDcDbDcDbDbDcEbDcEcEcEcEcEcEcDcEcDcDcEcDcEcEbDcEbDbDcEbDcEcEcEcEcEcEbDcEbDbDcDbDcDu0u1bAQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaau2u3c3dmc3dmc3bEdmbEdzdmc3dmdmdmbEdmc3dmc3dmc3bEdmbEdzdmc3dmdmdmbEdmc3dmc3dmc3bEdmbEdzdmc3dmdmdmbEdmc3dmc3dmc3bEdmbEdzdmc3dmdmdmbEdmc3dmc3dmc3bEdmbEdzdmc3dmdmdmbEsk.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rs5s3.rsW.9.rhnbDcDcDcEcDcEcEbDcEbDbDcEbDcEcEcEcEcEcEcDcEcDcDcDcDcDcDbDcDbDbDbDbDbDbDcDbDcDcDcEcDcEcEbDcEbDbDcEbDcEcEbDcEbDbDcDbDcDcDbDcDbDbDcDbDcDcDcEcDcEcEbDcEbDbDcEu4u5u6QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtcmu7u8bEbEc3bEdmbEdmbEbEbEbEdmbEc3bEdmbEbEc3bEdmbEdmbEbEbEbEdmbEc3bEdmbEbEc3bEdmbEdmbEbEbEbEdmbEc3bEdmbEbEc3bEdmbEdmbEbEbEbEdmbEc3bEdmbEbEc3bEdmbEdmbEbEbEbEdmbE.F.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rt..rt.fbdycEcEcEcDcEcDcDcDcDcDcDbDcDbDbDcEbDcEcEbDcEbDbDcEbDcEcEcEcEcEcEcEcEcEcEcEcEcEcEbDcEbDbDcEbDcEcEcDcEcDcDcEcDcEcEbDcEbDbDcEbDcEcEcEcEcEcEcDcEcDcDcDcDcDu9d7bHQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#Wv.fmdmdmbEc3bEdmc3dmc3c3bEc3dmbEc3dmdmdmbEc3bEdmc3dmc3c3bEc3dmbEc3dmdmdmbEc3bEdmc3dmc3c3bEc3dmbEc3dmdmdmbEc3bEdmc3dmc3c3bEc3dmbEc3dmdmdmbEc3bEdmc3dmc3c3bEc3sA.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rdmeKbDcDbDbDbDbDbDbDbDbDbDbDcEbDcEcEbDcEbDbDcDbDcDcDbDcDbDbDbDbDbDbDbDbDbDbDcDbDcDcDbDcDbDbDcEbDcEcEbDcEbDbDbDbDbDbDcEbDcEcEcDcEcDcDbDcDbDbDbDbDbDbDv#vadZQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#hvbvccqdmdmbEbEc3bEbEdmbEbEc3bEbEc3bEcqdmdmbEbEc3bEbEdmbEbEc3bEbEc3bEcqdmdmbEbEc3bEbEdmbEbEc3bEbEc3bEcqdmdmbEbEc3bEbEdmbEbEc3bEbEc3bEcqdmdmbEbEc3bEbEdmbE#9.9.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rbrgAcDcDcDcDcDbDcDbDbDcDbDcDcDcvcDcvcvbDcvbDbDbDbDbDbDcDbDcDcDcEcDcEcEbDcEbDbDcEbDcEcEcDcEcDcDbDcDbDbDcDbDcDcDcEcDcEcEbDcEbDbDcDbDcDcDcDcDcDcDbDcDvdvevfQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtvgvhb7c3bEdmc3bEcqdmbEdmc3bEbEc3bEbEbEc3bEdmc3bEcqdmbEdmc3bEbEc3bEbEbEc3bEdmc3bEcqdmbEdmc3bEbEc3bEbEbEc3bEdmc3bEcqdmbEdmc3bEbEc3bEbEbEc3bEdmc3bEcqdmbEc3sA.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rskhFbDbDcEbDcEcEcEcEcEcEbDcEbDbDcDbDcDcDcDcDcDcDcEcDcEcEbDcEbDbDcvbDcvcvbDcvbDbDbDbDbDbDcDbDcDcDbDcDbDbDbDbDbDbDcDbDcDcDbDcDbDbDbDbDbDbDcEbDcEcEvivjh8QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtb4vkvlc3bEc3bEbEc3bEcqdmbEc3dmbEcqbEbEc3bEc3bEbEc3bEcqdmbEc3dmbEcqbEbEc3bEc3bEbEc3bEcqdmbEc3dmbEcqbEbEc3bEc3bEbEc3bEcqdmbEc3dmbEcqbEbEc3bEc3bEbEc3bEaz.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rsAj4cDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDcDbDcDcDcDcDcDcDbDcDbDbDcDbDcDcDbDcDbDbDcDbDcDcDcEcDcEcEbDcEbDbDcDbDcDcDbDcDbDbDbDvmvndDQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtbYvobWcqdmbEc3bEbEbEbEbEbEc3bEbEdmbEbEcqdmbEc3bEbEbEbEbEbEc3bEbEdmbEbEcqdmbEc3bEbEbEbEbEbEc3bEbEdmbEbEcqdmbEc3bEbEbEbEbEbEc3bEbEdmbEbEcqdmbEc3bEbE#9.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rl1cEbDcEcEcvcEcvcvbDcvbDbDcEbDcEcEbDcEbDbDbDbDbDbDcDbDcDcDbDcDbDbDbDbDbDbDbDbDbDbDcvbDcvcvbDcvbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDcEbDcEvpdwtGQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtvqvrvscqbEc3cqbEc3bEbEc3bEc3bEc3bEbEbEcqbEc3cqbEc3bEbEc3bEc3bEc3bEbEbEcqbEc3cqbEc3bEbEc3bEc3bEc3bEbEbEcqbEc3cqbEc3bEbEc3bEc3bEc3bEbEbEcqbEc3cq#9.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rfscDbDcDbDbDbDbDbDbDcvbDcvcvbDcvbDbDcDbDcDcDbDcDbDbDbDbDbDbDcDbDcDcDbDcDbDbDcDbDcDcDbDcDbDbDbDbDbDbDcDbDcDcDcvcDcvcvbDcvbDbDcDbDcDcDbDvtdqtyQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtexvuvvbEbEcqbEcqbEbEbEbEcqbEbEcqbEc3bEbEbEcqbEcqbEbEbEbEcqbEbEcqbEc3bEbEbEcqbEcqbEbEbEbEcqbEbEcqbEc3bEbEbEcqbEcqbEbEbEbEcqbEbEcqbEc3bEbEbEcqsk.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.ri4bDbDbDbDbDbDcvbDcvcvbDcvbDbDbDbDbDbDbDbDbDbDcvbDcvcvbDcvbDbDbDbDbDbDbDbDbDbDbDbDbDbDcvbDcvcvbDcvbDbDbDbDbDbDbDbDbDbDbqbDbqbqbDbqvwdhdgQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#gvxepbrbEbEbrbEcqbEbEbEcqbEbEbrbEc3bEbrbEbEbrbEcqbEbEbEcqbEbEbrbEc3bEbrbEbEbrbEcqbEbEbEcqbEbEbrbEc3bEbrbEbEbrbEcqbEbEbEcqbEbEbrbEc3bEbrbEsH.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rgucEbDbDbqbDbqbqbDbqbDbDcDbDcDcDbDcDbDbDbDbDbDbDcDbDcDcDbDcDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDbDcvbDcvcvbDcvbDbDcDbDcDcDvydbvzQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtvAvBvCbEbEbrbEcqbrbEcqbEbEbEbEcqbEbEcqbEbEbrbEcqbrbEcqbEbEbEbEcqbEbEcqbEbEbrbEcqbrbEcqbEbEbEbEcqbEbEcqbEbEbrbEcqbrbEcqbEbEbEbEcqbEbEcq.M.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.re2cVbDbDbDbDbDbDbDbqbDbqbqbDbqbDbDbDbDbDbDbqbDbqbqbDbqbDbDbDbDbDbDcvbDcvcvbDcvbDbDbqbDbqbqcvbqcvcvbDcvbDbDbqbDbqbqbDbqbDbDbDbDvDvEvFQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.5vGvHcqbrbEbrbEbrbrbEbrbEbEbrbEbrbrbEcqbrbEbrbEbrbrbEbrbEbEbrbEbrbrbEcqbrbEbrbEbrbrbEbrbEbEbrbEbrbrbEcqbrbEbrbEbrbrbEbrbEbEbrbEbrbr.F.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rcqeJbDbqbDbDbDbDbDbDbDbDbDbDcvbDcvcvbDcvbDbDbDbDbDbDbDbDbDbDbqbDbqbqaxbqaxaxbDaxbDbDbDbDbDbDcvbDcvcvbDcvbDbDbDbDbDbDcvbDcvc2vIc0.kQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtd6vJd4bEbEbEbEcqbEbrcqazbEcqbEbEbEbEbEbEbEbEbEcqbEbrcqazbEcqbEbEbEbEbEbEbEbEbEcqbEbrcqazbEcqbEbEbEbEbEbEbEbEbEcqbEbrcqazbEcqbEbEdH.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r#9hHbDbDbDbDbDaxbDaxaxcvaxcvcvbDcvbDbDaxbDaxaxbDaxbDbDbDbDbDbDbDbDbDbDbDbDbDbDbqbDbqbqbDbqbDbDbqbDbqbqbDbqbDbDbDbDbDbDaxdcvKvLQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtvMvNbfbEbEazbEbEbEbEazbEazbEazbrbEbEazbEbEazbEbEbEbEazbEazbEazbrbEbEazbEbEazbEbEbEbEazbEazbEazbrbEbEazbEbEazbEbEbEbEazbEazbE#9sA.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.Fi2cvcvbDcvbDbDbDbDbDbDbDbDbDbDbqbDbqbqbDbqbDbDaxbDaxaxcvaxcvcvbqcvbqbqbDbqbDbDbDbDbDbDbDbDbDbDaxbDaxaxcvaxcvcvbDcvbDvOcyvPQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtbbvQvRbrbEazbEbEbrbrbEbEbEbEbEazazbEbEbrbEazbEbEbrbrbEbEbEbEbEazazbEbEbrbEazbEbEbrbrbEbEbEbEbEazazbEbEbrbEazbEbEbrbrbEbEbEaz.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rlNaxbDbDbqbDbqbqbDbqbDbDaxbDaxaxbqaxbqbqbDbqbDbDbDbDbDbDbDbDbDbDaxbDaxaxbqaxbqbqaxbqaxaxbqaxbqbqbDbqbDbDbDbDbDbDaxcuvS.kQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.AvTa7bEbr#9bEbEbEaz#9brazbEbEbrazbEazbEbr#9bEbEbEaz#9brazbEbEbrazbEazbEbr#9bEbEbEaz#9brazbEbEbrazbEazbEbr#9bEbEbEaz#9br.F.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rfPbqbDbqbqbDbqbDbDaxbDaxaxbDaxbDbDbDbDbDbDaxbDaxaxbqaxbqbqaxbqaxaxbDaxbDbDbDbDbDbDbDbDbDbDbDbDbDbDbqbDbqbq#8bqvUcovVQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.kaMvWvXbE#9azbEbrbEbEbEazbEazbEbrbEbEazbE#9azbEbrbEbEbEazbEazbEbrbEbEazbE#9azbEbrbEbEbEazbEazbEbrbEbEazbE#9azbEbrbEbEsk.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.riYbD#8bD#8#8bq#8bqbqaxbqaxaxbqaxbqbqbDbqbDbD#8bD#8#8bD#8bDbDbDbDbDbDaxbDaxaxbqaxbqbqaxbqaxaxaxaxaxaxaxaxaxaxvYvZv0QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtv1v2v3azbEazaz#9azbE#9brazbEaz#9brazbEazbEazaz#9azbE#9brazbEaz#9brazbEazbEazaz#9azbE#9brazbEaz#9brazbEazbEazaz#9az.F.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rgVcDbqbDbqbDbD#8bD#8#8bD#8bDbDbDbDbDbDbqbDbqbqbqbqbqbqaxbqaxaxbDaxbDbD#8bD#8#8bD#8bDbDbDbDbDbDbDbDbDbDbDbDv4v5abQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaSv6aQbEbrbEazbE#9brbE#9brbEbEbE#9brazbEbrbEazbE#9brbE#9brbEbEbE#9brazbEbrbEazbE#9brbE#9brbEbEbE#9brazbEbrbEazdVsY.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rfbcEaxaxaxaxaxaxbDaxbDbDaxbDaxax#8ax#8#8#8#8#8#8ax#8axaxbDaxbDbDaxbDaxaxbqaxbqbqaxbqaxaxaxaxaxax#8ax#8v7v8v9QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtabw.w#az#9azbEaz#9brazazbrazazbEbEaz#9az#9azbEaz#9brazazbrazazbEbEaz#9az#9azbEaz#9brazazbrazazbEbEaz#9az#9azbr.9.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rc3cE#8axaxbDaxbDbDaxbDaxaxbqaxbqbqbqbqbqbqaxbqaxaxbDaxbDbDaxbDaxax#8ax#8#8#8#8#8#8bq#8bqbqbDbqbDbDaxwab5wbQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtdawcwdazbEazbEbr#9#9braz#9#9brazbEbr#9azbEazbEbr#9#9braz#9#9brazbEbr#9azbEazbEbr#9#9braz#9#9brazbEbr#9azdIsH.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rbrfi#8ax#8#8bq#8bqbq#8bq#8#8#8#8#8#8bD#8bDbDbDbDbDbD#8bD#8#8ax#8axaxbqaxbqbqbDbqbDbD#8bD#8#8ax#8axwewf#RQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtc6wgaAaz#9azbEazazbEazaz#9#9az#9br#9#9az#9azbEazazbEazaz#9#9az#9br#9#9az#9azbEazazbEazaz#9#9az#9br#9#9sksA.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.FiUaxbDaxbDbD#8bD#8#8bq#8bqbqaxbqaxax#8ax#8#8ax#8axax#8ax#8#8#8#8#8#8ax#8axaxaxaxaxaxaxaxaxaxwhbUwiQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtata#wj#9#9az#9azbr#9azbEaz#9azbEazbraz#9#9az#9azbr#9azbEaz#9azbEazbraz#9#9az#9azbr#9azbEaz#9azbEazbr#9.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rj4#8#8ax#8axax#8ax#8#8ax#8axaxbDaxbDbDaxbDaxaxaxaxaxaxbDaxbDbDaxbDaxax#8ax#8#8bq#8bqbqaxbqwkbNeWQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtanwlwmbr#9azbr#9az#9br#9#9#9az#9braz#9br#9azbr#9az#9br#9#9#9az#9braz#9br#9azbr#9az#9br#9#9#9az#9sZ.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.riaaxbqbqaxbqaxax#8ax#8#8bq#8bqbq#8bq#8#8#8#8#8#8ax#8axax#8ax#8#8bq#8bqbq#8bq#8#8#8#8#8#8wnbI#hQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtahwowp#9#9az#9az#9azaz#9br#9az#9azazaz#9#9az#9az#9azaz#9br#9az#9azazaz#9#9az#9az#9azaz#9br#9az.F.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.ray#8#8#8#8ax#8axax#8ax#8#8ax#8axax#8ax#8#8ax#8axax#8ax#8#8#8#8#8#8#8#8#8#8ax#8axaxaxbVwq#bQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaa#Pwraz#9#9#9az#9az#1#9br#9#1#9#9#1#9az#9#9#9az#9az#1#9br#9#1#9#9#1#9az#9#9#9az#9az#1#9br#9.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rh0#8ax#8axax#8ax#8#8bq#8bqbqaxbqaxax#0ax#0#0#8#0#8#8ax#8axaxaxaxaxaxaxaxaxaxbqaxbqwswtwuQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtwvwwck#1#9az#9#9az#9az#9azazaz#9#1azaz#1#9az#9#9az#9az#9azazaz#9#1azaz#1#9az#9#9az#9az#9td.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rfQwx#8#0#8#0#0#8#0#8#8#8#8#8#8#8#8#8#8ax#8axax#0ax#0#0ax#0axax#8ax#8#8#0#8#0#0#8wywzbHQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#WwAwB#1.Faz#1#9az#1#9#9#1azaz#1#9#1az#1.Faz#1#9az#1#9#9#1azaz#1#9#1az#1.Faz#1#9az#1sQ.9.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.reLcU#8#8ax#8axaxaxaxaxax#0ax#0#0ax#0axax#8ax#8#8#8#8#8#8ax#8axax#8ax#8#8ax#8wCbjbiQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#hwDwE#9#1az.F#9az#9#9#9#9az#9az#9#9az#9#1az.F#9az#9#9#9#9az#9az#9#9az#9#1az.F#9az#9sA.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rdVdl#0#8#8#8#8#8#8.E#8.E.E#8.E#8#8ax#8axax.Eax.E.Eax.Eaxax#8ax#8#8.E#8.E.EwFbdwGQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtwHwIwJ#9az.Faz.F#9.F#1#9az.F#9#9.F#9#9#9az.Faz.F#9.F#1#9az.F#9#9.F#9#9#9az.FazsHsA.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rskg1ax.Eaxax#8ax#8#8#8#8#8#8ax#8axax#8ax#8#8#8#8#8#8#8#8#8#8.E#8.E.Eax.EwKa9wLQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtwMwNwO#1.F#9az#9az#9.F.Fazaz.F#9.Faz#9#1.F#9az#9az#9.F.Fazaz.F#9.Faz#9#1.F#9sk.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rsAjvax#8ax#8#8.E#8.E.E.E.E.E.E#8.E#8#8.E#8.E.Eax.Eaxax.Eax.E.E#0.E#0wPa6a5QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#ywQwR#9.F.F#1.F#9az#1.F#9.Faz.F.F#9#9#9.F.F#1.F#9az#1.F#9.Faz.F.F#9#9#9.FsH.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rl0#0#0.E#0.E.E#8.E#8#8ax#8axax.Eax.E.Eax.Eaxax.Eax.E.E#8.E#8#8axwSwTwUQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtwVwWwX#9#9#9.F#9.Faz#1#9#9.Faz.F.F#9#1#9#9#9.F#9.Faz#1#9#9.Faz.F.F#9#1.F.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rfP.E.E.E#8.E#8#8.E#8.E.E#8.E#8#8.E#8.E.E#8.E#8#8.E#8.E.E.E.E.EwYwZ.BQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtbMw0#k.F#9.F#1.F#9.F.F.F.F#9.Faz.F.F.F.F#9.F#1.F#9.F.F.F.F#9.Faz.F.FsH.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.riN#8#8#8#8.E#8.E.Eax.Eaxax.Eax.E.E#8.E#8#8.E#8.E.E#8.E#8#8.ew1aNQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#g.Ww2.F#1#9.F.F#9#9#9.F#1.F#9#1#9.F#9.F#1#9.F.F#9#9#9.F#1.F#9#1sksA.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rgR#0.E.E.E.E.E.E.E.E#8.E#8#8.E#8.E.E.E.E.E.Eax.Eaxax.Eaxw3w4.lQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtw5w6#.#1.F.F.F.F.F#9#9.F.F.F.M.F#9.F.F#1.F.F.F.F.F#9#9.F.F.F.M.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.reTax.E#8.E#8#8.E#8.E.E.E.E.E.E.E.E.E.E#8.E#8#8.E#8.E.Ew7aEaTQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.5w8w9.F.F#9.F.F.M#1.F#9#9.F.F#9.F#9#1.F.F#9.F.F.M#1.F#9#9.M.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rbEcX.E.E.E.E.E.E#8.E#8#8.E#8.E.E.E.E.E.E.E.E.E.E.TaPx.au.kQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.Xx#.V.F#9.F.F.F.F.F.F.F.F.M.F.F.F#9.F.F#9.F.F.F.F.F.F.F.9.9.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r#9fj.E.E.E#8.E#8#8.E#8.E.E.E.E.E.E#8.E#8#8.E#8.ExaapxbQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtxcxdxe.M.F#9.F.F.F#9.F.F.F.M.F.F.M.F.F.M.F#9.F.F.F#9sH.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.Fh6.E.T.E.E.E.E.E.E.E.E.E.E.T.E.T.T.E.T.E.E.ExfxgaDQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtxhxixj.F.M#1.F.M.F.F.M.F.F.F#1.M.F#1.M.F.M#1.F.M.F.9.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rj9.T#0.T#0#0.T#0.T.T.E.T.E.E#0.E#0#0.E#0aPxkxl.kQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.Axmxn.M.F.F.F.F.F.F.M.F.F.F.M.F.F.F.F.M.F.F.F.F.9.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rgI.E.E.E.E.E.E.E.E.E.E.E.E.E.E.T.E.T.TxoxpxqQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.kxrxsxt.F.r.F.F.F.9.M.F.r.F.F.M.F.F.F.M.F.r.F.9.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.2.T.E.E.E.E.E.E.E.E.E.E.T.E.T.T.E.Txu#Yv0QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaYxvxw.F.M.F.F.F.F.F.F.r.F.F.r.F.F.F.F.F.M.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rgV#8.E.T.T.p.T.p.p.E.p.E.E.E.E.E.Exx#SabQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaSxyxz.F.r.F.M.r.F.F.9.F.r.F.M.r.F.r.F.F.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rfk#0.E.p.E.E.E.E.E.E.E.E.E.E.TxAxBxCQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtabxDxE.F.F.F.M.F.F.M.F.F.M.F.F.9.F.r.M.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rdzcv.E.p.E.p.p.E.p.E.E.1.E.1xF#GchQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtxGxHxI.F.r.r.M.F.r.F.M.r.F.M.F.F.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.razcE.E.E.E.E.E.E.E.E.E.E.p#BxJ#RQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaCxKxL.F.F.r.r.F.F.r.F.r.F.F.9.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.Fg8.p.T.T.p.T.p.p.p.pxM#uc.QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtabxNxO.M.F.M.r.F.r.F.r.r.F.MsH.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rjL.E.E.E.E.E.E.E.E#H#oeWQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtxPxQal.r.F.r.F.M.r.F.r.r.M.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rk0.p.1.p.1.1.p.1xR#i#zQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtahxSxT.F.r.r.r.r.F.r.F.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.U.p.p.E.p.E#v#cbTQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtaaxUxV.F.F.F.r.r.r.F.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rh0.p.p.p.pxW.7.#QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtxXxYxZ.r.F.F.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rhn.E.p.px0x1bHQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtx2x3x4.r.r.r.F.9.r.r.r.r.r.r.r.r.r.r.r.r.r.rh0x5x6x7.QQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt#Qx8x9.r.r.r.r.r.r.r.r.r.r.r.r.r.r.rbrj4y..KbuQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQty#yayb.r.F.r.r.r.r.r.r.r.rgvycydye.YQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtxPyfygyhyi.Myjyktk#MylymQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtyn#yQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt", +"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt"}; From 65e82f72788b3df5a9ea89b11b6db3d6c20b9ff2 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 22 Feb 2021 10:44:47 +0100 Subject: [PATCH 019/668] windows installer: update to new website --- deploy/PackageContents.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/PackageContents.xml.in b/deploy/PackageContents.xml.in index d3556388..a16b5c9a 100644 --- a/deploy/PackageContents.xml.in +++ b/deploy/PackageContents.xml.in @@ -29,7 +29,7 @@ From 995023fec9ea6f7361f8821ef5324ed0cf42e71f Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 22 Feb 2021 10:46:10 +0100 Subject: [PATCH 020/668] windows installer: update env vars for node icons --- deploy/PackageContents.xml.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deploy/PackageContents.xml.in b/deploy/PackageContents.xml.in index a16b5c9a..20c8377d 100644 --- a/deploy/PackageContents.xml.in +++ b/deploy/PackageContents.xml.in @@ -38,6 +38,7 @@ + @@ -46,6 +47,7 @@ + @@ -54,6 +56,7 @@ + From ff4b750ab11f326000d0d193232372529f5df32e Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 22 Feb 2021 10:56:34 +0100 Subject: [PATCH 021/668] windows installer: updated installer art to new design --- deploy/resources/banner.png | Bin 20888 -> 8283 bytes deploy/resources/dialog.png | Bin 93445 -> 35812 bytes deploy/resources/serlio.ico | Bin 156864 -> 1150 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/deploy/resources/banner.png b/deploy/resources/banner.png index 07f23afa566ff301bf3bd3ba312ba7023fa136f8..b0eb95833b391f57fa33ca55aa4d8fcbf8557b89 100644 GIT binary patch literal 8283 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%0D|{S}zY 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 literal 93445 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 z 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 From 574212cf4075e6711354e88055308ed6f6c26315 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 22 Feb 2021 10:58:04 +0100 Subject: [PATCH 022/668] doc: update installation instructions for icons --- doc/usage.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/usage.md b/doc/usage.md index e66e7ee9..a65c716f 100644 --- a/doc/usage.md +++ b/doc/usage.md @@ -17,6 +17,7 @@ PATH=\plug-ins;%PATH% MAYA_PLUG_IN_PATH=\plug-ins MAYA_SCRIPT_PATH=\scripts + XBMLANGPATH=\icons ``` 1. Start maya 1. Open the plugin manager: Windows -> Settings/Preferences -> Plug-in Manager @@ -34,6 +35,7 @@ PLUGINDIR= # replace with the actual path MAYA_PLUG_IN_PATH=$PLUGINDIR/plug-ins MAYA_SCRIPT_PATH=$PLUGINDIR/scripts + XBMLANGPATH=$PLUGINDIR/icons/%B ``` 1. Start Maya (Note: you may want to start maya from a shell to see the serlio log output). 1. Open the plugin manager: Windows -> Settings/Preferences -> Plug-in Manager From 936fab7faa66140304bbd8ab3caafb49952d4b6c Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 22 Feb 2021 11:00:35 +0100 Subject: [PATCH 023/668] doc: updated icon for readme --- doc/img/serlio_32.png | Bin 3911 -> 1692 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/img/serlio_32.png b/doc/img/serlio_32.png index d51bbff723c9d1bfbce794a20546e328747c08ff..58f037a092494a1ece1a9f5c5d36cf9257136d43 100644 GIT binary patch delta 1688 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 V(pu=RvmF2c002ovPDHLkV1gB Date: Sat, 20 Mar 2021 17:48:07 +0100 Subject: [PATCH 024/668] redesign installer art --- deploy/resources/banner.png | Bin 8283 -> 2927 bytes deploy/resources/dialog.png | Bin 35812 -> 15427 bytes deploy/resources/serlio.ico | Bin 1150 -> 9662 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/deploy/resources/banner.png b/deploy/resources/banner.png index b0eb95833b391f57fa33ca55aa4d8fcbf8557b89..7cf015c436a9ac228deac60daa134d1c4598b383 100644 GIT binary patch literal 2927 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%0D|{S}zY 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 diff --git a/deploy/resources/serlio.ico b/deploy/resources/serlio.ico index 842044391ecb7da0f61c85c18d5fda67201f06e7..f0064c946a9258418eb7f39b7299b270715658ee 100644 GIT binary patch literal 9662 zcmd^_X>3&26~~9rDqW&PYNb|crKYLWs8SUvRX?4PXFy8TQ&v+Y;moZ+k@j?SN5DElBOasI~WFWZM!N#Zm|9SIf-VAS^1DJfN z(c#{A-}8IFbM85JIoB@ND*Su?c^CfwwyXZTF4xywF4y-UXmS-m*v_9Fxc6E9>}!bs zeno3iT77rsv!CU6ALd(oYSw?Qs4-zJq!IkHrOk;_)|_;seJHQ0dvx;}Zy@)vhk5^_ z`rX3%gs&8N61G4_iW=jW%9`C$*bpas>*A!aK2GYo(xqcKf3b6|x4~}I_tdVJDqos(k8Kw819ds&AI^WZ{b1gA?&o_e@$Bx> zb&nN$5`O}z0QU@hTuPb}rKll6_SHwS7vh>iU8qBiKV9HU(lL@Rf$_}({%ih&`9+W) zKeOtYmB5Snjv`OulO^87oD#3QALU|2Te6@O_`1qI@RQTDyUA{rssrxVb#4&fV4k!M zY!d&$JVTG6ygSgV8<0pfst zz<6lr&Xl&nP0~8BQN@RP*Jy$Ghx5=snAe&}Ed^gó{<+Z)nq-Z37#B5Q18!Qqtu9 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 LYPC_$KgIHY*hNyv literal 1150 zcma)*TS${(7{|X=;B5p2(M4X`b$DA{so~twsp;J2p%a;sHwE4dr0#+!%0udObLyts zz8&4cZEkaFMutTi6=f0Dg%uri(>4A*@9Y}{g}vkdJ>z}f-~V~u6UT|@w|_rJ-$L%f zK8`!Yaa<9Jx~ONTp8|J^#{Q?>GL-DSKVEUN*D4+~xfK2W*~Syz>E=DVvBVtcw@GxQ zIQ`EW91<9KDXf8-tZzm)?TKqO?wE#^^+)TsNsA12Y3G1l`f|uA#~?35_gHyey%sU} zXdNb#IuQSYr01jc2L%)2zMH*DFt%?(f}Y=O4{=lniN({f_*INYXrU2~u-YT=Lp{4$)ff++`6Tcrw-P3o5);8D#2((n*7gsix4z-^ z`#6LaobAA{TaK~GD#lVxHF@6bQNiq0!Q_@zq*^O!EtPqmttY2I4edc47M|Y2>RKGm=sDUK z85Wm~X#$g7s+!Q+(?Y;3OM(K936Ejh95 zj>+>po##qmzJbnG4d#{8&Ka{j7_dudFR#F0m%-qWVZ^7TKG|2{{P>8v*ptwDT`^7C zKc|B$R!?hJ=k5t(*q!XNmDAeg#7R@+;ifw;6?h@Kz3gY%8>CbYCx83sx8 zrq)QeNymlx|BBv(?uakh(B_V7mMGsMy@AWU){4V_o1Z?)ajOL)PA}qi(J>ltYtf4e Oi}a%Qv#SLh$Jk#$cQLB~ From c7103e47c36fdc1a24572d2c723aaa4479e4f7b1 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Sat, 20 Mar 2021 18:23:32 +0100 Subject: [PATCH 025/668] build system: switch to correct MSVC toolchain for PRT 2.3 --- src/common.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common.cmake b/src/common.cmake index 5d319783..6be8102a 100644 --- a/src/common.cmake +++ b/src/common.cmake @@ -37,7 +37,7 @@ endfunction() if (NOT prt_DIR) if (SRL_WINDOWS) set(PRT_OS "win10") - set(PRT_TC "vc141") + set(PRT_TC "vc142") elseif (SRL_LINUX) set(PRT_OS "rhel7") set(PRT_TC "gcc63") From ebfcd2b9ed32bedcae2b64e38eb9c42e6327a609 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Tue, 23 Mar 2021 15:56:43 +0100 Subject: [PATCH 026/668] linux: handle return value of remove_all system calls --- src/serlio/utils/Utilities.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/serlio/utils/Utilities.cpp b/src/serlio/utils/Utilities.cpp index 2cd37d01..822c0c55 100644 --- a/src/serlio/utils/Utilities.cpp +++ b/src/serlio/utils/Utilities.cpp @@ -18,6 +18,7 @@ */ #include "utils/Utilities.h" +#include "utils/LogHandler.h" #include "prt/API.h" #include "prt/StringUtils.h" @@ -30,10 +31,12 @@ struct IUnknown; # include # include #else +# include # include # include #endif +#include #include #include #include @@ -236,7 +239,16 @@ void remove_all(const std::wstring& path) { int ret = SHFileOperationW(&fileop); delete[] pszFrom; #else - system((std::string("rm -rf ") + toOSNarrowFromUTF16(path)).c_str()); + const auto exitCode = std::system((std::string("rm -rf ") + toOSNarrowFromUTF16(path)).c_str()); + if (exitCode < 0) { + LOG_ERR << "Failed to delete " << path << ": " << std::strerror(errno); + } + else { + if (WIFEXITED(exitCode) != 0) + LOG_ERR << "Failed to delete " << path << ", exit code " << WEXITSTATUS(exitCode); + else + LOG_ERR << "Failed to delete " << path << ", unknown reason!"; + } #endif } From 7ec03f44468758f8656f90c27e1d8a3ab41643f6 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Tue, 23 Mar 2021 16:43:06 +0100 Subject: [PATCH 027/668] minor code cleanup --- src/serlio/modifiers/MayaCallbacks.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index f219d407..67311fdb 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -77,12 +77,10 @@ struct TextureUVOrder { uint8_t prtUvSetIndex; }; -// maya pbr stingray shader only supports first 4 uvsets -> reoder so first 4 are most important ones -// other shaders support >4 sets const std::vector TEXTURE_UV_ORDERS = []() -> std::vector { // clang-format off return { - // maya uvset name | maya idx | prt idx | CGA key + // first 4 uv sets are selected to be compatible with the Maya PBR Stingray shader { L"map1", 0, 0 }, // colormap { L"dirtMap", 1, 2 }, // dirtmap { L"normalMap", 2, 5 }, // normalmap @@ -114,7 +112,7 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c LOG_DBG << "-- MayaCallbacks::addMesh"; LOG_DBG << " faceCountsSize = " << faceCountsSize; LOG_DBG << " vertexIndicesSize = " << vertexIndicesSize; - LOG_DBG << " mayaVertices.length = " << mayaVertices.length(); + LOG_DBG << " mayaVertices.length = " << mayaVertices.length(); LOG_DBG << " mayaFaceCounts.length = " << mayaFaceCounts.length(); LOG_DBG << " mayaVertexIndices.length = " << mayaVertexIndices.length(); } From 76aa63fb6cb569c164c5e5ef7647a1cdfb33b749 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Tue, 23 Mar 2021 16:46:24 +0100 Subject: [PATCH 028/668] bug fix: only create uv sets if there are any generated UVs --- src/serlio/modifiers/MayaCallbacks.cpp | 52 +++++++++++++------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 67311fdb..44e98086 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -133,36 +133,36 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c mFnMesh.clearUVs(); // -- add texture coordinates - for (const TextureUVOrder& o : TEXTURE_UV_ORDERS) { - uint8_t uvSet = o.prtUvSetIndex; - - if (uvSetsCount > uvSet && uvsSizes[uvSet] > 0) { + if (uvSetsCount > 0) { + for (const TextureUVOrder& o : TEXTURE_UV_ORDERS) { + const uint8_t uvSet = o.prtUvSetIndex; + const MString uvSetName = o.mayaUvSetName; + + if (uvSetsCount > uvSet && uvsSizes[uvSet] > 0) { + MFloatArray mU; + MFloatArray mV; + for (size_t uvIdx = 0; uvIdx < uvsSizes[uvSet] / 2; ++uvIdx) { + mU.append(static_cast(uvs[uvSet][uvIdx * 2 + 0])); // maya mesh only supports float uvs + mV.append(static_cast(uvs[uvSet][uvIdx * 2 + 1])); + } - MFloatArray mU; - MFloatArray mV; - for (size_t uvIdx = 0; uvIdx < uvsSizes[uvSet] / 2; ++uvIdx) { - mU.append(static_cast(uvs[uvSet][uvIdx * 2 + 0])); // maya mesh only supports float uvs - mV.append(static_cast(uvs[uvSet][uvIdx * 2 + 1])); - } + if (uvSet > 0) { + mFnMesh.createUVSetDataMeshWithName(uvSetName, &stat); + MCHECK(stat); + } - MString uvSetName = o.mayaUvSetName; + MCHECK(mFnMesh.setUVs(mU, mV, &uvSetName)); - if (uvSet != 0) { - mFnMesh.createUVSetDataMeshWithName(uvSetName, &stat); - MCHECK(stat); + MIntArray mUVCounts = toMayaIntArray(uvCounts[uvSet], uvCountsSizes[uvSet]); + MIntArray mUVIndices = toMayaIntArray(uvIndices[uvSet], uvIndicesSizes[uvSet]); + MCHECK(mFnMesh.assignUVs(mUVCounts, mUVIndices, &uvSetName)); } - - MCHECK(mFnMesh.setUVs(mU, mV, &uvSetName)); - - MIntArray mUVCounts = toMayaIntArray(uvCounts[uvSet], uvCountsSizes[uvSet]); - MIntArray mUVIndices = toMayaIntArray(uvIndices[uvSet], uvIndicesSizes[uvSet]); - MCHECK(mFnMesh.assignUVs(mUVCounts, mUVIndices, &uvSetName)); - } - else { - if (uvSet > 0) { - // add empty set to keep order consistent - mFnMesh.createUVSetDataMeshWithName(o.mayaUvSetName, &stat); - MCHECK(stat); + else { + if (uvSet > 0) { + // add empty set to keep order consistent + mFnMesh.createUVSetDataMeshWithName(uvSetName, &stat); + MCHECK(stat); + } } } } From 8f978d7ca5a7b47cc2d651923308b6e3467b8ccc Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Tue, 23 Mar 2021 18:33:22 +0100 Subject: [PATCH 029/668] code cleanup: extract helper functions --- src/serlio/modifiers/MayaCallbacks.cpp | 147 +++++++++++++------------ 1 file changed, 79 insertions(+), 68 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 44e98086..ae278263 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -69,8 +69,6 @@ MFloatPointArray toMayaFloatPointArray(double const* a, size_t s) { return mfpa; } -} // namespace - struct TextureUVOrder { MString mayaUvSetName; uint8_t mayaUvSetIndex; @@ -96,14 +94,85 @@ const std::vector TEXTURE_UV_ORDERS = []() -> std::vector uvSet && uvsSizes[uvSet] > 0) { + MFloatArray mU; + MFloatArray mV; + for (size_t uvIdx = 0; uvIdx < uvsSizes[uvSet] / 2; ++uvIdx) { + mU.append(static_cast(uvs[uvSet][uvIdx * 2 + 0])); // maya mesh only supports float uvs + mV.append(static_cast(uvs[uvSet][uvIdx * 2 + 1])); + } + + if (uvSet > 0) { + MStatus status; + fnMesh.createUVSetDataMeshWithName(uvSetName, &status); + MCHECK(status); + } + + MCHECK(fnMesh.setUVs(mU, mV, &uvSetName)); + + MIntArray mUVCounts = toMayaIntArray(uvCounts[uvSet], uvCountsSizes[uvSet]); + MIntArray mUVIndices = toMayaIntArray(uvIndices[uvSet], uvIndicesSizes[uvSet]); + MCHECK(fnMesh.assignUVs(mUVCounts, mUVIndices, &uvSetName)); + } + else { + if (uvSet > 0) { + // add empty set to keep order consistent + MStatus status; + fnMesh.createUVSetDataMeshWithName(uvSetName, &status); + MCHECK(status); + } + } + } +} + +void assignVertexNormals(MFnMesh& mFnMesh, MIntArray& mayaFaceCounts, MIntArray& mayaVertexIndices, const double* nrm, + size_t nrmSize, const uint32_t* normalIndices, size_t normalIndicesSize) { + if (nrmSize == 0) + return; + + assert(normalIndicesSize == mayaVertexIndices.length()); + // guaranteed by MayaEncoder, see prtx::VertexNormalProcessor::SET_MISSING_TO_FACE_NORMALS + + // convert to native maya normal layout + MVectorArray expandedNormals(static_cast(mayaVertexIndices.length())); + MIntArray faceList(static_cast(mayaVertexIndices.length())); + + int indexCount = 0; + for (int i = 0; i < mayaFaceCounts.length(); i++) { + int faceLength = mayaFaceCounts[i]; + + for (int j = 0; j < faceLength; j++) { + faceList[indexCount] = i; + int idx = normalIndices[indexCount]; + expandedNormals.set(&nrm[idx * 3], indexCount); + indexCount++; + } + } + + MCHECK(mFnMesh.setFaceVertexNormals(expandedNormals, faceList, mayaVertexIndices)); +} + +} // namespace + void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, const double* nrm, size_t nrmSize, const uint32_t* faceCounts, size_t faceCountsSize, const uint32_t* vertexIndices, - size_t vertexIndicesSize, const uint32_t* normalIndices, - MAYBE_UNUSED size_t normalIndicesSize, double const* const* uvs, size_t const* uvsSizes, - uint32_t const* const* uvCounts, size_t const* uvCountsSizes, - uint32_t const* const* uvIndices, size_t const* uvIndicesSizes, size_t uvSetsCount, - const uint32_t* faceRanges, size_t faceRangesSize, const prt::AttributeMap** materials, - const prt::AttributeMap** reports, const int32_t*) { + size_t vertexIndicesSize, const uint32_t* normalIndices, size_t normalIndicesSize, + double const* const* uvs, size_t const* uvsSizes, uint32_t const* const* uvCounts, + size_t const* uvCountsSizes, uint32_t const* const* uvIndices, size_t const* uvIndicesSizes, + size_t uvSetsCount, const uint32_t* faceRanges, size_t faceRangesSize, + const prt::AttributeMap** materials, const prt::AttributeMap** reports, const int32_t*) { MFloatPointArray mayaVertices = toMayaFloatPointArray(vtx, vtxSize); MIntArray mayaFaceCounts = toMayaIntArray(faceCounts, faceCountsSize); MIntArray mayaVertexIndices = toMayaIntArray(vertexIndices, vertexIndicesSize); @@ -118,7 +187,6 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c } MStatus stat; - MCHECK(stat); MFnMeshData dataCreator; MObject newOutputData = dataCreator.create(&stat); @@ -130,65 +198,8 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c MCHECK(stat); MFnMesh mFnMesh(oMesh); - mFnMesh.clearUVs(); - - // -- add texture coordinates - if (uvSetsCount > 0) { - for (const TextureUVOrder& o : TEXTURE_UV_ORDERS) { - const uint8_t uvSet = o.prtUvSetIndex; - const MString uvSetName = o.mayaUvSetName; - - if (uvSetsCount > uvSet && uvsSizes[uvSet] > 0) { - MFloatArray mU; - MFloatArray mV; - for (size_t uvIdx = 0; uvIdx < uvsSizes[uvSet] / 2; ++uvIdx) { - mU.append(static_cast(uvs[uvSet][uvIdx * 2 + 0])); // maya mesh only supports float uvs - mV.append(static_cast(uvs[uvSet][uvIdx * 2 + 1])); - } - - if (uvSet > 0) { - mFnMesh.createUVSetDataMeshWithName(uvSetName, &stat); - MCHECK(stat); - } - - MCHECK(mFnMesh.setUVs(mU, mV, &uvSetName)); - - MIntArray mUVCounts = toMayaIntArray(uvCounts[uvSet], uvCountsSizes[uvSet]); - MIntArray mUVIndices = toMayaIntArray(uvIndices[uvSet], uvIndicesSizes[uvSet]); - MCHECK(mFnMesh.assignUVs(mUVCounts, mUVIndices, &uvSetName)); - } - else { - if (uvSet > 0) { - // add empty set to keep order consistent - mFnMesh.createUVSetDataMeshWithName(uvSetName, &stat); - MCHECK(stat); - } - } - } - } - - if (nrmSize > 0) { - assert(normalIndicesSize == vertexIndicesSize); - // guaranteed by MayaEncoder, see prtx::VertexNormalProcessor::SET_MISSING_TO_FACE_NORMALS - - // convert to native maya normal layout - MVectorArray expandedNormals(static_cast(vertexIndicesSize)); - MIntArray faceList(static_cast(vertexIndicesSize)); - - int indexCount = 0; - for (int i = 0; i < faceCountsSize; i++) { - int faceLength = mayaFaceCounts[i]; - - for (int j = 0; j < faceLength; j++) { - faceList[indexCount] = i; - int idx = normalIndices[indexCount]; - expandedNormals.set(&nrm[idx * 3], indexCount); - indexCount++; - } - } - - MCHECK(mFnMesh.setFaceVertexNormals(expandedNormals, faceList, mayaVertexIndices)); - } + assignTextureCoordinates(mFnMesh, uvs, uvsSizes, uvCounts, uvCountsSizes, uvIndices, uvIndicesSizes, uvSetsCount); + assignVertexNormals(mFnMesh, mayaFaceCounts, mayaVertexIndices, nrm, nrmSize, normalIndices, normalIndicesSize); MFnMesh outputMesh(outMeshObj); outputMesh.copyInPlace(oMesh); From 1d6b1909629049f112b3a8ed5ea2da733fd537ff Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Wed, 24 Mar 2021 17:22:06 +0100 Subject: [PATCH 030/668] Fix "unused variable" warning. --- src/serlio/modifiers/MayaCallbacks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index ae278263..86bd7de5 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -138,7 +138,7 @@ void assignTextureCoordinates(MFnMesh& fnMesh, double const* const* uvs, size_t } void assignVertexNormals(MFnMesh& mFnMesh, MIntArray& mayaFaceCounts, MIntArray& mayaVertexIndices, const double* nrm, - size_t nrmSize, const uint32_t* normalIndices, size_t normalIndicesSize) { + size_t nrmSize, const uint32_t* normalIndices, MAYBE_UNUSED size_t normalIndicesSize) { if (nrmSize == 0) return; From 5a78deb92224694ea37d121219d48991d8c59bff Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Wed, 24 Mar 2021 17:23:01 +0100 Subject: [PATCH 031/668] Cleanup: factor out helper function to create data structure for materials. --- src/serlio/modifiers/MayaCallbacks.cpp | 97 +++++++++++++------------- 1 file changed, 50 insertions(+), 47 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 86bd7de5..929be41b 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -164,53 +164,12 @@ void assignVertexNormals(MFnMesh& mFnMesh, MIntArray& mayaFaceCounts, MIntArray& MCHECK(mFnMesh.setFaceVertexNormals(expandedNormals, faceList, mayaVertexIndices)); } -} // namespace - -void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, const double* nrm, size_t nrmSize, - const uint32_t* faceCounts, size_t faceCountsSize, const uint32_t* vertexIndices, - size_t vertexIndicesSize, const uint32_t* normalIndices, size_t normalIndicesSize, - double const* const* uvs, size_t const* uvsSizes, uint32_t const* const* uvCounts, - size_t const* uvCountsSizes, uint32_t const* const* uvIndices, size_t const* uvIndicesSizes, - size_t uvSetsCount, const uint32_t* faceRanges, size_t faceRangesSize, - const prt::AttributeMap** materials, const prt::AttributeMap** reports, const int32_t*) { - MFloatPointArray mayaVertices = toMayaFloatPointArray(vtx, vtxSize); - MIntArray mayaFaceCounts = toMayaIntArray(faceCounts, faceCountsSize); - MIntArray mayaVertexIndices = toMayaIntArray(vertexIndices, vertexIndicesSize); - - if (DBG) { - LOG_DBG << "-- MayaCallbacks::addMesh"; - LOG_DBG << " faceCountsSize = " << faceCountsSize; - LOG_DBG << " vertexIndicesSize = " << vertexIndicesSize; - LOG_DBG << " mayaVertices.length = " << mayaVertices.length(); - LOG_DBG << " mayaFaceCounts.length = " << mayaFaceCounts.length(); - LOG_DBG << " mayaVertexIndices.length = " << mayaVertexIndices.length(); - } +constexpr unsigned int maxStringLength = 400; +constexpr unsigned int maxFloatArrayLength = 5; +constexpr unsigned int maxStringArrayLength = 2; - MStatus stat; - - MFnMeshData dataCreator; - MObject newOutputData = dataCreator.create(&stat); - MCHECK(stat); - - MFnMesh mFnMesh1; - MObject oMesh = mFnMesh1.create(mayaVertices.length(), mayaFaceCounts.length(), mayaVertices, mayaFaceCounts, - mayaVertexIndices, newOutputData, &stat); - MCHECK(stat); - - MFnMesh mFnMesh(oMesh); - assignTextureCoordinates(mFnMesh, uvs, uvsSizes, uvCounts, uvCountsSizes, uvIndices, uvIndicesSizes, uvSetsCount); - assignVertexNormals(mFnMesh, mayaFaceCounts, mayaVertexIndices, nrm, nrmSize, normalIndices, normalIndicesSize); - - MFnMesh outputMesh(outMeshObj); - outputMesh.copyInPlace(oMesh); - - // create material metadata - constexpr unsigned int maxStringLength = 400; - constexpr unsigned int maxFloatArrayLength = 5; - constexpr unsigned int maxStringArrayLength = 2; - - adsk::Data::Structure* fStructure; // Structure to use for creation - fStructure = adsk::Data::Structure::structureByName(PRT_MATERIAL_STRUCTURE.c_str()); +adsk::Data::Structure* getMaterialDataStructure(const prt::AttributeMap** materials, size_t faceRangesSize) { + adsk::Data::Structure* fStructure = adsk::Data::Structure::structureByName(PRT_MATERIAL_STRUCTURE.c_str()); if ((fStructure == nullptr) && (materials != nullptr) && (faceRangesSize > 1)) { const prt::AttributeMap* mat = materials[0]; @@ -236,7 +195,7 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c case prt::Attributable::PT_FLOAT: type = adsk::Data::Member::kDouble; size = 1; break; case prt::Attributable::PT_INT: type = adsk::Data::Member::kInt32; size = 1; break; - //workaround: using kString type crashes maya when setting metadata elememts. Therefore we use array of kUInt8 + // workaround: using kString type crashes maya when setting metadata elements. Therefore we use array of kUInt8 case prt::Attributable::PT_STRING: type = adsk::Data::Member::kUInt8; size = maxStringLength; break; case prt::Attributable::PT_BOOL_ARRAY: type = adsk::Data::Member::kBoolean; size = maxStringLength; break; case prt::Attributable::PT_INT_ARRAY: type = adsk::Data::Member::kInt32; size = maxStringLength; break; @@ -264,6 +223,49 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c adsk::Data::Structure::registerStructure(*fStructure); } + return fStructure; +} + +} // namespace + +void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, const double* nrm, size_t nrmSize, + const uint32_t* faceCounts, size_t faceCountsSize, const uint32_t* vertexIndices, + size_t vertexIndicesSize, const uint32_t* normalIndices, size_t normalIndicesSize, + double const* const* uvs, size_t const* uvsSizes, uint32_t const* const* uvCounts, + size_t const* uvCountsSizes, uint32_t const* const* uvIndices, size_t const* uvIndicesSizes, + size_t uvSetsCount, const uint32_t* faceRanges, size_t faceRangesSize, + const prt::AttributeMap** materials, const prt::AttributeMap** reports, const int32_t*) { + MFloatPointArray mayaVertices = toMayaFloatPointArray(vtx, vtxSize); + MIntArray mayaFaceCounts = toMayaIntArray(faceCounts, faceCountsSize); + MIntArray mayaVertexIndices = toMayaIntArray(vertexIndices, vertexIndicesSize); + + if (DBG) { + LOG_DBG << "-- MayaCallbacks::addMesh"; + LOG_DBG << " faceCountsSize = " << faceCountsSize; + LOG_DBG << " vertexIndicesSize = " << vertexIndicesSize; + LOG_DBG << " mayaVertices.length = " << mayaVertices.length(); + LOG_DBG << " mayaFaceCounts.length = " << mayaFaceCounts.length(); + LOG_DBG << " mayaVertexIndices.length = " << mayaVertexIndices.length(); + } + + MStatus stat; + + MFnMeshData dataCreator; + MObject newOutputData = dataCreator.create(&stat); + MCHECK(stat); + + MFnMesh mFnMesh1; + MObject oMesh = mFnMesh1.create(mayaVertices.length(), mayaFaceCounts.length(), mayaVertices, mayaFaceCounts, + mayaVertexIndices, newOutputData, &stat); + MCHECK(stat); + + MFnMesh mFnMesh(oMesh); + assignTextureCoordinates(mFnMesh, uvs, uvsSizes, uvCounts, uvCountsSizes, uvIndices, uvIndicesSizes, uvSetsCount); + assignVertexNormals(mFnMesh, mayaFaceCounts, mayaVertexIndices, nrm, nrmSize, normalIndices, normalIndicesSize); + + MFnMesh outputMesh(outMeshObj); + outputMesh.copyInPlace(oMesh); + MCHECK(stat); MFnMesh inputMesh(inMeshObj); @@ -271,6 +273,7 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c newMetadata.makeUnique(); MCHECK(stat); adsk::Data::Channel newChannel = newMetadata.channel(PRT_MATERIAL_CHANNEL); + const adsk::Data::Structure* fStructure = getMaterialDataStructure(materials, faceRangesSize); adsk::Data::Stream newStream(*fStructure, PRT_MATERIAL_STREAM); newChannel.setDataStream(newStream); From 68c5939c0ceec6cc535d93c099f46821fbd52155 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Wed, 24 Mar 2021 17:24:41 +0100 Subject: [PATCH 032/668] Cleanup: rename constants according to conventions --- src/serlio/modifiers/MayaCallbacks.cpp | 32 +++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 929be41b..bcc96563 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -164,9 +164,9 @@ void assignVertexNormals(MFnMesh& mFnMesh, MIntArray& mayaFaceCounts, MIntArray& MCHECK(mFnMesh.setFaceVertexNormals(expandedNormals, faceList, mayaVertexIndices)); } -constexpr unsigned int maxStringLength = 400; -constexpr unsigned int maxFloatArrayLength = 5; -constexpr unsigned int maxStringArrayLength = 2; +constexpr unsigned int MATERIAL_MAX_STRING_LENGTH = 400; +constexpr unsigned int MATERIAL_MAX_FLOAT_ARRAY_LENGTH = 5; +constexpr unsigned int MATERIAL_MAX_STRING_ARRAY_LENGTH = 2; adsk::Data::Structure* getMaterialDataStructure(const prt::AttributeMap** materials, size_t faceRangesSize) { adsk::Data::Structure* fStructure = adsk::Data::Structure::structureByName(PRT_MATERIAL_STRUCTURE.c_str()); @@ -196,11 +196,11 @@ adsk::Data::Structure* getMaterialDataStructure(const prt::AttributeMap** materi case prt::Attributable::PT_INT: type = adsk::Data::Member::kInt32; size = 1; break; // workaround: using kString type crashes maya when setting metadata elements. Therefore we use array of kUInt8 - case prt::Attributable::PT_STRING: type = adsk::Data::Member::kUInt8; size = maxStringLength; break; - case prt::Attributable::PT_BOOL_ARRAY: type = adsk::Data::Member::kBoolean; size = maxStringLength; break; - case prt::Attributable::PT_INT_ARRAY: type = adsk::Data::Member::kInt32; size = maxStringLength; break; - case prt::Attributable::PT_FLOAT_ARRAY: type = adsk::Data::Member::kDouble; size = maxFloatArrayLength; break; - case prt::Attributable::PT_STRING_ARRAY: type = adsk::Data::Member::kUInt8; size = maxStringLength; arrayLength = maxStringArrayLength; break; + case prt::Attributable::PT_STRING: type = adsk::Data::Member::kUInt8; size = MATERIAL_MAX_STRING_LENGTH; break; + case prt::Attributable::PT_BOOL_ARRAY: type = adsk::Data::Member::kBoolean; size = MATERIAL_MAX_STRING_LENGTH; break; + case prt::Attributable::PT_INT_ARRAY: type = adsk::Data::Member::kInt32; size = MATERIAL_MAX_STRING_LENGTH; break; + case prt::Attributable::PT_FLOAT_ARRAY: type = adsk::Data::Member::kDouble; size = MATERIAL_MAX_FLOAT_ARRAY_LENGTH; break; + case prt::Attributable::PT_STRING_ARRAY: type = adsk::Data::Member::kUInt8; size = MATERIAL_MAX_STRING_LENGTH; arrayLength = MATERIAL_MAX_STRING_ARRAY_LENGTH; break; case prt::Attributable::PT_UNDEFINED: break; case prt::Attributable::PT_BLIND_DATA: break; @@ -318,29 +318,29 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c const wchar_t* str = mat->getString(key); if (wcslen(str) == 0) break; - checkStringLength(str, maxStringLength); - size_t maxStringLengthTmp = maxStringLength; + checkStringLength(str, MATERIAL_MAX_STRING_LENGTH); + size_t maxStringLengthTmp = MATERIAL_MAX_STRING_LENGTH; prt::StringUtils::toOSNarrowFromUTF16(str, (char*)handle.asUInt8(), &maxStringLengthTmp); break; } case prt::Attributable::PT_BOOL_ARRAY: { const bool* boolArray; boolArray = mat->getBoolArray(key, &arraySize); - for (unsigned int i = 0; i < arraySize && i < maxStringLength; i++) + for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) handle.asBoolean()[i] = boolArray[i]; break; } case prt::Attributable::PT_INT_ARRAY: { const int* intArray; intArray = mat->getIntArray(key, &arraySize); - for (unsigned int i = 0; i < arraySize && i < maxStringLength; i++) + for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) handle.asInt32()[i] = intArray[i]; break; } case prt::Attributable::PT_FLOAT_ARRAY: { const double* floatArray; floatArray = mat->getFloatArray(key, &arraySize); - for (unsigned int i = 0; i < arraySize && i < maxStringLength && i < maxFloatArrayLength; + for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH && i < MATERIAL_MAX_FLOAT_ARRAY_LENGTH; i++) handle.asDouble()[i] = floatArray[i]; break; @@ -349,7 +349,7 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c const wchar_t* const* stringArray = mat->getStringArray(key, &arraySize); - for (unsigned int i = 0; i < arraySize && i < maxStringLength; i++) { + for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) { if (wcslen(stringArray[i]) == 0) continue; @@ -360,8 +360,8 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c continue; } - checkStringLength(stringArray[i], maxStringLength); - size_t maxStringLengthTmp = maxStringLength; + checkStringLength(stringArray[i], MATERIAL_MAX_STRING_LENGTH); + size_t maxStringLengthTmp = MATERIAL_MAX_STRING_LENGTH; prt::StringUtils::toOSNarrowFromUTF16(stringArray[i], (char*)handle.asUInt8(), &maxStringLengthTmp); } From ba83869e25199f4f0154a4fddaba3299e169e6f6 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Wed, 24 Mar 2021 17:49:45 +0100 Subject: [PATCH 033/668] Cleanup: factoring out helper function to store material attributes on node --- src/serlio/modifiers/MayaCallbacks.cpp | 106 ++++++++++++------------- 1 file changed, 51 insertions(+), 55 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index bcc96563..e07d8d48 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -226,52 +226,14 @@ adsk::Data::Structure* getMaterialDataStructure(const prt::AttributeMap** materi return fStructure; } -} // namespace - -void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, const double* nrm, size_t nrmSize, - const uint32_t* faceCounts, size_t faceCountsSize, const uint32_t* vertexIndices, - size_t vertexIndicesSize, const uint32_t* normalIndices, size_t normalIndicesSize, - double const* const* uvs, size_t const* uvsSizes, uint32_t const* const* uvCounts, - size_t const* uvCountsSizes, uint32_t const* const* uvIndices, size_t const* uvIndicesSizes, - size_t uvSetsCount, const uint32_t* faceRanges, size_t faceRangesSize, - const prt::AttributeMap** materials, const prt::AttributeMap** reports, const int32_t*) { - MFloatPointArray mayaVertices = toMayaFloatPointArray(vtx, vtxSize); - MIntArray mayaFaceCounts = toMayaIntArray(faceCounts, faceCountsSize); - MIntArray mayaVertexIndices = toMayaIntArray(vertexIndices, vertexIndicesSize); - - if (DBG) { - LOG_DBG << "-- MayaCallbacks::addMesh"; - LOG_DBG << " faceCountsSize = " << faceCountsSize; - LOG_DBG << " vertexIndicesSize = " << vertexIndicesSize; - LOG_DBG << " mayaVertices.length = " << mayaVertices.length(); - LOG_DBG << " mayaFaceCounts.length = " << mayaFaceCounts.length(); - LOG_DBG << " mayaVertexIndices.length = " << mayaVertexIndices.length(); - } - +void fillMaterialDataStructure(MFnMesh& inputMesh, MFnMesh& outputMesh, const prt::AttributeMap** materials, + const uint32_t* faceRanges, size_t faceRangesSize) { MStatus stat; - - MFnMeshData dataCreator; - MObject newOutputData = dataCreator.create(&stat); - MCHECK(stat); - - MFnMesh mFnMesh1; - MObject oMesh = mFnMesh1.create(mayaVertices.length(), mayaFaceCounts.length(), mayaVertices, mayaFaceCounts, - mayaVertexIndices, newOutputData, &stat); - MCHECK(stat); - - MFnMesh mFnMesh(oMesh); - assignTextureCoordinates(mFnMesh, uvs, uvsSizes, uvCounts, uvCountsSizes, uvIndices, uvIndicesSizes, uvSetsCount); - assignVertexNormals(mFnMesh, mayaFaceCounts, mayaVertexIndices, nrm, nrmSize, normalIndices, normalIndicesSize); - - MFnMesh outputMesh(outMeshObj); - outputMesh.copyInPlace(oMesh); - + adsk::Data::Associations newMetadata(inputMesh.metadata(&stat)); MCHECK(stat); - MFnMesh inputMesh(inMeshObj); - adsk::Data::Associations newMetadata(inputMesh.metadata(&stat)); newMetadata.makeUnique(); - MCHECK(stat); + adsk::Data::Channel newChannel = newMetadata.channel(PRT_MATERIAL_CHANNEL); const adsk::Data::Structure* fStructure = getMaterialDataStructure(materials, faceRangesSize); adsk::Data::Stream newStream(*fStructure, PRT_MATERIAL_STREAM); @@ -280,21 +242,15 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c newMetadata.setChannel(newChannel); if (faceRangesSize > 1) { - + adsk::Data::Handle handle(*fStructure); for (size_t fri = 0; fri < faceRangesSize - 1; fri++) { - if (materials != nullptr) { - adsk::Data::Handle handle(*fStructure); - const prt::AttributeMap* mat = materials[fri]; size_t keyCount = 0; wchar_t const* const* keys = mat->getKeys(&keyCount); - for (int k = 0; k < keyCount; k++) { - wchar_t const* key = keys[k]; - const std::string keyNarrow = prtu::toOSNarrowFromUTF16(key); if (!handle.setPositionByMemberName(keyNarrow.c_str())) @@ -313,13 +269,13 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c handle.asInt32()[0] = mat->getInt(key); break; - // workaround: transporting string as uint8 array, because using asString crashes maya case prt::Attributable::PT_STRING: { const wchar_t* str = mat->getString(key); if (wcslen(str) == 0) break; checkStringLength(str, MATERIAL_MAX_STRING_LENGTH); size_t maxStringLengthTmp = MATERIAL_MAX_STRING_LENGTH; + // workaround: transporting string as uint8 array, because using asString crashes maya prt::StringUtils::toOSNarrowFromUTF16(str, (char*)handle.asUInt8(), &maxStringLengthTmp); break; } @@ -340,7 +296,8 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c case prt::Attributable::PT_FLOAT_ARRAY: { const double* floatArray; floatArray = mat->getFloatArray(key, &arraySize); - for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH && i < MATERIAL_MAX_FLOAT_ARRAY_LENGTH; + for (unsigned int i = 0; + i < arraySize && i < MATERIAL_MAX_STRING_LENGTH && i < MATERIAL_MAX_FLOAT_ARRAY_LENGTH; i++) handle.asDouble()[i] = floatArray[i]; break; @@ -387,16 +344,55 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c newStream.setElement(static_cast(fri), handle); } - - if (reports != nullptr) { - // todo - } } } outputMesh.setMetadata(newMetadata); } +} // namespace + +void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, const double* nrm, size_t nrmSize, + const uint32_t* faceCounts, size_t faceCountsSize, const uint32_t* vertexIndices, + size_t vertexIndicesSize, const uint32_t* normalIndices, size_t normalIndicesSize, + double const* const* uvs, size_t const* uvsSizes, uint32_t const* const* uvCounts, + size_t const* uvCountsSizes, uint32_t const* const* uvIndices, size_t const* uvIndicesSizes, + size_t uvSetsCount, const uint32_t* faceRanges, size_t faceRangesSize, + const prt::AttributeMap** materials, const prt::AttributeMap**, const int32_t*) { + MFloatPointArray mayaVertices = toMayaFloatPointArray(vtx, vtxSize); + MIntArray mayaFaceCounts = toMayaIntArray(faceCounts, faceCountsSize); + MIntArray mayaVertexIndices = toMayaIntArray(vertexIndices, vertexIndicesSize); + + if (DBG) { + LOG_DBG << "-- MayaCallbacks::addMesh"; + LOG_DBG << " faceCountsSize = " << faceCountsSize; + LOG_DBG << " vertexIndicesSize = " << vertexIndicesSize; + LOG_DBG << " mayaVertices.length = " << mayaVertices.length(); + LOG_DBG << " mayaFaceCounts.length = " << mayaFaceCounts.length(); + LOG_DBG << " mayaVertexIndices.length = " << mayaVertexIndices.length(); + } + + MStatus stat; + + MFnMeshData dataCreator; + MObject newOutputData = dataCreator.create(&stat); + MCHECK(stat); + + MObject oMesh = MFnMesh().create(mayaVertices.length(), mayaFaceCounts.length(), mayaVertices, mayaFaceCounts, + mayaVertexIndices, newOutputData, &stat); + MCHECK(stat); + + MFnMesh mFnMesh(oMesh); + assignTextureCoordinates(mFnMesh, uvs, uvsSizes, uvCounts, uvCountsSizes, uvIndices, uvIndicesSizes, uvSetsCount); + assignVertexNormals(mFnMesh, mayaFaceCounts, mayaVertexIndices, nrm, nrmSize, normalIndices, normalIndicesSize); + + MFnMesh inputMesh(inMeshObj); + MFnMesh outputMesh(outMeshObj); + outputMesh.copyInPlace(oMesh); + + fillMaterialDataStructure(inputMesh, outputMesh, materials, faceRanges, faceRangesSize); +} + prt::Status MayaCallbacks::attrBool(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* key, bool value) { mAttributeMapBuilder->setBool(key, value); return prt::STATUS_OK; From ae7da782017a981b3e9fc4fc10481870eca5b5e5 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Wed, 24 Mar 2021 17:50:27 +0100 Subject: [PATCH 034/668] Cleanup: naming --- src/serlio/modifiers/MayaCallbacks.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index e07d8d48..a98fb966 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -382,9 +382,9 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c mayaVertexIndices, newOutputData, &stat); MCHECK(stat); - MFnMesh mFnMesh(oMesh); - assignTextureCoordinates(mFnMesh, uvs, uvsSizes, uvCounts, uvCountsSizes, uvIndices, uvIndicesSizes, uvSetsCount); - assignVertexNormals(mFnMesh, mayaFaceCounts, mayaVertexIndices, nrm, nrmSize, normalIndices, normalIndicesSize); + MFnMesh fnMesh(oMesh); + assignTextureCoordinates(fnMesh, uvs, uvsSizes, uvCounts, uvCountsSizes, uvIndices, uvIndicesSizes, uvSetsCount); + assignVertexNormals(fnMesh, mayaFaceCounts, mayaVertexIndices, nrm, nrmSize, normalIndices, normalIndicesSize); MFnMesh inputMesh(inMeshObj); MFnMesh outputMesh(outMeshObj); From 0ae7d79679fd99ca4c3e819ed0a100d7491d78a5 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 3 May 2021 13:01:29 +0200 Subject: [PATCH 035/668] Revert "Cleanup: naming" This reverts commit ae7da782 --- src/serlio/modifiers/MayaCallbacks.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index a98fb966..e07d8d48 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -382,9 +382,9 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c mayaVertexIndices, newOutputData, &stat); MCHECK(stat); - MFnMesh fnMesh(oMesh); - assignTextureCoordinates(fnMesh, uvs, uvsSizes, uvCounts, uvCountsSizes, uvIndices, uvIndicesSizes, uvSetsCount); - assignVertexNormals(fnMesh, mayaFaceCounts, mayaVertexIndices, nrm, nrmSize, normalIndices, normalIndicesSize); + MFnMesh mFnMesh(oMesh); + assignTextureCoordinates(mFnMesh, uvs, uvsSizes, uvCounts, uvCountsSizes, uvIndices, uvIndicesSizes, uvSetsCount); + assignVertexNormals(mFnMesh, mayaFaceCounts, mayaVertexIndices, nrm, nrmSize, normalIndices, normalIndicesSize); MFnMesh inputMesh(inMeshObj); MFnMesh outputMesh(outMeshObj); From 472acd71f0c58e435d01df0417b6b51e34cfc0ed Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 3 May 2021 13:01:37 +0200 Subject: [PATCH 036/668] Revert "Cleanup: factoring out helper function to store material attributes on node" This reverts commit ba83869e --- src/serlio/modifiers/MayaCallbacks.cpp | 106 +++++++++++++------------ 1 file changed, 55 insertions(+), 51 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index e07d8d48..bcc96563 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -226,14 +226,52 @@ adsk::Data::Structure* getMaterialDataStructure(const prt::AttributeMap** materi return fStructure; } -void fillMaterialDataStructure(MFnMesh& inputMesh, MFnMesh& outputMesh, const prt::AttributeMap** materials, - const uint32_t* faceRanges, size_t faceRangesSize) { +} // namespace + +void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, const double* nrm, size_t nrmSize, + const uint32_t* faceCounts, size_t faceCountsSize, const uint32_t* vertexIndices, + size_t vertexIndicesSize, const uint32_t* normalIndices, size_t normalIndicesSize, + double const* const* uvs, size_t const* uvsSizes, uint32_t const* const* uvCounts, + size_t const* uvCountsSizes, uint32_t const* const* uvIndices, size_t const* uvIndicesSizes, + size_t uvSetsCount, const uint32_t* faceRanges, size_t faceRangesSize, + const prt::AttributeMap** materials, const prt::AttributeMap** reports, const int32_t*) { + MFloatPointArray mayaVertices = toMayaFloatPointArray(vtx, vtxSize); + MIntArray mayaFaceCounts = toMayaIntArray(faceCounts, faceCountsSize); + MIntArray mayaVertexIndices = toMayaIntArray(vertexIndices, vertexIndicesSize); + + if (DBG) { + LOG_DBG << "-- MayaCallbacks::addMesh"; + LOG_DBG << " faceCountsSize = " << faceCountsSize; + LOG_DBG << " vertexIndicesSize = " << vertexIndicesSize; + LOG_DBG << " mayaVertices.length = " << mayaVertices.length(); + LOG_DBG << " mayaFaceCounts.length = " << mayaFaceCounts.length(); + LOG_DBG << " mayaVertexIndices.length = " << mayaVertexIndices.length(); + } + MStatus stat; - adsk::Data::Associations newMetadata(inputMesh.metadata(&stat)); + + MFnMeshData dataCreator; + MObject newOutputData = dataCreator.create(&stat); MCHECK(stat); - newMetadata.makeUnique(); + MFnMesh mFnMesh1; + MObject oMesh = mFnMesh1.create(mayaVertices.length(), mayaFaceCounts.length(), mayaVertices, mayaFaceCounts, + mayaVertexIndices, newOutputData, &stat); + MCHECK(stat); + + MFnMesh mFnMesh(oMesh); + assignTextureCoordinates(mFnMesh, uvs, uvsSizes, uvCounts, uvCountsSizes, uvIndices, uvIndicesSizes, uvSetsCount); + assignVertexNormals(mFnMesh, mayaFaceCounts, mayaVertexIndices, nrm, nrmSize, normalIndices, normalIndicesSize); + + MFnMesh outputMesh(outMeshObj); + outputMesh.copyInPlace(oMesh); + MCHECK(stat); + MFnMesh inputMesh(inMeshObj); + + adsk::Data::Associations newMetadata(inputMesh.metadata(&stat)); + newMetadata.makeUnique(); + MCHECK(stat); adsk::Data::Channel newChannel = newMetadata.channel(PRT_MATERIAL_CHANNEL); const adsk::Data::Structure* fStructure = getMaterialDataStructure(materials, faceRangesSize); adsk::Data::Stream newStream(*fStructure, PRT_MATERIAL_STREAM); @@ -242,15 +280,21 @@ void fillMaterialDataStructure(MFnMesh& inputMesh, MFnMesh& outputMesh, const pr newMetadata.setChannel(newChannel); if (faceRangesSize > 1) { - adsk::Data::Handle handle(*fStructure); + for (size_t fri = 0; fri < faceRangesSize - 1; fri++) { + if (materials != nullptr) { + adsk::Data::Handle handle(*fStructure); + const prt::AttributeMap* mat = materials[fri]; size_t keyCount = 0; wchar_t const* const* keys = mat->getKeys(&keyCount); + for (int k = 0; k < keyCount; k++) { + wchar_t const* key = keys[k]; + const std::string keyNarrow = prtu::toOSNarrowFromUTF16(key); if (!handle.setPositionByMemberName(keyNarrow.c_str())) @@ -269,13 +313,13 @@ void fillMaterialDataStructure(MFnMesh& inputMesh, MFnMesh& outputMesh, const pr handle.asInt32()[0] = mat->getInt(key); break; + // workaround: transporting string as uint8 array, because using asString crashes maya case prt::Attributable::PT_STRING: { const wchar_t* str = mat->getString(key); if (wcslen(str) == 0) break; checkStringLength(str, MATERIAL_MAX_STRING_LENGTH); size_t maxStringLengthTmp = MATERIAL_MAX_STRING_LENGTH; - // workaround: transporting string as uint8 array, because using asString crashes maya prt::StringUtils::toOSNarrowFromUTF16(str, (char*)handle.asUInt8(), &maxStringLengthTmp); break; } @@ -296,8 +340,7 @@ void fillMaterialDataStructure(MFnMesh& inputMesh, MFnMesh& outputMesh, const pr case prt::Attributable::PT_FLOAT_ARRAY: { const double* floatArray; floatArray = mat->getFloatArray(key, &arraySize); - for (unsigned int i = 0; - i < arraySize && i < MATERIAL_MAX_STRING_LENGTH && i < MATERIAL_MAX_FLOAT_ARRAY_LENGTH; + for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH && i < MATERIAL_MAX_FLOAT_ARRAY_LENGTH; i++) handle.asDouble()[i] = floatArray[i]; break; @@ -344,55 +387,16 @@ void fillMaterialDataStructure(MFnMesh& inputMesh, MFnMesh& outputMesh, const pr newStream.setElement(static_cast(fri), handle); } + + if (reports != nullptr) { + // todo + } } } outputMesh.setMetadata(newMetadata); } -} // namespace - -void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, const double* nrm, size_t nrmSize, - const uint32_t* faceCounts, size_t faceCountsSize, const uint32_t* vertexIndices, - size_t vertexIndicesSize, const uint32_t* normalIndices, size_t normalIndicesSize, - double const* const* uvs, size_t const* uvsSizes, uint32_t const* const* uvCounts, - size_t const* uvCountsSizes, uint32_t const* const* uvIndices, size_t const* uvIndicesSizes, - size_t uvSetsCount, const uint32_t* faceRanges, size_t faceRangesSize, - const prt::AttributeMap** materials, const prt::AttributeMap**, const int32_t*) { - MFloatPointArray mayaVertices = toMayaFloatPointArray(vtx, vtxSize); - MIntArray mayaFaceCounts = toMayaIntArray(faceCounts, faceCountsSize); - MIntArray mayaVertexIndices = toMayaIntArray(vertexIndices, vertexIndicesSize); - - if (DBG) { - LOG_DBG << "-- MayaCallbacks::addMesh"; - LOG_DBG << " faceCountsSize = " << faceCountsSize; - LOG_DBG << " vertexIndicesSize = " << vertexIndicesSize; - LOG_DBG << " mayaVertices.length = " << mayaVertices.length(); - LOG_DBG << " mayaFaceCounts.length = " << mayaFaceCounts.length(); - LOG_DBG << " mayaVertexIndices.length = " << mayaVertexIndices.length(); - } - - MStatus stat; - - MFnMeshData dataCreator; - MObject newOutputData = dataCreator.create(&stat); - MCHECK(stat); - - MObject oMesh = MFnMesh().create(mayaVertices.length(), mayaFaceCounts.length(), mayaVertices, mayaFaceCounts, - mayaVertexIndices, newOutputData, &stat); - MCHECK(stat); - - MFnMesh mFnMesh(oMesh); - assignTextureCoordinates(mFnMesh, uvs, uvsSizes, uvCounts, uvCountsSizes, uvIndices, uvIndicesSizes, uvSetsCount); - assignVertexNormals(mFnMesh, mayaFaceCounts, mayaVertexIndices, nrm, nrmSize, normalIndices, normalIndicesSize); - - MFnMesh inputMesh(inMeshObj); - MFnMesh outputMesh(outMeshObj); - outputMesh.copyInPlace(oMesh); - - fillMaterialDataStructure(inputMesh, outputMesh, materials, faceRanges, faceRangesSize); -} - prt::Status MayaCallbacks::attrBool(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* key, bool value) { mAttributeMapBuilder->setBool(key, value); return prt::STATUS_OK; From 9a192ef274c3a3406184b9e0f42a1212a92de438 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 3 May 2021 13:01:42 +0200 Subject: [PATCH 037/668] Revert "Cleanup: rename constants according to conventions" This reverts commit 68c5939c --- src/serlio/modifiers/MayaCallbacks.cpp | 32 +++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index bcc96563..929be41b 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -164,9 +164,9 @@ void assignVertexNormals(MFnMesh& mFnMesh, MIntArray& mayaFaceCounts, MIntArray& MCHECK(mFnMesh.setFaceVertexNormals(expandedNormals, faceList, mayaVertexIndices)); } -constexpr unsigned int MATERIAL_MAX_STRING_LENGTH = 400; -constexpr unsigned int MATERIAL_MAX_FLOAT_ARRAY_LENGTH = 5; -constexpr unsigned int MATERIAL_MAX_STRING_ARRAY_LENGTH = 2; +constexpr unsigned int maxStringLength = 400; +constexpr unsigned int maxFloatArrayLength = 5; +constexpr unsigned int maxStringArrayLength = 2; adsk::Data::Structure* getMaterialDataStructure(const prt::AttributeMap** materials, size_t faceRangesSize) { adsk::Data::Structure* fStructure = adsk::Data::Structure::structureByName(PRT_MATERIAL_STRUCTURE.c_str()); @@ -196,11 +196,11 @@ adsk::Data::Structure* getMaterialDataStructure(const prt::AttributeMap** materi case prt::Attributable::PT_INT: type = adsk::Data::Member::kInt32; size = 1; break; // workaround: using kString type crashes maya when setting metadata elements. Therefore we use array of kUInt8 - case prt::Attributable::PT_STRING: type = adsk::Data::Member::kUInt8; size = MATERIAL_MAX_STRING_LENGTH; break; - case prt::Attributable::PT_BOOL_ARRAY: type = adsk::Data::Member::kBoolean; size = MATERIAL_MAX_STRING_LENGTH; break; - case prt::Attributable::PT_INT_ARRAY: type = adsk::Data::Member::kInt32; size = MATERIAL_MAX_STRING_LENGTH; break; - case prt::Attributable::PT_FLOAT_ARRAY: type = adsk::Data::Member::kDouble; size = MATERIAL_MAX_FLOAT_ARRAY_LENGTH; break; - case prt::Attributable::PT_STRING_ARRAY: type = adsk::Data::Member::kUInt8; size = MATERIAL_MAX_STRING_LENGTH; arrayLength = MATERIAL_MAX_STRING_ARRAY_LENGTH; break; + case prt::Attributable::PT_STRING: type = adsk::Data::Member::kUInt8; size = maxStringLength; break; + case prt::Attributable::PT_BOOL_ARRAY: type = adsk::Data::Member::kBoolean; size = maxStringLength; break; + case prt::Attributable::PT_INT_ARRAY: type = adsk::Data::Member::kInt32; size = maxStringLength; break; + case prt::Attributable::PT_FLOAT_ARRAY: type = adsk::Data::Member::kDouble; size = maxFloatArrayLength; break; + case prt::Attributable::PT_STRING_ARRAY: type = adsk::Data::Member::kUInt8; size = maxStringLength; arrayLength = maxStringArrayLength; break; case prt::Attributable::PT_UNDEFINED: break; case prt::Attributable::PT_BLIND_DATA: break; @@ -318,29 +318,29 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c const wchar_t* str = mat->getString(key); if (wcslen(str) == 0) break; - checkStringLength(str, MATERIAL_MAX_STRING_LENGTH); - size_t maxStringLengthTmp = MATERIAL_MAX_STRING_LENGTH; + checkStringLength(str, maxStringLength); + size_t maxStringLengthTmp = maxStringLength; prt::StringUtils::toOSNarrowFromUTF16(str, (char*)handle.asUInt8(), &maxStringLengthTmp); break; } case prt::Attributable::PT_BOOL_ARRAY: { const bool* boolArray; boolArray = mat->getBoolArray(key, &arraySize); - for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) + for (unsigned int i = 0; i < arraySize && i < maxStringLength; i++) handle.asBoolean()[i] = boolArray[i]; break; } case prt::Attributable::PT_INT_ARRAY: { const int* intArray; intArray = mat->getIntArray(key, &arraySize); - for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) + for (unsigned int i = 0; i < arraySize && i < maxStringLength; i++) handle.asInt32()[i] = intArray[i]; break; } case prt::Attributable::PT_FLOAT_ARRAY: { const double* floatArray; floatArray = mat->getFloatArray(key, &arraySize); - for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH && i < MATERIAL_MAX_FLOAT_ARRAY_LENGTH; + for (unsigned int i = 0; i < arraySize && i < maxStringLength && i < maxFloatArrayLength; i++) handle.asDouble()[i] = floatArray[i]; break; @@ -349,7 +349,7 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c const wchar_t* const* stringArray = mat->getStringArray(key, &arraySize); - for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) { + for (unsigned int i = 0; i < arraySize && i < maxStringLength; i++) { if (wcslen(stringArray[i]) == 0) continue; @@ -360,8 +360,8 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c continue; } - checkStringLength(stringArray[i], MATERIAL_MAX_STRING_LENGTH); - size_t maxStringLengthTmp = MATERIAL_MAX_STRING_LENGTH; + checkStringLength(stringArray[i], maxStringLength); + size_t maxStringLengthTmp = maxStringLength; prt::StringUtils::toOSNarrowFromUTF16(stringArray[i], (char*)handle.asUInt8(), &maxStringLengthTmp); } From 371658aae634a9d9d9394a5c5b49e09250c560fa Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 3 May 2021 13:01:47 +0200 Subject: [PATCH 038/668] Revert "Cleanup: factor out helper function to create data structure for materials." This reverts commit 5a78deb9 --- src/serlio/modifiers/MayaCallbacks.cpp | 97 +++++++++++++------------- 1 file changed, 47 insertions(+), 50 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 929be41b..86bd7de5 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -164,12 +164,53 @@ void assignVertexNormals(MFnMesh& mFnMesh, MIntArray& mayaFaceCounts, MIntArray& MCHECK(mFnMesh.setFaceVertexNormals(expandedNormals, faceList, mayaVertexIndices)); } -constexpr unsigned int maxStringLength = 400; -constexpr unsigned int maxFloatArrayLength = 5; -constexpr unsigned int maxStringArrayLength = 2; +} // namespace + +void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, const double* nrm, size_t nrmSize, + const uint32_t* faceCounts, size_t faceCountsSize, const uint32_t* vertexIndices, + size_t vertexIndicesSize, const uint32_t* normalIndices, size_t normalIndicesSize, + double const* const* uvs, size_t const* uvsSizes, uint32_t const* const* uvCounts, + size_t const* uvCountsSizes, uint32_t const* const* uvIndices, size_t const* uvIndicesSizes, + size_t uvSetsCount, const uint32_t* faceRanges, size_t faceRangesSize, + const prt::AttributeMap** materials, const prt::AttributeMap** reports, const int32_t*) { + MFloatPointArray mayaVertices = toMayaFloatPointArray(vtx, vtxSize); + MIntArray mayaFaceCounts = toMayaIntArray(faceCounts, faceCountsSize); + MIntArray mayaVertexIndices = toMayaIntArray(vertexIndices, vertexIndicesSize); + + if (DBG) { + LOG_DBG << "-- MayaCallbacks::addMesh"; + LOG_DBG << " faceCountsSize = " << faceCountsSize; + LOG_DBG << " vertexIndicesSize = " << vertexIndicesSize; + LOG_DBG << " mayaVertices.length = " << mayaVertices.length(); + LOG_DBG << " mayaFaceCounts.length = " << mayaFaceCounts.length(); + LOG_DBG << " mayaVertexIndices.length = " << mayaVertexIndices.length(); + } -adsk::Data::Structure* getMaterialDataStructure(const prt::AttributeMap** materials, size_t faceRangesSize) { - adsk::Data::Structure* fStructure = adsk::Data::Structure::structureByName(PRT_MATERIAL_STRUCTURE.c_str()); + MStatus stat; + + MFnMeshData dataCreator; + MObject newOutputData = dataCreator.create(&stat); + MCHECK(stat); + + MFnMesh mFnMesh1; + MObject oMesh = mFnMesh1.create(mayaVertices.length(), mayaFaceCounts.length(), mayaVertices, mayaFaceCounts, + mayaVertexIndices, newOutputData, &stat); + MCHECK(stat); + + MFnMesh mFnMesh(oMesh); + assignTextureCoordinates(mFnMesh, uvs, uvsSizes, uvCounts, uvCountsSizes, uvIndices, uvIndicesSizes, uvSetsCount); + assignVertexNormals(mFnMesh, mayaFaceCounts, mayaVertexIndices, nrm, nrmSize, normalIndices, normalIndicesSize); + + MFnMesh outputMesh(outMeshObj); + outputMesh.copyInPlace(oMesh); + + // create material metadata + constexpr unsigned int maxStringLength = 400; + constexpr unsigned int maxFloatArrayLength = 5; + constexpr unsigned int maxStringArrayLength = 2; + + adsk::Data::Structure* fStructure; // Structure to use for creation + fStructure = adsk::Data::Structure::structureByName(PRT_MATERIAL_STRUCTURE.c_str()); if ((fStructure == nullptr) && (materials != nullptr) && (faceRangesSize > 1)) { const prt::AttributeMap* mat = materials[0]; @@ -195,7 +236,7 @@ adsk::Data::Structure* getMaterialDataStructure(const prt::AttributeMap** materi case prt::Attributable::PT_FLOAT: type = adsk::Data::Member::kDouble; size = 1; break; case prt::Attributable::PT_INT: type = adsk::Data::Member::kInt32; size = 1; break; - // workaround: using kString type crashes maya when setting metadata elements. Therefore we use array of kUInt8 + //workaround: using kString type crashes maya when setting metadata elememts. Therefore we use array of kUInt8 case prt::Attributable::PT_STRING: type = adsk::Data::Member::kUInt8; size = maxStringLength; break; case prt::Attributable::PT_BOOL_ARRAY: type = adsk::Data::Member::kBoolean; size = maxStringLength; break; case prt::Attributable::PT_INT_ARRAY: type = adsk::Data::Member::kInt32; size = maxStringLength; break; @@ -223,49 +264,6 @@ adsk::Data::Structure* getMaterialDataStructure(const prt::AttributeMap** materi adsk::Data::Structure::registerStructure(*fStructure); } - return fStructure; -} - -} // namespace - -void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, const double* nrm, size_t nrmSize, - const uint32_t* faceCounts, size_t faceCountsSize, const uint32_t* vertexIndices, - size_t vertexIndicesSize, const uint32_t* normalIndices, size_t normalIndicesSize, - double const* const* uvs, size_t const* uvsSizes, uint32_t const* const* uvCounts, - size_t const* uvCountsSizes, uint32_t const* const* uvIndices, size_t const* uvIndicesSizes, - size_t uvSetsCount, const uint32_t* faceRanges, size_t faceRangesSize, - const prt::AttributeMap** materials, const prt::AttributeMap** reports, const int32_t*) { - MFloatPointArray mayaVertices = toMayaFloatPointArray(vtx, vtxSize); - MIntArray mayaFaceCounts = toMayaIntArray(faceCounts, faceCountsSize); - MIntArray mayaVertexIndices = toMayaIntArray(vertexIndices, vertexIndicesSize); - - if (DBG) { - LOG_DBG << "-- MayaCallbacks::addMesh"; - LOG_DBG << " faceCountsSize = " << faceCountsSize; - LOG_DBG << " vertexIndicesSize = " << vertexIndicesSize; - LOG_DBG << " mayaVertices.length = " << mayaVertices.length(); - LOG_DBG << " mayaFaceCounts.length = " << mayaFaceCounts.length(); - LOG_DBG << " mayaVertexIndices.length = " << mayaVertexIndices.length(); - } - - MStatus stat; - - MFnMeshData dataCreator; - MObject newOutputData = dataCreator.create(&stat); - MCHECK(stat); - - MFnMesh mFnMesh1; - MObject oMesh = mFnMesh1.create(mayaVertices.length(), mayaFaceCounts.length(), mayaVertices, mayaFaceCounts, - mayaVertexIndices, newOutputData, &stat); - MCHECK(stat); - - MFnMesh mFnMesh(oMesh); - assignTextureCoordinates(mFnMesh, uvs, uvsSizes, uvCounts, uvCountsSizes, uvIndices, uvIndicesSizes, uvSetsCount); - assignVertexNormals(mFnMesh, mayaFaceCounts, mayaVertexIndices, nrm, nrmSize, normalIndices, normalIndicesSize); - - MFnMesh outputMesh(outMeshObj); - outputMesh.copyInPlace(oMesh); - MCHECK(stat); MFnMesh inputMesh(inMeshObj); @@ -273,7 +271,6 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c newMetadata.makeUnique(); MCHECK(stat); adsk::Data::Channel newChannel = newMetadata.channel(PRT_MATERIAL_CHANNEL); - const adsk::Data::Structure* fStructure = getMaterialDataStructure(materials, faceRangesSize); adsk::Data::Stream newStream(*fStructure, PRT_MATERIAL_STREAM); newChannel.setDataStream(newStream); From dfc227070f9f5334e1958961f304d7248a35ad3a Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 3 May 2021 13:04:42 +0200 Subject: [PATCH 039/668] Cleanup: improve variable naming (2nd attempt) --- src/serlio/modifiers/MayaCallbacks.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 86bd7de5..82568320 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -193,16 +193,16 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c MCHECK(stat); MFnMesh mFnMesh1; - MObject oMesh = mFnMesh1.create(mayaVertices.length(), mayaFaceCounts.length(), mayaVertices, mayaFaceCounts, + MObject newMeshObj = mFnMesh1.create(mayaVertices.length(), mayaFaceCounts.length(), mayaVertices, mayaFaceCounts, mayaVertexIndices, newOutputData, &stat); MCHECK(stat); - MFnMesh mFnMesh(oMesh); - assignTextureCoordinates(mFnMesh, uvs, uvsSizes, uvCounts, uvCountsSizes, uvIndices, uvIndicesSizes, uvSetsCount); - assignVertexNormals(mFnMesh, mayaFaceCounts, mayaVertexIndices, nrm, nrmSize, normalIndices, normalIndicesSize); + MFnMesh newMesh(newMeshObj); + assignTextureCoordinates(newMesh, uvs, uvsSizes, uvCounts, uvCountsSizes, uvIndices, uvIndicesSizes, uvSetsCount); + assignVertexNormals(newMesh, mayaFaceCounts, mayaVertexIndices, nrm, nrmSize, normalIndices, normalIndicesSize); MFnMesh outputMesh(outMeshObj); - outputMesh.copyInPlace(oMesh); + outputMesh.copyInPlace(newMeshObj); // create material metadata constexpr unsigned int maxStringLength = 400; From 59c4d8f752a29786fd9531320c7ad146a2b9113e Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 3 May 2021 13:10:53 +0200 Subject: [PATCH 040/668] Cleanup: rename constants according to conventions (2nd attempt) --- src/serlio/modifiers/MayaCallbacks.cpp | 34 +++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 82568320..13eeeecf 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -164,6 +164,10 @@ void assignVertexNormals(MFnMesh& mFnMesh, MIntArray& mayaFaceCounts, MIntArray& MCHECK(mFnMesh.setFaceVertexNormals(expandedNormals, faceList, mayaVertexIndices)); } +constexpr unsigned int MATERIAL_MAX_STRING_LENGTH = 400; +constexpr unsigned int MATERIAL_MAX_FLOAT_ARRAY_LENGTH = 5; +constexpr unsigned int MATERIAL_MAX_STRING_ARRAY_LENGTH = 2; + } // namespace void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, const double* nrm, size_t nrmSize, @@ -205,10 +209,6 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c outputMesh.copyInPlace(newMeshObj); // create material metadata - constexpr unsigned int maxStringLength = 400; - constexpr unsigned int maxFloatArrayLength = 5; - constexpr unsigned int maxStringArrayLength = 2; - adsk::Data::Structure* fStructure; // Structure to use for creation fStructure = adsk::Data::Structure::structureByName(PRT_MATERIAL_STRUCTURE.c_str()); if ((fStructure == nullptr) && (materials != nullptr) && (faceRangesSize > 1)) { @@ -237,11 +237,11 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c case prt::Attributable::PT_INT: type = adsk::Data::Member::kInt32; size = 1; break; //workaround: using kString type crashes maya when setting metadata elememts. Therefore we use array of kUInt8 - case prt::Attributable::PT_STRING: type = adsk::Data::Member::kUInt8; size = maxStringLength; break; - case prt::Attributable::PT_BOOL_ARRAY: type = adsk::Data::Member::kBoolean; size = maxStringLength; break; - case prt::Attributable::PT_INT_ARRAY: type = adsk::Data::Member::kInt32; size = maxStringLength; break; - case prt::Attributable::PT_FLOAT_ARRAY: type = adsk::Data::Member::kDouble; size = maxFloatArrayLength; break; - case prt::Attributable::PT_STRING_ARRAY: type = adsk::Data::Member::kUInt8; size = maxStringLength; arrayLength = maxStringArrayLength; break; + case prt::Attributable::PT_STRING: type = adsk::Data::Member::kUInt8; size = MATERIAL_MAX_STRING_LENGTH; break; + case prt::Attributable::PT_BOOL_ARRAY: type = adsk::Data::Member::kBoolean; size = MATERIAL_MAX_STRING_LENGTH; break; + case prt::Attributable::PT_INT_ARRAY: type = adsk::Data::Member::kInt32; size = MATERIAL_MAX_STRING_LENGTH; break; + case prt::Attributable::PT_FLOAT_ARRAY: type = adsk::Data::Member::kDouble; size = MATERIAL_MAX_FLOAT_ARRAY_LENGTH; break; + case prt::Attributable::PT_STRING_ARRAY: type = adsk::Data::Member::kUInt8; size = MATERIAL_MAX_STRING_LENGTH; arrayLength = MATERIAL_MAX_STRING_ARRAY_LENGTH; break; case prt::Attributable::PT_UNDEFINED: break; case prt::Attributable::PT_BLIND_DATA: break; @@ -315,29 +315,29 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c const wchar_t* str = mat->getString(key); if (wcslen(str) == 0) break; - checkStringLength(str, maxStringLength); - size_t maxStringLengthTmp = maxStringLength; + checkStringLength(str, MATERIAL_MAX_STRING_LENGTH); + size_t maxStringLengthTmp = MATERIAL_MAX_STRING_LENGTH; prt::StringUtils::toOSNarrowFromUTF16(str, (char*)handle.asUInt8(), &maxStringLengthTmp); break; } case prt::Attributable::PT_BOOL_ARRAY: { const bool* boolArray; boolArray = mat->getBoolArray(key, &arraySize); - for (unsigned int i = 0; i < arraySize && i < maxStringLength; i++) + for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) handle.asBoolean()[i] = boolArray[i]; break; } case prt::Attributable::PT_INT_ARRAY: { const int* intArray; intArray = mat->getIntArray(key, &arraySize); - for (unsigned int i = 0; i < arraySize && i < maxStringLength; i++) + for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) handle.asInt32()[i] = intArray[i]; break; } case prt::Attributable::PT_FLOAT_ARRAY: { const double* floatArray; floatArray = mat->getFloatArray(key, &arraySize); - for (unsigned int i = 0; i < arraySize && i < maxStringLength && i < maxFloatArrayLength; + for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH && i < MATERIAL_MAX_FLOAT_ARRAY_LENGTH; i++) handle.asDouble()[i] = floatArray[i]; break; @@ -346,7 +346,7 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c const wchar_t* const* stringArray = mat->getStringArray(key, &arraySize); - for (unsigned int i = 0; i < arraySize && i < maxStringLength; i++) { + for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) { if (wcslen(stringArray[i]) == 0) continue; @@ -357,8 +357,8 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c continue; } - checkStringLength(stringArray[i], maxStringLength); - size_t maxStringLengthTmp = maxStringLength; + checkStringLength(stringArray[i], MATERIAL_MAX_STRING_LENGTH); + size_t maxStringLengthTmp = MATERIAL_MAX_STRING_LENGTH; prt::StringUtils::toOSNarrowFromUTF16(stringArray[i], (char*)handle.asUInt8(), &maxStringLengthTmp); } From b0ac16ce0d129551545848a36fd24a3f9582a9de Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 17 May 2021 16:01:59 +0200 Subject: [PATCH 041/668] Updated and consolidated licensing information. --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 44ba75ad..3350dfa8 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,11 @@ # ![](doc/img/serlio_32.png) Serlio - CityEngine Plugin for Autodesk Maya -Serlio is a plugin for [Autodesk Maya](https://www.autodesk.com/maya). It provides a modifier node which enables the execution of [CityEngine](http://www.esri.com/software/cityengine) ‘rules’ within a Maya scene. Therefore, a 3D environment artist does not have to leave their familiar Maya toolset anymore to make use of CityEngine’s procedural modeling power. Complicated export-import pipelines are no longer needed, which also means that the procedural building models do not need to be “baked” anymore. The buildings stay procedural during the entire modeling workflow. Consequently, the 3D environment artist can change the height, style and appearance of buildings easily with a parametric interface at any point during production. +Serlio is a plugin for [Autodesk Maya](https://www.autodesk.com/maya). It provides a modifier node which enables the execution of [CityEngine](https://www.esri.com/software/cityengine) ‘rules’ within a Maya scene. Therefore, a 3D environment artist does not have to leave their familiar Maya toolset anymore to make use of CityEngine’s procedural modeling power. Complicated export-import pipelines are no longer needed, which also means that the procedural building models do not need to be “baked” anymore. The buildings stay procedural during the entire modeling workflow. Consequently, the 3D environment artist can change the height, style and appearance of buildings easily with a parametric interface at any point during production. Serlio requires so-called rule packages (RPK) as input, which are authored in CityEngine. An RPK includes assets and a CGA rule file which encodes an architectural style. Comprehensive RPK examples are available below and can be used “out-of-the-box” in Serlio. Serlio is well suited for managing the procedural generation of architectural 3D content in digital sets. However, Serlio is restricted to the procedural generation of single buildings / objects. Serlio does not include the city layouting and street network editing tools of CityEngine i.e. the rich CityEngine toolset to design a city from scratch (or based on geographic data) is still needed. -*Serlio is free for non-commercial use.* Commercial use requires at least one commercial license of the latest CityEngine version installed in the organization. No redistribution is allowed. Please refer to the licensing section below for more detailed licensing information. - ## Documentation @@ -23,8 +21,12 @@ External documentation: * [CityEngine SDK API Reference](https://esri.github.io/cityengine-sdk/html/index.html) -## Licensing +## Licensing Information + +Serlio is free for personal, educational, and non-commercial use. Commercial use requires at least one commercial license of the latest CityEngine version installed in the organization. Redistribution or web service offerings are not allowed unless expressly permitted. + +Serlio is under the same license as the included CityEngine SDK. An exception is the Serlio source code (without CityEngine SDK, binaries, or object code), which is licensed under the Apache License, Version 2.0 (the “License”); you may not use this work except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 -Serlio is under the same license as the included [CityEngine SDK](https://github.com/Esri/esri-cityengine-sdk#licensing). +All content in the "Examples" directory/section is licensed under the APACHE 2.0 license as well. -An exception is the Serlio source code (without CityEngine SDK, binaries, or object code), which is licensed under the Apache License, Version 2.0 (the “License”); you may not use this work except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. +For questions or enquiries regarding licensing, please contact the Esri CityEngine team (cityengine-info@esri.com). From 701e75cae77d0345cc29bc7b07ea7c771126dc6c Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Thu, 20 May 2021 10:35:47 +0200 Subject: [PATCH 042/668] Add link to CityEngine SDK license section --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3350dfa8..69178ac1 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ External documentation: Serlio is free for personal, educational, and non-commercial use. Commercial use requires at least one commercial license of the latest CityEngine version installed in the organization. Redistribution or web service offerings are not allowed unless expressly permitted. -Serlio is under the same license as the included CityEngine SDK. An exception is the Serlio source code (without CityEngine SDK, binaries, or object code), which is licensed under the Apache License, Version 2.0 (the “License”); you may not use this work except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 +Serlio is under the same license as the included [CityEngine SDK](https://github.com/esri/cityengine-sdk#licensing). An exception is the Serlio source code (without CityEngine SDK, binaries, or object code), which is licensed under the Apache License, Version 2.0 (the “License”); you may not use this work except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 All content in the "Examples" directory/section is licensed under the APACHE 2.0 license as well. From ff40e8288307d246d5faa43b756d33dba64a8226 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 22 Oct 2021 14:44:19 +0200 Subject: [PATCH 043/668] Make order of imports consistent with CE In CE the imports in the inspector are sorted in the same order as they are delcared in the .cga rule file. We can extract that order by going through the ruleFileInfo annotation: "_$IMPORTS" --- src/serlio/modifiers/RuleAttributes.cpp | 29 +++++++++++++++++++++++++ src/serlio/modifiers/RuleAttributes.h | 3 +++ 2 files changed, 32 insertions(+) diff --git a/src/serlio/modifiers/RuleAttributes.cpp b/src/serlio/modifiers/RuleAttributes.cpp index 6933d488..cfa81d1c 100644 --- a/src/serlio/modifiers/RuleAttributes.cpp +++ b/src/serlio/modifiers/RuleAttributes.cpp @@ -57,6 +57,27 @@ std::wstring getNiceName(const std::wstring& fqAttrName) { } // namespace +std::map getImportOrderMap(const prt::RuleFileInfo* ruleFileInfo) { + std::map importOrderMap; + int importOrder = 0; + for (size_t i = 0; i < ruleFileInfo->getNumAnnotations(); i++) { + const prt::Annotation* an = ruleFileInfo->getAnnotation(i); + const wchar_t* anName = an->getName(); + if (!(std::wcscmp(anName, ANNOT_IMPORTS))) { + for (int argIdx = 0; argIdx < an->getNumArguments(); argIdx++) { + if (an->getArgument(argIdx)->getType() == prt::AAT_STR) { + const wchar_t* anKey = an->getArgument(argIdx)->getKey(); + if(!(std::wcscmp(anKey, ANNOT_IMPORTS_KEY))) { + std::wstring importRule = an->getArgument(argIdx)->getStr(); + importOrderMap[importRule]= importOrder++; + } + } + } + } + } + return importOrderMap; +} + RuleAttributes getRuleAttributes(const std::wstring& ruleFile, const prt::RuleFileInfo* ruleFileInfo) { RuleAttributes ra; @@ -65,6 +86,8 @@ RuleAttributes getRuleAttributes(const std::wstring& ruleFile, const prt::RuleFi if (idxExtension != std::wstring::npos) mainCgaRuleName = mainCgaRuleName.substr(0, idxExtension); + std::map importOrderMap = getImportOrderMap(ruleFileInfo); + for (size_t i = 0; i < ruleFileInfo->getNumAttributes(); i++) { const prt::RuleFileInfo::Entry* attr = ruleFileInfo->getAttribute(i); @@ -92,6 +115,9 @@ RuleAttributes getRuleAttributes(const std::wstring& ruleFile, const prt::RuleFi p.memberOfStartRuleFile = true; } + auto importOrder = importOrderMap.find(p.ruleFile); + p.ruleOrder = (importOrder != importOrderMap.end()) ? importOrder->second : ORDER_NONE; + bool hidden = false; for (size_t a = 0; a < attr->getNumAnnotations(); a++) { const prt::Annotation* an = attr->getAnnotation(a); @@ -157,6 +183,9 @@ void sortRuleAttributes(RuleAttributes& ra) { if (b.memberOfStartRuleFile) return false; + if(a.ruleOrder != b.ruleOrder) + return a.ruleOrder < b.ruleOrder; + return lowerCaseOrdering(a.ruleFile, b.ruleFile); }; diff --git a/src/serlio/modifiers/RuleAttributes.h b/src/serlio/modifiers/RuleAttributes.h index f18758c9..82c6ea28 100644 --- a/src/serlio/modifiers/RuleAttributes.h +++ b/src/serlio/modifiers/RuleAttributes.h @@ -40,6 +40,8 @@ constexpr const wchar_t* ANNOT_DIR = L"@Directory"; constexpr const wchar_t* ANNOT_FILE = L"@File"; constexpr const wchar_t* ANNOT_ORDER = L"@Order"; constexpr const wchar_t* ANNOT_GROUP = L"@Group"; +constexpr const wchar_t* ANNOT_IMPORTS = L"_$IMPORTS"; +constexpr const wchar_t* ANNOT_IMPORTS_KEY = L"fullPrefix"; constexpr int ORDER_FIRST = std::numeric_limits::min(); constexpr int ORDER_NONE = std::numeric_limits::max(); @@ -59,6 +61,7 @@ struct RuleAttribute { int groupOrder = ORDER_NONE; std::wstring ruleFile; + int ruleOrder = ORDER_NONE; bool memberOfStartRuleFile = false; }; From 3e97a2c51fad42e62b5ae20df9baf31b235eafae Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 28 Oct 2021 13:19:30 +0200 Subject: [PATCH 044/668] Add the rulefile to the key in the globalGroupOrder This fixes a bug, where the rule order is falsely changed due to multple rules having groups with the same name but different group order --- src/serlio/modifiers/RuleAttributes.cpp | 6 +++--- src/serlio/modifiers/RuleAttributes.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/serlio/modifiers/RuleAttributes.cpp b/src/serlio/modifiers/RuleAttributes.cpp index cfa81d1c..e196ed00 100644 --- a/src/serlio/modifiers/RuleAttributes.cpp +++ b/src/serlio/modifiers/RuleAttributes.cpp @@ -162,7 +162,7 @@ AttributeGroupOrder getGlobalGroupOrder(const RuleAttributes& ruleAttributes) { for (auto it = std::rbegin(attribute.groups); it != std::rend(attribute.groups); ++it) { std::vector g(it, std::rend(attribute.groups)); std::reverse(g.begin(), g.end()); - auto ggoIt = globalGroupOrder.emplace(g, ORDER_NONE).first; + auto ggoIt = globalGroupOrder.emplace(std::tuple(attribute.ruleFile,g), ORDER_NONE).first; ggoIt->second = std::min(attribute.groupOrder, ggoIt->second); } } @@ -220,7 +220,7 @@ void sortRuleAttributes(RuleAttributes& ra) { LOG_DBG << "globalGroupOrder:\n" << globalGroupOrder; auto getGroupOrder = [&globalGroupOrder](const RuleAttribute& ap) { - const auto it = globalGroupOrder.find(ap.groups); + const auto it = globalGroupOrder.find(std::tuple(ap.ruleFile,ap.groups)); return (it != globalGroupOrder.end()) ? it->second : ORDER_NONE; }; @@ -280,7 +280,7 @@ std::ostream& operator<<(std::ostream& ostr, const RuleAttribute& ap) { std::wostream& operator<<(std::wostream& wostr, const AttributeGroupOrder& ago) { for (const auto& i : ago) { - wostr << L"[ " << join(i.first, L" ") << L"] = " << i.second << L"\n"; + wostr << L"[ " << std::get<0>(i.first) << " " << join(std::get<1>(i.first), L" ") << L"] = " << i.second << L"\n"; } return wostr; } diff --git a/src/serlio/modifiers/RuleAttributes.h b/src/serlio/modifiers/RuleAttributes.h index 82c6ea28..91188369 100644 --- a/src/serlio/modifiers/RuleAttributes.h +++ b/src/serlio/modifiers/RuleAttributes.h @@ -47,7 +47,7 @@ constexpr int ORDER_FIRST = std::numeric_limits::min(); constexpr int ORDER_NONE = std::numeric_limits::max(); using AttributeGroup = std::vector; -using AttributeGroupOrder = std::map; +using AttributeGroupOrder = std::map, int>; struct RuleAttribute { std::wstring fqName; // fully qualified rule name (i.e. including style prefix) From 50304bd9f0809fd0b2f27f2d1cbeacf175f21c37 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 28 Oct 2021 19:12:27 +0200 Subject: [PATCH 045/668] Sort attributes that are all in the startrule alphabetically If two attributes are in the StartRule they are now sorted alphabetically --- src/serlio/modifiers/RuleAttributes.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serlio/modifiers/RuleAttributes.cpp b/src/serlio/modifiers/RuleAttributes.cpp index e196ed00..b61796ed 100644 --- a/src/serlio/modifiers/RuleAttributes.cpp +++ b/src/serlio/modifiers/RuleAttributes.cpp @@ -178,9 +178,9 @@ void sortRuleAttributes(RuleAttributes& ra) { auto compareRuleFile = [&](const RuleAttribute& a, const RuleAttribute& b) { // sort main rule attributes before the rest - if (a.memberOfStartRuleFile) + if (a.memberOfStartRuleFile && !b.memberOfStartRuleFile) return true; - if (b.memberOfStartRuleFile) + if (b.memberOfStartRuleFile && !a.memberOfStartRuleFile) return false; if(a.ruleOrder != b.ruleOrder) From de84f0023633d0b6e04282888692fbc0998d0f7a Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 28 Oct 2021 19:43:37 +0200 Subject: [PATCH 046/668] Sort attributes that are all in the same order alphabetically If two attributes are in the order they are now sorted alphabetically --- src/serlio/modifiers/RuleAttributes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/RuleAttributes.cpp b/src/serlio/modifiers/RuleAttributes.cpp index b61796ed..c289470d 100644 --- a/src/serlio/modifiers/RuleAttributes.cpp +++ b/src/serlio/modifiers/RuleAttributes.cpp @@ -244,7 +244,7 @@ void sortRuleAttributes(RuleAttributes& ra) { }; auto compareAttributeOrder = [&](const RuleAttribute& a, const RuleAttribute& b) { - if (a.order == ORDER_NONE && b.order == ORDER_NONE) + if (a.order == b.order) return lowerCaseOrdering(a.fqName, b.fqName); return a.order < b.order; From 85d0f433e0cbeb9c8bbea2ff7eba1b8879c3b805 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 29 Oct 2021 16:49:50 +0200 Subject: [PATCH 047/668] Upgrade prt to 2.5 Include include since references were missing otherwise --- src/codec/encoder/MayaEncoder.cpp | 1 + src/common.cmake | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index 69777eb3..88406ab4 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -43,6 +43,7 @@ #include #include #include +#include // PRT version < 2.1 #if ((PRT_VERSION_MAJOR <= 2) && ((PRT_VERSION_MAJOR < 2) || (PRT_VERSION_MINOR < 1))) diff --git a/src/common.cmake b/src/common.cmake index 6be8102a..f5b56410 100644 --- a/src/common.cmake +++ b/src/common.cmake @@ -37,7 +37,7 @@ endfunction() if (NOT prt_DIR) if (SRL_WINDOWS) set(PRT_OS "win10") - set(PRT_TC "vc142") + set(PRT_TC "vc1427") elseif (SRL_LINUX) set(PRT_OS "rhel7") set(PRT_TC "gcc63") @@ -46,7 +46,7 @@ if (NOT prt_DIR) set(PRT_TC "ac81") endif () - set(PRT_VERSION "2.3.6821") + set(PRT_VERSION "2.5.7799") set(PRT_CLS "${PRT_OS}-${PRT_TC}-x86_64-rel-opt") set(PRT_URL "https://github.com/esri/cityengine-sdk/releases/download/${PRT_VERSION}/esri_ce_sdk-${PRT_VERSION}-${PRT_CLS}.zip") From 95a0bb899fc710d966df8c782f24ebcefbb36edc Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 8 Nov 2021 14:53:32 +0100 Subject: [PATCH 048/668] review: fix compiler warning (sign mismatch) --- src/serlio/modifiers/MayaCallbacks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 13eeeecf..a1c042b4 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -150,7 +150,7 @@ void assignVertexNormals(MFnMesh& mFnMesh, MIntArray& mayaFaceCounts, MIntArray& MIntArray faceList(static_cast(mayaVertexIndices.length())); int indexCount = 0; - for (int i = 0; i < mayaFaceCounts.length(); i++) { + for (uint32_t i = 0; i < mayaFaceCounts.length(); i++) { int faceLength = mayaFaceCounts[i]; for (int j = 0; j < faceLength; j++) { From 3a581d3bd252366716d15610ce5993c40353526e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 8 Nov 2021 16:25:11 +0100 Subject: [PATCH 049/668] Add UI to remove RPK assignment --- src/serlio/scripts/serlioCreateUI.mel | 32 +++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index abf1306f..f244e7db 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -60,6 +60,37 @@ global proc createPrtNode() { select -r $select; } +global proc removePrtNode() { + string $select[] = `ls -sl`; + string $initialShapes[] = collectInitialShapes($select); + + if(!(size($initialShapes))) { + confirmDialog -title "Missing selection" -message "Please select shapes first in order to apply a rule." -button "OK"; + return; + } + + for($node in $initialShapes) { + select -r $node; + hyperShade -assign "lambert1"; + string $nodeHistory[] = `listHistory $node`; + + for($dpNode in $nodeHistory){ + $dpNodeType = `nodeType $dpNode`; + if($dpNodeType == "serlio" || $dpNodeType == "serlioMaterial" || $dpNodeType == "serlioArnoldMaterial" ){ + $previousDpNodePlug = `connectionInfo -sfd ($dpNode +".inMesh")`; + $nextDpNodePlug = `connectionInfo -dfs ($dpNode +".outMesh")`; + if (size($previousDpNodePlug)>0 && size($nextDpNodePlug)) { + connectAttr -force ( $previousDpNodePlug ) ($nextDpNodePlug); + delete $dpNode; + } + } + } + } + + select -r $select; +} + + global proc createPrtMaterialNode() { string $select[] = `ls -sl`; string $initialShapes[] = collectInitialShapes($select); @@ -109,6 +140,7 @@ global proc createPrtMenu() { setParent -m $gPrtMenu; menuItem -divider true; menuItem -label "Attach CityEngine Rule Package..." -c "createPrtNode" -annotation "Attach a CGA rule package to a geometry"; + menuItem -label "Remove Rule Package Assignment" -c "removePrtNode" -annotation "Remove a CGA rule package from a geometry"; menuItem -label "Create Materials" -c "createPrtMaterialNode" -annotation "Create Materials"; menuItem -label "Create Arnold Materials" -c "createArnoldMaterialNode" -annotation "Create Arnold Materials"; setParent -m ..; From f074eb1dc39294b96e98428a924036434190211f Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 8 Nov 2021 15:07:45 +0100 Subject: [PATCH 050/668] ci: update compilers to requirements for PRT 2.5 --- pipeline.groovy | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pipeline.groovy b/pipeline.groovy index a427847a..f2f8c0ff 100644 --- a/pipeline.groovy +++ b/pipeline.groovy @@ -21,24 +21,24 @@ import com.esri.zrh.jenkins.ce.PrtAppPipelineLibrary // TODO: abusing grp field to distinguish maya versions per task @Field final List CONFIGS = [ - [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC63, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, + [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2018', maya: PrtAppPipelineLibrary.Dependencies.MAYA2018 ], - [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC63, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, + [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2019', maya: PrtAppPipelineLibrary.Dependencies.MAYA2019 ], - [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC63, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, + [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2020', maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC142, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2018', maya: PrtAppPipelineLibrary.Dependencies.MAYA2018 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC142, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2019', maya: PrtAppPipelineLibrary.Dependencies.MAYA2019 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC142, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2020', maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], ] @Field final List TEST_CONFIGS = [ - [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC63, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, + [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC142, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], ] From 53a66a297cffeb887c83a265d7615ede10d96f31 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 8 Nov 2021 15:22:24 +0100 Subject: [PATCH 051/668] ci: switch pipeline to PRT 2.5 (and let cmake download PRT) --- pipeline.groovy | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/pipeline.groovy b/pipeline.groovy index f2f8c0ff..24c3ea54 100644 --- a/pipeline.groovy +++ b/pipeline.groovy @@ -21,25 +21,17 @@ import com.esri.zrh.jenkins.ce.PrtAppPipelineLibrary // TODO: abusing grp field to distinguish maya versions per task @Field final List CONFIGS = [ - [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, - cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2018', maya: PrtAppPipelineLibrary.Dependencies.MAYA2018 ], - [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, - cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2019', maya: PrtAppPipelineLibrary.Dependencies.MAYA2019 ], - [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, - cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2020', maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, - cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2018', maya: PrtAppPipelineLibrary.Dependencies.MAYA2018 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, - cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2019', maya: PrtAppPipelineLibrary.Dependencies.MAYA2019 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, - cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, grp: 'maya2020', maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], + [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2018', maya: PrtAppPipelineLibrary.Dependencies.MAYA2018 ], + [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2019', maya: PrtAppPipelineLibrary.Dependencies.MAYA2019 ], + [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2020', maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2018', maya: PrtAppPipelineLibrary.Dependencies.MAYA2018 ], + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2019', maya: PrtAppPipelineLibrary.Dependencies.MAYA2019 ], + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2020', maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], ] @Field final List TEST_CONFIGS = [ - [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, - cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, - cesdk: PrtAppPipelineLibrary.Dependencies.CESDK20201, maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], + [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], ] @@ -83,9 +75,8 @@ Map taskGenSerlioInstallers() { def taskBuildSerlio(cfg) { final String appName = 'serlio' - final List DEPS = [ cfg.cesdk, cfg.maya ] + final List DEPS = [ cfg.maya ] List defs = [ - [ key: 'prt_DIR', val: cfg.cesdk.p ], [ key: 'maya_DIR', val: cfg.maya.p ], [ key: 'SRL_VERSION_BUILD', val: env.BUILD_NUMBER ] ] @@ -104,9 +95,8 @@ def taskBuildSerlio(cfg) { def taskBuildSerlioTests(cfg) { final String appName = 'serlio-test' - final List DEPS = [ cfg.cesdk, cfg.maya ] + final List DEPS = [ cfg.maya ] List defs = [ - [ key: 'prt_DIR', val: cfg.cesdk.p ], [ key: 'maya_DIR', val: cfg.maya.p ], [ key: 'SRL_VERSION_BUILD', val: env.BUILD_NUMBER ] ] @@ -119,7 +109,7 @@ def taskBuildSerlioInstaller(cfg) { final String appName = 'serlio-installer' cepl.cleanCurrentDir() papl.checkout(REPO, myBranch, REPO_CREDS) - final List deps = [ cfg.cesdk, cfg.maya ] + final List deps = [ cfg.maya ] deps.each { d -> papl.fetchDependency(d, cfg) } // Toolchain definition for building MSI installers. @@ -138,7 +128,6 @@ def taskBuildSerlioInstaller(cfg) { cmd += "\n" cmd += "powershell .\\build.ps1" cmd += " -MAYA_DIR ${cfg.maya.p.call(cfg)}" // <-- !!! - cmd += " -CESDK_DIR ${PrtAppPipelineLibrary.Dependencies.CESDK.p.call(cfg)}" cmd += " -BUILD_NO ${env.BUILD_NUMBER}" cmd += " -TARGET InstallerFromScratch" From e847d99f906b34cc8501725325d5635ca9cfe81b Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 8 Nov 2021 16:15:45 +0100 Subject: [PATCH 052/668] cmake: fix wrong GCC definition for PRT 2.5 --- src/common.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common.cmake b/src/common.cmake index f5b56410..e290783d 100644 --- a/src/common.cmake +++ b/src/common.cmake @@ -40,7 +40,7 @@ if (NOT prt_DIR) set(PRT_TC "vc1427") elseif (SRL_LINUX) set(PRT_OS "rhel7") - set(PRT_TC "gcc63") + set(PRT_TC "gcc93") elseif (SRL_MACOS) set(PRT_OS "osx12") set(PRT_TC "ac81") From 338fbef238687e3a30aadc959c0b5ddb44252dc1 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Wed, 10 Nov 2021 09:57:01 +0100 Subject: [PATCH 053/668] cmake: switch to C++17 and deduplicate code --- src/codec/CMakeLists.txt | 4 ++-- src/common.cmake | 1 + src/serlio/CMakeLists.txt | 3 --- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/codec/CMakeLists.txt b/src/codec/CMakeLists.txt index 53d5f392..d97f9525 100644 --- a/src/codec/CMakeLists.txt +++ b/src/codec/CMakeLists.txt @@ -27,9 +27,9 @@ set_common_target_definitions(${CODEC_TARGET}) if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") target_compile_options(${CODEC_TARGET} PRIVATE -bigobj -GR -EHsc) elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") - target_compile_options(${CODEC_TARGET} PRIVATE -std=c++14 -stdlib=libc++ -fvisibility=hidden -fvisibility-inlines-hidden -Wl,--exclude-libs,ALL) + target_compile_options(${CODEC_TARGET} PRIVATE -stdlib=libc++ -fvisibility=hidden -fvisibility-inlines-hidden -Wl,--exclude-libs,ALL) elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - target_compile_options(${CODEC_TARGET} PRIVATE -std=c++14 -D_GLIBCXX_USE_CXX11_ABI=0 -march=nocona -fvisibility=hidden -fvisibility-inlines-hidden -Wl,--exclude-libs,ALL) + target_compile_options(${CODEC_TARGET} PRIVATE -D_GLIBCXX_USE_CXX11_ABI=0 -march=nocona -fvisibility=hidden -fvisibility-inlines-hidden -Wl,--exclude-libs,ALL) endif () target_link_libraries(${CODEC_TARGET} ${PRT_LINK_LIBRARIES}) diff --git a/src/common.cmake b/src/common.cmake index e290783d..0c231092 100644 --- a/src/common.cmake +++ b/src/common.cmake @@ -24,6 +24,7 @@ endif () ### common target functions function(set_common_target_definitions TGT) + set_target_properties(${TGT} PROPERTIES CXX_STANDARD 17) target_compile_definitions(${TGT} PRIVATE -DSRL_VERSION=\"${SRL_VERSION}\" # quoted to use it as string literal -DPRT_VERSION_MAJOR=${PRT_VERSION_MAJOR} diff --git a/src/serlio/CMakeLists.txt b/src/serlio/CMakeLists.txt index 7460db0b..71bb4907 100644 --- a/src/serlio/CMakeLists.txt +++ b/src/serlio/CMakeLists.txt @@ -66,9 +66,6 @@ endif () ### setup compiler flags -set_target_properties(${CLIENT_TARGET} PROPERTIES - CXX_STANDARD 14) - set_common_target_definitions(${SERLIO_TARGET}) target_include_directories(${SERLIO_TARGET} From 6816aac0dda88030d801b049acfabf3e9351c186 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Wed, 10 Nov 2021 09:59:54 +0100 Subject: [PATCH 054/668] cmake: remove outdated and unused macOS handling --- src/codec/CMakeLists.txt | 4 ---- src/common.cmake | 5 ----- 2 files changed, 9 deletions(-) diff --git a/src/codec/CMakeLists.txt b/src/codec/CMakeLists.txt index d97f9525..2990e427 100644 --- a/src/codec/CMakeLists.txt +++ b/src/codec/CMakeLists.txt @@ -26,8 +26,6 @@ set_common_target_definitions(${CODEC_TARGET}) if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") target_compile_options(${CODEC_TARGET} PRIVATE -bigobj -GR -EHsc) -elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") - target_compile_options(${CODEC_TARGET} PRIVATE -stdlib=libc++ -fvisibility=hidden -fvisibility-inlines-hidden -Wl,--exclude-libs,ALL) elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") target_compile_options(${CODEC_TARGET} PRIVATE -D_GLIBCXX_USE_CXX11_ABI=0 -march=nocona -fvisibility=hidden -fvisibility-inlines-hidden -Wl,--exclude-libs,ALL) endif () @@ -36,8 +34,6 @@ target_link_libraries(${CODEC_TARGET} ${PRT_LINK_LIBRARIES}) if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") target_link_libraries(${CODEC_TARGET} IPHlpApi Psapi DbgHelp) -elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") - # none elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") target_link_libraries(${CODEC_TARGET} z pthread dl rt) endif () diff --git a/src/common.cmake b/src/common.cmake index 0c231092..a091e921 100644 --- a/src/common.cmake +++ b/src/common.cmake @@ -16,8 +16,6 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") set(SRL_WINDOWS 1) elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(SRL_LINUX 1) -elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") - set(SRL_MACOS 1) endif () @@ -42,9 +40,6 @@ if (NOT prt_DIR) elseif (SRL_LINUX) set(PRT_OS "rhel7") set(PRT_TC "gcc93") - elseif (SRL_MACOS) - set(PRT_OS "osx12") - set(PRT_TC "ac81") endif () set(PRT_VERSION "2.5.7799") From 768133c4840035c4d0a42c8f43592c795e63073b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 11 Nov 2021 11:36:33 +0100 Subject: [PATCH 055/668] Cleanup: use int comparison for std::wcscmp --- src/serlio/modifiers/RuleAttributes.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/serlio/modifiers/RuleAttributes.cpp b/src/serlio/modifiers/RuleAttributes.cpp index c289470d..8d0a3713 100644 --- a/src/serlio/modifiers/RuleAttributes.cpp +++ b/src/serlio/modifiers/RuleAttributes.cpp @@ -63,11 +63,11 @@ std::map getImportOrderMap(const prt::RuleFileInfo* ruleFileIn for (size_t i = 0; i < ruleFileInfo->getNumAnnotations(); i++) { const prt::Annotation* an = ruleFileInfo->getAnnotation(i); const wchar_t* anName = an->getName(); - if (!(std::wcscmp(anName, ANNOT_IMPORTS))) { + if (std::wcscmp(anName, ANNOT_IMPORTS) == 0) { for (int argIdx = 0; argIdx < an->getNumArguments(); argIdx++) { if (an->getArgument(argIdx)->getType() == prt::AAT_STR) { const wchar_t* anKey = an->getArgument(argIdx)->getKey(); - if(!(std::wcscmp(anKey, ANNOT_IMPORTS_KEY))) { + if(std::wcscmp(anKey, ANNOT_IMPORTS_KEY) == 0) { std::wstring importRule = an->getArgument(argIdx)->getStr(); importOrderMap[importRule]= importOrder++; } @@ -122,14 +122,14 @@ RuleAttributes getRuleAttributes(const std::wstring& ruleFile, const prt::RuleFi for (size_t a = 0; a < attr->getNumAnnotations(); a++) { const prt::Annotation* an = attr->getAnnotation(a); const wchar_t* anName = an->getName(); - if (!(std::wcscmp(anName, ANNOT_HIDDEN))) + if (std::wcscmp(anName, ANNOT_HIDDEN) == 0) hidden = true; - else if (!(std::wcscmp(anName, ANNOT_ORDER))) { + else if (std::wcscmp(anName, ANNOT_ORDER) == 0) { if (an->getNumArguments() >= 1 && an->getArgument(0)->getType() == prt::AAT_FLOAT) { p.order = static_cast(an->getArgument(0)->getFloat()); } } - else if (!(std::wcscmp(anName, ANNOT_GROUP))) { + else if (std::wcscmp(anName, ANNOT_GROUP) == 0) { for (int argIdx = 0; argIdx < an->getNumArguments(); argIdx++) { if (an->getArgument(argIdx)->getType() == prt::AAT_STR) { p.groups.push_back(an->getArgument(argIdx)->getStr()); From 70530c00a1626b9c38fec44339ff5fb51eee4e2f Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 11 Nov 2021 11:45:49 +0100 Subject: [PATCH 056/668] Cleanup: avoid reuse of getArgument by using a variable --- src/serlio/modifiers/RuleAttributes.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/RuleAttributes.cpp b/src/serlio/modifiers/RuleAttributes.cpp index 8d0a3713..17682ca7 100644 --- a/src/serlio/modifiers/RuleAttributes.cpp +++ b/src/serlio/modifiers/RuleAttributes.cpp @@ -65,10 +65,11 @@ std::map getImportOrderMap(const prt::RuleFileInfo* ruleFileIn const wchar_t* anName = an->getName(); if (std::wcscmp(anName, ANNOT_IMPORTS) == 0) { for (int argIdx = 0; argIdx < an->getNumArguments(); argIdx++) { - if (an->getArgument(argIdx)->getType() == prt::AAT_STR) { - const wchar_t* anKey = an->getArgument(argIdx)->getKey(); + const prt::AnnotationArgument* anArg = an->getArgument(argIdx); + if (anArg->getType() == prt::AAT_STR) { + const wchar_t* anKey = anArg->getKey(); if(std::wcscmp(anKey, ANNOT_IMPORTS_KEY) == 0) { - std::wstring importRule = an->getArgument(argIdx)->getStr(); + std::wstring importRule = anArg->getStr(); importOrderMap[importRule]= importOrder++; } } From 9594d52ebf12e8c2ff6bba6fc82ad534388790af Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 11 Nov 2021 11:47:07 +0100 Subject: [PATCH 057/668] Cleanup: make importOrder const --- src/serlio/modifiers/RuleAttributes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/RuleAttributes.cpp b/src/serlio/modifiers/RuleAttributes.cpp index 17682ca7..358bad36 100644 --- a/src/serlio/modifiers/RuleAttributes.cpp +++ b/src/serlio/modifiers/RuleAttributes.cpp @@ -116,7 +116,7 @@ RuleAttributes getRuleAttributes(const std::wstring& ruleFile, const prt::RuleFi p.memberOfStartRuleFile = true; } - auto importOrder = importOrderMap.find(p.ruleFile); + const auto importOrder = importOrderMap.find(p.ruleFile); p.ruleOrder = (importOrder != importOrderMap.end()) ? importOrder->second : ORDER_NONE; bool hidden = false; From a079b51d14718f93858f248c3d589c6abbb73207 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 11 Nov 2021 14:38:30 +0100 Subject: [PATCH 058/668] Cleanup: replaced tuple with two entries with pair --- src/serlio/modifiers/RuleAttributes.cpp | 6 +++--- src/serlio/modifiers/RuleAttributes.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/serlio/modifiers/RuleAttributes.cpp b/src/serlio/modifiers/RuleAttributes.cpp index 358bad36..bb170ade 100644 --- a/src/serlio/modifiers/RuleAttributes.cpp +++ b/src/serlio/modifiers/RuleAttributes.cpp @@ -163,7 +163,7 @@ AttributeGroupOrder getGlobalGroupOrder(const RuleAttributes& ruleAttributes) { for (auto it = std::rbegin(attribute.groups); it != std::rend(attribute.groups); ++it) { std::vector g(it, std::rend(attribute.groups)); std::reverse(g.begin(), g.end()); - auto ggoIt = globalGroupOrder.emplace(std::tuple(attribute.ruleFile,g), ORDER_NONE).first; + auto ggoIt = globalGroupOrder.emplace(std::make_pair(attribute.ruleFile,g), ORDER_NONE).first; ggoIt->second = std::min(attribute.groupOrder, ggoIt->second); } } @@ -221,7 +221,7 @@ void sortRuleAttributes(RuleAttributes& ra) { LOG_DBG << "globalGroupOrder:\n" << globalGroupOrder; auto getGroupOrder = [&globalGroupOrder](const RuleAttribute& ap) { - const auto it = globalGroupOrder.find(std::tuple(ap.ruleFile,ap.groups)); + const auto it = globalGroupOrder.find(std::make_pair(ap.ruleFile,ap.groups)); return (it != globalGroupOrder.end()) ? it->second : ORDER_NONE; }; @@ -281,7 +281,7 @@ std::ostream& operator<<(std::ostream& ostr, const RuleAttribute& ap) { std::wostream& operator<<(std::wostream& wostr, const AttributeGroupOrder& ago) { for (const auto& i : ago) { - wostr << L"[ " << std::get<0>(i.first) << " " << join(std::get<1>(i.first), L" ") << L"] = " << i.second << L"\n"; + wostr << L"[ " << i.first.first << " " << join(i.first.second, L" ") << L"] = " << i.second << L"\n"; } return wostr; } diff --git a/src/serlio/modifiers/RuleAttributes.h b/src/serlio/modifiers/RuleAttributes.h index 91188369..b41f6878 100644 --- a/src/serlio/modifiers/RuleAttributes.h +++ b/src/serlio/modifiers/RuleAttributes.h @@ -47,7 +47,7 @@ constexpr int ORDER_FIRST = std::numeric_limits::min(); constexpr int ORDER_NONE = std::numeric_limits::max(); using AttributeGroup = std::vector; -using AttributeGroupOrder = std::map, int>; +using AttributeGroupOrder = std::map, int>; struct RuleAttribute { std::wstring fqName; // fully qualified rule name (i.e. including style prefix) From 402361389f513e61653cd1ec29e8c21cd39cfd0d Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 11 Nov 2021 14:39:26 +0100 Subject: [PATCH 059/668] Cleanup: make importOrderMap const --- src/serlio/modifiers/RuleAttributes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/RuleAttributes.cpp b/src/serlio/modifiers/RuleAttributes.cpp index bb170ade..e50a92b9 100644 --- a/src/serlio/modifiers/RuleAttributes.cpp +++ b/src/serlio/modifiers/RuleAttributes.cpp @@ -87,7 +87,7 @@ RuleAttributes getRuleAttributes(const std::wstring& ruleFile, const prt::RuleFi if (idxExtension != std::wstring::npos) mainCgaRuleName = mainCgaRuleName.substr(0, idxExtension); - std::map importOrderMap = getImportOrderMap(ruleFileInfo); + const std::map importOrderMap = getImportOrderMap(ruleFileInfo); for (size_t i = 0; i < ruleFileInfo->getNumAttributes(); i++) { const prt::RuleFileInfo::Entry* attr = ruleFileInfo->getAttribute(i); From 46d3fcb4aa4d316bf52428e52a5554c2a1e36a85 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 11 Nov 2021 15:08:07 +0100 Subject: [PATCH 060/668] Cleanup: check for null before asigning wstring --- src/serlio/modifiers/RuleAttributes.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/serlio/modifiers/RuleAttributes.cpp b/src/serlio/modifiers/RuleAttributes.cpp index e50a92b9..1c868c3f 100644 --- a/src/serlio/modifiers/RuleAttributes.cpp +++ b/src/serlio/modifiers/RuleAttributes.cpp @@ -69,8 +69,11 @@ std::map getImportOrderMap(const prt::RuleFileInfo* ruleFileIn if (anArg->getType() == prt::AAT_STR) { const wchar_t* anKey = anArg->getKey(); if(std::wcscmp(anKey, ANNOT_IMPORTS_KEY) == 0) { - std::wstring importRule = anArg->getStr(); - importOrderMap[importRule]= importOrder++; + const wchar_t* importRuleCharPtr = anArg->getStr(); + if (importRuleCharPtr != nullptr) { + std::wstring importRule = importRuleCharPtr; + importOrderMap[importRule] = importOrder++; + } } } } From 09f2cd2c14b214263dde40011d949e5aab0b3bed Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 15 Nov 2021 13:25:24 +0100 Subject: [PATCH 061/668] Always set default attribute values Previously this was only done when a construction history was present. --- src/serlio/modifiers/PRTModifierAction.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index c359f5f1..80a59bee 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -308,13 +308,12 @@ MStatus PRTModifierAction::updateRuleFiles(const MObject& node, const MString& r } mStartRule = prtu::detectStartRule(info); + mGenerateAttrs = getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, + *inPrtMesh); + if (DBG) + LOG_DBG << "default attrs: " << prtu::objectToXML(mGenerateAttrs); if (node != MObject::kNullObj) { - mGenerateAttrs = getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, - *inPrtMesh); - if (DBG) - LOG_DBG << "default attrs: " << prtu::objectToXML(mGenerateAttrs); - // derive necessary data from PRT rule info to populate node with dynamic rule attributes mRuleAttributes = getRuleAttributes(mRuleFile, info.get()); sortRuleAttributes(mRuleAttributes); From 28f6c2622ea067266af6c9ef41faef61329a2326 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 15 Nov 2021 13:54:15 +0100 Subject: [PATCH 062/668] Create custom command to create materials Use a custom command, to set the inMesh and outMesh values in order to support meshes with and without construction history --- src/serlio/CMakeLists.txt | 2 + src/serlio/materials/MaterialCommand.cpp | 178 +++++++++++++++++++++++ src/serlio/materials/MaterialCommand.h | 41 ++++++ src/serlio/scripts/serlioCreateUI.mel | 16 +- src/serlio/serlioPlugin.cpp | 5 + 5 files changed, 230 insertions(+), 12 deletions(-) create mode 100644 src/serlio/materials/MaterialCommand.cpp create mode 100644 src/serlio/materials/MaterialCommand.h diff --git a/src/serlio/CMakeLists.txt b/src/serlio/CMakeLists.txt index 7460db0b..3a3d5afd 100644 --- a/src/serlio/CMakeLists.txt +++ b/src/serlio/CMakeLists.txt @@ -20,6 +20,7 @@ add_library(${SERLIO_TARGET} SHARED materials/MaterialInfo.cpp materials/MaterialUtils.cpp materials/StingrayMaterialNode.cpp + materials/MaterialCommand.cpp utils/Utilities.cpp utils/ResolveMapCache.cpp utils/MayaUtilities.cpp @@ -44,6 +45,7 @@ if (CMAKE_GENERATOR MATCHES "Visual Studio.+") materials/MaterialInfo.h materials/MaterialUtils.h materials/StingrayMaterialNode.h + materials/MaterialCommand.h utils/Utilities.h utils/ResolveMapCache.h utils/MayaUtilities.h diff --git a/src/serlio/materials/MaterialCommand.cpp b/src/serlio/materials/MaterialCommand.cpp new file mode 100644 index 00000000..cc761e30 --- /dev/null +++ b/src/serlio/materials/MaterialCommand.cpp @@ -0,0 +1,178 @@ +/** + * Serlio - Esri CityEngine Plugin for Autodesk Maya + * + * See https://github.com/esri/serlio for build and usage instructions. + * + * Copyright (c) 2012-2019 Esri R&D Center Zurich + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "materials/StingrayMaterialNode.h" +#include "materials/ArnoldMaterialNode.h" +#include "materials/MaterialCommand.h" + +#include "utils/MayaUtilities.h" + +#include "maya/MArgList.h" +#include "maya/MFloatPointArray.h" +#include "maya/MFnMesh.h" +#include "maya/MGlobal.h" +#include "maya/MItDependencyGraph.h" +#include "maya/MItSelectionList.h" + +bool MaterialCommand::isUndoable() const { + return true; +} + +MStatus MaterialCommand::doIt(const MArgList& argList) { + MStatus status; + + if (argList.length() == 0) { + cerr << "Material type expected (stingray/arnold)" << endl; + displayError("Material type expected (stingray/arnold)"); + return MS::kFailure; + } + else if (argList.length() > 1) { + cerr << "Only one material type expected" << endl; + displayError("Only one material type expected"); + return MS::kFailure; + } + + MString materialType = argList.asString(0, &status); + MCHECK(status); + + MTypeId materialTypeId; + if (materialType == "stingray") { + materialTypeId = StingrayMaterialNode::id; + } + else if (materialType == "arnold") { + materialTypeId = ArnoldMaterialNode::id; + } + else { + cerr << "Material type expected (stingray/arnold)" << endl; + displayError("Material type expected (stingray/arnold)"); + return MS::kFailure; + } + + // Parse the selection list for selected components of the right type. + MSelectionList selList; + MGlobal::getActiveSelectionList(selList); + MItSelectionList selListIter(selList); + selListIter.setFilter(MFn::kMesh); + + bool found = false; + bool foundMultiple = false; + + for (; !selListIter.isDone(); selListIter.next()) { + MDagPath dagPath; + MObject component; + selListIter.getDagPath(dagPath, component); + + if (!found) { + // Ensure that this DAG path will point to the shape + // of our object. Set the DAG path for the polyModifierCmd. + if ((dagPath.extendToShape() == MStatus::kSuccess) || + (dagPath.extendToShapeDirectlyBelow(0) == MStatus::kSuccess)) { + fDagPath = dagPath; + found = true; + } + } + else { + foundMultiple = true; + break; + } + } + if (foundMultiple) { + displayWarning("Found more than one object with selected components."); + displayWarning("Only operating on first found object."); + } + + if (found) { + MObject materialDependencyNodeObject = fDGModifier.createNode(materialTypeId); + MFnDependencyNode materialDependencyNode(materialDependencyNodeObject); + + MPlug materialInMesh = materialDependencyNode.findPlug("inMesh", true, &status); + MPlug materialOutMesh = materialDependencyNode.findPlug("outMesh", true, &status); + + MObject rootObj = fDagPath.node(); + MFnDependencyNode rootNode(rootObj); + + MPlug geometryInMesh = rootNode.findPlug("inMesh", true, &status); + MPlug geometryOutMesh = rootNode.findPlug("outMesh", true, &status); + + if (MS::kSuccess != status) { + MGlobal::displayError("Status failed: no inMesh Attribute on node \"" + rootNode.name() + "\""); + return status; + } + //has construction history + if (geometryInMesh.isConnected()) { + MPlugArray parentPlugArray; + MPlug serlioOutMesh; + + geometryInMesh.connectedTo(parentPlugArray, true, false); + + serlioOutMesh = parentPlugArray[0]; + + fDGModifier.disconnect(serlioOutMesh, geometryInMesh); + fDGModifier.connect(serlioOutMesh, materialInMesh); + fDGModifier.connect(materialOutMesh, geometryInMesh); + } + // has no construction history + else { + MDataHandle meshHandle = geometryOutMesh.asMDataHandle(); + materialInMesh.setMDataHandle(meshHandle); + fDGModifier.connect(materialOutMesh, geometryInMesh); + } + + status = fDGModifier.doIt(); + + if (status == MS::kSuccess) { + setResult("PRT command succeeded!"); + } + else { + displayError("PRT command failed!"); + } + } + else { + displayError("PRT command failed: Unable to find selected components"); + status = MS::kFailure; + } + + return status; +} + +MStatus MaterialCommand::redoIt() { + MStatus status; + status = fDGModifier.doIt(); + + if (status == MS::kSuccess) { + setResult("PRT command succeeded!"); + } + else { + displayError("PRT command failed!"); + } + + return status; +} + +MStatus MaterialCommand::undoIt() { + MStatus status; + status = fDGModifier.undoIt(); + if (status == MS::kSuccess) { + setResult("PRT undo succeeded!"); + } + else { + setResult("PRT undo failed!"); + } + return status; +} \ No newline at end of file diff --git a/src/serlio/materials/MaterialCommand.h b/src/serlio/materials/MaterialCommand.h new file mode 100644 index 00000000..71ee952f --- /dev/null +++ b/src/serlio/materials/MaterialCommand.h @@ -0,0 +1,41 @@ +/** + * Serlio - Esri CityEngine Plugin for Autodesk Maya + * + * See https://github.com/esri/serlio for build and usage instructions. + * + * Copyright (c) 2012-2019 Esri R&D Center Zurich + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +#include "PRTContext.h" + +class MaterialCommand : public MPxCommand { +public: + bool isUndoable() const override; + + MStatus doIt(const MArgList&) override; + MStatus redoIt() override; + MStatus undoIt() override; + +private: + MDGModifier fDGModifier; + MDagModifier fDagModifier; + MDagPath fDagPath; +}; diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index abf1306f..e4f9fc75 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -70,12 +70,8 @@ global proc createPrtMaterialNode() { } for($node in $initialShapes) { - $prtMaterialName = `createNode serlioMaterial`; - $im = `connectionInfo -sfd ($node +".inMesh")`; - if (size($im)>0) { - connectAttr -force ( $prtMaterialName + ".outMesh" ) ( $node + ".inMesh" ); - connectAttr -force ( $im ) ( $prtMaterialName + ".inMesh" ); - } + select -r $node; + createMaterial "stingray"; } } @@ -89,12 +85,8 @@ global proc createArnoldMaterialNode() { } for($node in $initialShapes) { - $prtMaterialName = `createNode serlioArnoldMaterial`; - $im = `connectionInfo -sfd ($node +".inMesh")`; - if (size($im)>0) { - connectAttr -force ( $prtMaterialName + ".outMesh" ) ( $node + ".inMesh" ); - connectAttr -force ( $im ) ( $prtMaterialName + ".inMesh" ); - } + select -r $node; + createMaterial "arnold"; } } diff --git a/src/serlio/serlioPlugin.cpp b/src/serlio/serlioPlugin.cpp index aa593499..aee74196 100644 --- a/src/serlio/serlioPlugin.cpp +++ b/src/serlio/serlioPlugin.cpp @@ -25,6 +25,7 @@ #include "materials/ArnoldMaterialNode.h" #include "materials/StingrayMaterialNode.h" +#include "materials/MaterialCommand.h" #include "utils/MayaUtilities.h" @@ -42,6 +43,7 @@ constexpr bool DBG = false; constexpr const char* NODE_MODIFIER = "serlio"; constexpr const char* NODE_MATERIAL = "serlioMaterial"; constexpr const char* NODE_ARNOLD_MATERIAL = "serlioArnoldMaterial"; +constexpr const char* CMD_CREATE_MATERIAL = "createMaterial"; constexpr const char* CMD_ASSIGN = "serlioAssign"; constexpr const char* MEL_PROC_CREATE_UI = "serlioCreateUI"; constexpr const char* MEL_PROC_DELETE_UI = "serlioDeleteUI"; @@ -71,6 +73,9 @@ MStatus initializePlugin(MObject obj) { auto createModifierCommand = []() { return (void*)new PRTModifierCommand(); }; MCHECK(plugin.registerCommand(CMD_ASSIGN, createModifierCommand)); + auto createMaterialCommand = []() { return (void*)new MaterialCommand(); }; + MCHECK(plugin.registerCommand(CMD_CREATE_MATERIAL, createMaterialCommand)); + auto createModifierNode = []() { return (void*)new PRTModifierNode(); }; MCHECK(plugin.registerNode(NODE_MODIFIER, PRTModifierNode::id, createModifierNode, PRTModifierNode::initialize)); From be54e2c6116154449b573b51e42b66e0f147e957 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 15 Nov 2021 15:27:44 +0100 Subject: [PATCH 063/668] Set the meshData directly if we have no construction history --- src/serlio/modifiers/MayaCallbacks.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index a1c042b4..9a139b4a 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -35,6 +35,7 @@ #include "maya/MFnMeshData.h" #include "maya/adskDataAssociations.h" #include "maya/adskDataStream.h" +#include "maya/MFnDependencyNode.h" #include #include @@ -392,6 +393,16 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c } outputMesh.setMetadata(newMetadata); + + //manually set the plug value, since copyInPlace is broken for meshes without construction history + if (outMeshObj.apiType() == MFn::Type::kMesh) { + mFnMesh1.setMetadata(newMetadata); + + MFnDependencyNode depNodeFn; + depNodeFn.setObject(outMeshObj); + MPlug meshNodeOutMeshPlug = depNodeFn.findPlug("inMesh", true); + meshNodeOutMeshPlug.setValue(newOutputData); + } } prt::Status MayaCallbacks::attrBool(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* key, bool value) { From 2a231e07a2d01714bb5979e5915e759bd1d42dc0 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 15 Nov 2021 16:32:13 +0100 Subject: [PATCH 064/668] Remove tweaks from mesh without construction history before editing it --- src/serlio/modifiers/PRTModifierAction.cpp | 31 +++++++++++++++++++++ src/serlio/modifiers/PRTModifierAction.h | 1 + src/serlio/modifiers/PRTModifierCommand.cpp | 1 + 3 files changed, 33 insertions(+) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 80a59bee..85014640 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -324,6 +324,37 @@ MStatus PRTModifierAction::updateRuleFiles(const MObject& node, const MString& r return MS::kSuccess; } +MStatus PRTModifierAction::clearTweaks(MObject mesh) { + MStatus stat; + MFnDependencyNode depNodeFn; + depNodeFn.setObject(mesh); + bool fHasTweaks = false; + MPlug tweakPlug = depNodeFn.findPlug("pnts", true); + if (!tweakPlug.isNull()) { + if (!tweakPlug.isArray()) { + return MStatus::kFailure; + } + + MPlug tweak; + MFloatVector tweakData; + int i; + int numElements = tweakPlug.numElements(); + + for (i = 0; i < numElements; i++) { + tweak = tweakPlug.elementByPhysicalIndex(i, &stat); + if (stat == MS::kSuccess && !tweak.isNull()) { + + MFnNumericData numDataFn; + MObject object = numDataFn.create(MFnNumericData::k3Float); + numDataFn.setData(0, 0, 0); + tweak.setValue(object); + } + } + } + + return stat; +} + MStatus PRTModifierAction::doIt() { MStatus status; diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 2d5559c1..921e06ab 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -72,6 +72,7 @@ class PRTModifierAction : public polyModifierFty { void setRandomSeed(int32_t randomSeed) { mRandomSeed = randomSeed; }; + MStatus clearTweaks(MObject mesh); // polyModifierFty inherited methods MStatus doIt() override; diff --git a/src/serlio/modifiers/PRTModifierCommand.cpp b/src/serlio/modifiers/PRTModifierCommand.cpp index f786369e..e835645b 100644 --- a/src/serlio/modifiers/PRTModifierCommand.cpp +++ b/src/serlio/modifiers/PRTModifierCommand.cpp @@ -156,6 +156,7 @@ MStatus PRTModifierCommand::directModifier(MObject mesh) { fPRTModifierAction.setMesh(mesh, mesh); fPRTModifierAction.setRandomSeed(mInitialSeed); fPRTModifierAction.updateRuleFiles(MObject::kNullObj, mRulePkg); + fPRTModifierAction.clearTweaks(mesh); // Now, perform the PRT action status = fPRTModifierAction.doIt(); From 6e95eda0576f675da7e1463eab08c3ebd6496565 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 15 Nov 2021 18:20:41 +0100 Subject: [PATCH 065/668] Cleanup: remove obsolete tag --- src/serlio/modifiers/polyModifier/polyModifierCmd.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp b/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp index d4592a14..6248e877 100644 --- a/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp +++ b/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp @@ -1017,7 +1017,7 @@ MStatus polyModifierCmd::undoCachedMesh() // Need to force a DG evaluation now that the input has been changed. // - MString cmd( "dgeval -src " ); + MString cmd( "dgeval " ); cmd += meshNodeName; cmd += ".inMesh"; status = MGlobal::executeCommand( cmd, false, false ); @@ -1135,7 +1135,7 @@ MStatus polyModifierCmd::undoDirectModifier() // Need to force a DG evaluation now that the input has been changed. // - MString cmd("dgeval -src "); + MString cmd("dgeval "); cmd += meshNodeName; cmd += ".inMesh"; status = MGlobal::executeCommand( cmd, false, false ); From 6b1341cfa7a61ebff880366bcf7f9e0651aaecf7 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 15 Nov 2021 18:21:25 +0100 Subject: [PATCH 066/668] Update mesh surface after undo Otherwise the UI is not properly updated --- src/serlio/modifiers/polyModifier/polyModifierCmd.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp b/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp index 6248e877..b60169bf 100644 --- a/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp +++ b/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include @@ -1161,6 +1162,8 @@ MStatus polyModifierCmd::undoDirectModifier() status = meshNodeOutMeshPlug.setValue( fMeshData ); MCheckStatus( status, "Could not set meshData" ); } + MFnMesh fnMesh(meshNode); + fnMesh.updateSurface(); return status; } From 8fe9e79a8d4ae42142486d9d26a634f0e7ed079e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 16 Nov 2021 17:30:46 +0100 Subject: [PATCH 067/668] Update attribute tests --- src/test/tests.cpp | 76 +++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/src/test/tests.cpp b/src/test/tests.cpp index 87a0ba2c..d8dbc609 100644 --- a/src/test/tests.cpp +++ b/src/test/tests.cpp @@ -144,32 +144,32 @@ const AttributeGroup AG_B = {L"b"}; const AttributeGroup AG_BK = {L"b", L"k"}; const AttributeGroup AG_BKP = {L"b", L"k", L"p"}; -RuleAttribute getAttr(std::wstring fqName, AttributeGroup ag, int o, int go, std::wstring rf, bool sr) { - return RuleAttribute{fqName, L"", L"", L"", prt::AAT_UNKNOWN, ag, o, go, rf, sr}; +RuleAttribute getAttr(std::wstring fqName, AttributeGroup ag, int o, int go, std::wstring rf,int ro, bool sr) { + return RuleAttribute{fqName, L"", L"", L"", prt::AAT_UNKNOWN, ag, o, go, rf,ro, sr}; } TEST_CASE("global group order") { - const RuleAttribute A = getAttr(L"style$A", AG_BK, ORDER_NONE, ORDER_NONE, L"foo", true); - const RuleAttribute B = getAttr(L"style$B", AG_BK, ORDER_NONE, ORDER_NONE, L"foo", true); - const RuleAttribute C = getAttr(L"style$C", AG_BKP, ORDER_NONE, 10, L"foo", true); - const RuleAttribute D = getAttr(L"style$D", AG_A, ORDER_NONE, 20, L"foo", true); - const RuleAttribute E = getAttr(L"style$E", AG_AK, ORDER_NONE, ORDER_NONE, L"foo", true); + const RuleAttribute A = getAttr(L"style$A", AG_BK, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); + const RuleAttribute B = getAttr(L"style$B", AG_BK, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); + const RuleAttribute C = getAttr(L"style$C", AG_BKP, ORDER_NONE, 10, L"foo", ORDER_NONE, true); + const RuleAttribute D = getAttr(L"style$D", AG_A, ORDER_NONE, 20, L"foo", ORDER_NONE, true); + const RuleAttribute E = getAttr(L"style$E", AG_AK, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); const RuleAttributes inp = {A, B, C, D, E}; const AttributeGroupOrder ago = getGlobalGroupOrder(inp); CHECK(ago.size() == 5); - CHECK(ago.at(AG_BKP) == 10); - CHECK(ago.at(AG_BK) == 10); - CHECK(ago.at(AG_B) == 10); - CHECK(ago.at(AG_AK) == ORDER_NONE); - CHECK(ago.at(AG_A) == 20); + CHECK(ago.at(std::make_pair( L"foo",AG_BKP)) == 10); + CHECK(ago.at(std::make_pair( L"foo",AG_BK)) == 10); + CHECK(ago.at(std::make_pair( L"foo",AG_B)) == 10); + CHECK(ago.at(std::make_pair( L"foo",AG_AK)) == ORDER_NONE); + CHECK(ago.at(std::make_pair( L"foo",AG_A)) == 20); } TEST_CASE("rule attribute sorting") { SECTION("rule file 1") { - const RuleAttribute A = getAttr(L"style$A", AG_NONE, ORDER_NONE, ORDER_NONE, L"bar", true); - const RuleAttribute B = getAttr(L"style$B", AG_NONE, ORDER_NONE, ORDER_NONE, L"foo", false); + const RuleAttribute A = getAttr(L"style$A", AG_NONE, ORDER_NONE, ORDER_NONE, L"bar", ORDER_NONE, true); + const RuleAttribute B = getAttr(L"style$B", AG_NONE, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, false); RuleAttributes inp = {B, A}; const RuleAttributes exp = {A, B}; @@ -178,8 +178,8 @@ TEST_CASE("rule attribute sorting") { } SECTION("rule file 2") { - const RuleAttribute A = getAttr(L"style$A", AG_NONE, ORDER_NONE, ORDER_NONE, L"bar", false); - const RuleAttribute B = getAttr(L"style$B", AG_NONE, ORDER_NONE, ORDER_NONE, L"foo", true); + const RuleAttribute A = getAttr(L"style$A", AG_NONE, ORDER_NONE, ORDER_NONE, L"bar", ORDER_NONE, false); + const RuleAttribute B = getAttr(L"style$B", AG_NONE, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); RuleAttributes inp = {B, A}; const RuleAttributes exp = {B, A}; @@ -188,8 +188,8 @@ TEST_CASE("rule attribute sorting") { } SECTION("group order") { - const RuleAttribute A = getAttr(L"style$A", {L"foo"}, ORDER_NONE, 0, L"foo", true); - const RuleAttribute B = getAttr(L"style$B", {L"foo"}, ORDER_NONE, 1, L"foo", true); + const RuleAttribute A = getAttr(L"style$A", {L"foo"}, ORDER_NONE, 0, L"foo", ORDER_NONE, true); + const RuleAttribute B = getAttr(L"style$B", {L"foo"}, ORDER_NONE, 1, L"foo", ORDER_NONE, true); RuleAttributes inp = {B, A}; const RuleAttributes exp = {A, B}; @@ -198,8 +198,8 @@ TEST_CASE("rule attribute sorting") { } SECTION("nested groups") { - const RuleAttribute A = getAttr(L"style$A", {L"foo", L"bar"}, ORDER_NONE, ORDER_NONE, L"bar", true); - const RuleAttribute B = getAttr(L"style$B", {L"foo"}, ORDER_NONE, ORDER_NONE, L"foo", true); + const RuleAttribute A = getAttr(L"style$A", {L"foo", L"bar"}, ORDER_NONE, ORDER_NONE, L"bar", ORDER_NONE, true); + const RuleAttribute B = getAttr(L"style$B", {L"foo"}, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); RuleAttributes inp = {A, B}; const RuleAttributes exp = {B, A}; @@ -208,8 +208,8 @@ TEST_CASE("rule attribute sorting") { } SECTION("nested groups disjunct") { - const RuleAttribute A = getAttr(L"style$A", {L"foo1", L"bar"}, ORDER_NONE, ORDER_NONE, L"bar", true); - const RuleAttribute B = getAttr(L"style$B", {L"foo"}, ORDER_NONE, ORDER_NONE, L"foo", true); + const RuleAttribute A = getAttr(L"style$A", {L"foo1", L"bar"}, ORDER_NONE, ORDER_NONE, L"bar", ORDER_NONE, true); + const RuleAttribute B = getAttr(L"style$B", {L"foo"}, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); RuleAttributes inp = {A, B}; const RuleAttributes exp = {B, A}; @@ -218,9 +218,9 @@ TEST_CASE("rule attribute sorting") { } SECTION("nested groups on same level") { - const RuleAttribute A = getAttr(L"style$A", {L"foo", L"bar"}, ORDER_NONE, ORDER_NONE, L"foo", true); - const RuleAttribute B = getAttr(L"style$B", {L"foo"}, ORDER_NONE, ORDER_NONE, L"foo", true); - const RuleAttribute C = getAttr(L"style$C", {L"foo", L"baz"}, ORDER_NONE, ORDER_NONE, L"foo", true); + const RuleAttribute A = getAttr(L"style$A", {L"foo", L"bar"}, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); + const RuleAttribute B = getAttr(L"style$B", {L"foo"}, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); + const RuleAttribute C = getAttr(L"style$C", {L"foo", L"baz"}, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); RuleAttributes inp = {C, A, B}; const RuleAttributes exp = {B, A, C}; @@ -229,9 +229,9 @@ TEST_CASE("rule attribute sorting") { } SECTION("nested groups with group order") { - const RuleAttribute A = getAttr(L"style$A", {L"foo", L"bar"}, ORDER_NONE, ORDER_NONE, L"foo", true); - const RuleAttribute B = getAttr(L"style$B", {L"foo"}, ORDER_NONE, ORDER_NONE, L"foo", true); - const RuleAttribute C = getAttr(L"style$C", {L"foo", L"baz"}, ORDER_NONE, 0, L"foo", true); + const RuleAttribute A = getAttr(L"style$A", {L"foo", L"bar"}, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); + const RuleAttribute B = getAttr(L"style$B", {L"foo"}, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); + const RuleAttribute C = getAttr(L"style$C", {L"foo", L"baz"}, ORDER_NONE, 0, L"foo", ORDER_NONE, true); RuleAttributes inp = {C, A, B}; const RuleAttributes exp = {B, C, A}; @@ -240,11 +240,11 @@ TEST_CASE("rule attribute sorting") { } SECTION("all properties") { - const RuleAttribute A = getAttr(L"style$A", {L"First1", L"Second1", L"Third1"}, ORDER_NONE, 0, L"foo", true); - const RuleAttribute B = getAttr(L"style$B", {L"First"}, ORDER_NONE, 3, L"foo", true); - const RuleAttribute C = getAttr(L"style$C", {L"First", L"Second"}, 0, 2, L"foo", true); - const RuleAttribute D = getAttr(L"style$D", {L"First", L"Second"}, 1, 2, L"foo", true); - const RuleAttribute E = getAttr(L"style$E", {L"First", L"Second", L"Third"}, ORDER_NONE, 1, L"foo", true); + const RuleAttribute A = getAttr(L"style$A", {L"First1", L"Second1", L"Third1"}, ORDER_NONE, 0, L"foo", ORDER_NONE, true); + const RuleAttribute B = getAttr(L"style$B", {L"First"}, ORDER_NONE, 3, L"foo", ORDER_NONE, true); + const RuleAttribute C = getAttr(L"style$C", {L"First", L"Second"}, 0, 2, L"foo", ORDER_NONE, true); + const RuleAttribute D = getAttr(L"style$D", {L"First", L"Second"}, 1, 2, L"foo", ORDER_NONE, true); + const RuleAttribute E = getAttr(L"style$E", {L"First", L"Second", L"Third"}, ORDER_NONE, 1, L"foo", ORDER_NONE, true); RuleAttributes inp = {B, A, C, D, E}; const RuleAttributes exp = {A, B, C, D, E}; @@ -255,11 +255,11 @@ TEST_CASE("rule attribute sorting") { SECTION("review example") { // b k < b k p (group order=10) < a (group order=20) < a k < b k - const RuleAttribute A = getAttr(L"style$A", AG_BK, ORDER_NONE, ORDER_NONE, L"foo", true); - const RuleAttribute B = getAttr(L"style$B", AG_BK, ORDER_NONE, ORDER_NONE, L"foo", true); - const RuleAttribute C = getAttr(L"style$C", AG_BKP, ORDER_NONE, 10, L"foo", true); - const RuleAttribute D = getAttr(L"style$D", AG_A, ORDER_NONE, 20, L"foo", true); - const RuleAttribute E = getAttr(L"style$E", AG_AK, ORDER_NONE, ORDER_NONE, L"foo", true); + const RuleAttribute A = getAttr(L"style$A", AG_BK, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); + const RuleAttribute B = getAttr(L"style$B", AG_BK, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); + const RuleAttribute C = getAttr(L"style$C", AG_BKP, ORDER_NONE, 10, L"foo", ORDER_NONE, true); + const RuleAttribute D = getAttr(L"style$D", AG_A, ORDER_NONE, 20, L"foo", ORDER_NONE, true); + const RuleAttribute E = getAttr(L"style$E", AG_AK, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); RuleAttributes inp = {A, B, C, D, E}; const RuleAttributes exp = {A, B, C, D, E}; From 4d4defe022e8a1b9bb74544917d504239147f026 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 16 Nov 2021 23:15:27 +0100 Subject: [PATCH 068/668] Renamed rulenames in tests Otherwise the attributes are sorted by rulename and not by groups and fail the tests --- src/test/tests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/tests.cpp b/src/test/tests.cpp index d8dbc609..98f214da 100644 --- a/src/test/tests.cpp +++ b/src/test/tests.cpp @@ -198,7 +198,7 @@ TEST_CASE("rule attribute sorting") { } SECTION("nested groups") { - const RuleAttribute A = getAttr(L"style$A", {L"foo", L"bar"}, ORDER_NONE, ORDER_NONE, L"bar", ORDER_NONE, true); + const RuleAttribute A = getAttr(L"style$A", {L"foo", L"bar"}, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); const RuleAttribute B = getAttr(L"style$B", {L"foo"}, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); RuleAttributes inp = {A, B}; @@ -208,7 +208,7 @@ TEST_CASE("rule attribute sorting") { } SECTION("nested groups disjunct") { - const RuleAttribute A = getAttr(L"style$A", {L"foo1", L"bar"}, ORDER_NONE, ORDER_NONE, L"bar", ORDER_NONE, true); + const RuleAttribute A = getAttr(L"style$A", {L"foo1", L"bar"}, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); const RuleAttribute B = getAttr(L"style$B", {L"foo"}, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); RuleAttributes inp = {A, B}; From dd3a33d74f3e3a9c273f390658b2ddea82f506cc Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 18 Nov 2021 13:49:59 +0100 Subject: [PATCH 069/668] Cleanup: renamed custom mel command for consistency --- src/serlio/scripts/serlioCreateUI.mel | 4 ++-- src/serlio/serlioPlugin.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index e4f9fc75..c232ee3c 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -71,7 +71,7 @@ global proc createPrtMaterialNode() { for($node in $initialShapes) { select -r $node; - createMaterial "stingray"; + serlioCreateMaterial "stingray"; } } @@ -86,7 +86,7 @@ global proc createArnoldMaterialNode() { for($node in $initialShapes) { select -r $node; - createMaterial "arnold"; + serlioCreateMaterial "arnold"; } } diff --git a/src/serlio/serlioPlugin.cpp b/src/serlio/serlioPlugin.cpp index aee74196..1ba16440 100644 --- a/src/serlio/serlioPlugin.cpp +++ b/src/serlio/serlioPlugin.cpp @@ -43,7 +43,7 @@ constexpr bool DBG = false; constexpr const char* NODE_MODIFIER = "serlio"; constexpr const char* NODE_MATERIAL = "serlioMaterial"; constexpr const char* NODE_ARNOLD_MATERIAL = "serlioArnoldMaterial"; -constexpr const char* CMD_CREATE_MATERIAL = "createMaterial"; +constexpr const char* CMD_CREATE_MATERIAL = "serlioCreateMaterial"; constexpr const char* CMD_ASSIGN = "serlioAssign"; constexpr const char* MEL_PROC_CREATE_UI = "serlioCreateUI"; constexpr const char* MEL_PROC_DELETE_UI = "serlioDeleteUI"; From 4b871b5f93f8897f919e4d6ed3118413dd4c7b12 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 18 Nov 2021 13:52:40 +0100 Subject: [PATCH 070/668] Cleanup: removed cerr --- src/serlio/materials/MaterialCommand.cpp | 3 --- src/serlio/modifiers/PRTModifierCommand.cpp | 1 - 2 files changed, 4 deletions(-) diff --git a/src/serlio/materials/MaterialCommand.cpp b/src/serlio/materials/MaterialCommand.cpp index cc761e30..53226570 100644 --- a/src/serlio/materials/MaterialCommand.cpp +++ b/src/serlio/materials/MaterialCommand.cpp @@ -38,12 +38,10 @@ MStatus MaterialCommand::doIt(const MArgList& argList) { MStatus status; if (argList.length() == 0) { - cerr << "Material type expected (stingray/arnold)" << endl; displayError("Material type expected (stingray/arnold)"); return MS::kFailure; } else if (argList.length() > 1) { - cerr << "Only one material type expected" << endl; displayError("Only one material type expected"); return MS::kFailure; } @@ -59,7 +57,6 @@ MStatus MaterialCommand::doIt(const MArgList& argList) { materialTypeId = ArnoldMaterialNode::id; } else { - cerr << "Material type expected (stingray/arnold)" << endl; displayError("Material type expected (stingray/arnold)"); return MS::kFailure; } diff --git a/src/serlio/modifiers/PRTModifierCommand.cpp b/src/serlio/modifiers/PRTModifierCommand.cpp index e835645b..ec48e630 100644 --- a/src/serlio/modifiers/PRTModifierCommand.cpp +++ b/src/serlio/modifiers/PRTModifierCommand.cpp @@ -40,7 +40,6 @@ MStatus PRTModifierCommand::doIt(const MArgList& argList) { mRulePkg = argList.asString(0); } else { - cerr << "Expecting one parameter: the rpk name path" << endl; displayError(" Expecting one parameter: the operation type."); return MS::kFailure; } From eaae7dd6c425f8d66af6d4fc8a2aaf123607383c Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 18 Nov 2021 13:53:04 +0100 Subject: [PATCH 071/668] Cleanup: merge multiple display warnings --- src/serlio/materials/MaterialCommand.cpp | 3 +-- src/serlio/modifiers/PRTModifierCommand.cpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/serlio/materials/MaterialCommand.cpp b/src/serlio/materials/MaterialCommand.cpp index 53226570..da82d605 100644 --- a/src/serlio/materials/MaterialCommand.cpp +++ b/src/serlio/materials/MaterialCommand.cpp @@ -90,8 +90,7 @@ MStatus MaterialCommand::doIt(const MArgList& argList) { } } if (foundMultiple) { - displayWarning("Found more than one object with selected components."); - displayWarning("Only operating on first found object."); + displayWarning("Found more than one object with selected components. Only operating on first found object."); } if (found) { diff --git a/src/serlio/modifiers/PRTModifierCommand.cpp b/src/serlio/modifiers/PRTModifierCommand.cpp index ec48e630..2ea19278 100644 --- a/src/serlio/modifiers/PRTModifierCommand.cpp +++ b/src/serlio/modifiers/PRTModifierCommand.cpp @@ -73,8 +73,7 @@ MStatus PRTModifierCommand::doIt(const MArgList& argList) { } } if (foundMultiple) { - displayWarning("Found more than one object with selected components."); - displayWarning("Only operating on first found object."); + displayWarning("Found more than one object with selected components. Only operating on first found object."); } // Initialize the polyModifierCmd node type - mesh node already set From 35aa4a89b50c5a853d0cb2b5debf094b584cacff Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Fri, 19 Nov 2021 10:03:01 +0100 Subject: [PATCH 072/668] bug fix: include all extensions libraries from PRT 2.5 which provide decoders and adaptors --- src/serlio/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/CMakeLists.txt b/src/serlio/CMakeLists.txt index 71bb4907..c6ce5995 100644 --- a/src/serlio/CMakeLists.txt +++ b/src/serlio/CMakeLists.txt @@ -135,7 +135,7 @@ install(FILES ${SRL_PRT_LIBRARIES} DESTINATION ${INSTALL_FOLDER_PREFIX}/plug-ins # PRT: whitelist required extension libraries set(SRL_PRT_EXT_LIBRARIES ${PRT_EXT_LIBRARIES}) -list(FILTER SRL_PRT_EXT_LIBRARIES INCLUDE REGEX "com\\.esri\\.prt\\.codecs|com\\.esri\\.prt\\.adaptors|VueExport") +list(FILTER SRL_PRT_EXT_LIBRARIES INCLUDE REGEX "com\\.esri\\.prt\\.codecs|com\\.esri\\.prt\\.adaptors|com\\.esri\\.prt\\.oda|com\\.esri\\.prt\\.alembic|com\\.esri\\.prt\\.usd|usd_ms|tbb") install(FILES ${SRL_PRT_EXT_LIBRARIES} DESTINATION ${INSTALL_FOLDER_PREFIX}/plug-ins/ext) # doc From 3c3e16697008a9190286d949ea94f26877a1284b Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Fri, 19 Nov 2021 10:11:12 +0100 Subject: [PATCH 073/668] cleanup: more compact regex notation --- src/serlio/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/CMakeLists.txt b/src/serlio/CMakeLists.txt index c6ce5995..bdf79358 100644 --- a/src/serlio/CMakeLists.txt +++ b/src/serlio/CMakeLists.txt @@ -135,7 +135,7 @@ install(FILES ${SRL_PRT_LIBRARIES} DESTINATION ${INSTALL_FOLDER_PREFIX}/plug-ins # PRT: whitelist required extension libraries set(SRL_PRT_EXT_LIBRARIES ${PRT_EXT_LIBRARIES}) -list(FILTER SRL_PRT_EXT_LIBRARIES INCLUDE REGEX "com\\.esri\\.prt\\.codecs|com\\.esri\\.prt\\.adaptors|com\\.esri\\.prt\\.oda|com\\.esri\\.prt\\.alembic|com\\.esri\\.prt\\.usd|usd_ms|tbb") +list(FILTER SRL_PRT_EXT_LIBRARIES INCLUDE REGEX "com\\.esri\\.prt\\.(codecs|adaptors|oda|alembic|usd)|tbb|usd_ms") install(FILES ${SRL_PRT_EXT_LIBRARIES} DESTINATION ${INSTALL_FOLDER_PREFIX}/plug-ins/ext) # doc From 571eeaf84e61059700fe52f514289d906b3f9224 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Fri, 19 Nov 2021 10:31:17 +0100 Subject: [PATCH 074/668] fix regression: do not include alembic extension library (only encoder) --- src/serlio/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/CMakeLists.txt b/src/serlio/CMakeLists.txt index bdf79358..f8301ff6 100644 --- a/src/serlio/CMakeLists.txt +++ b/src/serlio/CMakeLists.txt @@ -135,7 +135,7 @@ install(FILES ${SRL_PRT_LIBRARIES} DESTINATION ${INSTALL_FOLDER_PREFIX}/plug-ins # PRT: whitelist required extension libraries set(SRL_PRT_EXT_LIBRARIES ${PRT_EXT_LIBRARIES}) -list(FILTER SRL_PRT_EXT_LIBRARIES INCLUDE REGEX "com\\.esri\\.prt\\.(codecs|adaptors|oda|alembic|usd)|tbb|usd_ms") +list(FILTER SRL_PRT_EXT_LIBRARIES INCLUDE REGEX "com\\.esri\\.prt\\.(codecs|adaptors|oda|usd)|tbb|usd_ms") install(FILES ${SRL_PRT_EXT_LIBRARIES} DESTINATION ${INSTALL_FOLDER_PREFIX}/plug-ins/ext) # doc From b570095b1aa8178caa1cac750b9009ebbd55b4e8 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Fri, 19 Nov 2021 10:40:12 +0100 Subject: [PATCH 075/668] fix regression: also include the resource files required by USD --- src/serlio/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/serlio/CMakeLists.txt b/src/serlio/CMakeLists.txt index f8301ff6..8175f338 100644 --- a/src/serlio/CMakeLists.txt +++ b/src/serlio/CMakeLists.txt @@ -137,6 +137,7 @@ install(FILES ${SRL_PRT_LIBRARIES} DESTINATION ${INSTALL_FOLDER_PREFIX}/plug-ins set(SRL_PRT_EXT_LIBRARIES ${PRT_EXT_LIBRARIES}) list(FILTER SRL_PRT_EXT_LIBRARIES INCLUDE REGEX "com\\.esri\\.prt\\.(codecs|adaptors|oda|usd)|tbb|usd_ms") install(FILES ${SRL_PRT_EXT_LIBRARIES} DESTINATION ${INSTALL_FOLDER_PREFIX}/plug-ins/ext) +install(DIRECTORY "${PRT_EXTENSION_PATH}/usd" DESTINATION ${INSTALL_FOLDER_PREFIX}/plug-ins/ext) # USD resource files # doc install(DIRECTORY ${PROJECT_SOURCE_DIR}/../doc DESTINATION ${INSTALL_FOLDER_PREFIX}) From a4a9ca775717dfdcb74d822769455ac386a6f2ad Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 22 Nov 2021 17:15:20 +0100 Subject: [PATCH 076/668] Cleanup: fix typo --- src/serlio/scripts/serlioCreateUI.mel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index 82098248..e0780963 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -65,7 +65,7 @@ global proc removePrtNode() { string $initialShapes[] = collectInitialShapes($select); if(!(size($initialShapes))) { - confirmDialog -title "Missing selection" -message "Please select shapes first in order to apply a rule." -button "OK"; + confirmDialog -title "Missing selection" -message "Please select shapes first in order to remove a rule." -button "OK"; return; } From 66ae3a03d3e33e37394728894cce39fbae32361a Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 23 Nov 2021 09:29:54 +0100 Subject: [PATCH 077/668] Switch rules instead of adding a second prt node --- src/serlio/scripts/serlioCreateUI.mel | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index abf1306f..dfb46d1f 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -51,10 +51,27 @@ global proc createPrtNode() { string $rulePackage[] = `fileDialog2 -fm 1 -cap "Select Rule Package" -ff $filters`; + int $alreadyHasExistingPrtNode = false; + if(size($rulePackage)) { for($node in $initialShapes) { select -r $node; - serlioAssign $rulePackage; + string $nodeHistory[] = `listHistory $node`; + + for($dpNode in $nodeHistory){ + $dpNodeType = `nodeType $dpNode`; + if($dpNodeType == "serlio"){ + setAttr -type "string" ($dpNode + ".Rule_Package") $rulePackage[0]; + evalDeferred "refreshEditorTemplates()"; + $alreadyHasExistingPrtNode = true; + } + } + + if (!alreadyHasExistingPrtNode){ + select -r $node; + serlioAssign $rulePackage; + } + $alreadyHasExistingPrtNode = false; } } select -r $select; From f539d0bba43977f344f2ce1f9b8da03589cb6720 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 23 Nov 2021 11:03:52 +0100 Subject: [PATCH 078/668] Check for nullptr before accessing its value --- src/serlio/modifiers/MayaCallbacks.cpp | 187 +++++++++++++------------ 1 file changed, 97 insertions(+), 90 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 9a139b4a..111b9e13 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -272,126 +272,133 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c newMetadata.makeUnique(); MCHECK(stat); adsk::Data::Channel newChannel = newMetadata.channel(PRT_MATERIAL_CHANNEL); - adsk::Data::Stream newStream(*fStructure, PRT_MATERIAL_STREAM); - newChannel.setDataStream(newStream); - newMetadata.setChannel(newChannel); + if (fStructure != nullptr) { + adsk::Data::Stream newStream(*fStructure, PRT_MATERIAL_STREAM); - if (faceRangesSize > 1) { + newChannel.setDataStream(newStream); + newMetadata.setChannel(newChannel); - for (size_t fri = 0; fri < faceRangesSize - 1; fri++) { + if (faceRangesSize > 1) { - if (materials != nullptr) { - adsk::Data::Handle handle(*fStructure); + for (size_t fri = 0; fri < faceRangesSize - 1; fri++) { - const prt::AttributeMap* mat = materials[fri]; + if (materials != nullptr) { + adsk::Data::Handle handle(*fStructure); - size_t keyCount = 0; - wchar_t const* const* keys = mat->getKeys(&keyCount); + const prt::AttributeMap* mat = materials[fri]; - for (int k = 0; k < keyCount; k++) { + size_t keyCount = 0; + wchar_t const* const* keys = mat->getKeys(&keyCount); - wchar_t const* key = keys[k]; + for (int k = 0; k < keyCount; k++) { - const std::string keyNarrow = prtu::toOSNarrowFromUTF16(key); + wchar_t const* key = keys[k]; - if (!handle.setPositionByMemberName(keyNarrow.c_str())) - continue; + const std::string keyNarrow = prtu::toOSNarrowFromUTF16(key); - size_t arraySize = 0; + if (!handle.setPositionByMemberName(keyNarrow.c_str())) + continue; - switch (mat->getType(key)) { - case prt::Attributable::PT_BOOL: - handle.asBoolean()[0] = mat->getBool(key); - break; - case prt::Attributable::PT_FLOAT: - handle.asDouble()[0] = mat->getFloat(key); - break; - case prt::Attributable::PT_INT: - handle.asInt32()[0] = mat->getInt(key); - break; + size_t arraySize = 0; - // workaround: transporting string as uint8 array, because using asString crashes maya - case prt::Attributable::PT_STRING: { - const wchar_t* str = mat->getString(key); - if (wcslen(str) == 0) + switch (mat->getType(key)) { + case prt::Attributable::PT_BOOL: + handle.asBoolean()[0] = mat->getBool(key); + break; + case prt::Attributable::PT_FLOAT: + handle.asDouble()[0] = mat->getFloat(key); + break; + case prt::Attributable::PT_INT: + handle.asInt32()[0] = mat->getInt(key); break; - checkStringLength(str, MATERIAL_MAX_STRING_LENGTH); - size_t maxStringLengthTmp = MATERIAL_MAX_STRING_LENGTH; - prt::StringUtils::toOSNarrowFromUTF16(str, (char*)handle.asUInt8(), &maxStringLengthTmp); - break; - } - case prt::Attributable::PT_BOOL_ARRAY: { - const bool* boolArray; - boolArray = mat->getBoolArray(key, &arraySize); - for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) - handle.asBoolean()[i] = boolArray[i]; - break; - } - case prt::Attributable::PT_INT_ARRAY: { - const int* intArray; - intArray = mat->getIntArray(key, &arraySize); - for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) - handle.asInt32()[i] = intArray[i]; - break; - } - case prt::Attributable::PT_FLOAT_ARRAY: { - const double* floatArray; - floatArray = mat->getFloatArray(key, &arraySize); - for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH && i < MATERIAL_MAX_FLOAT_ARRAY_LENGTH; - i++) - handle.asDouble()[i] = floatArray[i]; - break; - } - case prt::Attributable::PT_STRING_ARRAY: { - const wchar_t* const* stringArray = mat->getStringArray(key, &arraySize); + // workaround: transporting string as uint8 array, because using asString crashes maya + case prt::Attributable::PT_STRING: { + const wchar_t* str = mat->getString(key); + if (wcslen(str) == 0) + break; + checkStringLength(str, MATERIAL_MAX_STRING_LENGTH); + size_t maxStringLengthTmp = MATERIAL_MAX_STRING_LENGTH; + prt::StringUtils::toOSNarrowFromUTF16(str, (char*)handle.asUInt8(), + &maxStringLengthTmp); + break; + } + case prt::Attributable::PT_BOOL_ARRAY: { + const bool* boolArray; + boolArray = mat->getBoolArray(key, &arraySize); + for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) + handle.asBoolean()[i] = boolArray[i]; + break; + } + case prt::Attributable::PT_INT_ARRAY: { + const int* intArray; + intArray = mat->getIntArray(key, &arraySize); + for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) + handle.asInt32()[i] = intArray[i]; + break; + } + case prt::Attributable::PT_FLOAT_ARRAY: { + const double* floatArray; + floatArray = mat->getFloatArray(key, &arraySize); + for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH && + i < MATERIAL_MAX_FLOAT_ARRAY_LENGTH; + i++) + handle.asDouble()[i] = floatArray[i]; + break; + } + case prt::Attributable::PT_STRING_ARRAY: { - for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) { - if (wcslen(stringArray[i]) == 0) - continue; + const wchar_t* const* stringArray = mat->getStringArray(key, &arraySize); - if (i > 0) { - std::wstring keyToUse = key + std::to_wstring(i); - const std::string keyToUseNarrow = prtu::toOSNarrowFromUTF16(keyToUse); - if (!handle.setPositionByMemberName(keyToUseNarrow.c_str())) + for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) { + if (wcslen(stringArray[i]) == 0) continue; - } - checkStringLength(stringArray[i], MATERIAL_MAX_STRING_LENGTH); - size_t maxStringLengthTmp = MATERIAL_MAX_STRING_LENGTH; - prt::StringUtils::toOSNarrowFromUTF16(stringArray[i], (char*)handle.asUInt8(), - &maxStringLengthTmp); + if (i > 0) { + std::wstring keyToUse = key + std::to_wstring(i); + const std::string keyToUseNarrow = prtu::toOSNarrowFromUTF16(keyToUse); + if (!handle.setPositionByMemberName(keyToUseNarrow.c_str())) + continue; + } + + checkStringLength(stringArray[i], MATERIAL_MAX_STRING_LENGTH); + size_t maxStringLengthTmp = MATERIAL_MAX_STRING_LENGTH; + prt::StringUtils::toOSNarrowFromUTF16(stringArray[i], (char*)handle.asUInt8(), + &maxStringLengthTmp); + } + break; } - break; - } - case prt::Attributable::PT_UNDEFINED: - break; - case prt::Attributable::PT_BLIND_DATA: - break; - case prt::Attributable::PT_BLIND_DATA_ARRAY: - break; - case prt::Attributable::PT_COUNT: - break; + case prt::Attributable::PT_UNDEFINED: + break; + case prt::Attributable::PT_BLIND_DATA: + break; + case prt::Attributable::PT_BLIND_DATA_ARRAY: + break; + case prt::Attributable::PT_COUNT: + break; + } } - } - handle.setPositionByMemberName(PRT_MATERIAL_FACE_INDEX_START.c_str()); - *handle.asInt32() = faceRanges[fri]; + handle.setPositionByMemberName(PRT_MATERIAL_FACE_INDEX_START.c_str()); + *handle.asInt32() = faceRanges[fri]; - handle.setPositionByMemberName(PRT_MATERIAL_FACE_INDEX_END.c_str()); - *handle.asInt32() = faceRanges[fri + 1]; + handle.setPositionByMemberName(PRT_MATERIAL_FACE_INDEX_END.c_str()); + *handle.asInt32() = faceRanges[fri + 1]; - newStream.setElement(static_cast(fri), handle); - } + newStream.setElement(static_cast(fri), handle); + } - if (reports != nullptr) { - // todo + if (reports != nullptr) { + // todo + } } } + } + outputMesh.setMetadata(newMetadata); //manually set the plug value, since copyInPlace is broken for meshes without construction history From 4efe1d600b206e1282d5748539f1fdb17537b89d Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 29 Nov 2021 13:21:33 +0100 Subject: [PATCH 079/668] Cleanup: replace remove_all with std equivalent --- src/serlio/utils/ResolveMapCache.cpp | 6 ++--- src/serlio/utils/ResolveMapCache.h | 3 ++- src/serlio/utils/Utilities.cpp | 38 ---------------------------- src/serlio/utils/Utilities.h | 1 - 4 files changed, 5 insertions(+), 43 deletions(-) diff --git a/src/serlio/utils/ResolveMapCache.cpp b/src/serlio/utils/ResolveMapCache.cpp index 3a5cb473..9e5809c2 100644 --- a/src/serlio/utils/ResolveMapCache.cpp +++ b/src/serlio/utils/ResolveMapCache.cpp @@ -35,7 +35,7 @@ std::mutex resolveMapCacheMutex; ResolveMapCache::~ResolveMapCache() { if (!mRPKUnpackPath.empty()) - prtu::remove_all(mRPKUnpackPath); + std::filesystem::remove_all(mRPKUnpackPath); if (DBG) LOG_DBG << "Removed RPK unpack directory"; } @@ -59,10 +59,10 @@ ResolveMapCache::LookupResult ResolveMapCache::get(const std::wstring& rpk) { LOG_DBG << "rpk: cache timestamp: " << it->second.mTimeStamp; if (it->second.mTimeStamp != timeStamp) { mCache.erase(it); - std::wstring filename = prtu::filename(rpk); + std::filesystem::path filename = std::filesystem::path(rpk).filename(); if (!mRPKUnpackPath.empty() && !filename.empty()) - prtu::remove_all(mRPKUnpackPath + prtu::getDirSeparator() + prtu::filename(rpk)); + std::filesystem::remove_all(mRPKUnpackPath / filename); if (DBG) LOG_DBG << "RPK change detected, forcing reload and clearing cache for " << rpk; diff --git a/src/serlio/utils/ResolveMapCache.h b/src/serlio/utils/ResolveMapCache.h index 96d2c6a3..12971bde 100644 --- a/src/serlio/utils/ResolveMapCache.h +++ b/src/serlio/utils/ResolveMapCache.h @@ -23,6 +23,7 @@ #include #include +#include class ResolveMapCache { public: @@ -47,7 +48,7 @@ class ResolveMapCache { using Cache = std::map; Cache mCache; - const std::wstring mRPKUnpackPath; + const std::filesystem::path mRPKUnpackPath; }; using ResolveMapCacheUPtr = std::unique_ptr; \ No newline at end of file diff --git a/src/serlio/utils/Utilities.cpp b/src/serlio/utils/Utilities.cpp index 822c0c55..52c2bc80 100644 --- a/src/serlio/utils/Utilities.cpp +++ b/src/serlio/utils/Utilities.cpp @@ -214,44 +214,6 @@ std::wstring toFileURI(const std::wstring& p) { return schema + u16String; } -void remove_all(const std::wstring& path) { -#ifdef _WIN32 - std::wstring pc = path; - std::replace(pc.begin(), pc.end(), L'/', L'\\'); - const wchar_t* lpszDir = pc.c_str(); - - size_t len = wcslen(lpszDir); - wchar_t* pszFrom = new wchar_t[len + 2]; - wcscpy_s(pszFrom, len + 2, lpszDir); - pszFrom[len] = 0; - pszFrom[len + 1] = 0; - - SHFILEOPSTRUCTW fileop; - fileop.hwnd = NULL; // no status display - fileop.wFunc = FO_DELETE; // delete operation - fileop.pFrom = pszFrom; // source file name as double null terminated string - fileop.pTo = NULL; // no destination needed - fileop.fFlags = FOF_NOCONFIRMATION | FOF_SILENT; // do not prompt the user - fileop.fAnyOperationsAborted = FALSE; - fileop.lpszProgressTitle = NULL; - fileop.hNameMappings = NULL; - - int ret = SHFileOperationW(&fileop); - delete[] pszFrom; -#else - const auto exitCode = std::system((std::string("rm -rf ") + toOSNarrowFromUTF16(path)).c_str()); - if (exitCode < 0) { - LOG_ERR << "Failed to delete " << path << ": " << std::strerror(errno); - } - else { - if (WIFEXITED(exitCode) != 0) - LOG_ERR << "Failed to delete " << path << ", exit code " << WEXITSTATUS(exitCode); - else - LOG_ERR << "Failed to delete " << path << ", unknown reason!"; - } -#endif -} - std::wstring temp_directory_path() { #ifdef _WIN32 DWORD dwRetVal = 0; diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index a78d9d90..291784eb 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -99,7 +99,6 @@ SRL_TEST_EXPORTS_API std::wstring filename(const std::wstring& path); time_t getFileModificationTime(const std::wstring& p); std::wstring temp_directory_path(); std::wstring getProcessTempDir(const std::wstring& prefix); -void remove_all(const std::wstring& path); std::wstring toGenericPath(const std::wstring& osPath); template From f57ad5293586aaafff60f04bc2ff78582cd5d4fe Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 29 Nov 2021 13:43:34 +0100 Subject: [PATCH 080/668] Cleanup: replace valuetype with wstring for linux build --- src/serlio/utils/ResolveMapCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/utils/ResolveMapCache.cpp b/src/serlio/utils/ResolveMapCache.cpp index 9e5809c2..6215dfaf 100644 --- a/src/serlio/utils/ResolveMapCache.cpp +++ b/src/serlio/utils/ResolveMapCache.cpp @@ -82,7 +82,7 @@ ResolveMapCache::LookupResult ResolveMapCache::get(const std::wstring& rpk) { prt::Status status = prt::STATUS_UNSPECIFIED_ERROR; if (DBG) LOG_DBG << "createResolveMap from " << rpk; - rmce.mResolveMap.reset(prt::createResolveMap(rpkURI.c_str(), mRPKUnpackPath.c_str(), &status), PRTDestroyer()); + rmce.mResolveMap.reset(prt::createResolveMap(rpkURI.c_str(), mRPKUnpackPath.wstring().c_str(), &status), PRTDestroyer()); if (status != prt::STATUS_OK) return LOOKUP_FAILURE; From 5c828615381efeeed1ff7c6a0ccb288ae61db8a6 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 29 Nov 2021 13:56:49 +0100 Subject: [PATCH 081/668] Cleanup: replace filename with std equivalent --- src/serlio/modifiers/RuleAttributes.cpp | 6 ++---- src/serlio/utils/Utilities.cpp | 9 --------- src/serlio/utils/Utilities.h | 1 - 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/serlio/modifiers/RuleAttributes.cpp b/src/serlio/modifiers/RuleAttributes.cpp index 1c868c3f..4733f2d7 100644 --- a/src/serlio/modifiers/RuleAttributes.cpp +++ b/src/serlio/modifiers/RuleAttributes.cpp @@ -29,6 +29,7 @@ #include #include #include +#include namespace { @@ -85,10 +86,7 @@ std::map getImportOrderMap(const prt::RuleFileInfo* ruleFileIn RuleAttributes getRuleAttributes(const std::wstring& ruleFile, const prt::RuleFileInfo* ruleFileInfo) { RuleAttributes ra; - std::wstring mainCgaRuleName = prtu::filename(ruleFile); - size_t idxExtension = mainCgaRuleName.find(L".cgb"); - if (idxExtension != std::wstring::npos) - mainCgaRuleName = mainCgaRuleName.substr(0, idxExtension); + std::wstring mainCgaRuleName = std::filesystem::path(ruleFile).stem().wstring(); const std::map importOrderMap = getImportOrderMap(ruleFileInfo); diff --git a/src/serlio/utils/Utilities.cpp b/src/serlio/utils/Utilities.cpp index 52c2bc80..2024d11c 100644 --- a/src/serlio/utils/Utilities.cpp +++ b/src/serlio/utils/Utilities.cpp @@ -78,15 +78,6 @@ std::wstring getPluginRoot() { return rootPath; } -std::wstring filename(const std::wstring& path) { - size_t pos = path.find_last_of(L'/'); - if (pos != std::string::npos) { - return path.substr(pos + 1); - } - else - return path; -} - template <> char getDirSeparator() { #ifdef _WIN32 diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index 291784eb..14ac878a 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -95,7 +95,6 @@ std::vector toPtrVec(const std::vector>& sv) { } // poor mans std::filesystem - we don't want boost or c++17 dependency right now -SRL_TEST_EXPORTS_API std::wstring filename(const std::wstring& path); time_t getFileModificationTime(const std::wstring& p); std::wstring temp_directory_path(); std::wstring getProcessTempDir(const std::wstring& prefix); From c387f725d961599ba76f1349e18727ac9cc8e3d0 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 29 Nov 2021 16:34:06 +0100 Subject: [PATCH 082/668] Cleanup: use proper types in serlioRemove Also add for loop over all childplugs --- src/serlio/scripts/serlioCreateUI.mel | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index e0780963..50bee88c 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -75,14 +75,16 @@ global proc removePrtNode() { string $nodeHistory[] = `listHistory $node`; for($dpNode in $nodeHistory){ - $dpNodeType = `nodeType $dpNode`; + string $dpNodeType = `nodeType $dpNode`; if($dpNodeType == "serlio" || $dpNodeType == "serlioMaterial" || $dpNodeType == "serlioArnoldMaterial" ){ - $previousDpNodePlug = `connectionInfo -sfd ($dpNode +".inMesh")`; - $nextDpNodePlug = `connectionInfo -dfs ($dpNode +".outMesh")`; - if (size($previousDpNodePlug)>0 && size($nextDpNodePlug)) { - connectAttr -force ( $previousDpNodePlug ) ($nextDpNodePlug); - delete $dpNode; + string $previousDpNodePlug = `connectionInfo -sfd ($dpNode +".inMesh")`; + string $nextDpNodePlugs[] = `connectionInfo -dfs ($dpNode +".outMesh")`; + if ($previousDpNodePlug != "") { + for($nextDpNodePlug in $nextDpNodePlugs){ + connectAttr -force ( $previousDpNodePlug ) ($nextDpNodePlug); + } } + delete $dpNode; } } } From 76d80d69c98530e6502fd6a9c45250b8ffb44867 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 29 Nov 2021 16:35:54 +0100 Subject: [PATCH 083/668] Cleanup: use comparison for array length check --- src/serlio/scripts/AEserlioTemplate.mel | 2 +- src/serlio/scripts/serlioCreateUI.mel | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 25ca516e..d2daa910 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -62,7 +62,7 @@ global proc prtShowFileDialog(string $attr) { $file = `fileDialog2 -fm 1 -cap "Select File" -ff $filters`; } - if(size($file)) { + if(size($file) == 0) { setAttr -type "string" $attr $file[0]; if($attr == "Rule_Package") { evalDeferred "refreshEditorTemplates()"; diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index 50bee88c..c28cc88c 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -42,7 +42,7 @@ global proc createPrtNode() { string $select[] = `ls -sl`; string $initialShapes[] = collectInitialShapes($select); - if(!(size($initialShapes))) { + if(size($initialShapes) == 0) { confirmDialog -title "Missing selection" -message "Please select shapes first in order to apply a rule." -button "OK"; return; } @@ -51,7 +51,7 @@ global proc createPrtNode() { string $rulePackage[] = `fileDialog2 -fm 1 -cap "Select Rule Package" -ff $filters`; - if(size($rulePackage)) { + if(size($rulePackage) > 0 ) { for($node in $initialShapes) { select -r $node; serlioAssign $rulePackage; @@ -64,7 +64,7 @@ global proc removePrtNode() { string $select[] = `ls -sl`; string $initialShapes[] = collectInitialShapes($select); - if(!(size($initialShapes))) { + if(size($initialShapes) == 0) { confirmDialog -title "Missing selection" -message "Please select shapes first in order to remove a rule." -button "OK"; return; } @@ -97,7 +97,7 @@ global proc createPrtMaterialNode() { string $select[] = `ls -sl`; string $initialShapes[] = collectInitialShapes($select); - if(!(size($initialShapes))) { + if(size($initialShapes) == 0) { confirmDialog -title "Missing selection" -message "Please select shapes first in order to create materials." -button "OK"; return; } @@ -112,7 +112,7 @@ global proc createArnoldMaterialNode() { string $select[] = `ls -sl`; string $initialShapes[] = collectInitialShapes($select); - if(!(size($initialShapes))) { + if(size($initialShapes) == 0) { confirmDialog -title "Missing selection" -message "Please select shapes first in order to create materials." -button "OK"; return; } From 30cad310d0ee82f415f142e24ab4e92d365feae9 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 29 Nov 2021 16:39:51 +0100 Subject: [PATCH 084/668] Cleanup: make button annotations consistent --- src/serlio/scripts/serlioCreateUI.mel | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index c28cc88c..fc3a355f 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -65,7 +65,7 @@ global proc removePrtNode() { string $initialShapes[] = collectInitialShapes($select); if(size($initialShapes) == 0) { - confirmDialog -title "Missing selection" -message "Please select shapes first in order to remove a rule." -button "OK"; + confirmDialog -title "Missing selection" -message "Please select at least one shape to remove the associated rule package." -button "OK" return; } @@ -134,7 +134,7 @@ global proc createPrtMenu() { setParent -m $gPrtMenu; menuItem -divider true; menuItem -label "Attach CityEngine Rule Package..." -c "createPrtNode" -annotation "Attach a CGA rule package to a geometry"; - menuItem -label "Remove Rule Package Assignment" -c "removePrtNode" -annotation "Remove a CGA rule package from a geometry"; + menuItem -label "Remove CityEngine Rule Package" -c "removePrtNode" -annotation "Remove a CGA rule package from a geometry"; menuItem -label "Create Materials" -c "createPrtMaterialNode" -annotation "Create Materials"; menuItem -label "Create Arnold Materials" -c "createArnoldMaterialNode" -annotation "Create Arnold Materials"; setParent -m ..; From 3b8fa606ba0fa6513ebf8852d4e1cd3a64784dff Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 29 Nov 2021 16:58:50 +0100 Subject: [PATCH 085/668] Cleanup: Fix variable syntax Add $ before variable name --- src/serlio/scripts/serlioCreateUI.mel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index dfb46d1f..4f02b549 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -67,7 +67,7 @@ global proc createPrtNode() { } } - if (!alreadyHasExistingPrtNode){ + if (!$alreadyHasExistingPrtNode){ select -r $node; serlioAssign $rulePackage; } From 706fd308517132dddd4706b9ba09f421dc2c056b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 29 Nov 2021 16:59:42 +0100 Subject: [PATCH 086/668] Cleanup: add type definition for string --- src/serlio/scripts/serlioCreateUI.mel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index 4f02b549..d8d7af9b 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -59,7 +59,7 @@ global proc createPrtNode() { string $nodeHistory[] = `listHistory $node`; for($dpNode in $nodeHistory){ - $dpNodeType = `nodeType $dpNode`; + string $dpNodeType = `nodeType $dpNode`; if($dpNodeType == "serlio"){ setAttr -type "string" ($dpNode + ".Rule_Package") $rulePackage[0]; evalDeferred "refreshEditorTemplates()"; From 7c7523925c0b9f7d5103a8b77f1104dc579ade8c Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 30 Nov 2021 08:38:58 +0100 Subject: [PATCH 087/668] Cleanup: Only run addMesh with valid geometry --- src/codec/encoder/MayaEncoder.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index 88406ab4..f238f7d9 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -552,6 +552,9 @@ void MayaEncoder::encode(prtx::GenerateContext& context, size_t initialShapeInde void MayaEncoder::convertGeometry(const prtx::InitialShape& initialShape, const prtx::EncodePreparator::InstanceVector& instances, IMayaCallbacks* cb) { + if (instances.size() == 0) + return; + const bool emitMaterials = getOptions()->getBool(EO_EMIT_MATERIALS); const bool emitReports = getOptions()->getBool(EO_EMIT_REPORTS); @@ -618,6 +621,9 @@ void MayaEncoder::convertGeometry(const prtx::InitialShape& initialShape, } faceRanges.push_back(faceCount); // close last range + if (faceRanges.size() <= 1) + return; + assert(matAttrMaps.v.empty() || matAttrMaps.v.size() == faceRanges.size() - 1); assert(reportAttrMaps.v.empty() || reportAttrMaps.v.size() == faceRanges.size() - 1); assert(shapeIDs.size() == faceRanges.size() - 1); From b66b2cb8326df4e7422e4655535fdee8fb7843fd Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 30 Nov 2021 10:43:13 +0100 Subject: [PATCH 088/668] Cleanup: fix clang format --- src/serlio/modifiers/MayaCallbacks.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 111b9e13..13574c57 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -31,11 +31,11 @@ #include "maya/MFloatArray.h" #include "maya/MFloatPointArray.h" #include "maya/MFloatVectorArray.h" +#include "maya/MFnDependencyNode.h" #include "maya/MFnMesh.h" #include "maya/MFnMeshData.h" #include "maya/adskDataAssociations.h" #include "maya/adskDataStream.h" -#include "maya/MFnDependencyNode.h" #include #include @@ -199,7 +199,7 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c MFnMesh mFnMesh1; MObject newMeshObj = mFnMesh1.create(mayaVertices.length(), mayaFaceCounts.length(), mayaVertices, mayaFaceCounts, - mayaVertexIndices, newOutputData, &stat); + mayaVertexIndices, newOutputData, &stat); MCHECK(stat); MFnMesh newMesh(newMeshObj); @@ -395,13 +395,11 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c } } } - } - outputMesh.setMetadata(newMetadata); - //manually set the plug value, since copyInPlace is broken for meshes without construction history + // manually set the plug value, since copyInPlace is broken for meshes without construction history if (outMeshObj.apiType() == MFn::Type::kMesh) { mFnMesh1.setMetadata(newMetadata); From 958af9bbc7fabd5b76357c913c5e8e87cb0d6730 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 30 Nov 2021 10:43:57 +0100 Subject: [PATCH 089/668] Cleanup: add helper function for creating maya structure --- src/serlio/modifiers/MayaCallbacks.cpp | 108 +++++++++++++------------ 1 file changed, 58 insertions(+), 50 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 13574c57..6c5d1f78 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -169,6 +169,63 @@ constexpr unsigned int MATERIAL_MAX_STRING_LENGTH = 400; constexpr unsigned int MATERIAL_MAX_FLOAT_ARRAY_LENGTH = 5; constexpr unsigned int MATERIAL_MAX_STRING_ARRAY_LENGTH = 2; +adsk::Data::Structure* createNewMayaStructure(const prt::AttributeMap** materials) { + adsk::Data::Structure* fStructure; + + const prt::AttributeMap* mat = materials[0]; + + // Register our structure since it is not registered yet. + fStructure = adsk::Data::Structure::create(); + fStructure->setName(PRT_MATERIAL_STRUCTURE.c_str()); + + fStructure->addMember(adsk::Data::Member::kInt32, 1, PRT_MATERIAL_FACE_INDEX_START.c_str()); + fStructure->addMember(adsk::Data::Member::kInt32, 1, PRT_MATERIAL_FACE_INDEX_END.c_str()); + + size_t keyCount = 0; + wchar_t const* const* keys = mat->getKeys(&keyCount); + for (int k = 0; k < keyCount; k++) { + wchar_t const* key = keys[k]; + + adsk::Data::Member::eDataType type; + unsigned int size = 0; + unsigned int arrayLength = 1; + + // clang-format off + switch (mat->getType(key)) { + case prt::Attributable::PT_BOOL: type = adsk::Data::Member::kBoolean; size = 1; break; + case prt::Attributable::PT_FLOAT: type = adsk::Data::Member::kDouble; size = 1; break; + case prt::Attributable::PT_INT: type = adsk::Data::Member::kInt32; size = 1; break; + + //workaround: using kString type crashes maya when setting metadata elememts. Therefore we use array of kUInt8 + case prt::Attributable::PT_STRING: type = adsk::Data::Member::kUInt8; size = MATERIAL_MAX_STRING_LENGTH; break; + case prt::Attributable::PT_BOOL_ARRAY: type = adsk::Data::Member::kBoolean; size = MATERIAL_MAX_STRING_LENGTH; break; + case prt::Attributable::PT_INT_ARRAY: type = adsk::Data::Member::kInt32; size = MATERIAL_MAX_STRING_LENGTH; break; + case prt::Attributable::PT_FLOAT_ARRAY: type = adsk::Data::Member::kDouble; size = MATERIAL_MAX_FLOAT_ARRAY_LENGTH; break; + case prt::Attributable::PT_STRING_ARRAY: type = adsk::Data::Member::kUInt8; size = MATERIAL_MAX_STRING_LENGTH; arrayLength = MATERIAL_MAX_STRING_ARRAY_LENGTH; break; + + case prt::Attributable::PT_UNDEFINED: break; + case prt::Attributable::PT_BLIND_DATA: break; + case prt::Attributable::PT_BLIND_DATA_ARRAY: break; + case prt::Attributable::PT_COUNT: break; + } + // clang-format on + + if (size > 0) { + for (unsigned int i = 0; i < arrayLength; i++) { + std::wstring keyToUse = key; + if (i > 0) + keyToUse = key + std::to_wstring(i); + const std::string keyToUseNarrow = prtu::toOSNarrowFromUTF16(keyToUse); + fStructure->addMember(type, size, keyToUseNarrow.c_str()); + } + } + } + + adsk::Data::Structure::registerStructure(*fStructure); + + return fStructure; +} + } // namespace void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, const double* nrm, size_t nrmSize, @@ -213,56 +270,7 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c adsk::Data::Structure* fStructure; // Structure to use for creation fStructure = adsk::Data::Structure::structureByName(PRT_MATERIAL_STRUCTURE.c_str()); if ((fStructure == nullptr) && (materials != nullptr) && (faceRangesSize > 1)) { - const prt::AttributeMap* mat = materials[0]; - - // Register our structure since it is not registered yet. - fStructure = adsk::Data::Structure::create(); - fStructure->setName(PRT_MATERIAL_STRUCTURE.c_str()); - - fStructure->addMember(adsk::Data::Member::kInt32, 1, PRT_MATERIAL_FACE_INDEX_START.c_str()); - fStructure->addMember(adsk::Data::Member::kInt32, 1, PRT_MATERIAL_FACE_INDEX_END.c_str()); - - size_t keyCount = 0; - wchar_t const* const* keys = mat->getKeys(&keyCount); - for (int k = 0; k < keyCount; k++) { - wchar_t const* key = keys[k]; - - adsk::Data::Member::eDataType type; - unsigned int size = 0; - unsigned int arrayLength = 1; - - // clang-format off - switch (mat->getType(key)) { - case prt::Attributable::PT_BOOL: type = adsk::Data::Member::kBoolean; size = 1; break; - case prt::Attributable::PT_FLOAT: type = adsk::Data::Member::kDouble; size = 1; break; - case prt::Attributable::PT_INT: type = adsk::Data::Member::kInt32; size = 1; break; - - //workaround: using kString type crashes maya when setting metadata elememts. Therefore we use array of kUInt8 - case prt::Attributable::PT_STRING: type = adsk::Data::Member::kUInt8; size = MATERIAL_MAX_STRING_LENGTH; break; - case prt::Attributable::PT_BOOL_ARRAY: type = adsk::Data::Member::kBoolean; size = MATERIAL_MAX_STRING_LENGTH; break; - case prt::Attributable::PT_INT_ARRAY: type = adsk::Data::Member::kInt32; size = MATERIAL_MAX_STRING_LENGTH; break; - case prt::Attributable::PT_FLOAT_ARRAY: type = adsk::Data::Member::kDouble; size = MATERIAL_MAX_FLOAT_ARRAY_LENGTH; break; - case prt::Attributable::PT_STRING_ARRAY: type = adsk::Data::Member::kUInt8; size = MATERIAL_MAX_STRING_LENGTH; arrayLength = MATERIAL_MAX_STRING_ARRAY_LENGTH; break; - - case prt::Attributable::PT_UNDEFINED: break; - case prt::Attributable::PT_BLIND_DATA: break; - case prt::Attributable::PT_BLIND_DATA_ARRAY: break; - case prt::Attributable::PT_COUNT: break; - } - // clang-format on - - if (size > 0) { - for (unsigned int i = 0; i < arrayLength; i++) { - std::wstring keyToUse = key; - if (i > 0) - keyToUse = key + std::to_wstring(i); - const std::string keyToUseNarrow = prtu::toOSNarrowFromUTF16(keyToUse); - fStructure->addMember(type, size, keyToUseNarrow.c_str()); - } - } - } - - adsk::Data::Structure::registerStructure(*fStructure); + createNewMayaStructure(materials); } MCHECK(stat); From abfa8f1ec49063686f262b1026235c722587b47c Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 30 Nov 2021 10:46:31 +0100 Subject: [PATCH 090/668] Cleanup: add helper function for filling metadata --- src/serlio/modifiers/MayaCallbacks.cpp | 250 +++++++++++++------------ 1 file changed, 127 insertions(+), 123 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 6c5d1f78..efc373d2 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -226,6 +226,131 @@ adsk::Data::Structure* createNewMayaStructure(const prt::AttributeMap** material return fStructure; } +void fillMetadata(adsk::Data::Structure* fStructure, const uint32_t* faceRanges, size_t faceRangesSize, + const prt::AttributeMap** materials, const prt::AttributeMap** reports, + adsk::Data::Associations& newMetadata) { + assert(fStructure != nullptr); + assert(faceRangesSize > 1); + + adsk::Data::Stream newStream(*fStructure, PRT_MATERIAL_STREAM); + adsk::Data::Channel newChannel = newMetadata.channel(PRT_MATERIAL_CHANNEL); + newChannel.setDataStream(newStream); + newMetadata.setChannel(newChannel); + + for (size_t fri = 0; fri < faceRangesSize - 1; fri++) { + + if (materials != nullptr) { + adsk::Data::Handle handle(*fStructure); + + const prt::AttributeMap* mat = materials[fri]; + + size_t keyCount = 0; + wchar_t const* const* keys = mat->getKeys(&keyCount); + + for (int k = 0; k < keyCount; k++) { + + wchar_t const* key = keys[k]; + + const std::string keyNarrow = prtu::toOSNarrowFromUTF16(key); + + if (!handle.setPositionByMemberName(keyNarrow.c_str())) + continue; + + size_t arraySize = 0; + + switch (mat->getType(key)) { + case prt::Attributable::PT_BOOL: + handle.asBoolean()[0] = mat->getBool(key); + break; + case prt::Attributable::PT_FLOAT: + handle.asDouble()[0] = mat->getFloat(key); + break; + case prt::Attributable::PT_INT: + handle.asInt32()[0] = mat->getInt(key); + break; + + // workaround: transporting string as uint8 array, because using asString crashes maya + case prt::Attributable::PT_STRING: { + const wchar_t* str = mat->getString(key); + if (wcslen(str) == 0) + break; + checkStringLength(str, MATERIAL_MAX_STRING_LENGTH); + size_t maxStringLengthTmp = MATERIAL_MAX_STRING_LENGTH; + prt::StringUtils::toOSNarrowFromUTF16(str, (char*)handle.asUInt8(), &maxStringLengthTmp); + break; + } + case prt::Attributable::PT_BOOL_ARRAY: { + const bool* boolArray; + boolArray = mat->getBoolArray(key, &arraySize); + for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) + handle.asBoolean()[i] = boolArray[i]; + break; + } + case prt::Attributable::PT_INT_ARRAY: { + const int* intArray; + intArray = mat->getIntArray(key, &arraySize); + for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) + handle.asInt32()[i] = intArray[i]; + break; + } + case prt::Attributable::PT_FLOAT_ARRAY: { + const double* floatArray; + floatArray = mat->getFloatArray(key, &arraySize); + for (unsigned int i = 0; + i < arraySize && i < MATERIAL_MAX_STRING_LENGTH && i < MATERIAL_MAX_FLOAT_ARRAY_LENGTH; + i++) + handle.asDouble()[i] = floatArray[i]; + break; + } + case prt::Attributable::PT_STRING_ARRAY: { + + const wchar_t* const* stringArray = mat->getStringArray(key, &arraySize); + + for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) { + if (wcslen(stringArray[i]) == 0) + continue; + + if (i > 0) { + std::wstring keyToUse = key + std::to_wstring(i); + const std::string keyToUseNarrow = prtu::toOSNarrowFromUTF16(keyToUse); + if (!handle.setPositionByMemberName(keyToUseNarrow.c_str())) + continue; + } + + checkStringLength(stringArray[i], MATERIAL_MAX_STRING_LENGTH); + size_t maxStringLengthTmp = MATERIAL_MAX_STRING_LENGTH; + prt::StringUtils::toOSNarrowFromUTF16(stringArray[i], (char*)handle.asUInt8(), + &maxStringLengthTmp); + } + break; + } + + case prt::Attributable::PT_UNDEFINED: + break; + case prt::Attributable::PT_BLIND_DATA: + break; + case prt::Attributable::PT_BLIND_DATA_ARRAY: + break; + case prt::Attributable::PT_COUNT: + break; + } + } + + handle.setPositionByMemberName(PRT_MATERIAL_FACE_INDEX_START.c_str()); + *handle.asInt32() = faceRanges[fri]; + + handle.setPositionByMemberName(PRT_MATERIAL_FACE_INDEX_END.c_str()); + *handle.asInt32() = faceRanges[fri + 1]; + + newStream.setElement(static_cast(fri), handle); + } + + if (reports != nullptr) { + // todo + } + } +} + } // namespace void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, const double* nrm, size_t nrmSize, @@ -279,130 +404,9 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c adsk::Data::Associations newMetadata(inputMesh.metadata(&stat)); newMetadata.makeUnique(); MCHECK(stat); - adsk::Data::Channel newChannel = newMetadata.channel(PRT_MATERIAL_CHANNEL); - - if (fStructure != nullptr) { - adsk::Data::Stream newStream(*fStructure, PRT_MATERIAL_STREAM); - - newChannel.setDataStream(newStream); - newMetadata.setChannel(newChannel); - - if (faceRangesSize > 1) { - - for (size_t fri = 0; fri < faceRangesSize - 1; fri++) { - - if (materials != nullptr) { - adsk::Data::Handle handle(*fStructure); - - const prt::AttributeMap* mat = materials[fri]; - - size_t keyCount = 0; - wchar_t const* const* keys = mat->getKeys(&keyCount); - - for (int k = 0; k < keyCount; k++) { - - wchar_t const* key = keys[k]; - - const std::string keyNarrow = prtu::toOSNarrowFromUTF16(key); - - if (!handle.setPositionByMemberName(keyNarrow.c_str())) - continue; - - size_t arraySize = 0; - - switch (mat->getType(key)) { - case prt::Attributable::PT_BOOL: - handle.asBoolean()[0] = mat->getBool(key); - break; - case prt::Attributable::PT_FLOAT: - handle.asDouble()[0] = mat->getFloat(key); - break; - case prt::Attributable::PT_INT: - handle.asInt32()[0] = mat->getInt(key); - break; - // workaround: transporting string as uint8 array, because using asString crashes maya - case prt::Attributable::PT_STRING: { - const wchar_t* str = mat->getString(key); - if (wcslen(str) == 0) - break; - checkStringLength(str, MATERIAL_MAX_STRING_LENGTH); - size_t maxStringLengthTmp = MATERIAL_MAX_STRING_LENGTH; - prt::StringUtils::toOSNarrowFromUTF16(str, (char*)handle.asUInt8(), - &maxStringLengthTmp); - break; - } - case prt::Attributable::PT_BOOL_ARRAY: { - const bool* boolArray; - boolArray = mat->getBoolArray(key, &arraySize); - for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) - handle.asBoolean()[i] = boolArray[i]; - break; - } - case prt::Attributable::PT_INT_ARRAY: { - const int* intArray; - intArray = mat->getIntArray(key, &arraySize); - for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) - handle.asInt32()[i] = intArray[i]; - break; - } - case prt::Attributable::PT_FLOAT_ARRAY: { - const double* floatArray; - floatArray = mat->getFloatArray(key, &arraySize); - for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH && - i < MATERIAL_MAX_FLOAT_ARRAY_LENGTH; - i++) - handle.asDouble()[i] = floatArray[i]; - break; - } - case prt::Attributable::PT_STRING_ARRAY: { - - const wchar_t* const* stringArray = mat->getStringArray(key, &arraySize); - - for (unsigned int i = 0; i < arraySize && i < MATERIAL_MAX_STRING_LENGTH; i++) { - if (wcslen(stringArray[i]) == 0) - continue; - - if (i > 0) { - std::wstring keyToUse = key + std::to_wstring(i); - const std::string keyToUseNarrow = prtu::toOSNarrowFromUTF16(keyToUse); - if (!handle.setPositionByMemberName(keyToUseNarrow.c_str())) - continue; - } - - checkStringLength(stringArray[i], MATERIAL_MAX_STRING_LENGTH); - size_t maxStringLengthTmp = MATERIAL_MAX_STRING_LENGTH; - prt::StringUtils::toOSNarrowFromUTF16(stringArray[i], (char*)handle.asUInt8(), - &maxStringLengthTmp); - } - break; - } - - case prt::Attributable::PT_UNDEFINED: - break; - case prt::Attributable::PT_BLIND_DATA: - break; - case prt::Attributable::PT_BLIND_DATA_ARRAY: - break; - case prt::Attributable::PT_COUNT: - break; - } - } - - handle.setPositionByMemberName(PRT_MATERIAL_FACE_INDEX_START.c_str()); - *handle.asInt32() = faceRanges[fri]; - - handle.setPositionByMemberName(PRT_MATERIAL_FACE_INDEX_END.c_str()); - *handle.asInt32() = faceRanges[fri + 1]; - - newStream.setElement(static_cast(fri), handle); - } - - if (reports != nullptr) { - // todo - } - } - } + if (fStructure != nullptr && faceRangesSize > 1) { + fillMetadata(fStructure, faceRanges, faceRangesSize, materials, reports, newMetadata); } outputMesh.setMetadata(newMetadata); From 9f1ad897577a346dda063bf8a36c9c2346fecebc Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 30 Nov 2021 10:49:41 +0100 Subject: [PATCH 091/668] Cleanup: add helper function for updating the maya mesh --- src/serlio/modifiers/MayaCallbacks.cpp | 82 +++++++++++++++----------- 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index efc373d2..b4f4508d 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -351,28 +351,12 @@ void fillMetadata(adsk::Data::Structure* fStructure, const uint32_t* faceRanges, } } -} // namespace - -void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, const double* nrm, size_t nrmSize, - const uint32_t* faceCounts, size_t faceCountsSize, const uint32_t* vertexIndices, - size_t vertexIndicesSize, const uint32_t* normalIndices, size_t normalIndicesSize, - double const* const* uvs, size_t const* uvsSizes, uint32_t const* const* uvCounts, - size_t const* uvCountsSizes, uint32_t const* const* uvIndices, size_t const* uvIndicesSizes, - size_t uvSetsCount, const uint32_t* faceRanges, size_t faceRangesSize, - const prt::AttributeMap** materials, const prt::AttributeMap** reports, const int32_t*) { - MFloatPointArray mayaVertices = toMayaFloatPointArray(vtx, vtxSize); - MIntArray mayaFaceCounts = toMayaIntArray(faceCounts, faceCountsSize); - MIntArray mayaVertexIndices = toMayaIntArray(vertexIndices, vertexIndicesSize); - - if (DBG) { - LOG_DBG << "-- MayaCallbacks::addMesh"; - LOG_DBG << " faceCountsSize = " << faceCountsSize; - LOG_DBG << " vertexIndicesSize = " << vertexIndicesSize; - LOG_DBG << " mayaVertices.length = " << mayaVertices.length(); - LOG_DBG << " mayaFaceCounts.length = " << mayaFaceCounts.length(); - LOG_DBG << " mayaVertexIndices.length = " << mayaVertexIndices.length(); - } - +void updateMayaMesh(double const* const* uvs, size_t const* uvsSizes, uint32_t const* const* uvCounts, + size_t const* uvCountsSizes, uint32_t const* const* uvIndices, size_t const* uvIndicesSizes, + size_t uvSetsCount, const double* nrm, size_t nrmSize, const uint32_t* vertexIndices, + size_t vertexIndicesSize, const uint32_t* normalIndices, size_t normalIndicesSize, + MFloatPointArray& mayaVertices, MIntArray& mayaFaceCounts, MIntArray& mayaVertexIndices, + MObject& outMeshObj, adsk::Data::Associations& newMetadata) { MStatus stat; MFnMeshData dataCreator; @@ -391,14 +375,35 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c MFnMesh outputMesh(outMeshObj); outputMesh.copyInPlace(newMeshObj); - // create material metadata - adsk::Data::Structure* fStructure; // Structure to use for creation - fStructure = adsk::Data::Structure::structureByName(PRT_MATERIAL_STRUCTURE.c_str()); + outputMesh.setMetadata(newMetadata); + + // manually set the plug value, since copyInPlace is broken for meshes without construction history + if (outMeshObj.apiType() == MFn::Type::kMesh) { + mFnMesh1.setMetadata(newMetadata); + + MFnDependencyNode depNodeFn; + depNodeFn.setObject(outMeshObj); + MPlug meshNodeOutMeshPlug = depNodeFn.findPlug("inMesh", true); + meshNodeOutMeshPlug.setValue(newOutputData); + } +} + +} // namespace + +void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, const double* nrm, size_t nrmSize, + const uint32_t* faceCounts, size_t faceCountsSize, const uint32_t* vertexIndices, + size_t vertexIndicesSize, const uint32_t* normalIndices, size_t normalIndicesSize, + double const* const* uvs, size_t const* uvsSizes, uint32_t const* const* uvCounts, + size_t const* uvCountsSizes, uint32_t const* const* uvIndices, size_t const* uvIndicesSizes, + size_t uvSetsCount, const uint32_t* faceRanges, size_t faceRangesSize, + const prt::AttributeMap** materials, const prt::AttributeMap** reports, const int32_t*) { + MStatus stat; + adsk::Data::Structure* fStructure = adsk::Data::Structure::structureByName(PRT_MATERIAL_STRUCTURE.c_str()); + if ((fStructure == nullptr) && (materials != nullptr) && (faceRangesSize > 1)) { - createNewMayaStructure(materials); + fStructure = createNewMayaStructure(materials); // Structure to use for creation } - MCHECK(stat); MFnMesh inputMesh(inMeshObj); adsk::Data::Associations newMetadata(inputMesh.metadata(&stat)); @@ -409,17 +414,22 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c fillMetadata(fStructure, faceRanges, faceRangesSize, materials, reports, newMetadata); } - outputMesh.setMetadata(newMetadata); - - // manually set the plug value, since copyInPlace is broken for meshes without construction history - if (outMeshObj.apiType() == MFn::Type::kMesh) { - mFnMesh1.setMetadata(newMetadata); + MFloatPointArray mayaVertices = toMayaFloatPointArray(vtx, vtxSize); + MIntArray mayaFaceCounts = toMayaIntArray(faceCounts, faceCountsSize); + MIntArray mayaVertexIndices = toMayaIntArray(vertexIndices, vertexIndicesSize); - MFnDependencyNode depNodeFn; - depNodeFn.setObject(outMeshObj); - MPlug meshNodeOutMeshPlug = depNodeFn.findPlug("inMesh", true); - meshNodeOutMeshPlug.setValue(newOutputData); + if (DBG) { + LOG_DBG << "-- MayaCallbacks::addMesh"; + LOG_DBG << " faceCountsSize = " << faceCountsSize; + LOG_DBG << " vertexIndicesSize = " << vertexIndicesSize; + LOG_DBG << " mayaVertices.length = " << mayaVertices.length(); + LOG_DBG << " mayaFaceCounts.length = " << mayaFaceCounts.length(); + LOG_DBG << " mayaVertexIndices.length = " << mayaVertexIndices.length(); } + + updateMayaMesh(uvs, uvsSizes, uvCounts, uvCountsSizes, uvIndices, uvIndicesSizes, uvSetsCount, nrm, nrmSize, + vertexIndices, vertexIndicesSize, normalIndices, normalIndicesSize, mayaVertices, mayaFaceCounts, + mayaVertexIndices, outMeshObj, newMetadata); } prt::Status MayaCallbacks::attrBool(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* key, bool value) { From 03c6ed035364b886b8b122836187eb9467650baa Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 30 Nov 2021 15:02:23 +0100 Subject: [PATCH 092/668] Cleanup: convert type of rootpath of to filesystem::path --- src/serlio/PRTContext.cpp | 6 +++--- src/serlio/PRTContext.h | 2 +- src/serlio/materials/MaterialUtils.cpp | 9 +++++---- src/serlio/utils/Utilities.cpp | 13 ++++--------- src/serlio/utils/Utilities.h | 3 ++- 5 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/serlio/PRTContext.cpp b/src/serlio/PRTContext.cpp index 609122c7..f4b516a1 100644 --- a/src/serlio/PRTContext.cpp +++ b/src/serlio/PRTContext.cpp @@ -53,7 +53,7 @@ PRTContext::PRTContext(const std::vector& addExtDirs) : mPluginRoo } if (ENABLE_LOG_FILE) { - const std::wstring logPath = mPluginRootPath + prtu::getDirSeparator() + L"serlio.log"; + const std::wstring logPath = (mPluginRootPath / L"serlio.log").wstring(); theFileLogHandler = prt::FileLogHandler::create(prt::LogHandler::ALL, prt::LogHandler::ALL_COUNT, logPath.c_str()); prt::addLogHandler(theFileLogHandler); @@ -63,9 +63,9 @@ PRTContext::PRTContext(const std::vector& addExtDirs) : mPluginRoo LOG_INF << "Initializing Serlio Version " << SRL_VERSION << " ..."; if (DBG) - LOG_DBG << "initialized prt logger, plugin root path is " << mPluginRootPath; + LOG_DBG << "initialized prt logger, plugin root path is " << mPluginRootPath.wstring(); - std::vector extensionPaths = {mPluginRootPath + PRT_EXT_SUBDIR}; + std::vector extensionPaths = {(mPluginRootPath / PRT_EXT_SUBDIR).wstring()}; extensionPaths.insert(extensionPaths.end(), addExtDirs.begin(), addExtDirs.end()); if (DBG) LOG_DBG << "looking for prt extensions at\n" << extensionPaths; diff --git a/src/serlio/PRTContext.h b/src/serlio/PRTContext.h index 51af8115..80c815e8 100644 --- a/src/serlio/PRTContext.h +++ b/src/serlio/PRTContext.h @@ -44,7 +44,7 @@ struct SRL_TEST_EXPORTS_API PRTContext final { return static_cast(thePRT); } - const std::wstring mPluginRootPath; // the path where serlio dso resides + const std::filesystem::path mPluginRootPath; // the path where serlio dso resides ObjectUPtr thePRT; CacheObjectUPtr theCache; prt::ConsoleLogHandler* theLogHandler = nullptr; diff --git a/src/serlio/materials/MaterialUtils.cpp b/src/serlio/materials/MaterialUtils.cpp index 7a331d5d..2f6df771 100644 --- a/src/serlio/materials/MaterialUtils.cpp +++ b/src/serlio/materials/MaterialUtils.cpp @@ -186,10 +186,11 @@ std::wstring synchronouslyCreateShadingEngine(const std::wstring& desiredShading std::wstring getStingrayShaderPath() { static const std::wstring sfxFile = []() { // mel command wants forward slashes - const std::wstring shadersPath = prtu::toGenericPath(PRTContext::get().mPluginRootPath + L"../shaders/"); - std::wstring p = shadersPath + L"serlioShaderStingray.sfx"; - LOG_DBG << "stingray shader located at " << p; - return p; + const std::wstring shaderPath = + (PRTContext::get().mPluginRootPath.parent_path() / L"shaders/serlioShaderStingray.sfx") + .generic_wstring(); + LOG_DBG << "stingray shader located at " << shaderPath; + return shaderPath; }(); return sfxFile; } diff --git a/src/serlio/utils/Utilities.cpp b/src/serlio/utils/Utilities.cpp index 2024d11c..14a9fd3e 100644 --- a/src/serlio/utils/Utilities.cpp +++ b/src/serlio/utils/Utilities.cpp @@ -46,7 +46,8 @@ struct IUnknown; namespace prtu { // plugin root = location of serlio shared library -std::wstring getPluginRoot() { +std::filesystem::path getPluginRoot() { + std::filesystem::path rootPath; #ifdef _WIN32 char dllPath[_MAX_PATH]; char drive[8]; @@ -61,20 +62,14 @@ std::wstring getPluginRoot() { throw std::runtime_error("failed to get plugin location"); } - _splitpath_s(dllPath, drive, 8, dir, _MAX_PATH, 0, 0, 0, 0); - std::wstring rootPath = prtu::toUTF16FromOSNarrow(drive); - rootPath.append(prtu::toUTF16FromOSNarrow(dir)); + rootPath = std::filesystem::path(dllPath).parent_path(); #else Dl_info dl_info; dladdr((const void*)getPluginRoot, &dl_info); const std::string tmp(dl_info.dli_fname); - std::wstring rootPath = prtu::toUTF16FromOSNarrow(tmp.substr(0, tmp.find_last_of(prtu::getDirSeparator()))); + rootPath = std::filesystem::path(tmp).parent_path(); #endif - // ensure path separator at end - if (*rootPath.rbegin() != prtu::getDirSeparator()) - rootPath.append(1, prtu::getDirSeparator()); - return rootPath; } diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index 14ac878a..53ddcb4c 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -78,7 +79,7 @@ using ResolveMapSPtr = std::shared_ptr; namespace prtu { -std::wstring getPluginRoot(); +std::filesystem::path getPluginRoot(); template std::vector toPtrVec(const std::vector>& sv) { From 90463619a1ecce5a1b741565b4a6b7e30d0b35d2 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 30 Nov 2021 15:02:52 +0100 Subject: [PATCH 093/668] Cleanup: remove unused file util function --- src/serlio/utils/Utilities.cpp | 6 ------ src/serlio/utils/Utilities.h | 1 - 2 files changed, 7 deletions(-) diff --git a/src/serlio/utils/Utilities.cpp b/src/serlio/utils/Utilities.cpp index 14a9fd3e..6581b27e 100644 --- a/src/serlio/utils/Utilities.cpp +++ b/src/serlio/utils/Utilities.cpp @@ -264,12 +264,6 @@ time_t getFileModificationTime(const std::wstring& p) { return -1; } -std::wstring toGenericPath(const std::wstring& osPath) { - std::wstring genPath = osPath; - std::replace(genPath.begin(), genPath.end(), L'\\', L'/'); - return genPath; -} - std::string objectToXML(prt::Object const* obj) { if (obj == nullptr) throw std::invalid_argument("object pointer is not valid"); diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index 53ddcb4c..6449a04e 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -99,7 +99,6 @@ std::vector toPtrVec(const std::vector>& sv) { time_t getFileModificationTime(const std::wstring& p); std::wstring temp_directory_path(); std::wstring getProcessTempDir(const std::wstring& prefix); -std::wstring toGenericPath(const std::wstring& osPath); template C getDirSeparator(); From a4c6d6d2df8de1356bd429a576e7c5d6ec2142e6 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 30 Nov 2021 15:11:31 +0100 Subject: [PATCH 094/668] Cleanup: fix comparison typo --- src/serlio/scripts/AEserlioTemplate.mel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index d2daa910..80ff26f7 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -62,7 +62,7 @@ global proc prtShowFileDialog(string $attr) { $file = `fileDialog2 -fm 1 -cap "Select File" -ff $filters`; } - if(size($file) == 0) { + if(size($file) > 0) { setAttr -type "string" $attr $file[0]; if($attr == "Rule_Package") { evalDeferred "refreshEditorTemplates()"; From 8f785ba2eb0754fea48714c0b1e060efb809cfba Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 30 Nov 2021 15:50:15 +0100 Subject: [PATCH 095/668] Cleanup: convert type of processTempDir to filesystem::path --- src/serlio/PRTContext.cpp | 2 +- src/serlio/utils/Utilities.cpp | 10 ++++------ src/serlio/utils/Utilities.h | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/serlio/PRTContext.cpp b/src/serlio/PRTContext.cpp index f4b516a1..b9db4109 100644 --- a/src/serlio/PRTContext.cpp +++ b/src/serlio/PRTContext.cpp @@ -86,7 +86,7 @@ PRTContext::PRTContext(const std::vector& addExtDirs) : mPluginRoo } else { theCache.reset(prt::CacheObject::create(prt::CacheObject::CACHE_TYPE_DEFAULT)); - mResolveMapCache = std::make_unique(prtu::getProcessTempDir(SRL_TMP_PREFIX)); + mResolveMapCache = std::make_unique(prtu::getProcessTempDir(SRL_TMP_PREFIX).wstring()); } } diff --git a/src/serlio/utils/Utilities.cpp b/src/serlio/utils/Utilities.cpp index 6581b27e..9fa5514f 100644 --- a/src/serlio/utils/Utilities.cpp +++ b/src/serlio/utils/Utilities.cpp @@ -232,18 +232,16 @@ std::wstring temp_directory_path() { #endif } -std::wstring getProcessTempDir(const std::wstring& prefix) { - std::wstring tp = prtu::temp_directory_path(); - wchar_t sep = prtu::getDirSeparator(); - if (*tp.rbegin() != sep) - tp += sep; +std::filesystem::path getProcessTempDir(const std::wstring& prefix) { + std::filesystem::path tmpPath = std::filesystem::temp_directory_path(); + std::wstring n = prefix; #ifdef _WIN32 n += std::to_wstring(::_getpid()); // prevent warning in win32 #else n += std::to_wstring(::getpid()); #endif - return {tp.append(n)}; + return tmpPath / n; } time_t getFileModificationTime(const std::wstring& p) { diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index 6449a04e..831470dc 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -98,7 +98,7 @@ std::vector toPtrVec(const std::vector>& sv) { // poor mans std::filesystem - we don't want boost or c++17 dependency right now time_t getFileModificationTime(const std::wstring& p); std::wstring temp_directory_path(); -std::wstring getProcessTempDir(const std::wstring& prefix); +std::filesystem::path getProcessTempDir(const std::wstring& prefix); template C getDirSeparator(); From e7903ad39f66aed4bf14cc7a8d7a99a6b79092fa Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 30 Nov 2021 15:56:06 +0100 Subject: [PATCH 096/668] Cleanup: remove unused file util functions --- src/serlio/utils/Utilities.cpp | 64 +--------------------------------- src/serlio/utils/Utilities.h | 8 ----- 2 files changed, 1 insertion(+), 71 deletions(-) diff --git a/src/serlio/utils/Utilities.cpp b/src/serlio/utils/Utilities.cpp index 9fa5514f..a7897040 100644 --- a/src/serlio/utils/Utilities.cpp +++ b/src/serlio/utils/Utilities.cpp @@ -31,7 +31,7 @@ struct IUnknown; # include # include #else -# include +# include # include # include #endif @@ -73,36 +73,6 @@ std::filesystem::path getPluginRoot() { return rootPath; } -template <> -char getDirSeparator() { -#ifdef _WIN32 - static const char SEPARATOR = '\\'; -#else - static const char SEPARATOR = '/'; -#endif - return SEPARATOR; -} - -template <> -wchar_t getDirSeparator() { -#ifdef _WIN32 - static const wchar_t SEPARATOR = L'\\'; -#else - static const wchar_t SEPARATOR = L'/'; -#endif - return SEPARATOR; -} - -template <> -std::string getDirSeparator() { - return std::string(1, getDirSeparator()); -} - -template <> -std::wstring getDirSeparator() { - return std::wstring(1, getDirSeparator()); -} - int fromHex(wchar_t c) { // clang-format off switch (c) { @@ -200,38 +170,6 @@ std::wstring toFileURI(const std::wstring& p) { return schema + u16String; } -std::wstring temp_directory_path() { -#ifdef _WIN32 - DWORD dwRetVal = 0; - wchar_t lpTempPathBuffer[MAX_PATH]; - - dwRetVal = GetTempPathW(MAX_PATH, lpTempPathBuffer); - if (dwRetVal > MAX_PATH || (dwRetVal == 0)) { - return L".\tmp"; - } - else { - return std::wstring(lpTempPathBuffer); - } - -#else - - char const* folder = getenv("TMPDIR"); - if (folder == nullptr) { - folder = getenv("TMP"); - if (folder == nullptr) { - folder = getenv("TEMP"); - if (folder == nullptr) { - folder = getenv("TEMPDIR"); - if (folder == nullptr) - folder = "/tmp"; - } - } - } - - return toUTF16FromOSNarrow(std::string(folder)); -#endif -} - std::filesystem::path getProcessTempDir(const std::wstring& prefix) { std::filesystem::path tmpPath = std::filesystem::temp_directory_path(); diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index 831470dc..bc81d1c6 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -97,16 +97,8 @@ std::vector toPtrVec(const std::vector>& sv) { // poor mans std::filesystem - we don't want boost or c++17 dependency right now time_t getFileModificationTime(const std::wstring& p); -std::wstring temp_directory_path(); std::filesystem::path getProcessTempDir(const std::wstring& prefix); -template -C getDirSeparator(); -template <> -char getDirSeparator(); -template <> -wchar_t getDirSeparator(); - int fromHex(wchar_t c); wchar_t toHex(int i); From 1aafdd9950f49c3d3d719b7072fcbc6e774a02f9 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 30 Nov 2021 15:59:58 +0100 Subject: [PATCH 097/668] Cleanup: remove unused boolean --- src/serlio/modifiers/PRTModifierAction.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 85014640..231b0ddf 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -328,7 +328,6 @@ MStatus PRTModifierAction::clearTweaks(MObject mesh) { MStatus stat; MFnDependencyNode depNodeFn; depNodeFn.setObject(mesh); - bool fHasTweaks = false; MPlug tweakPlug = depNodeFn.findPlug("pnts", true); if (!tweakPlug.isNull()) { if (!tweakPlug.isArray()) { From 9e74d4c146373edaeb6d13ba1514ba38b1f7832b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 1 Dec 2021 09:02:32 +0100 Subject: [PATCH 098/668] Cleanup: added missing semicolon --- src/serlio/scripts/serlioCreateUI.mel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index 12168833..08a774b7 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -80,7 +80,7 @@ global proc removePrtNode() { string $initialShapes[] = collectInitialShapes($select); if(size($initialShapes) == 0) { - confirmDialog -title "Missing selection" -message "Please select at least one shape to remove the associated rule package." -button "OK" + confirmDialog -title "Missing selection" -message "Please select at least one shape to remove the associated rule package." -button "OK"; return; } From 061d97a5b67b26b03f36f7172702394657bbfed7 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 1 Dec 2021 10:09:46 +0100 Subject: [PATCH 099/668] Cleanup: replace std::replace with path::make_preferred --- src/serlio/utils/Utilities.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/serlio/utils/Utilities.cpp b/src/serlio/utils/Utilities.cpp index a7897040..a4a07229 100644 --- a/src/serlio/utils/Utilities.cpp +++ b/src/serlio/utils/Utilities.cpp @@ -183,15 +183,14 @@ std::filesystem::path getProcessTempDir(const std::wstring& prefix) { } time_t getFileModificationTime(const std::wstring& p) { + std::wstring pn = std::filesystem::path(p).make_preferred().wstring(); #ifdef _WIN32 - std::wstring pn = p; - std::replace(pn.begin(), pn.end(), L'/', L'\\'); struct _stat st; int ierr = _wstat(pn.c_str(), &st); #else struct stat st; - int ierr = stat(prtu::toOSNarrowFromUTF16(p).c_str(), &st); + int ierr = stat(prtu::toOSNarrowFromUTF16(pn).c_str(), &st); #endif if (ierr == 0) { From 479f9351ff2c5612214398786423473aac2b101c Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Wed, 1 Dec 2021 10:21:51 +0100 Subject: [PATCH 100/668] workaround: shorten task name to get shorter Jenkins workspace paths MSI installer creation tripped over include paths >260 chars --- pipeline.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pipeline.groovy b/pipeline.groovy index 24c3ea54..0f13df4e 100644 --- a/pipeline.groovy +++ b/pipeline.groovy @@ -60,15 +60,15 @@ Map getTasks(String branchName = null) { // -- TASK GENERATORS Map taskGenSerlio() { - return cepl.generateTasks('serlio', this.&taskBuildSerlio, CONFIGS) + return cepl.generateTasks('srl', this.&taskBuildSerlio, CONFIGS) } Map taskGenSerlioTests() { - return cepl.generateTasks('serlio-test', this.&taskBuildSerlioTests, TEST_CONFIGS) + return cepl.generateTasks('srl-tst', this.&taskBuildSerlioTests, TEST_CONFIGS) } Map taskGenSerlioInstallers() { - return cepl.generateTasks('serlio-installers', this.&taskBuildSerlioInstaller, CONFIGS.findAll { it.os == cepl.CFG_OS_WIN10}) + return cepl.generateTasks('srl-msi', this.&taskBuildSerlioInstaller, CONFIGS.findAll { it.os == cepl.CFG_OS_WIN10}) } // -- TASK BUILDERS From a1fd12c47a0dd08f2b1197e9daf77a3d0d0f349d Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 1 Dec 2021 15:05:57 +0100 Subject: [PATCH 101/668] Cleanup: replaced size comparison with empty function --- src/codec/encoder/MayaEncoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index f238f7d9..094e317d 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -552,7 +552,7 @@ void MayaEncoder::encode(prtx::GenerateContext& context, size_t initialShapeInde void MayaEncoder::convertGeometry(const prtx::InitialShape& initialShape, const prtx::EncodePreparator::InstanceVector& instances, IMayaCallbacks* cb) { - if (instances.size() == 0) + if (instances.empty()) return; const bool emitMaterials = getOptions()->getBool(EO_EMIT_MATERIALS); From aa1511159f98b667f70ff4402540c3cd3319a11f Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 1 Dec 2021 15:07:12 +0100 Subject: [PATCH 102/668] Cleanup: removed redundant arguments in updateMayaMesh --- src/serlio/modifiers/MayaCallbacks.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index b4f4508d..2b6be7f4 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -353,10 +353,9 @@ void fillMetadata(adsk::Data::Structure* fStructure, const uint32_t* faceRanges, void updateMayaMesh(double const* const* uvs, size_t const* uvsSizes, uint32_t const* const* uvCounts, size_t const* uvCountsSizes, uint32_t const* const* uvIndices, size_t const* uvIndicesSizes, - size_t uvSetsCount, const double* nrm, size_t nrmSize, const uint32_t* vertexIndices, - size_t vertexIndicesSize, const uint32_t* normalIndices, size_t normalIndicesSize, - MFloatPointArray& mayaVertices, MIntArray& mayaFaceCounts, MIntArray& mayaVertexIndices, - MObject& outMeshObj, adsk::Data::Associations& newMetadata) { + size_t uvSetsCount, const double* nrm, size_t nrmSize, const uint32_t* normalIndices, + size_t normalIndicesSize, MFloatPointArray& mayaVertices, MIntArray& mayaFaceCounts, + MIntArray& mayaVertexIndices, MObject& outMeshObj, adsk::Data::Associations& newMetadata) { MStatus stat; MFnMeshData dataCreator; @@ -428,8 +427,8 @@ void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, c } updateMayaMesh(uvs, uvsSizes, uvCounts, uvCountsSizes, uvIndices, uvIndicesSizes, uvSetsCount, nrm, nrmSize, - vertexIndices, vertexIndicesSize, normalIndices, normalIndicesSize, mayaVertices, mayaFaceCounts, - mayaVertexIndices, outMeshObj, newMetadata); + normalIndices, normalIndicesSize, mayaVertices, mayaFaceCounts, mayaVertexIndices, outMeshObj, + newMetadata); } prt::Status MayaCallbacks::attrBool(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* key, bool value) { From 75d6a2188486eb83e0e107b756537ae4faae6820 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 1 Dec 2021 15:25:35 +0100 Subject: [PATCH 103/668] Cleanup: use const refs where possible Note: can't set mayaVertexIndices to const, since they are passed to MFnMesh::setVertexNormal() that uses non-const inputs --- src/serlio/modifiers/MayaCallbacks.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 2b6be7f4..6bd2e123 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -138,7 +138,7 @@ void assignTextureCoordinates(MFnMesh& fnMesh, double const* const* uvs, size_t } } -void assignVertexNormals(MFnMesh& mFnMesh, MIntArray& mayaFaceCounts, MIntArray& mayaVertexIndices, const double* nrm, +void assignVertexNormals(MFnMesh& mFnMesh, const MIntArray& mayaFaceCounts, MIntArray& mayaVertexIndices, const double* nrm, size_t nrmSize, const uint32_t* normalIndices, MAYBE_UNUSED size_t normalIndicesSize) { if (nrmSize == 0) return; @@ -354,8 +354,8 @@ void fillMetadata(adsk::Data::Structure* fStructure, const uint32_t* faceRanges, void updateMayaMesh(double const* const* uvs, size_t const* uvsSizes, uint32_t const* const* uvCounts, size_t const* uvCountsSizes, uint32_t const* const* uvIndices, size_t const* uvIndicesSizes, size_t uvSetsCount, const double* nrm, size_t nrmSize, const uint32_t* normalIndices, - size_t normalIndicesSize, MFloatPointArray& mayaVertices, MIntArray& mayaFaceCounts, - MIntArray& mayaVertexIndices, MObject& outMeshObj, adsk::Data::Associations& newMetadata) { + size_t normalIndicesSize, const MFloatPointArray& mayaVertices, const MIntArray& mayaFaceCounts, + MIntArray& mayaVertexIndices, const MObject& outMeshObj, const adsk::Data::Associations& newMetadata) { MStatus stat; MFnMeshData dataCreator; From cb01c036e3ce841491496c374d0efd44a157e15c Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 2 Dec 2021 09:07:43 +0100 Subject: [PATCH 104/668] Add isEmpty function to serializedGeometry --- src/codec/encoder/MayaEncoder.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index 094e317d..ecf31f69 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -86,6 +86,13 @@ struct SerializedGeometry { vertexIndices.reserve(numIndices); normalIndices.reserve(numIndices); } + + bool isEmpty() const { + if (coords.empty() || counts.empty() || vertexIndices.empty()) { + return true; + } + return false; + } }; const prtx::EncodePreparator::PreparationFlags PREP_FLAGS = From 156da8ff9b2cdc6c6069ab28567e98f9d11cb8ff Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 2 Dec 2021 09:08:13 +0100 Subject: [PATCH 105/668] Cleanup: replace facerange check with SerializedGeometry::isEmpty --- src/codec/encoder/MayaEncoder.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index ecf31f69..f950f582 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -584,6 +584,10 @@ void MayaEncoder::convertGeometry(const prtx::InitialShape& initialShape, const SerializedGeometry sg = detail::serializeGeometry(geometries, materials); + if (sg.isEmpty()) { + return; + } + if (DBG) { log_debug("resolvemap: %s") % prtx::PRTUtils::objectToXML(initialShape.getResolveMap()); log_debug("encoder #materials = %s") % materials.size(); @@ -628,9 +632,6 @@ void MayaEncoder::convertGeometry(const prtx::InitialShape& initialShape, } faceRanges.push_back(faceCount); // close last range - if (faceRanges.size() <= 1) - return; - assert(matAttrMaps.v.empty() || matAttrMaps.v.size() == faceRanges.size() - 1); assert(reportAttrMaps.v.empty() || reportAttrMaps.v.size() == faceRanges.size() - 1); assert(shapeIDs.size() == faceRanges.size() - 1); From 1d140fe167c0a8bd8ee5b03f08e3a86938ae000f Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 2 Dec 2021 10:23:56 +0100 Subject: [PATCH 106/668] Cleanup: remove redundant braces --- src/codec/encoder/MayaEncoder.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index f950f582..f59de908 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -88,10 +88,7 @@ struct SerializedGeometry { } bool isEmpty() const { - if (coords.empty() || counts.empty() || vertexIndices.empty()) { - return true; - } - return false; + return coords.empty() || counts.empty() || vertexIndices.empty(); } }; @@ -584,9 +581,8 @@ void MayaEncoder::convertGeometry(const prtx::InitialShape& initialShape, const SerializedGeometry sg = detail::serializeGeometry(geometries, materials); - if (sg.isEmpty()) { + if (sg.isEmpty()) return; - } if (DBG) { log_debug("resolvemap: %s") % prtx::PRTUtils::objectToXML(initialShape.getResolveMap()); From ffa23e85f1cfe114ab02f268dae1033eff5461af Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 3 Dec 2021 15:06:41 +0100 Subject: [PATCH 107/668] Flush cache on rpk reload --- src/serlio/modifiers/PRTModifierAction.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 231b0ddf..9c07eed6 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -281,6 +281,7 @@ MStatus PRTModifierAction::updateRuleFiles(const MObject& node, const MString& r mRuleFile.clear(); mStartRule.clear(); mRuleAttributes.clear(); + PRTContext::get().theCache.get()->flushAll(); ResolveMapSPtr resolveMap = getResolveMap(); if (!resolveMap) { From bc6ecbe11934891a7610524b5d338a1eb40084d0 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 6 Dec 2021 09:59:35 +0100 Subject: [PATCH 108/668] Cleanup: change return type of getStingrayShaderPath to filesystem::path --- src/serlio/materials/MaterialUtils.cpp | 10 ++++------ src/serlio/materials/MaterialUtils.h | 2 +- src/serlio/materials/StingrayMaterialNode.cpp | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/serlio/materials/MaterialUtils.cpp b/src/serlio/materials/MaterialUtils.cpp index 2f6df771..27a5700f 100644 --- a/src/serlio/materials/MaterialUtils.cpp +++ b/src/serlio/materials/MaterialUtils.cpp @@ -183,12 +183,10 @@ std::wstring synchronouslyCreateShadingEngine(const std::wstring& desiredShading return output; } -std::wstring getStingrayShaderPath() { - static const std::wstring sfxFile = []() { - // mel command wants forward slashes - const std::wstring shaderPath = - (PRTContext::get().mPluginRootPath.parent_path() / L"shaders/serlioShaderStingray.sfx") - .generic_wstring(); +std::filesystem::path getStingrayShaderPath() { + static const std::filesystem::path sfxFile = []() { + const std::filesystem::path shaderPath = + (PRTContext::get().mPluginRootPath.parent_path() / L"shaders/serlioShaderStingray.sfx"); LOG_DBG << "stingray shader located at " << shaderPath; return shaderPath; }(); diff --git a/src/serlio/materials/MaterialUtils.h b/src/serlio/materials/MaterialUtils.h index c1ada139..559e0c2c 100644 --- a/src/serlio/materials/MaterialUtils.h +++ b/src/serlio/materials/MaterialUtils.h @@ -29,6 +29,6 @@ void assignMaterialMetadata(const adsk::Data::Structure& materialStructure, cons std::wstring synchronouslyCreateShadingEngine(const std::wstring& desiredShadingEngineName, const MELVariable& shadingEngineVariable, MStatus& status); -std::wstring getStingrayShaderPath(); +std::filesystem::path getStingrayShaderPath(); } // namespace MaterialUtils \ No newline at end of file diff --git a/src/serlio/materials/StingrayMaterialNode.cpp b/src/serlio/materials/StingrayMaterialNode.cpp index ad85a8a5..68992084 100644 --- a/src/serlio/materials/StingrayMaterialNode.cpp +++ b/src/serlio/materials/StingrayMaterialNode.cpp @@ -85,7 +85,7 @@ void appendToMaterialScriptBuilder(MELScriptBuilder& sb, const MaterialInfo& mat sb.connectAttr(MEL_VAR_SHADER_NODE, L"outColor", MEL_VARIABLE_SHADING_ENGINE, L"surfaceShader"); // stingray specifics - const MELStringLiteral sfxFile(MaterialUtils::getStingrayShaderPath()); + const MELStringLiteral sfxFile(MaterialUtils::getStingrayShaderPath().generic_wstring()); sb.addCmdLine(L"shaderfx -sfxnode " + MEL_VAR_SHADER_NODE.mel() + L" -loadGraph " + sfxFile.mel() + L";"); sb.setAttr(MEL_VAR_SHADER_NODE, L"initgraph", true); From fbaf2054574a5846c5ec08b0700966f0c89debd4 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 6 Dec 2021 10:04:03 +0100 Subject: [PATCH 109/668] Cleanup: extract nested function call to local variable for readability --- src/serlio/PRTContext.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/serlio/PRTContext.cpp b/src/serlio/PRTContext.cpp index b9db4109..be96c508 100644 --- a/src/serlio/PRTContext.cpp +++ b/src/serlio/PRTContext.cpp @@ -86,7 +86,8 @@ PRTContext::PRTContext(const std::vector& addExtDirs) : mPluginRoo } else { theCache.reset(prt::CacheObject::create(prt::CacheObject::CACHE_TYPE_DEFAULT)); - mResolveMapCache = std::make_unique(prtu::getProcessTempDir(SRL_TMP_PREFIX).wstring()); + std::filesystem::path serlioTmpDir = prtu::getProcessTempDir(SRL_TMP_PREFIX); + mResolveMapCache = std::make_unique(serlioTmpDir.wstring()); } } From 7b9c01e893a24c3650e1f0aebea178ef588d5606 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 6 Dec 2021 10:07:50 +0100 Subject: [PATCH 110/668] Cleanup: replace temporary wstring with local const variable --- src/serlio/utils/ResolveMapCache.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/serlio/utils/ResolveMapCache.cpp b/src/serlio/utils/ResolveMapCache.cpp index 6215dfaf..9e032979 100644 --- a/src/serlio/utils/ResolveMapCache.cpp +++ b/src/serlio/utils/ResolveMapCache.cpp @@ -82,7 +82,9 @@ ResolveMapCache::LookupResult ResolveMapCache::get(const std::wstring& rpk) { prt::Status status = prt::STATUS_UNSPECIFIED_ERROR; if (DBG) LOG_DBG << "createResolveMap from " << rpk; - rmce.mResolveMap.reset(prt::createResolveMap(rpkURI.c_str(), mRPKUnpackPath.wstring().c_str(), &status), PRTDestroyer()); + const std::wstring mRPKUnpackPathString = mRPKUnpackPath.wstring(); + rmce.mResolveMap.reset(prt::createResolveMap(rpkURI.c_str(), mRPKUnpackPathString.c_str(), &status), + PRTDestroyer()); if (status != prt::STATUS_OK) return LOOKUP_FAILURE; From f5fd19df449706365a9be80d42f23d3d8ffb9229 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 6 Dec 2021 10:08:37 +0100 Subject: [PATCH 111/668] Cleanup: remove old comment --- src/serlio/utils/Utilities.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index bc81d1c6..13b9d47f 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -95,7 +95,6 @@ std::vector toPtrVec(const std::vector>& sv) { return pv; } -// poor mans std::filesystem - we don't want boost or c++17 dependency right now time_t getFileModificationTime(const std::wstring& p); std::filesystem::path getProcessTempDir(const std::wstring& prefix); From 96bcfa66f1af689355134041a6a7a0e64893be5e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 6 Dec 2021 10:57:58 +0100 Subject: [PATCH 112/668] Ignore contstruction history setting This results in always creating a serlio node. Directly applying the rule on a footprint is not a use-case we want to support. Construction history can still be manually removed. --- src/serlio/modifiers/polyModifier/polyModifierCmd.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp b/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp index b60169bf..0473129c 100644 --- a/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp +++ b/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp @@ -319,8 +319,7 @@ void polyModifierCmd::collectNodeState() } int result; - MGlobal::executeCommand( "constructionHistory -q -tgl", result ); - fHasRecordHistory = (0 != result); + fHasRecordHistory = true; } MStatus polyModifierCmd::createModifierNode( MObject& modifierNode ) From 929e3c52434169dbd63775296a184c50553d503b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 6 Dec 2021 11:07:22 +0100 Subject: [PATCH 113/668] Cleanup: remove fHasRecordHistory (since we always want it to create a serlio node) --- .../polyModifier/polyModifierCmd.cpp | 82 ++++++------------- .../modifiers/polyModifier/polyModifierCmd.h | 1 - 2 files changed, 23 insertions(+), 60 deletions(-) diff --git a/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp b/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp index 0473129c..89a9a485 100644 --- a/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp +++ b/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp @@ -149,26 +149,10 @@ MStatus polyModifierCmd::doModifyPoly() // collectNodeState(); - if( !fHasHistory && !fHasRecordHistory ) - { - MObject meshNode = fDagPath.node(); - - // Pre-process the mesh - Cache old mesh (including tweaks, if applicable) - // - cacheMeshData(); - cacheMeshTweaks(); - - // Call the directModifier - // - status = directModifier( meshNode ); - } - else - { - MObject modifierNode; - createModifierNode( modifierNode ); - initModifierNode( modifierNode ); - status = connectNodes( modifierNode ); - } + MObject modifierNode; + createModifierNode( modifierNode ); + initModifierNode( modifierNode ); + status = connectNodes( modifierNode ); } return status; @@ -178,25 +162,14 @@ MStatus polyModifierCmd::redoModifyPoly() { MStatus status = MS::kSuccess; - if( !fHasHistory && !fHasRecordHistory ) - { - MObject meshNode = fDagPath.node(); - - // Call the directModifier - No need to pre-process the mesh data again - // since we already have it. - // - status = directModifier( meshNode ); - } - else + // Call the redo on the DG and DAG modifiers + // + if( !fHasHistory ) { - // Call the redo on the DG and DAG modifiers - // - if( !fHasHistory ) - { - fDagModifier.doIt(); - } - status = fDGModifier.doIt(); + fDagModifier.doIt(); } + status = fDGModifier.doIt(); + return status; } @@ -205,28 +178,21 @@ MStatus polyModifierCmd::undoModifyPoly() { MStatus status = MS::kSuccess; - if( !fHasHistory && !fHasRecordHistory ) + fDGModifier.undoIt(); + + // undoCachedMesh must be called before undoTweakProcessing because + // undoCachedMesh copies the original mesh *without* tweaks back onto + // the existing mesh. Any changes done before the copy will be lost. + // + if( !fHasHistory ) { - status = undoDirectModifier(); + status = undoCachedMesh(); + MCheckStatus( status, "undoCachedMesh" ); + fDagModifier.undoIt(); } - else - { - fDGModifier.undoIt(); - // undoCachedMesh must be called before undoTweakProcessing because - // undoCachedMesh copies the original mesh *without* tweaks back onto - // the existing mesh. Any changes done before the copy will be lost. - // - if( !fHasHistory ) - { - status = undoCachedMesh(); - MCheckStatus( status, "undoCachedMesh" ); - fDagModifier.undoIt(); - } - - status = undoTweakProcessing(); - MCheckStatus( status, "undoTweakProcessing" ); - } + status = undoTweakProcessing(); + MCheckStatus( status, "undoTweakProcessing" ); return status; } @@ -319,7 +285,6 @@ void polyModifierCmd::collectNodeState() } int result; - fHasRecordHistory = true; } MStatus polyModifierCmd::createModifierNode( MObject& modifierNode ) @@ -739,7 +704,7 @@ MStatus polyModifierCmd::processTweaks( modifyPolyData& data ) // Only have to clear the tweaks off the duplicate mesh if we do not have history // and we want history. // - if( !fHasHistory && fHasRecordHistory ) + if( !fHasHistory ) { depNodeFn.setObject( data.upstreamNodeShape ); upstreamTweakPlug = depNodeFn.findPlug( "pnts", true); @@ -973,7 +938,6 @@ MStatus polyModifierCmd::undoCachedMesh() // Only need to restore the cached mesh if there was no history. Also // check to make sure that we are in the record history state. // - MStatusAssert( (fHasRecordHistory), "fHasRecordHistory == true" ); if( !fHasHistory ) { diff --git a/src/serlio/modifiers/polyModifier/polyModifierCmd.h b/src/serlio/modifiers/polyModifier/polyModifierCmd.h index 9b9bf52b..75a02ede 100644 --- a/src/serlio/modifiers/polyModifier/polyModifierCmd.h +++ b/src/serlio/modifiers/polyModifier/polyModifierCmd.h @@ -508,7 +508,6 @@ class polyModifierCmd : public MPxCommand // bool fHasHistory; bool fHasTweaks; - bool fHasRecordHistory; // Cached Tweak Data (for undo) // From 533ba02e8d90b65a31bba032b28f6d7202f2308d Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 6 Dec 2021 14:19:16 +0100 Subject: [PATCH 114/668] Avoid creation of multiple serlio nodes for one mesh It is possible for multiple mesh shapes to be in the construction history. Only add serlio nodes to meshes that are not already used as inputs for serlio nodes. --- src/serlio/scripts/serlioCreateUI.mel | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index 08a774b7..f8b73813 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -38,6 +38,16 @@ proc string[] collectInitialShapes(string $nodes[]) { return $result; } +proc int isSerlioInput(string $node){ + string $hist[] = `listHistory -lv 1 -f true $node`; + if (size($hist) > 1) { + if(`nodeType $hist[1]` == "serlio"){ + return true; + } + } + return false; +} + global proc createPrtNode() { string $select[] = `ls -sl`; string $initialShapes[] = collectInitialShapes($select); @@ -65,7 +75,7 @@ global proc createPrtNode() { } } - if (!$alreadyHasExistingPrtNode){ + if (!$alreadyHasExistingPrtNode && !isSerlioInput($node)){ select -r $node; serlioAssign $rulePackage; } From 3b26ccfcdef058e42998f96bd903e68e33b5b061 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 6 Dec 2021 14:20:13 +0100 Subject: [PATCH 115/668] Avoid adding duplicates to the array --- src/serlio/scripts/serlioCreateUI.mel | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index f8b73813..3540ddf9 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -20,7 +20,9 @@ proc string[] appendAarray(string $a0[], string $a1[]) { string $result[] = $a0; for($e in $a1) - $result[size($result)] = $e; + if(!stringArrayContains($e, $result)){ + $result[size($result)] = $e; + } return $result; } From 87b2fe0cc24602fde58dee2ecfadf5b111401bcc Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 6 Dec 2021 16:29:31 +0100 Subject: [PATCH 116/668] Cleanup: removed unused drive and dir variables --- src/serlio/utils/Utilities.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/serlio/utils/Utilities.cpp b/src/serlio/utils/Utilities.cpp index a4a07229..7e588323 100644 --- a/src/serlio/utils/Utilities.cpp +++ b/src/serlio/utils/Utilities.cpp @@ -50,8 +50,6 @@ std::filesystem::path getPluginRoot() { std::filesystem::path rootPath; #ifdef _WIN32 char dllPath[_MAX_PATH]; - char drive[8]; - char dir[_MAX_PATH]; HMODULE hModule = 0; GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, From e9252c9f6903a10ca5778934ce6731724bfcf956 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 6 Dec 2021 14:22:55 +0100 Subject: [PATCH 117/668] Cleanup: remove directModifier code This code is nolonger needed, because we decided to always create a serlio node --- src/serlio/modifiers/PRTModifierCommand.cpp | 15 --- src/serlio/modifiers/PRTModifierCommand.h | 1 - .../polyModifier/polyModifierCmd.cpp | 106 ------------------ .../modifiers/polyModifier/polyModifierCmd.h | 13 --- 4 files changed, 135 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierCommand.cpp b/src/serlio/modifiers/PRTModifierCommand.cpp index 2ea19278..ad41b432 100644 --- a/src/serlio/modifiers/PRTModifierCommand.cpp +++ b/src/serlio/modifiers/PRTModifierCommand.cpp @@ -146,18 +146,3 @@ MStatus PRTModifierCommand::initModifierNode(MObject modifierNode) { return status; } - -MStatus PRTModifierCommand::directModifier(MObject mesh) { - MStatus status; - PRTModifierAction fPRTModifierAction; - - fPRTModifierAction.setMesh(mesh, mesh); - fPRTModifierAction.setRandomSeed(mInitialSeed); - fPRTModifierAction.updateRuleFiles(MObject::kNullObj, mRulePkg); - fPRTModifierAction.clearTweaks(mesh); - - // Now, perform the PRT action - status = fPRTModifierAction.doIt(); - - return status; -} diff --git a/src/serlio/modifiers/PRTModifierCommand.h b/src/serlio/modifiers/PRTModifierCommand.h index a28db547..9fabda0e 100644 --- a/src/serlio/modifiers/PRTModifierCommand.h +++ b/src/serlio/modifiers/PRTModifierCommand.h @@ -33,7 +33,6 @@ class PRTModifierCommand : public polyModifierCmd { MStatus undoIt() override; MStatus initModifierNode(MObject modifierNode) override; - MStatus directModifier(MObject mesh) override; private: MString mRulePkg; diff --git a/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp b/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp index 89a9a485..3bfe6995 100644 --- a/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp +++ b/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp @@ -122,23 +122,6 @@ MStatus polyModifierCmd::initModifierNode( MObject /* modifierNode */ ) return MS::kSuccess; } -MStatus polyModifierCmd::directModifier( MObject /* mesh */ ) -// -// Description: -// -// Override this method in a derived class to provide an implementation for -// directly modifying the mesh (writing on the mesh itself). This method is -// only called in the case where history does not exist and history is turned -// off (ie. DG operations are not desirable). -// -// The argument 'MObject mesh', is not used by this base class implementation. -// However, it may be used by derived classes. To avoid compiler warnings -// of unreferenced parameters, we comment out the parameter name. -// -{ - return MS::kSuccess; -} - MStatus polyModifierCmd::doModifyPoly() { MStatus status = MS::kFailure; @@ -1042,95 +1025,6 @@ MStatus polyModifierCmd::undoTweakProcessing() return status; } -MStatus polyModifierCmd::undoDirectModifier() -{ - MStatus status; - - MFnDependencyNode depNodeFn; - MFnDagNode dagNodeFn; - - MObject meshNode = fDagPath.node(); - depNodeFn.setObject( meshNode ); - - // For the case with tweaks, we cannot write the mesh directly back onto - // the cachedInMesh, since the shape can have out of date information from the - // cachedInMesh. Thus we temporarily create an duplicate mesh, place our - // old mesh on the outMesh attribute of our duplicate mesh, connect the - // duplicate mesh shape to the mesh shape, and force a DG evaluation. - // - // For the case without tweaks, we can simply write onto the outMesh, since - // the shape relies solely on an outMesh when there is no history nor tweaks. - // - if( fHasTweaks ) - { - // Retrieve the inMesh and name of our mesh node (for the DG eval) - // - depNodeFn.setObject( meshNode ); - MPlug meshNodeInMeshPlug = depNodeFn.findPlug( "inMesh", true, &status ); - MCheckStatus( status, "Could not retrieve inMesh" ); - MString meshNodeName = depNodeFn.name(); - - // Duplicate our current mesh - // - dagNodeFn.setObject( meshNode ); - MObject dupMeshNode = dagNodeFn.duplicate(); - - // The dagNodeFn::duplicate() returns a transform, but we need a shape - // so retrieve the DAG path and extend it to the shape. - // - MDagPath dupMeshDagPath; - MDagPath::getAPathTo( dupMeshNode, dupMeshDagPath ); - dupMeshDagPath.extendToShape(); - - // Retrieve the outMesh of the duplicate mesh and set our mesh data back - // on it. - // - depNodeFn.setObject( dupMeshDagPath.node() ); - MPlug dupMeshNodeOutMeshPlug = depNodeFn.findPlug( "outMesh", true, &status ); - MCheckStatus( status, "Could not retrieve outMesh" ); - status = dupMeshNodeOutMeshPlug.setValue( fMeshData ); - - // Temporarily connect the duplicate mesh node to our mesh node - // - MDGModifier dgModifier; - dgModifier.connect( dupMeshNodeOutMeshPlug, meshNodeInMeshPlug ); - status = dgModifier.doIt(); - MCheckStatus( status, "Could not connect dupMeshNode -> meshNode" ); - - // Need to force a DG evaluation now that the input has been changed. - // - MString cmd("dgeval "); - cmd += meshNodeName; - cmd += ".inMesh"; - status = MGlobal::executeCommand( cmd, false, false ); - MCheckStatus( status, "Could not force DG eval" ); - - // Disconnect and delete the duplicate mesh node now - // - dgModifier.undoIt(); - MGlobal::deleteNode( dupMeshNode ); - - // Restore the tweaks on the mesh - // - status = undoTweakProcessing(); - } - else - { - // Restore the original mesh by writing the old mesh data (fMeshData) back - // onto the outMesh of our meshNode - // - depNodeFn.setObject( meshNode ); - MPlug meshNodeOutMeshPlug = depNodeFn.findPlug( "outMesh", true, &status ); - MCheckStatus( status, "Could not retrieve outMesh" ); - status = meshNodeOutMeshPlug.setValue( fMeshData ); - MCheckStatus( status, "Could not set meshData" ); - } - MFnMesh fnMesh(meshNode); - fnMesh.updateSurface(); - - return status; -} - MStatus polyModifierCmd::getFloat3PlugValue( MPlug plug, MFloatVector & value ) { // Retrieve the value as an MObject diff --git a/src/serlio/modifiers/polyModifier/polyModifierCmd.h b/src/serlio/modifiers/polyModifier/polyModifierCmd.h index 75a02ede..844188f8 100644 --- a/src/serlio/modifiers/polyModifier/polyModifierCmd.h +++ b/src/serlio/modifiers/polyModifier/polyModifierCmd.h @@ -388,18 +388,6 @@ class polyModifierCmd : public MPxCommand // virtual MStatus initModifierNode( MObject modifierNode ); - // directModifier - Derived classes should override this method to provide - // direct modifications on the meshNode in the case where - // no history exists and construction history is turned off. - // (ie. no DG operations desired) - // - // This method is called only if history does not exist and - // history is turned off. At this point, a handle to the - // meshNode is passed in so a derived class may directly - // modify the mesh. - // - virtual MStatus directModifier( MObject mesh ); - MStatus doModifyPoly(); MStatus redoModifyPoly(); MStatus undoModifyPoly(); @@ -478,7 +466,6 @@ class polyModifierCmd : public MPxCommand // MStatus undoCachedMesh(); MStatus undoTweakProcessing(); - MStatus undoDirectModifier(); ///////////////////////////////////// // polyModifierCmd Utility Methods // From 5ea8541353aefc8f8d157323b3250c00d5d7f02a Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 6 Dec 2021 14:23:20 +0100 Subject: [PATCH 118/668] Remove clearTweaks function This function is nolonger used and can be removed --- src/serlio/modifiers/PRTModifierAction.cpp | 30 ---------------------- src/serlio/modifiers/PRTModifierAction.h | 1 - 2 files changed, 31 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 231b0ddf..80a59bee 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -324,36 +324,6 @@ MStatus PRTModifierAction::updateRuleFiles(const MObject& node, const MString& r return MS::kSuccess; } -MStatus PRTModifierAction::clearTweaks(MObject mesh) { - MStatus stat; - MFnDependencyNode depNodeFn; - depNodeFn.setObject(mesh); - MPlug tweakPlug = depNodeFn.findPlug("pnts", true); - if (!tweakPlug.isNull()) { - if (!tweakPlug.isArray()) { - return MStatus::kFailure; - } - - MPlug tweak; - MFloatVector tweakData; - int i; - int numElements = tweakPlug.numElements(); - - for (i = 0; i < numElements; i++) { - tweak = tweakPlug.elementByPhysicalIndex(i, &stat); - if (stat == MS::kSuccess && !tweak.isNull()) { - - MFnNumericData numDataFn; - MObject object = numDataFn.create(MFnNumericData::k3Float); - numDataFn.setData(0, 0, 0); - tweak.setValue(object); - } - } - } - - return stat; -} - MStatus PRTModifierAction::doIt() { MStatus status; diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 921e06ab..2d5559c1 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -72,7 +72,6 @@ class PRTModifierAction : public polyModifierFty { void setRandomSeed(int32_t randomSeed) { mRandomSeed = randomSeed; }; - MStatus clearTweaks(MObject mesh); // polyModifierFty inherited methods MStatus doIt() override; From 8eb11ee2e4d69d54d278401abae5c33e8e84f5a5 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 6 Dec 2021 14:38:57 +0100 Subject: [PATCH 119/668] Cleanup: removed unreferenced int --- src/serlio/modifiers/polyModifier/polyModifierCmd.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp b/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp index 3bfe6995..0fe279cb 100644 --- a/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp +++ b/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp @@ -266,8 +266,6 @@ void polyModifierCmd::collectNodeState() } } } - - int result; } MStatus polyModifierCmd::createModifierNode( MObject& modifierNode ) From 17b3993c71facc8369d4aa414ebbd61dc6524316 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 6 Dec 2021 14:39:12 +0100 Subject: [PATCH 120/668] Cleanup: remove cache functions for directModifier --- .../polyModifier/polyModifierCmd.cpp | 108 ------------------ .../modifiers/polyModifier/polyModifierCmd.h | 5 - 2 files changed, 113 deletions(-) diff --git a/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp b/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp index 0fe279cb..63f2a503 100644 --- a/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp +++ b/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp @@ -804,114 +804,6 @@ MStatus polyModifierCmd::connectNodes( MObject modifierNode ) return status; } -MStatus polyModifierCmd::cacheMeshData() -{ - MStatus status = MS::kSuccess; - - MFnDependencyNode depNodeFn; - MFnDagNode dagNodeFn; - - MObject meshNode = fDagPath.node(); - MObject dupMeshNode; - MPlug dupMeshNodeOutMeshPlug; - - // Duplicate the mesh - // - dagNodeFn.setObject( meshNode ); - dupMeshNode = dagNodeFn.duplicate(); - - MDagPath dupMeshDagPath; - MDagPath::getAPathTo( dupMeshNode, dupMeshDagPath ); - dupMeshDagPath.extendToShape(); - - depNodeFn.setObject( dupMeshDagPath.node() ); - dupMeshNodeOutMeshPlug = depNodeFn.findPlug( "outMesh", true, &status ); - MCheckStatus( status, "Could not retrieve outMesh" ); - - // Retrieve the meshData - // - status = dupMeshNodeOutMeshPlug.getValue( fMeshData ); - MCheckStatus( status, "Could not retrieve meshData" ); - - // Delete the duplicated node - // - MGlobal::deleteNode( dupMeshNode ); - - return status; -} - -MStatus polyModifierCmd::cacheMeshTweaks() -{ - MStatus status = MS::kSuccess; - - // Clear tweak undo information (to be rebuilt) - // - fTweakIndexArray.clear(); - fTweakVectorArray.clear(); - - // Extract the tweaks and store them in our local tweak cache members - // - if( fHasTweaks ) - { - // Declare our function sets - // - MFnDependencyNode depNodeFn; - - MObject meshNode = fDagPath.node(); - MPlug meshTweakPlug; - - // Declare our tweak processing variables - // - MPlug tweak; - MPlug tweakChild; - MObject tweakData; - MObjectArray tweakDataArray; - MFloatVector tweakVector; - - MPlugArray tempPlugArray; - - unsigned i; - - depNodeFn.setObject( meshNode ); - meshTweakPlug = depNodeFn.findPlug( "pnts", true); - - // ASSERT: meshTweakPlug should be an array plug! - // - MStatusAssert( (meshTweakPlug.isArray()), - "meshTweakPlug.isArray() -- meshTweakPlug is not an array plug" ); - unsigned numElements = meshTweakPlug.numElements(); - - // Gather meshTweakPlug data - // - for( i = 0; i < numElements; i++ ) - { - // MPlug::numElements() only returns the number of physical elements - // in the array plug. Thus we must use elementByPhysical index when using - // the index i. - // - tweak = meshTweakPlug.elementByPhysicalIndex(i); - - // If the method fails, the element is NULL. Only append the index - // if it is a valid plug. - // - if( !tweak.isNull() ) - { - // Cache the logical index of this element plug - // - unsigned logicalIndex = tweak.logicalIndex(); - - // Collect tweak data and cache the indices and float vectors - // - getFloat3PlugValue( tweak, tweakVector ); - fTweakIndexArray.append( logicalIndex ); - fTweakVectorArray.append( tweakVector ); - } - } - } - - return status; -} - MStatus polyModifierCmd::undoCachedMesh() { MStatus status; diff --git a/src/serlio/modifiers/polyModifier/polyModifierCmd.h b/src/serlio/modifiers/polyModifier/polyModifierCmd.h index 844188f8..43905734 100644 --- a/src/serlio/modifiers/polyModifier/polyModifierCmd.h +++ b/src/serlio/modifiers/polyModifier/polyModifierCmd.h @@ -457,11 +457,6 @@ class polyModifierCmd : public MPxCommand // MStatus connectNodes( MObject modifierNode ); - // Mesh caching methods - Only used in the directModifier case - // - MStatus cacheMeshData(); - MStatus cacheMeshTweaks(); - // Undo methods // MStatus undoCachedMesh(); From 498011cec62823c7dc098bcc0a84350b02644710 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 6 Dec 2021 14:50:48 +0100 Subject: [PATCH 121/668] Remove workaround for setting meshdata in directModifier --- src/serlio/modifiers/MayaCallbacks.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 6bd2e123..9da03d10 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -375,16 +375,6 @@ void updateMayaMesh(double const* const* uvs, size_t const* uvsSizes, uint32_t c outputMesh.copyInPlace(newMeshObj); outputMesh.setMetadata(newMetadata); - - // manually set the plug value, since copyInPlace is broken for meshes without construction history - if (outMeshObj.apiType() == MFn::Type::kMesh) { - mFnMesh1.setMetadata(newMetadata); - - MFnDependencyNode depNodeFn; - depNodeFn.setObject(outMeshObj); - MPlug meshNodeOutMeshPlug = depNodeFn.findPlug("inMesh", true); - meshNodeOutMeshPlug.setValue(newOutputData); - } } } // namespace From a3a39c7f3acb9da34ce8c2c3193b8e9b8b3e82fa Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 6 Dec 2021 15:59:18 +0100 Subject: [PATCH 122/668] Cleanup: update comments in polyModify to reflect the new behaviour --- .../modifiers/polyModifier/polyModifierCmd.h | 112 ++++-------------- 1 file changed, 22 insertions(+), 90 deletions(-) diff --git a/src/serlio/modifiers/polyModifier/polyModifierCmd.h b/src/serlio/modifiers/polyModifier/polyModifierCmd.h index 43905734..98a58950 100644 --- a/src/serlio/modifiers/polyModifier/polyModifierCmd.h +++ b/src/serlio/modifiers/polyModifier/polyModifierCmd.h @@ -125,44 +125,12 @@ // (Figure 2. Node with Tweaks) // // -// The last of the questions deals with whether or not the user has construction -// history turned on or off. This will change how the node should be modified -// as well as what the node will look like in the DG following the operation. With -// history turned on, the user has selected that they would like to keep a -// history chain. So in that case, the resulting mesh would look like the above -// diagrams following the operation. On the other hand, with history turned off, -// the user has selected that they would not like to see a history chain. From here -// there are two possible choices to modify the mesh: -// -// (1) Operate on the mesh directly -// (2) Use the DG, like in the above diagrams, then collapse the nodes down into the mesh. -// -// The only exception to note out of this case is that if the node already possesses -// history (as would be answered by the first question), this preference is ignored. -// If a node has history, we continue to use history. The user is imposed with the -// task of deleting the history on the object first if they would not like to continue -// using history. -// -// -// With History: -// -// ____ ____ -// / \ / \ -// | Hist | O --------> O | mesh | O -// \____/ | | \____/ | -// outMesh inMesh outMesh -// -// -// Without History: -// -// ____ -// / \ -// O | mesh | O (All history compressed onto the inMesh attribute) -// | \____/ | -// inMesh outMesh -// -// -// (Figure 3. Node with History preference) +// Since we don't care if the user has turned the construction history off, we +// always generate an modifier node. Otherwise, the user is unable to make any +// changes to the serlio node. The user is imposed with the task of deleting +// the history on the object first if they would not like to continue +// using history. This deviates from the original polyModifierCmd implementation, +// where the history is collapsed, but makes more sense for our use case. // // // This section has described the "why" part of the question regarding this command. @@ -181,17 +149,12 @@ // // 1) History // -// For history, there are 4 cases that need to be considered: +// For history, there are 2 cases that need to be considered: // -// (a) History (yes) - RecordHistory (yes) -// (b) History (yes) - RecordHistory (no) -// (c) History (no) - RecordHistory (yes) -// (d) History (no) - RecordHistory (no) +// (a) History (yes) +// (b) History (no) // -// For (a) and (b), this command treats the node identically. Regardless of -// whether recording history is turned on or off, if history already exists -// on the node, we treat the node as though recording history is on. As such -// the command performs the following steps: +// For (a), The command performs the following steps: // // (i) Create a modifier node. // (ii) Find the node directly upstream to the mesh node. @@ -200,7 +163,7 @@ // (v) Connect the modifier node to the mesh node. // (vi) Done! // -// For (c), polyModifierCmd needs to generate an input mesh to drive the +// For (b), polyModifierCmd needs to generate an input mesh to drive the // modifier node. To do this, the mesh node is duplicated and connected // like the upstream node in the previous two cases: // @@ -210,47 +173,19 @@ // (iv) Connect the modifier node to the mesh node // (v) Done! // -// For (d), this command is a bit more complicated. There are two approaches -// that can be done to respect the fact that no history is desired. The first -// involves using the approach in case (c) and simply "baking" or "flattening" -// the nodes down into the mesh node. Unfortunately, this presents some -// serious problems with undo, as the Maya API in its current state does not -// support construction history manipulation. Resorting to the MEL command: -// "delete -ch" would be possible, however undoing the operation would not be -// trivial as calling an undo from within an undo could destabilize the undo -// queue. -// -// The second alternative and one that is currently implemented by this class -// is to respect the "No Construction History" preference strictly by -// not modifying the history chain at all and simply operating directly on the -// mesh. In order to do this and maintain generality, a hook is provided for -// derived classes to override and place in the code used to directly modify the -// mesh. polyModifierCmd will only call this method under the circumstances -// of case (d). To prevent code duplication between the operations done in the -// modifierNode and the command's directModifier implementation, the concept of -// a factory is used. It is recommended that an instance of such a factory is -// stored locally on the command much like it will be on the node. See -// polyModifierNode.h and polyModifierFty.h for more details. -// -// // 2) Tweaks // // Tweaks are handled as noted above in the description section. However, how // they are treated is dependent on the state of history. Using the four cases // above: // -// For (a), (b) and (c), it is as described in the description section: +// It is as described in the description section: // // (i) Create a tweak node. // (ii) Extract the tweaks from the mesh node. // (iii) Copy the tweaks onto the tweak node. // (iv) Clear the tweaks from the mesh node. -// (v) Clear the tweaks from the duplicate mesh node (for case (c) only!) -// -// For (d), we have yet another limitation. Tweaks are not handled in this case -// because of the same circumstances which give rise to the limitation in the -// history section. As such, topological changes may result in some odd behaviour -// unless the workaround provided in the limitations section is used. +// (v) Clear the tweaks from the duplicate mesh node (for case (b) only!) // // // How to use: @@ -287,26 +222,23 @@ // // 4) Add an instance of your polyModifierFty to the command // 5) Cache any input parameters for the factory on the command -// 6) Override the polyModifierCmd::directModifier() method -// 7) Place your factory setup code and call its doIt() in directModifier() // // --- // -// 8) Override the MPxCommand::doIt() method -// 9) Place your setup code inside the doIt() -// 10) Place the polyModifierCmd setup code inside the doIt() +// 6) Override the MPxCommand::doIt() method +// 7) Place your setup code inside the doIt() +// 8) Place the polyModifierCmd setup code inside the doIt() // (ie. setMeshNode(), setModifierNodeType()) -// 11) Call polyModifierCmd::doModifyPoly() inside the doIt() +// 9) Call polyModifierCmd::doModifyPoly() inside the doIt() // // --- // -// 12) Override the MPxCommand::redoIt() method -// 13) Call polyModifierCmd::redoModifyPoly() in redoIt() +// 10) Override the MPxCommand::redoIt() method +// 11) Call polyModifierCmd::redoModifyPoly() in redoIt() // // --- // -// 14) Override the MPxCommand::undoIt() method -// 15) Call polyModifierCmd::undoModifyPoly() in undoIt() +// 12) Override the MPxCommand::undoIt() method // // For more details on each of these steps, please visit the associated method/class // headers. @@ -316,11 +248,11 @@ // // There is one limitation in polyModifierCmd: // -// (1) Duplicate mesh created under the "No History / History turned on" case not undoable +// (1) Duplicate mesh created under the "No History" case not undoable // // Case (1): // -// Under the "No History / History turned on" case, history is allowed so the DG +// Under the "No History" case, history is allowed so the DG // is used to perform the operation. However, every polyModifierNode requires // an input mesh and without any prior history, a mesh input needs to be created. // polyModifierCmd compensates for this by duplicating the meshNode and marking From 7b4aa64435e1a963e27fed2fc2a6f1f34b4cd124 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 6 Dec 2021 17:04:20 +0100 Subject: [PATCH 123/668] Return true in isSerlioInput even if tweaks and other modifications are made upstream --- src/serlio/scripts/serlioCreateUI.mel | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index 3540ddf9..29bdb981 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -41,11 +41,13 @@ proc string[] collectInitialShapes(string $nodes[]) { } proc int isSerlioInput(string $node){ - string $hist[] = `listHistory -lv 1 -f true $node`; + string $hist[] = `listHistory -f true $node`; if (size($hist) > 1) { - if(`nodeType $hist[1]` == "serlio"){ + for ($node in $hist){ + if(`nodeType $node` == "serlio"){ return true; } + } } return false; } From 23f69c8bac41044036125c47558880787be19c1c Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 7 Dec 2021 09:30:13 +0100 Subject: [PATCH 124/668] Cleanup: renamed helper function --- src/serlio/scripts/serlioCreateUI.mel | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index 29bdb981..73c5bb8a 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -40,7 +40,7 @@ proc string[] collectInitialShapes(string $nodes[]) { return $result; } -proc int isSerlioInput(string $node){ +proc int isSerlioNodePredecessor(string $node){ string $hist[] = `listHistory -f true $node`; if (size($hist) > 1) { for ($node in $hist){ @@ -79,7 +79,7 @@ global proc createPrtNode() { } } - if (!$alreadyHasExistingPrtNode && !isSerlioInput($node)){ + if (!$alreadyHasExistingPrtNode && !isSerlioNodePredecessor($node)){ select -r $node; serlioAssign $rulePackage; } From 1ccb1e6a43ecbd60fb283e9e9067441844168504 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 7 Dec 2021 10:27:50 +0100 Subject: [PATCH 125/668] Cleanup: remove old comment --- src/serlio/modifiers/polyModifier/polyModifierCmd.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp b/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp index 63f2a503..740fab61 100644 --- a/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp +++ b/src/serlio/modifiers/polyModifier/polyModifierCmd.cpp @@ -221,7 +221,6 @@ void polyModifierCmd::collectNodeState() // // - HasHistory (Construction History exists) // - HasTweaks - // - HasRecordHistory (Construction History is turned on) // fDagPath.extendToShape(); MObject meshNodeShape = fDagPath.node(); From 26f8c72def5da597cb7b776ff3f5845e6139fd9b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 7 Dec 2021 10:44:46 +0100 Subject: [PATCH 126/668] Cleanup: remove unused member variable --- src/serlio/modifiers/polyModifier/polyModifierCmd.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/serlio/modifiers/polyModifier/polyModifierCmd.h b/src/serlio/modifiers/polyModifier/polyModifierCmd.h index 98a58950..46488976 100644 --- a/src/serlio/modifiers/polyModifier/polyModifierCmd.h +++ b/src/serlio/modifiers/polyModifier/polyModifierCmd.h @@ -428,10 +428,6 @@ class polyModifierCmd : public MPxCommand MIntArray fTweakIndexArray; MFloatVectorArray fTweakVectorArray; - // Cached Mesh Data (for undo in the 'No History'/'History turned off' case) - // - MObject fMeshData; - // DG and DAG Modifier // // - We need both DAG and DG modifiers since the MDagModifier::createNode() From 8977f51602205de60e032822c03afbee14759223 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 7 Dec 2021 11:06:37 +0100 Subject: [PATCH 127/668] Don't add materials to serlio node predecessor meshes These meshes are most likely used as an initial shape and should not be considered as an output of a serlio node. This commit also contains a minor refactor in order to combine the createMaterialNode functions --- src/serlio/scripts/serlioCreateUI.mel | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index 73c5bb8a..cb033d52 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -121,23 +121,7 @@ global proc removePrtNode() { select -r $select; } - -global proc createPrtMaterialNode() { - string $select[] = `ls -sl`; - string $initialShapes[] = collectInitialShapes($select); - - if(size($initialShapes) == 0) { - confirmDialog -title "Missing selection" -message "Please select shapes first in order to create materials." -button "OK"; - return; - } - - for($node in $initialShapes) { - select -r $node; - serlioCreateMaterial "stingray"; - } -} - -global proc createArnoldMaterialNode() { +global proc createMaterialNode(string $materialType) { string $select[] = `ls -sl`; string $initialShapes[] = collectInitialShapes($select); @@ -148,7 +132,8 @@ global proc createArnoldMaterialNode() { for($node in $initialShapes) { select -r $node; - serlioCreateMaterial "arnold"; + if(!isSerlioNodePredecessor($node)) + serlioCreateMaterial $materialType; } } @@ -164,8 +149,8 @@ global proc createPrtMenu() { menuItem -divider true; menuItem -label "Attach CityEngine Rule Package..." -c "createPrtNode" -annotation "Attach a CGA rule package to a geometry"; menuItem -label "Remove CityEngine Rule Package" -c "removePrtNode" -annotation "Remove a CGA rule package from a geometry"; - menuItem -label "Create Materials" -c "createPrtMaterialNode" -annotation "Create Materials"; - menuItem -label "Create Arnold Materials" -c "createArnoldMaterialNode" -annotation "Create Arnold Materials"; + menuItem -label "Create Materials" -c "createMaterialNode(\"stingray\")" -annotation "Create Materials"; + menuItem -label "Create Arnold Materials" -c "createMaterialNode(\"arnold\")" -annotation "Create Arnold Materials"; setParent -m ..; } } From a87ae4d9f0463e9c6265411f4206ee4b098c6863 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 8 Dec 2021 15:27:06 +0100 Subject: [PATCH 128/668] Reuse user seed for generating default attributes Also generate seed before updateRuleFiles --- src/serlio/modifiers/PRTModifierAction.cpp | 8 +++----- src/serlio/modifiers/PRTModifierNode.cpp | 6 +++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 9c07eed6..1e0f1e8f 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -62,7 +62,7 @@ const AttributeMapUPtr AttributeMapUPtr getDefaultAttributeValues(const std::wstring& ruleFile, const std::wstring& startRule, const prt::ResolveMap& resolveMap, prt::CacheObject& cache, - const PRTMesh& prtMesh) { + const PRTMesh& prtMesh, const int32_t seed) { AttributeMapBuilderUPtr mayaCallbacksAttributeBuilder(prt::AttributeMapBuilder::create()); MayaCallbacks mayaCallbacks(MObject::kNullObj, MObject::kNullObj, mayaCallbacksAttributeBuilder); @@ -71,8 +71,6 @@ AttributeMapUPtr getDefaultAttributeValues(const std::wstring& ruleFile, const s isb->setGeometry(prtMesh.vertexCoords(), prtMesh.vcCount(), prtMesh.indices(), prtMesh.indicesCount(), prtMesh.faceCounts(), prtMesh.faceCountsCount()); - const int32_t seed = mu::computeSeed(prtMesh.vertexCoords(), prtMesh.vcCount()); - isb->setAttributes(ruleFile.c_str(), startRule.c_str(), seed, L"", EMPTY_ATTRIBUTES.get(), &resolveMap); const InitialShapeUPtr shape(isb->createInitialShapeAndReset()); @@ -167,7 +165,7 @@ MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { const std::list cgaAttributes = getNodeAttributesCorrespondingToCGA(fNode); const AttributeMapUPtr defaultAttributeValues = - getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, *inPrtMesh); + getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, *inPrtMesh, mRandomSeed); AttributeMapBuilderUPtr aBuilder(prt::AttributeMapBuilder::create()); for (const auto& attrObj : cgaAttributes) { @@ -310,7 +308,7 @@ MStatus PRTModifierAction::updateRuleFiles(const MObject& node, const MString& r mStartRule = prtu::detectStartRule(info); mGenerateAttrs = getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, - *inPrtMesh); + *inPrtMesh, mRandomSeed); if (DBG) LOG_DBG << "default attrs: " << prtu::objectToXML(mGenerateAttrs); diff --git a/src/serlio/modifiers/PRTModifierNode.cpp b/src/serlio/modifiers/PRTModifierNode.cpp index 1128ae42..bf8d2e02 100644 --- a/src/serlio/modifiers/PRTModifierNode.cpp +++ b/src/serlio/modifiers/PRTModifierNode.cpp @@ -104,6 +104,9 @@ MStatus PRTModifierNode::compute(const MPlug& plug, MDataBlock& data) { // Set the mesh object and component List on the factory fPRTModifierAction.setMesh(iMesh, oMesh); + MDataHandle randomSeed = data.inputValue(mRandomSeed, &status); + fPRTModifierAction.setRandomSeed(randomSeed.asInt()); + if (rulePkgData.asString() != currentRulePkgData.asString()) { fPRTModifierAction.updateRuleFiles(thisMObject(), rulePkgData.asString()); } @@ -112,9 +115,6 @@ MStatus PRTModifierNode::compute(const MPlug& plug, MDataBlock& data) { if (status != MStatus::kSuccess) return status; - MDataHandle randomSeed = data.inputValue(mRandomSeed, &status); - fPRTModifierAction.setRandomSeed(randomSeed.asInt()); - // Now, perform the PRT status = fPRTModifierAction.doIt(); From 22c973a9f35afa67bdc076e7136c903422cac25f Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 8 Dec 2021 15:31:27 +0100 Subject: [PATCH 129/668] Cleanup: remove obsolete computeSeed function --- src/serlio/utils/MayaUtilities.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/serlio/utils/MayaUtilities.cpp b/src/serlio/utils/MayaUtilities.cpp index a5904348..295fd2db 100644 --- a/src/serlio/utils/MayaUtilities.cpp +++ b/src/serlio/utils/MayaUtilities.cpp @@ -22,17 +22,6 @@ int32_t computeSeed(const MFloatPointArray& vertices) { return computeSeed(a); } -int32_t computeSeed(const double* vertices, size_t count) { - MFloatPoint a(0.0, 0.0, 0.0); - for (unsigned int vi = 0; vi < count; vi += 3) { - a[0] += static_cast(vertices[vi + 0]); - a[1] += static_cast(vertices[vi + 1]); - a[2] += static_cast(vertices[vi + 2]); - } - a = a / static_cast(count); - return computeSeed(a); -} - void statusCheck(const MStatus& status, const char* file, int line) { if (MS::kSuccess != status) { LOG_ERR << "maya status error at " << file << ":" << line << ": " << status.errorString().asChar() << " (code " From dc440260ee801e614ae1680aa472c5254fad20cc Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 9 Dec 2021 08:59:39 +0100 Subject: [PATCH 130/668] Cmake: add support for maya 2022 --- src/common.cmake | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/common.cmake b/src/common.cmake index a091e921..18276c69 100644 --- a/src/common.cmake +++ b/src/common.cmake @@ -77,7 +77,9 @@ if (NOT maya_DIR) set(AUTODESK_INSTALL_LOCATION "/usr/autodesk") set(MAYA_BASE_NAME maya) endif () - if (EXISTS "${AUTODESK_INSTALL_LOCATION}/${MAYA_BASE_NAME}2020") + if (EXISTS "${AUTODESK_INSTALL_LOCATION}/${MAYA_BASE_NAME}2022") + set(maya_DIR "${AUTODESK_INSTALL_LOCATION}/${MAYA_BASE_NAME}2022") + elseif (EXISTS "${AUTODESK_INSTALL_LOCATION}/${MAYA_BASE_NAME}2020") set(maya_DIR "${AUTODESK_INSTALL_LOCATION}/${MAYA_BASE_NAME}2020") elseif (EXISTS "${AUTODESK_INSTALL_LOCATION}/${MAYA_BASE_NAME}2019") set(maya_DIR "${AUTODESK_INSTALL_LOCATION}/${MAYA_BASE_NAME}2019") @@ -93,6 +95,8 @@ elseif (maya_DIR MATCHES "[Mm]aya2019") set(maya_VERSION_MAJOR "2019") elseif (maya_DIR MATCHES "[Mm]aya2020") set(maya_VERSION_MAJOR "2020") +elseif (maya_DIR MATCHES "[Mm]aya2022") + set(maya_VERSION_MAJOR "2022") endif () function(srl_add_dependency_maya TGT) From fcb22592d80c727e61c7fa35b6b54d68b59d5f00 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 9 Dec 2021 09:23:46 +0100 Subject: [PATCH 131/668] CI: add Maya 2022 to pipeline --- pipeline.groovy | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pipeline.groovy b/pipeline.groovy index 0f13df4e..24afbbe4 100644 --- a/pipeline.groovy +++ b/pipeline.groovy @@ -24,14 +24,16 @@ import com.esri.zrh.jenkins.ce.PrtAppPipelineLibrary [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2018', maya: PrtAppPipelineLibrary.Dependencies.MAYA2018 ], [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2019', maya: PrtAppPipelineLibrary.Dependencies.MAYA2019 ], [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2020', maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], + [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2022', maya: PrtAppPipelineLibrary.Dependencies.MAYA2022 ], [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2018', maya: PrtAppPipelineLibrary.Dependencies.MAYA2018 ], [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2019', maya: PrtAppPipelineLibrary.Dependencies.MAYA2019 ], [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2020', maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2022', maya: PrtAppPipelineLibrary.Dependencies.MAYA2022 ], ] @Field final List TEST_CONFIGS = [ - [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], + [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, maya: PrtAppPipelineLibrary.Dependencies.MAYA2022 ], + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, maya: PrtAppPipelineLibrary.Dependencies.MAYA2022 ], ] From adcc0c0ea0413c1321ed34aa322f219a43bf13c2 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 9 Dec 2021 09:25:54 +0100 Subject: [PATCH 132/668] Add maya 2022 to windows installers --- deploy/PackageContents.xml.in | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/deploy/PackageContents.xml.in b/deploy/PackageContents.xml.in index 20c8377d..bafda7c5 100644 --- a/deploy/PackageContents.xml.in +++ b/deploy/PackageContents.xml.in @@ -59,4 +59,13 @@ + + + + + + + + + From 8e53df10688d9a1af88ffd3ab195ea97587d5277 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 9 Dec 2021 11:59:18 +0100 Subject: [PATCH 133/668] Temporarily switch pipeline to test environment --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index f1dc56d5..09575847 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -14,7 +14,7 @@ import com.esri.zrh.jenkins.PslFactory // -- SETUP -psl.runsHere('production') +psl.runsHere('testing') env.PIPELINE_ARCHIVING_ALLOWED = "true" properties([ disableConcurrentBuilds() ]) From 7694de51c3fea89b298eb4b57fbde3fd26cc2b09 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 9 Dec 2021 12:08:08 +0100 Subject: [PATCH 134/668] Temporarily switch pipeline to development branch --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 09575847..40e0cb23 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -5,7 +5,7 @@ import groovy.transform.Field // -- PIPELINE LIBRARIES -@Library('psl') +@Library('psl@chr11115/maya2022') import com.esri.zrh.jenkins.PipelineSupportLibrary import com.esri.zrh.jenkins.PslFactory From e8a8c06c735f9b07d997d5b0126773f265a20a3e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 10 Dec 2021 15:53:47 +0100 Subject: [PATCH 135/668] Cleanup: added filters to function arguments for prt file dialoges With this argument we can easily allow all file types for custom attributes --- src/serlio/scripts/AEserlioTemplate.mel | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 80ff26f7..a14f73fc 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -49,11 +49,7 @@ global proc int prtRPKChange(string $attr, string $varname) { return $oldpkg == $newpkg; } -global proc prtShowFileDialog(string $attr) { - $node = prtNode($attr); - $plug = prtPlug($attr); - $filters = `attributeQuery -n $node -nn $plug`; - +global proc prtShowFileDialog(string $attr, string $filters) { string $file[]; if(startsWith($filters, "@Directory")) { @@ -70,31 +66,34 @@ global proc prtShowFileDialog(string $attr) { } } -global proc prtFileBrowse(string $attr, string $varname){ +global proc prtFileBrowse(string $attr, string $varname, string $filters){ $varname = prtPlug($varname); + $filters = prtPlug($filters); setUITemplate -pst attributeEditorTemplate; rowLayout -nc 4; text -label `niceName($attr)`; string $control = prtControlName($attr); textField -fileName `getAttr $attr` $control; connectControl -fileName $control $attr; - symbolButton -image "navButtonBrowse.xpm" -c ("prtShowFileDialog(\"" + $attr + "\")") ($control + "_browse"); + symbolButton -image "navButtonBrowse.xpm" -c ("prtShowFileDialog(\"" + $attr + "\",\"" + $filters + "\" )") ($control + "_browse"); button -label "Reload" -command ("prtReloadRPK(\"" + $attr + "\")"); setParent ..; setUITemplate -ppt; } -global proc prtFileBrowseReplace(string $attr, string $varname){ +global proc prtFileBrowseReplace(string $attr, string $varname, string $filters){ + $filters = prtPlug($filters); if(prtRPKChange($attr, $varname)) { string $control = prtControlName($attr); connectControl $control $attr; - symbolButton -edit -c ("prtShowFileDialog(\"" + $attr + "\")") ($control + "_browse"); + symbolButton -edit -c ("prtShowFileDialog(\"" + $attr + "\",\"" + $filters + "\" )") ($control + "_browse"); } } -global proc prtFileBrowseReplaceRPK(string $attr, string $varname){ +global proc prtFileBrowseReplaceRPK(string $attr, string $varname, string $filters){ + $filters = prtPlug($filters); if(prtRPKChange($attr, $varname)) { - prtFileBrowseReplace($attr, $varname); + prtFileBrowseReplace($attr, $varname, $filters); } else { $varname = prtPlug($varname); $newpkg = `getAttr $attr`; @@ -127,7 +126,8 @@ global proc AEserlioTemplate(string $node) { editorTemplate -beginLayout "CGA Rules" -collapse 0; string $varname = ("AEprtTemplate_" + $node); eval ("global string $" + $varname + "=\"" + `getAttr ($node + ".Rule_Package")` + "\""); - editorTemplate -callCustom "prtFileBrowse" "prtFileBrowseReplaceRPK" "Rule_Package" $varname; + string $filter = niceName($node + ".Rule_Package"); + editorTemplate -callCustom "prtFileBrowse" "prtFileBrowseReplaceRPK" "Rule_Package" $varname $filter; editorTemplate -l `niceName($node+".Random_Seed")` -adc "Random_Seed"; @@ -177,7 +177,7 @@ global proc AEserlioTemplate(string $node) { if(`addAttr -q -uaf ($longNameWithNode) `) { - editorTemplate -callCustom "prtFileBrowse" "prtFileBrowseReplace" $attr $varname; + editorTemplate -callCustom "prtFileBrowse" "prtFileBrowseReplace" $attr $varname "All Files (*.*)"; } else if(`addAttr -q -uac ($longNameWithNode) `) { editorTemplate -callCustom "prtColorChooser" "prtColorChooserReplace" $attr $varname; $skip = 3; From 58f8676a929cdc65288bbf6328fbf386adef62b7 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 10 Dec 2021 15:55:25 +0100 Subject: [PATCH 136/668] Cleanup: rename function to reflect more universal usage --- src/serlio/scripts/AEserlioTemplate.mel | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index a14f73fc..d9a8efa6 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -17,16 +17,16 @@ * limitations under the License. */ -proc string prtNode(string $attr) { +proc string prtGetNodeName(string $attr) { return `match "^[^\.]*" $attr`; } -proc string prtPlug(string $attr) { +proc string prtRemoveNodeName(string $attr) { return `substitute "^[^.]*\\." $attr ""`; } proc string prtControlName(string $attr) { - return substituteAllString(`prtPlug($attr)`, ".", "_"); + return substituteAllString(`prtRemoveNodeName($attr)`, ".", "_"); } proc string niceName(string $longName) { @@ -34,16 +34,16 @@ proc string niceName(string $longName) { } global proc prtReloadRPK(string $attr) { - $node = prtNode($attr); + $node = prtGetNodeName($attr); $attrCurrentRule = $node + ".currentRule_Package"; setAttr -type "string" $attrCurrentRule ""; dgdirty $node; } global proc int prtRPKChange(string $attr, string $varname) { - $varname = prtPlug($varname); + $varname = prtRemoveNodeName($varname); $oldpkg = eval("$tmp=$" + $varname); - $node = prtNode($attr); + $node = prtGetNodeName($attr); $newpkg = `getAttr ($node + ".Rule_Package")`; return $oldpkg == $newpkg; @@ -67,8 +67,8 @@ global proc prtShowFileDialog(string $attr, string $filters) { } global proc prtFileBrowse(string $attr, string $varname, string $filters){ - $varname = prtPlug($varname); - $filters = prtPlug($filters); + $varname = prtRemoveNodeName($varname); + $filters = prtRemoveNodeName($filters); setUITemplate -pst attributeEditorTemplate; rowLayout -nc 4; text -label `niceName($attr)`; @@ -82,7 +82,7 @@ global proc prtFileBrowse(string $attr, string $varname, string $filters){ } global proc prtFileBrowseReplace(string $attr, string $varname, string $filters){ - $filters = prtPlug($filters); + $filters = prtRemoveNodeName($filters); if(prtRPKChange($attr, $varname)) { string $control = prtControlName($attr); connectControl $control $attr; @@ -91,11 +91,11 @@ global proc prtFileBrowseReplace(string $attr, string $varname, string $filters) } global proc prtFileBrowseReplaceRPK(string $attr, string $varname, string $filters){ - $filters = prtPlug($filters); + $filters = prtRemoveNodeName($filters); if(prtRPKChange($attr, $varname)) { prtFileBrowseReplace($attr, $varname, $filters); } else { - $varname = prtPlug($varname); + $varname = prtRemoveNodeName($varname); $newpkg = `getAttr $attr`; evalDeferred ("$" + $varname + "=\"" + $newpkg + "\"; refreshEditorTemplates();"); } From 34fb05e1cbf680bace3d10d2581e04e246678048 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 13 Dec 2021 10:07:43 +0100 Subject: [PATCH 137/668] Temporarily switch pipeline to main development branch --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 40e0cb23..f1986db8 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -5,7 +5,7 @@ import groovy.transform.Field // -- PIPELINE LIBRARIES -@Library('psl@chr11115/maya2022') +@Library('psl') import com.esri.zrh.jenkins.PipelineSupportLibrary import com.esri.zrh.jenkins.PslFactory @@ -14,7 +14,7 @@ import com.esri.zrh.jenkins.PslFactory // -- SETUP -psl.runsHere('testing') +psl.runsHere('development') env.PIPELINE_ARCHIVING_ALLOWED = "true" properties([ disableConcurrentBuilds() ]) From c512e7553f2a14c6f4afa55777679f23c72094b0 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 13 Dec 2021 10:23:47 +0100 Subject: [PATCH 138/668] Switch pipeline back to production --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index f1986db8..f1dc56d5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -14,7 +14,7 @@ import com.esri.zrh.jenkins.PslFactory // -- SETUP -psl.runsHere('development') +psl.runsHere('production') env.PIPELINE_ARCHIVING_ALLOWED = "true" properties([ disableConcurrentBuilds() ]) From ac6e09959596e295bffd4d525ac28fb9a2d15819 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 14 Dec 2021 16:23:21 +0100 Subject: [PATCH 139/668] Switch text button to symbol with annotation --- src/serlio/scripts/AEserlioTemplate.mel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 80ff26f7..91b380aa 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -79,7 +79,7 @@ global proc prtFileBrowse(string $attr, string $varname){ textField -fileName `getAttr $attr` $control; connectControl -fileName $control $attr; symbolButton -image "navButtonBrowse.xpm" -c ("prtShowFileDialog(\"" + $attr + "\")") ($control + "_browse"); - button -label "Reload" -command ("prtReloadRPK(\"" + $attr + "\")"); + symbolButton -ann "reload rule package" -image "refresh.png" -c ("prtReloadRPK(\"" + $attr + "\")") ($control + "_reload"); setParent ..; setUITemplate -ppt; } From da58e4c517fdaa4f2c8904222aa58a3e7e7800fe Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 14 Dec 2021 16:25:22 +0100 Subject: [PATCH 140/668] fix update button for duplicate serlio nodes using the same rule package --- src/serlio/scripts/AEserlioTemplate.mel | 1 + 1 file changed, 1 insertion(+) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 91b380aa..79edbd84 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -89,6 +89,7 @@ global proc prtFileBrowseReplace(string $attr, string $varname){ string $control = prtControlName($attr); connectControl $control $attr; symbolButton -edit -c ("prtShowFileDialog(\"" + $attr + "\")") ($control + "_browse"); + symbolButton -edit -c ("prtReloadRPK(\"" + $attr + "\")") ($control + "_reload"); } } From 54a5c349b3bf3d98d120b0e1175029cd893b2352 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 22 Dec 2021 09:14:56 +0100 Subject: [PATCH 141/668] Add hidden user_set and force_default attributes --- src/serlio/modifiers/PRTModifierAction.cpp | 41 +++++++++++++++++++--- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index ec6e2808..4005073e 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -57,6 +57,9 @@ constexpr const wchar_t* MIN_KEY = L"min"; constexpr const wchar_t* MAX_KEY = L"max"; constexpr const wchar_t* RESTRICTED_KEY = L"restricted"; +constexpr const wchar_t* ATTRIBUTE_USER_SET_SUFFIX = L"_user_set"; +constexpr const wchar_t* ATTRIBUTE_FORCE_RESET_SUFFIX = L"_force_default"; + const AttributeMapUPtr EMPTY_ATTRIBUTES(AttributeMapBuilderUPtr(prt::AttributeMapBuilder::create())->createAttributeMap()); @@ -115,8 +118,8 @@ std::list getNodeAttributesCorrespondingToCGA(const MFnDependencyNode& const MFnAttribute attr(attrObj); - // CGA rule attributes are maya dynamic attributes - if (!attr.isDynamic()) + // CGA rule attributes are maya dynamic attributes and not hidden + if (!attr.isDynamic() || attr.isHidden()) continue; // maya annoyance: color attributes automatically get per-component plugs/child attrs @@ -522,8 +525,11 @@ MStatus PRTModifierAction::createNodeAttributes(const MObject& nodeObj, const pr void PRTModifierAction::removeUnusedAttribs(MFnDependencyNode& node) { auto isInUse = [this](const MString& attrName) { - auto it = std::find_if(mRuleAttributes.begin(), mRuleAttributes.end(), - [&attrName](const auto& ra) { return (ra.mayaFullName == attrName.asWChar()); }); + auto it = std::find_if(mRuleAttributes.begin(), mRuleAttributes.end(), [&attrName](const auto& ra) { + return (ra.mayaFullName == attrName.asWChar() || + ra.mayaFullName + ATTRIBUTE_USER_SET_SUFFIX == attrName.asWChar() || + ra.mayaFullName + ATTRIBUTE_FORCE_RESET_SUFFIX == attrName.asWChar()); + }); return (it != mRuleAttributes.end()); }; @@ -637,6 +643,33 @@ MStatus PRTModifierAction::addParameter(MFnDependencyNode& node, MObject& attr, MCHECK(tAttr.setHidden(false)); MCHECK(tAttr.setStorable(true)); MCHECK(node.addAttribute(attr)); + + //add hidden user_set attribute + MStatus stat; + MFnNumericAttribute nAttrUserSet; + MObject attrUserSet = nAttrUserSet.create(tAttr.name() + ATTRIBUTE_USER_SET_SUFFIX, + tAttr.shortName() + ATTRIBUTE_USER_SET_SUFFIX, + MFnNumericData::kBoolean, false, &stat); + + if (!(node.hasAttribute(nAttrUserSet.shortName()))) { + MCHECK(nAttrUserSet.setKeyable(true)); + MCHECK(nAttrUserSet.setHidden(true)); + MCHECK(nAttrUserSet.setStorable(true)); + MCHECK(node.addAttribute(attrUserSet)); + } + + //add hidden force_default attribute + MFnNumericAttribute nAttrForceDefault; + MObject attrForceDefault = nAttrForceDefault.create(tAttr.name() + ATTRIBUTE_FORCE_RESET_SUFFIX, + tAttr.shortName() + ATTRIBUTE_FORCE_RESET_SUFFIX, + MFnNumericData::kBoolean, false, &stat); + + if (!(node.hasAttribute(nAttrForceDefault.shortName()))) { + MCHECK(nAttrForceDefault.setKeyable(true)); + MCHECK(nAttrForceDefault.setHidden(true)); + MCHECK(nAttrForceDefault.setStorable(true)); + MCHECK(node.addAttribute(attrForceDefault)); + } } return MS::kSuccess; } From 7cc52480beb38c931fbcd5206adf8722cfb85227 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 22 Dec 2021 09:17:05 +0100 Subject: [PATCH 142/668] Cleanup: use consistent name for force_default --- src/serlio/modifiers/PRTModifierAction.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 4005073e..1f744a1f 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -58,7 +58,7 @@ constexpr const wchar_t* MAX_KEY = L"max"; constexpr const wchar_t* RESTRICTED_KEY = L"restricted"; constexpr const wchar_t* ATTRIBUTE_USER_SET_SUFFIX = L"_user_set"; -constexpr const wchar_t* ATTRIBUTE_FORCE_RESET_SUFFIX = L"_force_default"; +constexpr const wchar_t* ATTRIBUTE_FORCE_DEFAULT_SUFFIX = L"_force_default"; const AttributeMapUPtr EMPTY_ATTRIBUTES(AttributeMapBuilderUPtr(prt::AttributeMapBuilder::create())->createAttributeMap()); @@ -528,7 +528,7 @@ void PRTModifierAction::removeUnusedAttribs(MFnDependencyNode& node) { auto it = std::find_if(mRuleAttributes.begin(), mRuleAttributes.end(), [&attrName](const auto& ra) { return (ra.mayaFullName == attrName.asWChar() || ra.mayaFullName + ATTRIBUTE_USER_SET_SUFFIX == attrName.asWChar() || - ra.mayaFullName + ATTRIBUTE_FORCE_RESET_SUFFIX == attrName.asWChar()); + ra.mayaFullName + ATTRIBUTE_FORCE_DEFAULT_SUFFIX == attrName.asWChar()); }); return (it != mRuleAttributes.end()); }; @@ -660,8 +660,8 @@ MStatus PRTModifierAction::addParameter(MFnDependencyNode& node, MObject& attr, //add hidden force_default attribute MFnNumericAttribute nAttrForceDefault; - MObject attrForceDefault = nAttrForceDefault.create(tAttr.name() + ATTRIBUTE_FORCE_RESET_SUFFIX, - tAttr.shortName() + ATTRIBUTE_FORCE_RESET_SUFFIX, + MObject attrForceDefault = nAttrForceDefault.create(tAttr.name() + ATTRIBUTE_FORCE_DEFAULT_SUFFIX, + tAttr.shortName() + ATTRIBUTE_FORCE_DEFAULT_SUFFIX, MFnNumericData::kBoolean, false, &stat); if (!(node.hasAttribute(nAttrForceDefault.shortName()))) { From 795836c287e07acdcfb3c1c0378137d3d62caeba Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 22 Dec 2021 09:37:00 +0100 Subject: [PATCH 143/668] Merge attribute UI code --- src/serlio/scripts/AEserlioTemplate.mel | 36 ++++++++++++++++++------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 79edbd84..bfca7804 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -103,19 +103,40 @@ global proc prtFileBrowseReplaceRPK(string $attr, string $varname){ } } -global proc prtColorChooser(string $attr, string $varname) { +global proc prtAttributeControl(string $attr, string $varname){ setUITemplate -pst attributeEditorTemplate; string $control = prtControlName($attr); - colorSliderGrp -label `niceName($attr)` $control; + int $nrOfColumns = 3; + int $isFileAttribute = `addAttr -q -uaf ($attr)`; + + if($isFileAttribute){ + $nrOfColumns = 5; + } + + rowColumnLayout -numberOfColumns $nrOfColumns -adjustableColumn 1 ($control + "_column_layout"); + + $label = `niceName $attr`; + + attrControlGrp -hideMapButton true -label $label -attribute $attr $control; + + if ($isFileAttribute){ + symbolButton -image "navButtonBrowse.xpm" -c ("prtShowFileDialog(\"" + $attr + "\")") ($control + "_browse"); + symbolButton -ann "reload file" -image "refresh.png" -c ("prtReloadRPK(\"" + $attr + "\")") ($control + "_reload"); + } + connectControl $control $attr; setParent ..; setUITemplate -ppt; } -global proc prtColorChooserReplace(string $attr, string $varname){ +global proc prtAttributeControlReplace(string $attr, string $varname){ if(prtRPKChange($attr, $varname)) { string $control = prtControlName($attr); connectControl $control $attr; + if (`addAttr -q -uaf ($attr)`){ + symbolButton -edit -c ("prtShowFileDialog(\"" + $attr + "\")") ($control + "_browse"); + symbolButton -edit -c ("prtReloadRPK(\"" + $attr + "\")") ($control + "_reload"); + } } } @@ -177,14 +198,9 @@ global proc AEserlioTemplate(string $node) { } - if(`addAttr -q -uaf ($longNameWithNode) `) { - editorTemplate -callCustom "prtFileBrowse" "prtFileBrowseReplace" $attr $varname; - } else if(`addAttr -q -uac ($longNameWithNode) `) { - editorTemplate -callCustom "prtColorChooser" "prtColorChooserReplace" $attr $varname; + editorTemplate -callCustom "prtAttributeControl" "prtAttributeControlReplace" $attr $varname; + if(`addAttr -q -uac ($longNameWithNode) `) $skip = 3; - } else { - editorTemplate -l `niceName($longNameWithNode)` -adc $attr; - } } if ($currentGroupName != "") From 95d84aa4f3ef8218ce72edd82b665399c51f6dec Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 22 Dec 2021 09:44:26 +0100 Subject: [PATCH 144/668] Add edit highlights in UI --- src/serlio/scripts/AEserlioTemplate.mel | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index bfca7804..fc664c6a 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -103,21 +103,35 @@ global proc prtFileBrowseReplaceRPK(string $attr, string $varname){ } } +global proc prtUpdateEditHighlights(string $attr){ + int $userSet = `getAttr ($attr + "_user_set")`; + string $control = prtControlName($attr); + + symbolButton -edit -visible $userSet ($control + "_user_set"); + + if ($userSet){ + rowColumnLayout -edit -bgc 0.32 0.32 0.32 ($control + "_column_layout"); + } else { + rowColumnLayout -edit -bgc 0.4 0.4 0.4 -noBackground true ($control + "_column_layout"); + } +} + global proc prtAttributeControl(string $attr, string $varname){ setUITemplate -pst attributeEditorTemplate; string $control = prtControlName($attr); - int $nrOfColumns = 3; + int $nrOfColumns = 4; int $isFileAttribute = `addAttr -q -uaf ($attr)`; if($isFileAttribute){ - $nrOfColumns = 5; + $nrOfColumns = 6; } - rowColumnLayout -numberOfColumns $nrOfColumns -adjustableColumn 1 ($control + "_column_layout"); + rowColumnLayout -numberOfColumns $nrOfColumns -bgc 0.4 0.4 0.4 -noBackground true -adjustableColumn 2 ($control + "_column_layout"); $label = `niceName $attr`; - attrControlGrp -hideMapButton true -label $label -attribute $attr $control; + symbolButton -visible false -hlc 0.2715 0.2715 0.2715 -i "pencilCursor.png" -w 25 -h 25 -enable false ($control + "_user_set"); + attrControlGrp -hideMapButton true -label $label -changeCommand("prtUpdateEditHighlights " + $attr) -attribute $attr $control; if ($isFileAttribute){ symbolButton -image "navButtonBrowse.xpm" -c ("prtShowFileDialog(\"" + $attr + "\")") ($control + "_browse"); @@ -127,6 +141,7 @@ global proc prtAttributeControl(string $attr, string $varname){ connectControl $control $attr; setParent ..; setUITemplate -ppt; + prtUpdateEditHighlights($attr); } global proc prtAttributeControlReplace(string $attr, string $varname){ @@ -138,6 +153,7 @@ global proc prtAttributeControlReplace(string $attr, string $varname){ symbolButton -edit -c ("prtReloadRPK(\"" + $attr + "\")") ($control + "_reload"); } } + prtUpdateEditHighlights($attr); } global proc AEserlioTemplate(string $node) { From eee3c3a9bea3ba2cec9a466678ffb271f2fdd242 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 22 Dec 2021 09:55:06 +0100 Subject: [PATCH 145/668] Add helper functions for hidden attributes --- src/serlio/modifiers/PRTModifierAction.cpp | 42 ++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 1f744a1f..275d1d6e 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -90,6 +90,48 @@ AttributeMapUPtr getDefaultAttributeValues(const std::wstring& ruleFile, const s return AttributeMapUPtr(mayaCallbacksAttributeBuilder->createAttributeMap()); } +bool getIsUserSet(const MFnDependencyNode& node, const MFnAttribute& attribute) { + MString userSetAttributeName = attribute.name() + ATTRIBUTE_USER_SET_SUFFIX; + + MStatus attrStat; + MObject userSetAttributeObj = node.attribute(userSetAttributeName, &attrStat); + if (attrStat == MS::kSuccess) { + const MPlug plug(node.object(), userSetAttributeObj); + bool isUserSet = plug.asBool(&attrStat); + MCHECK(attrStat); + return isUserSet; + } + return false; +} + +MStatus setIsUserSet(const MFnDependencyNode& node, const MFnAttribute& attribute, bool value) { + MString userSetAttributeName = attribute.name() + ATTRIBUTE_USER_SET_SUFFIX; + + MStatus attrStat; + MObject userSetAttributeObj = node.attribute(userSetAttributeName, &attrStat); + if (attrStat == MS::kSuccess) { + MPlug plug(node.object(), userSetAttributeObj); + + MCHECK(plug.setBool(value)); + } + return attrStat; +} + +bool getAndResetForceDefault(const MFnDependencyNode& node, const MFnAttribute& attribute) { + MString userSetAttributeName = attribute.name() + ATTRIBUTE_FORCE_DEFAULT_SUFFIX; + + MStatus attrStat; + MObject userSetAttributeObj = node.attribute(userSetAttributeName, &attrStat); + if (attrStat == MS::kSuccess) { + MPlug plug(node.object(), userSetAttributeObj); + bool isUserSet = plug.asBool(&attrStat); + MCHECK(attrStat); + MCHECK(plug.setBool(false)); + return isUserSet; + } + return false; +} + } // namespace PRTModifierAction::PRTModifierAction() { From 35bf9499eb936ed52760f2a715f5ffe251c3ccdc Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 22 Dec 2021 09:55:38 +0100 Subject: [PATCH 146/668] Cleanup: fix formatting issues --- src/serlio/modifiers/PRTModifierAction.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 275d1d6e..1627d98f 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -209,8 +209,8 @@ MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { const std::list cgaAttributes = getNodeAttributesCorrespondingToCGA(fNode); - const AttributeMapUPtr defaultAttributeValues = - getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, *inPrtMesh, mRandomSeed); + const AttributeMapUPtr defaultAttributeValues = getDefaultAttributeValues( + mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, *inPrtMesh, mRandomSeed); AttributeMapBuilderUPtr aBuilder(prt::AttributeMapBuilder::create()); for (const auto& attrObj : cgaAttributes) { @@ -686,7 +686,7 @@ MStatus PRTModifierAction::addParameter(MFnDependencyNode& node, MObject& attr, MCHECK(tAttr.setStorable(true)); MCHECK(node.addAttribute(attr)); - //add hidden user_set attribute + // add hidden user_set attribute MStatus stat; MFnNumericAttribute nAttrUserSet; MObject attrUserSet = nAttrUserSet.create(tAttr.name() + ATTRIBUTE_USER_SET_SUFFIX, @@ -700,7 +700,7 @@ MStatus PRTModifierAction::addParameter(MFnDependencyNode& node, MObject& attr, MCHECK(node.addAttribute(attrUserSet)); } - //add hidden force_default attribute + // add hidden force_default attribute MFnNumericAttribute nAttrForceDefault; MObject attrForceDefault = nAttrForceDefault.create(tAttr.name() + ATTRIBUTE_FORCE_DEFAULT_SUFFIX, tAttr.shortName() + ATTRIBUTE_FORCE_DEFAULT_SUFFIX, From 0c972490ccd89a43bab1b19b687c627263f0ce48 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 22 Dec 2021 10:03:03 +0100 Subject: [PATCH 147/668] Use private helper function for filling attributes --- src/serlio/modifiers/PRTModifierAction.cpp | 147 ++++++++++++--------- src/serlio/modifiers/PRTModifierAction.h | 3 + src/serlio/utils/Utilities.h | 1 + 3 files changed, 90 insertions(+), 61 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 1627d98f..e62d4351 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -132,6 +132,8 @@ bool getAndResetForceDefault(const MFnDependencyNode& node, const MFnAttribute& return false; } +enum class PrtAttributeType { BOOL, FLOAT, COLOR, STRING, ENUM }; + } // namespace PRTModifierAction::PRTModifierAction() { @@ -184,21 +186,12 @@ std::list getNodeAttributesCorrespondingToCGA(const MFnDependencyNode& const RuleAttribute RULE_NOT_FOUND{}; -MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { +template +MStatus PRTModifierAction::iterateThroughAttributesAndApply(const MObject& node, T attrFunction) { MStatus stat; const MFnDependencyNode fNode(node, &stat); MCHECK(stat); - auto resolveMap = getResolveMap(); - if (!resolveMap) - return MStatus::kInvalidParameter; - - const wchar_t* ruleFileURI = resolveMap->getString(mRuleFile.c_str()); - if (ruleFileURI == nullptr) - return MStatus::kInvalidParameter; - - const RuleFileInfoUPtr info(prt::createRuleFileInfo(ruleFileURI)); - auto reverseLookupAttribute = [this](const std::wstring& mayaFullAttrName) { auto it = std::find_if(mRuleAttributes.begin(), mRuleAttributes.end(), [&mayaFullAttrName](const auto& ra) { return (ra.mayaFullName == mayaFullAttrName); }); @@ -209,13 +202,8 @@ MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { const std::list cgaAttributes = getNodeAttributesCorrespondingToCGA(fNode); - const AttributeMapUPtr defaultAttributeValues = getDefaultAttributeValues( - mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, *inPrtMesh, mRandomSeed); - AttributeMapBuilderUPtr aBuilder(prt::AttributeMapBuilder::create()); - for (const auto& attrObj : cgaAttributes) { MFnAttribute fnAttr(attrObj); - const MPlug plug(node, attrObj); const MString fullAttrName = fnAttr.name(); const RuleAttribute ruleAttr = reverseLookupAttribute(fullAttrName.asWChar()); @@ -230,27 +218,58 @@ MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { if (nAttr.unitType() == MFnNumericData::kBoolean) { assert(ruleAttrType == prt::AAT_BOOL); - bool val; - MCHECK(plug.getValue(val)); - - const auto defVal = defaultAttributeValues->getBool(fqAttrName.c_str()); - if (val != defVal) - aBuilder->setBool(fqAttrName.c_str(), val); + attrFunction(fNode, fnAttr, ruleAttr, fqAttrName, PrtAttributeType::BOOL); } else if (nAttr.unitType() == MFnNumericData::kDouble) { assert(ruleAttrType == prt::AAT_FLOAT); - double val; - MCHECK(plug.getValue(val)); - - const auto defVal = defaultAttributeValues->getFloat(fqAttrName.c_str()); - if (val != defVal) - aBuilder->setFloat(fqAttrName.c_str(), val); + attrFunction(fNode, fnAttr, ruleAttr, fqAttrName, PrtAttributeType::FLOAT); } else if (nAttr.isUsedAsColor()) { assert(ruleAttrType == prt::AAT_STR); - const wchar_t* defColStr = defaultAttributeValues->getString(fqAttrName.c_str()); + attrFunction(fNode, fnAttr, ruleAttr, fqAttrName, PrtAttributeType::COLOR); + } + } + else if (attrObj.hasFn(MFn::kTypedAttribute)) { + assert(ruleAttrType == prt::AAT_STR); + + attrFunction(fNode, fnAttr, ruleAttr, fqAttrName, PrtAttributeType::STRING); + } + else if (attrObj.hasFn(MFn::kEnumAttribute)) { + attrFunction(fNode, fnAttr, ruleAttr, fqAttrName, PrtAttributeType::ENUM); + } + } + return MStatus::kSuccess; +} + +MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { + AttributeMapBuilderSPtr aBuilder(prt::AttributeMapBuilder::create(), PRTDestroyer()); + + const auto fillAttributeFromNode = [this, aBuilder](const MFnDependencyNode& fnNode, + const MFnAttribute& fnAttribute, + const RuleAttribute& ruleAttribute, std::wstring fqAttrName, + const PrtAttributeType attrType) mutable { + MPlug plug(fnNode.object(), fnAttribute.object()); + + switch (attrType) { + case PrtAttributeType::BOOL: { + bool boolVal; + MCHECK(plug.getValue(boolVal)); + + if (getIsUserSet(fnNode, fnAttribute)) + aBuilder->setBool(fqAttrName.c_str(), boolVal); + break; + } + case PrtAttributeType::FLOAT: { + double doubleVal; + MCHECK(plug.getValue(doubleVal)); + + if (getIsUserSet(fnNode, fnAttribute)) + aBuilder->setFloat(fqAttrName.c_str(), doubleVal); + break; + } + case PrtAttributeType::COLOR: { MObject rgb; MCHECK(plug.getValue(rgb)); MFnNumericData fRGB(rgb); @@ -259,46 +278,52 @@ MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { MCHECK(fRGB.getData3Float(col[0], col[1], col[2])); const std::wstring colStr = prtu::getColorString(col); - if (std::wcscmp(colStr.c_str(), defColStr) != 0) + if (getIsUserSet(fnNode, fnAttribute)) aBuilder->setString(fqAttrName.c_str(), colStr.c_str()); + break; } - } - else if (attrObj.hasFn(MFn::kTypedAttribute)) { - assert(ruleAttrType == prt::AAT_STR); - - MString val; - MCHECK(plug.getValue(val)); + case PrtAttributeType::STRING: { + MString stringVal; + MCHECK(plug.getValue(stringVal)); - const auto defVal = defaultAttributeValues->getString(fqAttrName.c_str()); - if (std::wcscmp(val.asWChar(), defVal) != 0) - aBuilder->setString(fqAttrName.c_str(), val.asWChar()); - } - else if (attrObj.hasFn(MFn::kEnumAttribute)) { - MFnEnumAttribute eAttr(attrObj); - - short di; - short i; - MCHECK(eAttr.getDefault(di)); - MCHECK(plug.getValue(i)); - if (i != di) { - switch (ruleAttrType) { - case prt::AAT_STR: - aBuilder->setString(fqAttrName.c_str(), eAttr.fieldName(i).asWChar()); - break; - case prt::AAT_FLOAT: - aBuilder->setFloat(fqAttrName.c_str(), eAttr.fieldName(i).asDouble()); - break; - case prt::AAT_BOOL: - aBuilder->setBool(fqAttrName.c_str(), eAttr.fieldName(i).asInt() != 0); - break; - default: - LOG_ERR << "Cannot handle attribute type " << ruleAttrType << " for attr " << fqAttrName; + if (getIsUserSet(fnNode, fnAttribute)) + aBuilder->setString(fqAttrName.c_str(), stringVal.asWChar()); + break; + } + case PrtAttributeType::ENUM: { + MFnEnumAttribute eAttr(fnAttribute.object()); + + short defEnumVal; + short enumVal; + MCHECK(plug.getValue(enumVal)); + + if (getIsUserSet(fnNode, fnAttribute)) { + switch (ruleAttribute.mType) { + case prt::AAT_STR: + aBuilder->setString(fqAttrName.c_str(), eAttr.fieldName(enumVal).asWChar()); + break; + case prt::AAT_FLOAT: + aBuilder->setFloat(fqAttrName.c_str(), eAttr.fieldName(enumVal).asDouble()); + break; + case prt::AAT_BOOL: + aBuilder->setBool(fqAttrName.c_str(), eAttr.fieldName(enumVal).asInt() != 0); + break; + default: + LOG_ERR << "Cannot handle attribute type " << ruleAttribute.mType << " for attr " + << fqAttrName; + } } + break; } + + default: + break; } - } + }; + iterateThroughAttributesAndApply(node, fillAttributeFromNode); mGenerateAttrs.reset(aBuilder->createAttributeMap()); + return MStatus::kSuccess; } diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 2d5559c1..1809e4da 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -107,6 +107,9 @@ class PRTModifierAction : public polyModifierFty { MStatus createNodeAttributes(const MObject& node, const prt::RuleFileInfo* info); void removeUnusedAttribs(MFnDependencyNode& node); + template + MStatus iterateThroughAttributesAndApply(const MObject& node, T attrFunction); + static MStatus addParameter(MFnDependencyNode& node, MObject& attr, MFnAttribute& tAttr); static MStatus addBoolParameter(MFnDependencyNode& node, MObject& attr, const RuleAttribute& name, bool defaultValue); diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index 13b9d47f..15fd62f1 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -67,6 +67,7 @@ using CacheObjectUPtr = std::unique_ptr; using AttributeMapUPtr = std::unique_ptr; using AttributeMapVector = std::vector; using AttributeMapBuilderUPtr = std::unique_ptr; +using AttributeMapBuilderSPtr = std::shared_ptr; using AttributeMapBuilderVector = std::vector; using InitialShapeUPtr = std::unique_ptr; using InitialShapeBuilderUPtr = std::unique_ptr; From 5fed554cbf266e29858f4e75de4f80c858ab3428 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 22 Dec 2021 10:03:20 +0100 Subject: [PATCH 148/668] Cleanup: remove unused local variable --- src/serlio/modifiers/PRTModifierAction.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index e62d4351..56b5b293 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -293,7 +293,6 @@ MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { case PrtAttributeType::ENUM: { MFnEnumAttribute eAttr(fnAttribute.object()); - short defEnumVal; short enumVal; MCHECK(plug.getValue(enumVal)); From d0e0253aeee24a34fe1fad3fa0e649231c35c5e2 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 22 Dec 2021 10:10:20 +0100 Subject: [PATCH 149/668] Update hidden user_set attribute values --- src/serlio/modifiers/PRTModifierAction.cpp | 78 ++++++++++++++++++++++ src/serlio/modifiers/PRTModifierAction.h | 1 + src/serlio/modifiers/PRTModifierNode.cpp | 2 + 3 files changed, 81 insertions(+) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 56b5b293..b70edda9 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -326,6 +326,84 @@ MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { return MStatus::kSuccess; } +MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { + const auto updateUserSetAttribute = [this](const MFnDependencyNode& fnNode, const MFnAttribute& fnAttribute, + const RuleAttribute& ruleAttribute, std::wstring fqAttrName, + const PrtAttributeType attrType) { + const AttributeMapUPtr defaultAttributeValues = getDefaultAttributeValues( + mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, *inPrtMesh, mRandomSeed); + const MPlug plug(fnNode.object(), fnAttribute.object()); + bool isDefaultValue = false; + + switch (attrType) { + case PrtAttributeType::BOOL: { + const auto defBoolVal = defaultAttributeValues->getBool(fqAttrName.c_str()); + bool boolVal; + MCHECK(plug.getValue(boolVal)); + + isDefaultValue = defBoolVal == boolVal; + break; + } + case PrtAttributeType::FLOAT: { + const auto defDoubleVal = defaultAttributeValues->getFloat(fqAttrName.c_str()); + double doubleVal; + MCHECK(plug.getValue(doubleVal)); + + isDefaultValue = defDoubleVal == doubleVal; + break; + } + case PrtAttributeType::COLOR: { + const wchar_t* defColStr = defaultAttributeValues->getString(fqAttrName.c_str()); + + MObject rgb; + MCHECK(plug.getValue(rgb)); + MFnNumericData fRGB(rgb); + + prtu::Color col; + MCHECK(fRGB.getData3Float(col[0], col[1], col[2])); + const std::wstring colStr = prtu::getColorString(col); + + isDefaultValue = std::wcscmp(colStr.c_str(), defColStr) == 0; + break; + } + case PrtAttributeType::STRING: { + const auto defStringVal = defaultAttributeValues->getString(fqAttrName.c_str()); + + MString stringVal; + MCHECK(plug.getValue(stringVal)); + + isDefaultValue = std::wcscmp(stringVal.asWChar(), defStringVal) == 0; + break; + } + case PrtAttributeType::ENUM: { + MFnEnumAttribute eAttr(fnAttribute.object()); + + short defEnumVal; + short enumVal; + MCHECK(eAttr.getDefault(defEnumVal)); + MCHECK(plug.getValue(enumVal)); + + isDefaultValue = defEnumVal == enumVal; + break; + } + + default: + break; + } + + if (getAndResetForceDefault(fnNode, fnAttribute)) { + setIsUserSet(fnNode, fnAttribute, false); + } + else { + setIsUserSet(fnNode, fnAttribute, !isDefaultValue); + } + }; + + iterateThroughAttributesAndApply(node, updateUserSetAttribute); + + return MStatus::kSuccess; +} + // Sets the mesh object for the action to operate on void PRTModifierAction::setMesh(MObject& _inMesh, MObject& _outMesh) { inMesh = _inMesh; diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 1809e4da..7849b26c 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -68,6 +68,7 @@ class PRTModifierAction : public polyModifierFty { MStatus updateRuleFiles(const MObject& node, const MString& rulePkg); MStatus fillAttributesFromNode(const MObject& node); + MStatus updateUserSetAttributes(const MObject& node); void setMesh(MObject& _inMesh, MObject& _outMesh); void setRandomSeed(int32_t randomSeed) { mRandomSeed = randomSeed; diff --git a/src/serlio/modifiers/PRTModifierNode.cpp b/src/serlio/modifiers/PRTModifierNode.cpp index bf8d2e02..52ed7e09 100644 --- a/src/serlio/modifiers/PRTModifierNode.cpp +++ b/src/serlio/modifiers/PRTModifierNode.cpp @@ -104,6 +104,8 @@ MStatus PRTModifierNode::compute(const MPlug& plug, MDataBlock& data) { // Set the mesh object and component List on the factory fPRTModifierAction.setMesh(iMesh, oMesh); + fPRTModifierAction.updateUserSetAttributes(thisMObject()); + MDataHandle randomSeed = data.inputValue(mRandomSeed, &status); fPRTModifierAction.setRandomSeed(randomSeed.asInt()); From 9ec124b3b81f31f6235b98376df5e89020804087 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 22 Dec 2021 10:31:33 +0100 Subject: [PATCH 150/668] Fix disabled enums after file reload --- src/serlio/scripts/AEserlioTemplate.mel | 1 + 1 file changed, 1 insertion(+) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index fc664c6a..de29c5f8 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -38,6 +38,7 @@ global proc prtReloadRPK(string $attr) { $attrCurrentRule = $node + ".currentRule_Package"; setAttr -type "string" $attrCurrentRule ""; dgdirty $node; + evalDeferred "refreshEditorTemplates()"; } global proc int prtRPKChange(string $attr, string $varname) { From 22fe0f57c25065b2a2b6f2cf407871505634e532 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 22 Dec 2021 10:32:13 +0100 Subject: [PATCH 151/668] Add reset attribute button --- src/serlio/scripts/AEserlioTemplate.mel | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index de29c5f8..5d5dcea3 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -117,6 +117,12 @@ global proc prtUpdateEditHighlights(string $attr){ } } +global proc prtSetAttrUserSetAndUpdateUI(string $attr){ + setAttr ($attr + "_force_default") true; + setAttr ($attr + "_user_set") false; + prtUpdateEditHighlights($attr); +} + global proc prtAttributeControl(string $attr, string $varname){ setUITemplate -pst attributeEditorTemplate; string $control = prtControlName($attr); @@ -138,6 +144,7 @@ global proc prtAttributeControl(string $attr, string $varname){ symbolButton -image "navButtonBrowse.xpm" -c ("prtShowFileDialog(\"" + $attr + "\")") ($control + "_browse"); symbolButton -ann "reload file" -image "refresh.png" -c ("prtReloadRPK(\"" + $attr + "\")") ($control + "_reload"); } + symbolButton -image "undo_s.png" -annotation "reset rule attribute to default value" -c ("prtSetAttrUserSetAndUpdateUI (\"" + $attr +"\")") ($control + "_force_default_reset"); connectControl $control $attr; setParent ..; @@ -152,7 +159,8 @@ global proc prtAttributeControlReplace(string $attr, string $varname){ if (`addAttr -q -uaf ($attr)`){ symbolButton -edit -c ("prtShowFileDialog(\"" + $attr + "\")") ($control + "_browse"); symbolButton -edit -c ("prtReloadRPK(\"" + $attr + "\")") ($control + "_reload"); - } + } + symbolButton -edit -c ("prtSetAttrUserSetAndUpdateUI (\"" + $attr +"\")") ($control + "_force_default_reset"); } prtUpdateEditHighlights($attr); } From 98c48f570204464555c4dd57e2aa2dc0b8c6955e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 22 Dec 2021 10:45:14 +0100 Subject: [PATCH 152/668] Update UI after attribute changes This updates the attribute slider to the currently used value in the corresponding prt attribute. I.e. update the attribute when it's was reset to the default value. It was also possible that an attribute did not properly update after UI changes (e.g. when changing a float attribute via the field instead of the slider) this should fix that issue. --- src/serlio/modifiers/PRTModifierAction.cpp | 119 +++++++++++++++++++++ src/serlio/modifiers/PRTModifierAction.h | 1 + src/serlio/modifiers/PRTModifierNode.cpp | 2 + 3 files changed, 122 insertions(+) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index b70edda9..ca6fbcc3 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -404,6 +404,125 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { return MStatus::kSuccess; } +MStatus PRTModifierAction::updateUI(const MObject& node) { + const auto updateUIFromAttributes = [this](const MFnDependencyNode& fnNode, const MFnAttribute& fnAttribute, + const RuleAttribute& ruleAttribute, std::wstring fqAttrName, + const PrtAttributeType attrType) { + const AttributeMapUPtr defaultAttributeValues = getDefaultAttributeValues( + mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, *inPrtMesh, mRandomSeed); + MPlug plug(fnNode.object(), fnAttribute.object()); + + switch (attrType) { + case PrtAttributeType::BOOL: { + const auto defBoolVal = defaultAttributeValues->getBool(fqAttrName.c_str()); + bool boolVal; + MCHECK(plug.getValue(boolVal)); + + const bool isDefaultValue = defBoolVal == boolVal; + + if (getIsUserSet(fnNode, fnAttribute)) { + plug.setBool(boolVal); + } + else { + if (!isDefaultValue) { + plug.setBool(defBoolVal); + } + } + break; + } + case PrtAttributeType::FLOAT: { + const auto defDoubleVal = defaultAttributeValues->getFloat(fqAttrName.c_str()); + double doubleVal; + MCHECK(plug.getValue(doubleVal)); + + const bool isDefaultValue = defDoubleVal == doubleVal; + + if (getIsUserSet(fnNode, fnAttribute)) { + plug.setDouble(doubleVal); + } + else { + if (!isDefaultValue) { + plug.setDouble(defDoubleVal); + } + } + break; + } + case PrtAttributeType::COLOR: { + const wchar_t* defColStr = defaultAttributeValues->getString(fqAttrName.c_str()); + + MObject rgb; + MCHECK(plug.getValue(rgb)); + MFnNumericData fRGB(rgb); + + prtu::Color col; + MCHECK(fRGB.getData3Float(col[0], col[1], col[2])); + const std::wstring colStr = prtu::getColorString(col); + + prtu::Color defaultColor = prtu::parseColor(defColStr); + MFnNumericData fdefaultColor; + MObject defaultColorObj = fdefaultColor.create(MFnNumericData::Type::k3Float); + fdefaultColor.setData3Float(defaultColor[0], defaultColor[1], defaultColor[2]); + + const bool isDefaultValue = std::wcscmp(colStr.c_str(), defColStr) == 0; + + if (getIsUserSet(fnNode, fnAttribute)) { + plug.setMObject(rgb); + } + else { + if (!isDefaultValue) { + plug.setMObject(defaultColorObj); + } + } + break; + } + case PrtAttributeType::STRING: { + const auto defStringVal = defaultAttributeValues->getString(fqAttrName.c_str()); + + MString stringVal; + MCHECK(plug.getValue(stringVal)); + + const bool isDefaultValue = std::wcscmp(stringVal.asWChar(), defStringVal) == 0; + + if (getIsUserSet(fnNode, fnAttribute)) { + plug.setString(stringVal); + } + else { + if (!isDefaultValue) { + plug.setString(defStringVal); + } + } + break; + } + case PrtAttributeType::ENUM: { + MFnEnumAttribute eAttr(fnAttribute.object()); + + short defEnumVal; + short enumVal; + MCHECK(eAttr.getDefault(defEnumVal)); + MCHECK(plug.getValue(enumVal)); + + const bool isDefaultValue = defEnumVal == enumVal; + if (getIsUserSet(fnNode, fnAttribute)) { + plug.setShort(enumVal); + } + else { + if (!isDefaultValue) { + plug.setShort(defEnumVal); + } + } + break; + } + + default: + break; + } + }; + + iterateThroughAttributesAndApply(node, updateUIFromAttributes); + + return MStatus::kSuccess; +} + // Sets the mesh object for the action to operate on void PRTModifierAction::setMesh(MObject& _inMesh, MObject& _outMesh) { inMesh = _inMesh; diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 7849b26c..501c3600 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -69,6 +69,7 @@ class PRTModifierAction : public polyModifierFty { MStatus updateRuleFiles(const MObject& node, const MString& rulePkg); MStatus fillAttributesFromNode(const MObject& node); MStatus updateUserSetAttributes(const MObject& node); + MStatus updateUI(const MObject& node); void setMesh(MObject& _inMesh, MObject& _outMesh); void setRandomSeed(int32_t randomSeed) { mRandomSeed = randomSeed; diff --git a/src/serlio/modifiers/PRTModifierNode.cpp b/src/serlio/modifiers/PRTModifierNode.cpp index 52ed7e09..7db37fa1 100644 --- a/src/serlio/modifiers/PRTModifierNode.cpp +++ b/src/serlio/modifiers/PRTModifierNode.cpp @@ -120,6 +120,8 @@ MStatus PRTModifierNode::compute(const MPlug& plug, MDataBlock& data) { // Now, perform the PRT status = fPRTModifierAction.doIt(); + fPRTModifierAction.updateUI(thisMObject()); + currentRulePkgData.setString(rulePkgData.asString()); // Mark the output mesh as clean From 7a1ba358630d053c9b9a5dbe0294da9c940bd04d Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 22 Dec 2021 11:11:57 +0100 Subject: [PATCH 153/668] Only update background, if hidden user_set attribute is available During ruleswitch it was possible that a re-evaluation of the background was triggered, when the attribute nolonger existed. --- src/serlio/scripts/AEserlioTemplate.mel | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 5d5dcea3..c9f73b0b 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -105,6 +105,12 @@ global proc prtFileBrowseReplaceRPK(string $attr, string $varname){ } global proc prtUpdateEditHighlights(string $attr){ + string $node = plugNode($attr); + string $attributeName = plugAttr($attr); + int $attributeExists = `attributeExists $attributeName $node`; + if (!$attributeExists) + return; + int $userSet = `getAttr ($attr + "_user_set")`; string $control = prtControlName($attr); From e8753dac58eeb389297b53882eb72de7344cfc6e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 22 Dec 2021 11:23:11 +0100 Subject: [PATCH 154/668] Cleanup: remove redundant local variable --- src/serlio/scripts/AEserlioTemplate.mel | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index c9f73b0b..b528ec04 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -107,8 +107,7 @@ global proc prtFileBrowseReplaceRPK(string $attr, string $varname){ global proc prtUpdateEditHighlights(string $attr){ string $node = plugNode($attr); string $attributeName = plugAttr($attr); - int $attributeExists = `attributeExists $attributeName $node`; - if (!$attributeExists) + if (!`attributeExists $attributeName $node`) return; int $userSet = `getAttr ($attr + "_user_set")`; From 9602a0f50861191a2d19f14c2a05bf0d1f24df4c Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 22 Dec 2021 13:56:21 +0100 Subject: [PATCH 155/668] Fix scaling issue --- src/serlio/modifiers/MayaCallbacks.cpp | 2 +- src/serlio/modifiers/PRTMesh.cpp | 6 +++--- src/serlio/utils/MayaUtilities.h | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 9da03d10..86d94570 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -64,7 +64,7 @@ MFloatPointArray toMayaFloatPointArray(double const* a, size_t s) { MFloatPointArray mfpa(numPoints); for (unsigned int i = 0; i < numPoints; ++i) { mfpa.set(MFloatPoint(static_cast(a[i * 3 + 0]), static_cast(a[i * 3 + 1]), - static_cast(a[i * 3 + 2])), + static_cast(a[i * 3 + 2])) * mu::PRT_TO_SERLIO_SCALE, i); } return mfpa; diff --git a/src/serlio/modifiers/PRTMesh.cpp b/src/serlio/modifiers/PRTMesh.cpp index c4ac8756..2e93b1c2 100644 --- a/src/serlio/modifiers/PRTMesh.cpp +++ b/src/serlio/modifiers/PRTMesh.cpp @@ -42,9 +42,9 @@ PRTMesh::PRTMesh(const MObject& mesh) { const unsigned int vertexArrayLength = vertexArray.length(); mVertexCoordsVec.reserve(3 * vertexArrayLength); for (unsigned int i = 0; i < vertexArrayLength; ++i) { - mVertexCoordsVec.push_back(vertexArray[i].x); - mVertexCoordsVec.push_back(vertexArray[i].y); - mVertexCoordsVec.push_back(vertexArray[i].z); + mVertexCoordsVec.push_back(vertexArray[i].x / mu::PRT_TO_SERLIO_SCALE); + mVertexCoordsVec.push_back(vertexArray[i].y / mu::PRT_TO_SERLIO_SCALE); + mVertexCoordsVec.push_back(vertexArray[i].z / mu::PRT_TO_SERLIO_SCALE); } // faces diff --git a/src/serlio/utils/MayaUtilities.h b/src/serlio/utils/MayaUtilities.h index 15ef0f71..dc631863 100644 --- a/src/serlio/utils/MayaUtilities.h +++ b/src/serlio/utils/MayaUtilities.h @@ -13,6 +13,8 @@ // utility functions with dependencies on the Maya API namespace mu { +// Standard conversion from meters (PRT) to centimeters (maya) +constexpr float PRT_TO_SERLIO_SCALE = 100.0f; int32_t computeSeed(const MFloatPointArray& vertices); int32_t computeSeed(const double* vertices, size_t count); From 92ac6ebc97e88e20be80a74056c030b8f6392360 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 23 Dec 2021 10:04:18 +0100 Subject: [PATCH 156/668] Remove reload button for file attributes --- src/serlio/scripts/AEserlioTemplate.mel | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index b528ec04..7ea237d4 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -135,7 +135,7 @@ global proc prtAttributeControl(string $attr, string $varname){ int $isFileAttribute = `addAttr -q -uaf ($attr)`; if($isFileAttribute){ - $nrOfColumns = 6; + $nrOfColumns = 5; } rowColumnLayout -numberOfColumns $nrOfColumns -bgc 0.4 0.4 0.4 -noBackground true -adjustableColumn 2 ($control + "_column_layout"); @@ -147,7 +147,6 @@ global proc prtAttributeControl(string $attr, string $varname){ if ($isFileAttribute){ symbolButton -image "navButtonBrowse.xpm" -c ("prtShowFileDialog(\"" + $attr + "\")") ($control + "_browse"); - symbolButton -ann "reload file" -image "refresh.png" -c ("prtReloadRPK(\"" + $attr + "\")") ($control + "_reload"); } symbolButton -image "undo_s.png" -annotation "reset rule attribute to default value" -c ("prtSetAttrUserSetAndUpdateUI (\"" + $attr +"\")") ($control + "_force_default_reset"); @@ -163,9 +162,7 @@ global proc prtAttributeControlReplace(string $attr, string $varname){ connectControl $control $attr; if (`addAttr -q -uaf ($attr)`){ symbolButton -edit -c ("prtShowFileDialog(\"" + $attr + "\")") ($control + "_browse"); - symbolButton -edit -c ("prtReloadRPK(\"" + $attr + "\")") ($control + "_reload"); } - symbolButton -edit -c ("prtSetAttrUserSetAndUpdateUI (\"" + $attr +"\")") ($control + "_force_default_reset"); } prtUpdateEditHighlights($attr); } From cbc045362d041e249b3461d657394ca9889c244c Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 23 Dec 2021 10:04:46 +0100 Subject: [PATCH 157/668] Only show reset attribute button on edited attributes --- src/serlio/scripts/AEserlioTemplate.mel | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 7ea237d4..639a7e7a 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -117,8 +117,10 @@ global proc prtUpdateEditHighlights(string $attr){ if ($userSet){ rowColumnLayout -edit -bgc 0.32 0.32 0.32 ($control + "_column_layout"); + symbolButton -edit -visible true ($control + "_force_default_reset"); } else { rowColumnLayout -edit -bgc 0.4 0.4 0.4 -noBackground true ($control + "_column_layout"); + symbolButton -edit -visible false ($control + "_force_default_reset"); } } @@ -148,7 +150,7 @@ global proc prtAttributeControl(string $attr, string $varname){ if ($isFileAttribute){ symbolButton -image "navButtonBrowse.xpm" -c ("prtShowFileDialog(\"" + $attr + "\")") ($control + "_browse"); } - symbolButton -image "undo_s.png" -annotation "reset rule attribute to default value" -c ("prtSetAttrUserSetAndUpdateUI (\"" + $attr +"\")") ($control + "_force_default_reset"); + symbolButton -image "undo_s.png" -visible false -annotation "reset rule attribute to default value" -c ("prtSetAttrUserSetAndUpdateUI (\"" + $attr +"\")") ($control + "_force_default_reset"); connectControl $control $attr; setParent ..; @@ -163,6 +165,7 @@ global proc prtAttributeControlReplace(string $attr, string $varname){ if (`addAttr -q -uaf ($attr)`){ symbolButton -edit -c ("prtShowFileDialog(\"" + $attr + "\")") ($control + "_browse"); } + symbolButton -edit -c ("prtSetAttrUserSetAndUpdateUI (\"" + $attr +"\")") ($control + "_force_default_reset"); } prtUpdateEditHighlights($attr); } From 783e9ebd20371c991c4495956ccf9c7f3ccbdf38 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 23 Dec 2021 12:59:51 +0100 Subject: [PATCH 158/668] Fix update highlights when a rule file is used multiple times --- src/serlio/scripts/AEserlioTemplate.mel | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 1113c466..adc35c8c 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -159,10 +159,11 @@ global proc prtAttributeControl(string $attr, string $varname){ global proc prtAttributeControlReplace(string $attr, string $varname){ if(prtRPKChange($attr, $varname)) { string $control = prtControlName($attr); - connectControl $control $attr; + attrControlGrp -edit -changeCommand("prtUpdateEditHighlights " + $attr) -attribute $attr $control; if (`addAttr -q -uaf ($attr)`){ symbolButton -edit -c ("prtShowFileDialog(\"" + $attr + "\",\"All Files (*.*)\" )") ($control + "_browse"); } + connectControl $control $attr; symbolButton -edit -c ("prtSetAttrUserSetAndUpdateUI (\"" + $attr +"\")") ($control + "_force_default_reset"); } prtUpdateEditHighlights($attr); From 21fa6ebaabf8490e6e4dcd9d55b96a2a7eaa8b85 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 23 Dec 2021 15:22:43 +0100 Subject: [PATCH 159/668] Fix UI attribute control connection for string values --- src/serlio/scripts/AEserlioTemplate.mel | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index adc35c8c..1bc074b4 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -33,6 +33,11 @@ proc string niceName(string $longName) { return `attributeName -nice $longName`; } +proc int prtIsStringAttr(string $attr){ + string $datatype[] = `addAttr -query -dataType $attr`; + return `strcmp "string" $datatype[0]` == 0; +} + global proc prtReloadRPK(string $attr) { $node = prtGetNodeName($attr); $attrCurrentRule = $node + ".currentRule_Package"; @@ -150,7 +155,11 @@ global proc prtAttributeControl(string $attr, string $varname){ } symbolButton -image "undo_s.png" -visible false -annotation "reset rule attribute to default value" -c ("prtSetAttrUserSetAndUpdateUI (\"" + $attr +"\")") ($control + "_force_default_reset"); - connectControl $control $attr; + if(prtIsStringAttr($attr)){ + connectControl -index 2 $control $attr; + } else { + connectControl $control $attr; + } setParent ..; setUITemplate -ppt; prtUpdateEditHighlights($attr); @@ -163,7 +172,11 @@ global proc prtAttributeControlReplace(string $attr, string $varname){ if (`addAttr -q -uaf ($attr)`){ symbolButton -edit -c ("prtShowFileDialog(\"" + $attr + "\",\"All Files (*.*)\" )") ($control + "_browse"); } - connectControl $control $attr; + if(prtIsStringAttr($attr)){ + connectControl -index 2 $control $attr; + } else { + connectControl $control $attr; + } symbolButton -edit -c ("prtSetAttrUserSetAndUpdateUI (\"" + $attr +"\")") ($control + "_force_default_reset"); } prtUpdateEditHighlights($attr); From febdc0040f172f24e9627bb9a20d2a32555451d8 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 30 Dec 2021 11:14:41 +0100 Subject: [PATCH 160/668] Create helper functions for better readability --- src/serlio/scripts/AEserlioTemplate.mel | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 1bc074b4..9fdaddb3 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -38,6 +38,14 @@ proc int prtIsStringAttr(string $attr){ return `strcmp "string" $datatype[0]` == 0; } +proc int prtIsFileAttr(string $attr){ + return `addAttr -q -uaf ($attr)`; +} + +proc int prtIsColorAttr(string $attr){ + return `addAttr -q -uac ($attr)`; +} + global proc prtReloadRPK(string $attr) { $node = prtGetNodeName($attr); $attrCurrentRule = $node + ".currentRule_Package"; From 297c1e0d4aaee76b375d6f21c7f8c383d8f19536 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 30 Dec 2021 11:15:35 +0100 Subject: [PATCH 161/668] Cleanup: use helper function where applicable --- src/serlio/scripts/AEserlioTemplate.mel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 9fdaddb3..4738cf45 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -177,7 +177,7 @@ global proc prtAttributeControlReplace(string $attr, string $varname){ if(prtRPKChange($attr, $varname)) { string $control = prtControlName($attr); attrControlGrp -edit -changeCommand("prtUpdateEditHighlights " + $attr) -attribute $attr $control; - if (`addAttr -q -uaf ($attr)`){ + if (prtIsFileAttr($attr)){ symbolButton -edit -c ("prtShowFileDialog(\"" + $attr + "\",\"All Files (*.*)\" )") ($control + "_browse"); } if(prtIsStringAttr($attr)){ From 7c45b8ccaf8159a5b1c7b5be6a3c210b64904ea6 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 30 Dec 2021 11:19:20 +0100 Subject: [PATCH 162/668] Use scriptJob instead of changeCommand for attribute change callbacks This also allows us to use the old ColorPicker where changeCommand only worked partially (previously only worked on slider change and not when the color picker was used) --- src/serlio/scripts/AEserlioTemplate.mel | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 4738cf45..e029a79c 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -156,7 +156,12 @@ global proc prtAttributeControl(string $attr, string $varname){ $label = `niceName $attr`; symbolButton -visible false -hlc 0.2715 0.2715 0.2715 -i "pencilCursor.png" -w 25 -h 25 -enable false ($control + "_user_set"); - attrControlGrp -hideMapButton true -label $label -changeCommand("prtUpdateEditHighlights " + $attr) -attribute $attr $control; + + if (prtIsColorAttr($attr)){ + colorSliderGrp-label $label $control; + } else { + attrControlGrp -label $label -attribute $attr $control; + } if ($isFileAttribute){ symbolButton -image "navButtonBrowse.xpm" -c ("prtShowFileDialog(\"" + $attr + "\",\"All Files (*.*)\" )") ($control + "_browse"); @@ -168,6 +173,9 @@ global proc prtAttributeControl(string $attr, string $varname){ } else { connectControl $control $attr; } + string $changeCommand = "prtUpdateEditHighlights " + $attr; + scriptJob -rp -p $control -ac $attr $changeCommand; + setParent ..; setUITemplate -ppt; prtUpdateEditHighlights($attr); @@ -176,16 +184,20 @@ global proc prtAttributeControl(string $attr, string $varname){ global proc prtAttributeControlReplace(string $attr, string $varname){ if(prtRPKChange($attr, $varname)) { string $control = prtControlName($attr); - attrControlGrp -edit -changeCommand("prtUpdateEditHighlights " + $attr) -attribute $attr $control; if (prtIsFileAttr($attr)){ symbolButton -edit -c ("prtShowFileDialog(\"" + $attr + "\",\"All Files (*.*)\" )") ($control + "_browse"); } if(prtIsStringAttr($attr)){ connectControl -index 2 $control $attr; + } else if (prtIsColorAttr($attr)){ + connectControl $control $attr; } else { + attrControlGrp -edit -attribute $attr $control; connectControl $control $attr; } symbolButton -edit -c ("prtSetAttrUserSetAndUpdateUI (\"" + $attr +"\")") ($control + "_force_default_reset"); + string $changeCommand = "prtUpdateEditHighlights " + $attr; + scriptJob -rp -p $control -ac $attr $changeCommand; } prtUpdateEditHighlights($attr); } From 2adb4fce5b4b48cd55cdbebfee2e9169853c1d77 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 30 Dec 2021 17:55:01 +0100 Subject: [PATCH 163/668] Select mesh after creating material --- src/serlio/scripts/serlioCreateUI.mel | 1 + 1 file changed, 1 insertion(+) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index cb033d52..08a7f221 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -135,6 +135,7 @@ global proc createMaterialNode(string $materialType) { if(!isSerlioNodePredecessor($node)) serlioCreateMaterial $materialType; } + select -r $select; } global proc createPrtMenu() { From 37a4b9158f46d2fba7c54dac34959b372b70eaea Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 4 Jan 2022 09:26:17 +0100 Subject: [PATCH 164/668] Check entire history for serlio node When an initial shape is generated without constuction history and a serlio-node is appended the geometry node gets duplicated as a placeholder input. Therefore its possible that a geometrynode returned by "collectInitialShapes" is actually one of these duplicate nodes. In order to avoid generating additional serlio nodes for these duplicates, we have to check the future and past connections. --- src/serlio/scripts/serlioCreateUI.mel | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index 08a7f221..0c8c9f90 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -41,7 +41,9 @@ proc string[] collectInitialShapes(string $nodes[]) { } proc int isSerlioNodePredecessor(string $node){ - string $hist[] = `listHistory -f true $node`; + string $hist[] = `listHistory -f false $node`; + string $future[] = `listHistory -f true $node`; + $hist = appendAarray($hist, $future); if (size($hist) > 1) { for ($node in $hist){ if(`nodeType $node` == "serlio"){ From 2d13ad993a1941052008740528c52be600bf2a8d Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 4 Jan 2022 09:27:50 +0100 Subject: [PATCH 165/668] Make isSerlioNodePredecessor able to check for any node type --- src/serlio/scripts/serlioCreateUI.mel | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index 0c8c9f90..fb678948 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -40,15 +40,15 @@ proc string[] collectInitialShapes(string $nodes[]) { return $result; } -proc int isSerlioNodePredecessor(string $node){ +proc int hasNodeTypeInHistory(string $node, string $nodeTypes[]){ string $hist[] = `listHistory -f false $node`; string $future[] = `listHistory -f true $node`; $hist = appendAarray($hist, $future); if (size($hist) > 1) { for ($node in $hist){ - if(`nodeType $node` == "serlio"){ + string $nodeType = `nodeType $node`; + if(stringArrayContains($nodeType, $nodeTypes)) return true; - } } } return false; @@ -81,7 +81,7 @@ global proc createPrtNode() { } } - if (!$alreadyHasExistingPrtNode && !isSerlioNodePredecessor($node)){ + if (!$alreadyHasExistingPrtNode && !hasNodeTypeInHistory($node, {"serlio"})){ select -r $node; serlioAssign $rulePackage; } From c062abacb86a43560958e84349d686a46f03b55e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 4 Jan 2022 09:28:59 +0100 Subject: [PATCH 166/668] Only generate material if a serlioNode is present and no previous materials have been generated --- src/serlio/scripts/serlioCreateUI.mel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index fb678948..d2f47c6c 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -134,7 +134,7 @@ global proc createMaterialNode(string $materialType) { for($node in $initialShapes) { select -r $node; - if(!isSerlioNodePredecessor($node)) + if(hasNodeTypeInHistory($node, {"serlio"}) && !hasNodeTypeInHistory($node, {"serlioMaterial","serlioArnoldMaterial"})) serlioCreateMaterial $materialType; } select -r $select; From e42461847058f436509a1d2a17f1712607f38b75 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 4 Jan 2022 13:57:55 +0100 Subject: [PATCH 167/668] Cleanup: use helper function for removing nodes --- src/serlio/scripts/serlioCreateUI.mel | 33 +++++++++++++++------------ 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index d2f47c6c..4c71c6f5 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -54,6 +54,23 @@ proc int hasNodeTypeInHistory(string $node, string $nodeTypes[]){ return false; } +proc deleteNodeTypesFromHistory(string $node, string $nodeTypes[]){ + string $nodeHistory[] = `listHistory $node`; + + for($dpNode in $nodeHistory){ + string $dpNodeType = `nodeType $dpNode`; + if(stringArrayContains($dpNodeType, $nodeTypes)){ + string $previousDpNodePlug = `connectionInfo -sfd ($dpNode +".inMesh")`; + string $nextDpNodePlugs[] = `connectionInfo -dfs ($dpNode +".outMesh")`; + if ($previousDpNodePlug != "") { + for($nextDpNodePlug in $nextDpNodePlugs) + connectAttr -force $previousDpNodePlug $nextDpNodePlug; + } + delete $dpNode; + } + } +} + global proc createPrtNode() { string $select[] = `ls -sl`; string $initialShapes[] = collectInitialShapes($select); @@ -103,21 +120,7 @@ global proc removePrtNode() { for($node in $initialShapes) { select -r $node; hyperShade -assign "lambert1"; - string $nodeHistory[] = `listHistory $node`; - - for($dpNode in $nodeHistory){ - string $dpNodeType = `nodeType $dpNode`; - if($dpNodeType == "serlio" || $dpNodeType == "serlioMaterial" || $dpNodeType == "serlioArnoldMaterial" ){ - string $previousDpNodePlug = `connectionInfo -sfd ($dpNode +".inMesh")`; - string $nextDpNodePlugs[] = `connectionInfo -dfs ($dpNode +".outMesh")`; - if ($previousDpNodePlug != "") { - for($nextDpNodePlug in $nextDpNodePlugs){ - connectAttr -force ( $previousDpNodePlug ) ($nextDpNodePlug); - } - } - delete $dpNode; - } - } + deleteNodeTypesFromHistory($node, {"serlio", "serlioMaterial", "serlioArnoldMaterial"}); } select -r $select; From 8918f40fc7277969cd4c6d014163e6771f38e475 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 4 Jan 2022 13:58:23 +0100 Subject: [PATCH 168/668] Cleanup: remove if braces --- src/serlio/scripts/serlioCreateUI.mel | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index 4c71c6f5..48c0a3c9 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -98,10 +98,9 @@ global proc createPrtNode() { } } - if (!$alreadyHasExistingPrtNode && !hasNodeTypeInHistory($node, {"serlio"})){ - select -r $node; + if (!$alreadyHasExistingPrtNode && !hasNodeTypeInHistory($node, {"serlio"})) serlioAssign $rulePackage; - } + $alreadyHasExistingPrtNode = false; } } From 8fe940dfda8e585e98b8b5eed2f3bdaa0c8f01f7 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 4 Jan 2022 14:01:20 +0100 Subject: [PATCH 169/668] Add command to set default/initial shading group --- src/serlio/utils/MELScriptBuilder.cpp | 4 ++++ src/serlio/utils/MELScriptBuilder.h | 1 + 2 files changed, 5 insertions(+) diff --git a/src/serlio/utils/MELScriptBuilder.cpp b/src/serlio/utils/MELScriptBuilder.cpp index f1f759ee..b965b5a5 100644 --- a/src/serlio/utils/MELScriptBuilder.cpp +++ b/src/serlio/utils/MELScriptBuilder.cpp @@ -118,6 +118,10 @@ void MELScriptBuilder::setsAddFaceRange(const std::wstring& setName, const std:: << "];\n"; } +void MELScriptBuilder::setsUseInitialShadingGroup(const std::wstring& meshName) { + commandStream << "sets -forceElement initialShadingGroup " << meshName << ";"; +} + void MELScriptBuilder::createShader(const std::wstring& shaderType, const MELVariable& nodeName) { const auto mel = nodeName.mel(); commandStream << mel << " = `shadingNode -asShader -skipSelect -name " << mel << " " << shaderType << "`;\n"; diff --git a/src/serlio/utils/MELScriptBuilder.h b/src/serlio/utils/MELScriptBuilder.h index e0d6b1d7..a4ae3b8c 100644 --- a/src/serlio/utils/MELScriptBuilder.h +++ b/src/serlio/utils/MELScriptBuilder.h @@ -73,6 +73,7 @@ class MELScriptBuilder { void setsCreate(const MELVariable& setName); void setsAddFaceRange(const std::wstring& setName, const std::wstring& meshName, int faceStart, int faceEnd); + void setsUseInitialShadingGroup(const std::wstring& meshName); void createShader(const std::wstring& shaderType, const MELVariable& nodeName); void createTextureShadingNode(const MELVariable& nodeName); From fd96628f642f4c0e50020a1ed159c756dd7dbc12 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 4 Jan 2022 14:57:23 +0100 Subject: [PATCH 170/668] Add commands to modify undo queue --- src/serlio/utils/MELScriptBuilder.cpp | 15 +++++++++++++++ src/serlio/utils/MELScriptBuilder.h | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/src/serlio/utils/MELScriptBuilder.cpp b/src/serlio/utils/MELScriptBuilder.cpp index b965b5a5..7e045e26 100644 --- a/src/serlio/utils/MELScriptBuilder.cpp +++ b/src/serlio/utils/MELScriptBuilder.cpp @@ -132,6 +132,21 @@ void MELScriptBuilder::createTextureShadingNode(const MELVariable& nodeName) { commandStream << mel << "= `shadingNode -asTexture -skipSelect -name " << mel << " file`;\n"; } +void MELScriptBuilder::cacheUndoState(const MELVariable& undoName) { + const auto mel = undoName.mel(); + commandStream << mel << " = `undoInfo -q -state`;\n"; +} + +void MELScriptBuilder::applyCachedUndoState(const MELVariable& undoName) { + const auto mel = undoName.mel(); + commandStream << "undoInfo -stateWithoutFlush " << mel << ";\n"; +} + +void MELScriptBuilder::setUndoState(bool undoState) { + std::wstring undoString = undoState ? L"on" : L"off"; + commandStream << "undoInfo -stateWithoutFlush " << undoString << ";\n"; +} + void MELScriptBuilder::addCmdLine(const std::wstring& line) { commandStream << line << L"\n"; } diff --git a/src/serlio/utils/MELScriptBuilder.h b/src/serlio/utils/MELScriptBuilder.h index a4ae3b8c..945b0467 100644 --- a/src/serlio/utils/MELScriptBuilder.h +++ b/src/serlio/utils/MELScriptBuilder.h @@ -78,6 +78,10 @@ class MELScriptBuilder { void createShader(const std::wstring& shaderType, const MELVariable& nodeName); void createTextureShadingNode(const MELVariable& nodeName); + void cacheUndoState(const MELVariable& undoName); + void applyCachedUndoState(const MELVariable& undoName); + void setUndoState(bool undoState); + void python(const std::wstring& pythonCmd); void addCmdLine(const std::wstring& line); From 45764433f939e5fa360353b21014aa873711623a Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 4 Jan 2022 14:58:30 +0100 Subject: [PATCH 171/668] Disable undo queue during material generation --- src/serlio/materials/ArnoldMaterialNode.cpp | 7 ++++++- src/serlio/materials/StingrayMaterialNode.cpp | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/serlio/materials/ArnoldMaterialNode.cpp b/src/serlio/materials/ArnoldMaterialNode.cpp index c25a264d..a770fcce 100644 --- a/src/serlio/materials/ArnoldMaterialNode.cpp +++ b/src/serlio/materials/ArnoldMaterialNode.cpp @@ -38,6 +38,7 @@ const std::wstring MATERIAL_BASE_NAME = L"serlioArnoldMaterial"; std::once_flag pluginDependencyCheckFlag; const std::vector PLUGIN_DEPENDENCIES = {"mtoa"}; +const MELVariable MEL_UNDO_STATE(L"serlioMaterialUndoState"); const MELVariable MEL_VAR_SHADER_NODE(L"shaderNode"); const MELVariable MEL_VAR_MAP_FILE(L"mapFile"); const MELVariable MEL_VAR_MAP_NODE(L"mapNode"); @@ -331,6 +332,10 @@ MStatus ArnoldMaterialNode::compute(const MPlug& plug, MDataBlock& data) { MaterialUtils::getMaterialsByStructure(*materialStructure, MATERIAL_BASE_NAME); MELScriptBuilder scriptBuilder; + scriptBuilder.declInt(MEL_UNDO_STATE); + scriptBuilder.cacheUndoState(MEL_UNDO_STATE); + scriptBuilder.setUndoState(false); + scriptBuilder.declString(MEL_VARIABLE_SHADING_ENGINE); scriptBuilder.declString(MEL_VAR_SHADER_NODE); scriptBuilder.declString(MEL_VAR_MAP_FILE); @@ -382,6 +387,6 @@ MStatus ArnoldMaterialNode::compute(const MPlug& plug, MDataBlock& data) { LOG_DBG << "assigned arnold shading engine (" << faceRange.first << ":" << faceRange.second << "): " << shadingEngineName; } - + scriptBuilder.applyCachedUndoState(MEL_UNDO_STATE); return scriptBuilder.execute(); } diff --git a/src/serlio/materials/StingrayMaterialNode.cpp b/src/serlio/materials/StingrayMaterialNode.cpp index 68992084..c7fec5df 100644 --- a/src/serlio/materials/StingrayMaterialNode.cpp +++ b/src/serlio/materials/StingrayMaterialNode.cpp @@ -51,6 +51,7 @@ const std::wstring MATERIAL_BASE_NAME = L"serlioStingrayMaterial"; std::once_flag pluginDependencyCheckFlag; const std::vector PLUGIN_DEPENDENCIES = {"shaderFXPlugin"}; +const MELVariable MEL_UNDO_STATE(L"serlioMaterialUndoState"); const MELVariable MEL_VAR_SHADER_NODE(L"shaderNode"); const MELVariable MEL_VAR_MAP_FILE(L"mapFile"); const MELVariable MEL_VAR_MAP_NODE(L"mapNode"); @@ -193,6 +194,10 @@ MStatus StingrayMaterialNode::compute(const MPlug& plug, MDataBlock& data) { MaterialUtils::getMaterialsByStructure(*materialStructure, MATERIAL_BASE_NAME); MELScriptBuilder scriptBuilder; + scriptBuilder.declInt(MEL_UNDO_STATE); + scriptBuilder.cacheUndoState(MEL_UNDO_STATE); + scriptBuilder.setUndoState(false); + scriptBuilder.declString(MEL_VARIABLE_SHADING_ENGINE); // declare MEL variables required by appendToMaterialScriptBuilder() @@ -235,7 +240,7 @@ MStatus StingrayMaterialNode::compute(const MPlug& plug, MDataBlock& data) { LOG_DBG << "assigned stingray shading engine (" << faceRange.first << ":" << faceRange.second << "): " << shadingEngineName; } - + scriptBuilder.applyCachedUndoState(MEL_UNDO_STATE); LOG_DBG << "scheduling stringray material script"; return scriptBuilder.execute(); // note: script is executed asynchronously } From 81e605b9e5a742415f778d45553ffb9991e96f3b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 4 Jan 2022 14:59:41 +0100 Subject: [PATCH 172/668] Set initial shading group before trying to undo a material command Otherwise it's possible that existing connections to other shading groups prevent a successful undo procedure --- src/serlio/materials/MaterialCommand.cpp | 9 +++++++++ src/serlio/materials/MaterialCommand.h | 1 + 2 files changed, 10 insertions(+) diff --git a/src/serlio/materials/MaterialCommand.cpp b/src/serlio/materials/MaterialCommand.cpp index da82d605..bc5530a0 100644 --- a/src/serlio/materials/MaterialCommand.cpp +++ b/src/serlio/materials/MaterialCommand.cpp @@ -20,6 +20,7 @@ #include "materials/StingrayMaterialNode.h" #include "materials/ArnoldMaterialNode.h" #include "materials/MaterialCommand.h" +#include "utils/MELScriptBuilder.h" #include "utils/MayaUtilities.h" @@ -114,6 +115,7 @@ MStatus MaterialCommand::doIt(const MArgList& argList) { if (geometryInMesh.isConnected()) { MPlugArray parentPlugArray; MPlug serlioOutMesh; + fGeometryName = rootNode.uniqueName(); geometryInMesh.connectedTo(parentPlugArray, true, false); @@ -163,6 +165,13 @@ MStatus MaterialCommand::redoIt() { MStatus MaterialCommand::undoIt() { MStatus status; + MELScriptBuilder scriptBuilder; + + //apply initial shading group to avoid other connected shading groups to disrupt undo process + scriptBuilder.setsUseInitialShadingGroup(fGeometryName.asWChar()); + std::wstring output; + MCHECK(scriptBuilder.executeSync(output)); + status = fDGModifier.undoIt(); if (status == MS::kSuccess) { setResult("PRT undo succeeded!"); diff --git a/src/serlio/materials/MaterialCommand.h b/src/serlio/materials/MaterialCommand.h index 71ee952f..1b5c6da0 100644 --- a/src/serlio/materials/MaterialCommand.h +++ b/src/serlio/materials/MaterialCommand.h @@ -38,4 +38,5 @@ class MaterialCommand : public MPxCommand { MDGModifier fDGModifier; MDagModifier fDagModifier; MDagPath fDagPath; + MString fGeometryName; }; From 3a1a1efedcafcf2ed69fe91f03f24801c72c1a51 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 4 Jan 2022 15:08:16 +0100 Subject: [PATCH 173/668] Use name for backwards compatibility with older maya versions --- src/serlio/materials/MaterialCommand.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/materials/MaterialCommand.cpp b/src/serlio/materials/MaterialCommand.cpp index bc5530a0..fd529db8 100644 --- a/src/serlio/materials/MaterialCommand.cpp +++ b/src/serlio/materials/MaterialCommand.cpp @@ -115,7 +115,7 @@ MStatus MaterialCommand::doIt(const MArgList& argList) { if (geometryInMesh.isConnected()) { MPlugArray parentPlugArray; MPlug serlioOutMesh; - fGeometryName = rootNode.uniqueName(); + fGeometryName = rootNode.name(); geometryInMesh.connectedTo(parentPlugArray, true, false); From d5c985c27c874efc80a3296e9e583446f1571636 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 6 Jan 2022 11:50:51 +0100 Subject: [PATCH 174/668] Cleanup: fix typo --- src/serlio/scripts/serlioCreateUI.mel | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index 48c0a3c9..6092a447 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -17,7 +17,7 @@ * limitations under the License. */ -proc string[] appendAarray(string $a0[], string $a1[]) { +proc string[] appendArray(string $a0[], string $a1[]) { string $result[] = $a0; for($e in $a1) if(!stringArrayContains($e, $result)){ @@ -31,10 +31,10 @@ proc string[] collectInitialShapes(string $nodes[]) { for($node in $nodes) { string $type = `nodeType $node`; if($type == "mesh") { - $result = appendAarray($result, {$node}); + $result = appendArray($result, {$node}); } else if($type == "transform") { string $tmp[] = collectInitialShapes(`listRelatives -ni -pa -children $node`); - $result = appendAarray($result, $tmp); + $result = appendArray($result, $tmp); } } return $result; @@ -43,7 +43,7 @@ proc string[] collectInitialShapes(string $nodes[]) { proc int hasNodeTypeInHistory(string $node, string $nodeTypes[]){ string $hist[] = `listHistory -f false $node`; string $future[] = `listHistory -f true $node`; - $hist = appendAarray($hist, $future); + $hist = appendArray($hist, $future); if (size($hist) > 1) { for ($node in $hist){ string $nodeType = `nodeType $node`; From 485120301c74ecf4c0af34ad88838e157aae96cb Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 6 Jan 2022 13:34:01 +0100 Subject: [PATCH 175/668] Cleanup: remove redundant if statement --- src/serlio/scripts/serlioCreateUI.mel | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index 6092a447..b0c4409b 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -44,12 +44,10 @@ proc int hasNodeTypeInHistory(string $node, string $nodeTypes[]){ string $hist[] = `listHistory -f false $node`; string $future[] = `listHistory -f true $node`; $hist = appendArray($hist, $future); - if (size($hist) > 1) { - for ($node in $hist){ - string $nodeType = `nodeType $node`; - if(stringArrayContains($nodeType, $nodeTypes)) - return true; - } + for ($node in $hist){ + string $nodeType = `nodeType $node`; + if(stringArrayContains($nodeType, $nodeTypes)) + return true; } return false; } From d4fa0bb673b75769e5d052e21f13c150b283cacc Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 6 Jan 2022 13:55:09 +0100 Subject: [PATCH 176/668] Cleanup: renaming functions for setting/getting undoState --- src/serlio/materials/ArnoldMaterialNode.cpp | 4 ++-- src/serlio/materials/StingrayMaterialNode.cpp | 4 ++-- src/serlio/utils/MELScriptBuilder.cpp | 4 ++-- src/serlio/utils/MELScriptBuilder.h | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/serlio/materials/ArnoldMaterialNode.cpp b/src/serlio/materials/ArnoldMaterialNode.cpp index a770fcce..9b96120c 100644 --- a/src/serlio/materials/ArnoldMaterialNode.cpp +++ b/src/serlio/materials/ArnoldMaterialNode.cpp @@ -333,7 +333,7 @@ MStatus ArnoldMaterialNode::compute(const MPlug& plug, MDataBlock& data) { MELScriptBuilder scriptBuilder; scriptBuilder.declInt(MEL_UNDO_STATE); - scriptBuilder.cacheUndoState(MEL_UNDO_STATE); + scriptBuilder.getUndoState(MEL_UNDO_STATE); scriptBuilder.setUndoState(false); scriptBuilder.declString(MEL_VARIABLE_SHADING_ENGINE); @@ -387,6 +387,6 @@ MStatus ArnoldMaterialNode::compute(const MPlug& plug, MDataBlock& data) { LOG_DBG << "assigned arnold shading engine (" << faceRange.first << ":" << faceRange.second << "): " << shadingEngineName; } - scriptBuilder.applyCachedUndoState(MEL_UNDO_STATE); + scriptBuilder.setUndoState(MEL_UNDO_STATE); return scriptBuilder.execute(); } diff --git a/src/serlio/materials/StingrayMaterialNode.cpp b/src/serlio/materials/StingrayMaterialNode.cpp index c7fec5df..eb85e556 100644 --- a/src/serlio/materials/StingrayMaterialNode.cpp +++ b/src/serlio/materials/StingrayMaterialNode.cpp @@ -195,7 +195,7 @@ MStatus StingrayMaterialNode::compute(const MPlug& plug, MDataBlock& data) { MELScriptBuilder scriptBuilder; scriptBuilder.declInt(MEL_UNDO_STATE); - scriptBuilder.cacheUndoState(MEL_UNDO_STATE); + scriptBuilder.getUndoState(MEL_UNDO_STATE); scriptBuilder.setUndoState(false); scriptBuilder.declString(MEL_VARIABLE_SHADING_ENGINE); @@ -240,7 +240,7 @@ MStatus StingrayMaterialNode::compute(const MPlug& plug, MDataBlock& data) { LOG_DBG << "assigned stingray shading engine (" << faceRange.first << ":" << faceRange.second << "): " << shadingEngineName; } - scriptBuilder.applyCachedUndoState(MEL_UNDO_STATE); + scriptBuilder.setUndoState(MEL_UNDO_STATE); LOG_DBG << "scheduling stringray material script"; return scriptBuilder.execute(); // note: script is executed asynchronously } diff --git a/src/serlio/utils/MELScriptBuilder.cpp b/src/serlio/utils/MELScriptBuilder.cpp index 7e045e26..cae0b60e 100644 --- a/src/serlio/utils/MELScriptBuilder.cpp +++ b/src/serlio/utils/MELScriptBuilder.cpp @@ -132,12 +132,12 @@ void MELScriptBuilder::createTextureShadingNode(const MELVariable& nodeName) { commandStream << mel << "= `shadingNode -asTexture -skipSelect -name " << mel << " file`;\n"; } -void MELScriptBuilder::cacheUndoState(const MELVariable& undoName) { +void MELScriptBuilder::getUndoState(const MELVariable& undoName) { const auto mel = undoName.mel(); commandStream << mel << " = `undoInfo -q -state`;\n"; } -void MELScriptBuilder::applyCachedUndoState(const MELVariable& undoName) { +void MELScriptBuilder::setUndoState(const MELVariable& undoName) { const auto mel = undoName.mel(); commandStream << "undoInfo -stateWithoutFlush " << mel << ";\n"; } diff --git a/src/serlio/utils/MELScriptBuilder.h b/src/serlio/utils/MELScriptBuilder.h index 945b0467..8083662b 100644 --- a/src/serlio/utils/MELScriptBuilder.h +++ b/src/serlio/utils/MELScriptBuilder.h @@ -78,8 +78,8 @@ class MELScriptBuilder { void createShader(const std::wstring& shaderType, const MELVariable& nodeName); void createTextureShadingNode(const MELVariable& nodeName); - void cacheUndoState(const MELVariable& undoName); - void applyCachedUndoState(const MELVariable& undoName); + void getUndoState(const MELVariable& undoName); + void setUndoState(const MELVariable& undoName); void setUndoState(bool undoState); void python(const std::wstring& pythonCmd); From b6d3c10b62339cd6506bf9be25f349cd11b78e4e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 6 Jan 2022 15:18:19 +0100 Subject: [PATCH 177/668] Use double precision for scaling geometry --- src/serlio/modifiers/MayaCallbacks.cpp | 5 +++-- src/serlio/utils/MayaUtilities.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 86d94570..5fee20f6 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -63,8 +63,9 @@ MFloatPointArray toMayaFloatPointArray(double const* a, size_t s) { const unsigned int numPoints = static_cast(s) / 3; MFloatPointArray mfpa(numPoints); for (unsigned int i = 0; i < numPoints; ++i) { - mfpa.set(MFloatPoint(static_cast(a[i * 3 + 0]), static_cast(a[i * 3 + 1]), - static_cast(a[i * 3 + 2])) * mu::PRT_TO_SERLIO_SCALE, + mfpa.set(MFloatPoint(static_cast(a[i * 3 + 0] * mu::PRT_TO_SERLIO_SCALE), + static_cast(a[i * 3 + 1] * mu::PRT_TO_SERLIO_SCALE), + static_cast(a[i * 3 + 2] * mu::PRT_TO_SERLIO_SCALE)), i); } return mfpa; diff --git a/src/serlio/utils/MayaUtilities.h b/src/serlio/utils/MayaUtilities.h index dc631863..4cbc0121 100644 --- a/src/serlio/utils/MayaUtilities.h +++ b/src/serlio/utils/MayaUtilities.h @@ -14,7 +14,7 @@ // utility functions with dependencies on the Maya API namespace mu { // Standard conversion from meters (PRT) to centimeters (maya) -constexpr float PRT_TO_SERLIO_SCALE = 100.0f; +constexpr double PRT_TO_SERLIO_SCALE = 100.0f; int32_t computeSeed(const MFloatPointArray& vertices); int32_t computeSeed(const double* vertices, size_t count); From 5fc45c6f99b080571b1242f817ced6d5a74f0da9 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 6 Jan 2022 15:35:11 +0100 Subject: [PATCH 178/668] Throw an error, if attributes were not created successfully --- src/serlio/modifiers/PRTModifierAction.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index ca6fbcc3..94cc222e 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -913,6 +913,8 @@ MStatus PRTModifierAction::addParameter(MFnDependencyNode& node, MObject& attr, MObject attrUserSet = nAttrUserSet.create(tAttr.name() + ATTRIBUTE_USER_SET_SUFFIX, tAttr.shortName() + ATTRIBUTE_USER_SET_SUFFIX, MFnNumericData::kBoolean, false, &stat); + if (stat != MS::kSuccess) + throw stat; if (!(node.hasAttribute(nAttrUserSet.shortName()))) { MCHECK(nAttrUserSet.setKeyable(true)); @@ -933,6 +935,8 @@ MStatus PRTModifierAction::addParameter(MFnDependencyNode& node, MObject& attr, MCHECK(nAttrForceDefault.setStorable(true)); MCHECK(node.addAttribute(attrForceDefault)); } + if (stat != MS::kSuccess) + throw stat; } return MS::kSuccess; } From fa43c2c599f2fbfbe5f1915406976e7be9d386b0 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 6 Jan 2022 15:37:09 +0100 Subject: [PATCH 179/668] Don't set hidden attributes keyable --- src/serlio/modifiers/PRTModifierAction.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 94cc222e..9f5ce1e5 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -917,7 +917,6 @@ MStatus PRTModifierAction::addParameter(MFnDependencyNode& node, MObject& attr, throw stat; if (!(node.hasAttribute(nAttrUserSet.shortName()))) { - MCHECK(nAttrUserSet.setKeyable(true)); MCHECK(nAttrUserSet.setHidden(true)); MCHECK(nAttrUserSet.setStorable(true)); MCHECK(node.addAttribute(attrUserSet)); @@ -930,7 +929,6 @@ MStatus PRTModifierAction::addParameter(MFnDependencyNode& node, MObject& attr, MFnNumericData::kBoolean, false, &stat); if (!(node.hasAttribute(nAttrForceDefault.shortName()))) { - MCHECK(nAttrForceDefault.setKeyable(true)); MCHECK(nAttrForceDefault.setHidden(true)); MCHECK(nAttrForceDefault.setStorable(true)); MCHECK(node.addAttribute(attrForceDefault)); From fc8fd937e8563d979669c09f40cdc0e2719d353b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 6 Jan 2022 15:54:52 +0100 Subject: [PATCH 180/668] Cleanup: make iterateThroughAttributesAndApply a local non-member function This allows us to remove the empty template function declaration from the header file --- src/serlio/modifiers/PRTModifierAction.cpp | 41 +++++++++++----------- src/serlio/modifiers/PRTModifierAction.h | 3 -- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 9f5ce1e5..c6905496 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -134,22 +134,6 @@ bool getAndResetForceDefault(const MFnDependencyNode& node, const MFnAttribute& enum class PrtAttributeType { BOOL, FLOAT, COLOR, STRING, ENUM }; -} // namespace - -PRTModifierAction::PRTModifierAction() { - AttributeMapBuilderUPtr optionsBuilder(prt::AttributeMapBuilder::create()); - - mMayaEncOpts = prtu::createValidatedOptions(ENC_ID_MAYA); - - optionsBuilder->setString(L"name", FILE_CGA_ERROR); - const AttributeMapUPtr errOptions(optionsBuilder->createAttributeMapAndReset()); - mCGAErrorOptions = prtu::createValidatedOptions(ENC_ID_CGA_ERROR, errOptions.get()); - - optionsBuilder->setString(L"name", FILE_CGA_PRINT); - const AttributeMapUPtr printOptions(optionsBuilder->createAttributeMapAndReset()); - mCGAPrintOptions = prtu::createValidatedOptions(ENC_ID_CGA_PRINT, printOptions.get()); -} - std::list getNodeAttributesCorrespondingToCGA(const MFnDependencyNode& node) { std::list rawAttrs; std::list ignoreList; @@ -187,12 +171,12 @@ std::list getNodeAttributesCorrespondingToCGA(const MFnDependencyNode& const RuleAttribute RULE_NOT_FOUND{}; template -MStatus PRTModifierAction::iterateThroughAttributesAndApply(const MObject& node, T attrFunction) { +MStatus iterateThroughAttributesAndApply(const MObject& node, const RuleAttributes& mRuleAttributes, T attrFunction) { MStatus stat; const MFnDependencyNode fNode(node, &stat); MCHECK(stat); - auto reverseLookupAttribute = [this](const std::wstring& mayaFullAttrName) { + auto reverseLookupAttribute = [mRuleAttributes](const std::wstring& mayaFullAttrName) { auto it = std::find_if(mRuleAttributes.begin(), mRuleAttributes.end(), [&mayaFullAttrName](const auto& ra) { return (ra.mayaFullName == mayaFullAttrName); }); if (it != mRuleAttributes.end()) @@ -242,6 +226,21 @@ MStatus PRTModifierAction::iterateThroughAttributesAndApply(const MObject& node, } return MStatus::kSuccess; } +} // namespace + +PRTModifierAction::PRTModifierAction() { + AttributeMapBuilderUPtr optionsBuilder(prt::AttributeMapBuilder::create()); + + mMayaEncOpts = prtu::createValidatedOptions(ENC_ID_MAYA); + + optionsBuilder->setString(L"name", FILE_CGA_ERROR); + const AttributeMapUPtr errOptions(optionsBuilder->createAttributeMapAndReset()); + mCGAErrorOptions = prtu::createValidatedOptions(ENC_ID_CGA_ERROR, errOptions.get()); + + optionsBuilder->setString(L"name", FILE_CGA_PRINT); + const AttributeMapUPtr printOptions(optionsBuilder->createAttributeMapAndReset()); + mCGAPrintOptions = prtu::createValidatedOptions(ENC_ID_CGA_PRINT, printOptions.get()); +} MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { AttributeMapBuilderSPtr aBuilder(prt::AttributeMapBuilder::create(), PRTDestroyer()); @@ -320,7 +319,7 @@ MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { } }; - iterateThroughAttributesAndApply(node, fillAttributeFromNode); + iterateThroughAttributesAndApply(node, mRuleAttributes, fillAttributeFromNode); mGenerateAttrs.reset(aBuilder->createAttributeMap()); return MStatus::kSuccess; @@ -399,7 +398,7 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { } }; - iterateThroughAttributesAndApply(node, updateUserSetAttribute); + iterateThroughAttributesAndApply(node, mRuleAttributes, updateUserSetAttribute); return MStatus::kSuccess; } @@ -518,7 +517,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { } }; - iterateThroughAttributesAndApply(node, updateUIFromAttributes); + iterateThroughAttributesAndApply(node, mRuleAttributes, updateUIFromAttributes); return MStatus::kSuccess; } diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 501c3600..f639e1c8 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -109,9 +109,6 @@ class PRTModifierAction : public polyModifierFty { MStatus createNodeAttributes(const MObject& node, const prt::RuleFileInfo* info); void removeUnusedAttribs(MFnDependencyNode& node); - template - MStatus iterateThroughAttributesAndApply(const MObject& node, T attrFunction); - static MStatus addParameter(MFnDependencyNode& node, MObject& attr, MFnAttribute& tAttr); static MStatus addBoolParameter(MFnDependencyNode& node, MObject& attr, const RuleAttribute& name, bool defaultValue); From 74d7e9e00f7d4327491ac1cfaacededb970bc613 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 6 Jan 2022 16:30:16 +0100 Subject: [PATCH 181/668] Cleanup: make ruleAttrType [[maybe_used]] --- src/serlio/modifiers/PRTModifierAction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index c6905496..a29c8311 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -194,7 +194,7 @@ MStatus iterateThroughAttributesAndApply(const MObject& node, const RuleAttribut assert(!ruleAttr.fqName.empty()); // poor mans check for RULE_NOT_FOUND const std::wstring fqAttrName = ruleAttr.fqName; - const auto ruleAttrType = ruleAttr.mType; + [[maybe_unused]] const auto ruleAttrType = ruleAttr.mType; if (attrObj.hasFn(MFn::kNumericAttribute)) { MFnNumericAttribute nAttr(attrObj); From 9898ad33b31ae17ad4ec0366f46c1f4b471edb60 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 6 Jan 2022 16:35:11 +0100 Subject: [PATCH 182/668] Cleanup: remove unnecessary arguments from lambda function --- src/serlio/modifiers/PRTModifierAction.cpp | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index a29c8311..a39c8d5a 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -193,7 +193,6 @@ MStatus iterateThroughAttributesAndApply(const MObject& node, const RuleAttribut const RuleAttribute ruleAttr = reverseLookupAttribute(fullAttrName.asWChar()); assert(!ruleAttr.fqName.empty()); // poor mans check for RULE_NOT_FOUND - const std::wstring fqAttrName = ruleAttr.fqName; [[maybe_unused]] const auto ruleAttrType = ruleAttr.mType; if (attrObj.hasFn(MFn::kNumericAttribute)) { @@ -202,26 +201,26 @@ MStatus iterateThroughAttributesAndApply(const MObject& node, const RuleAttribut if (nAttr.unitType() == MFnNumericData::kBoolean) { assert(ruleAttrType == prt::AAT_BOOL); - attrFunction(fNode, fnAttr, ruleAttr, fqAttrName, PrtAttributeType::BOOL); + attrFunction(fNode, fnAttr, ruleAttr, PrtAttributeType::BOOL); } else if (nAttr.unitType() == MFnNumericData::kDouble) { assert(ruleAttrType == prt::AAT_FLOAT); - attrFunction(fNode, fnAttr, ruleAttr, fqAttrName, PrtAttributeType::FLOAT); + attrFunction(fNode, fnAttr, ruleAttr, PrtAttributeType::FLOAT); } else if (nAttr.isUsedAsColor()) { assert(ruleAttrType == prt::AAT_STR); - attrFunction(fNode, fnAttr, ruleAttr, fqAttrName, PrtAttributeType::COLOR); + attrFunction(fNode, fnAttr, ruleAttr, PrtAttributeType::COLOR); } } else if (attrObj.hasFn(MFn::kTypedAttribute)) { assert(ruleAttrType == prt::AAT_STR); - attrFunction(fNode, fnAttr, ruleAttr, fqAttrName, PrtAttributeType::STRING); + attrFunction(fNode, fnAttr, ruleAttr, PrtAttributeType::STRING); } else if (attrObj.hasFn(MFn::kEnumAttribute)) { - attrFunction(fNode, fnAttr, ruleAttr, fqAttrName, PrtAttributeType::ENUM); + attrFunction(fNode, fnAttr, ruleAttr, PrtAttributeType::ENUM); } } return MStatus::kSuccess; @@ -246,10 +245,11 @@ MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { AttributeMapBuilderSPtr aBuilder(prt::AttributeMapBuilder::create(), PRTDestroyer()); const auto fillAttributeFromNode = [this, aBuilder](const MFnDependencyNode& fnNode, - const MFnAttribute& fnAttribute, - const RuleAttribute& ruleAttribute, std::wstring fqAttrName, + const MFnAttribute& fnAttribute, const RuleAttribute& ruleAttribute, const PrtAttributeType attrType) mutable { MPlug plug(fnNode.object(), fnAttribute.object()); + const std::wstring fqAttrName = ruleAttribute.fqName; + const prt::AnnotationArgumentType ruleAttrType = ruleAttribute.mType; switch (attrType) { case PrtAttributeType::BOOL: { @@ -296,7 +296,7 @@ MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { MCHECK(plug.getValue(enumVal)); if (getIsUserSet(fnNode, fnAttribute)) { - switch (ruleAttribute.mType) { + switch (ruleAttrType) { case prt::AAT_STR: aBuilder->setString(fqAttrName.c_str(), eAttr.fieldName(enumVal).asWChar()); break; @@ -307,7 +307,7 @@ MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { aBuilder->setBool(fqAttrName.c_str(), eAttr.fieldName(enumVal).asInt() != 0); break; default: - LOG_ERR << "Cannot handle attribute type " << ruleAttribute.mType << " for attr " + LOG_ERR << "Cannot handle attribute type " << ruleAttrType << " for attr " << fqAttrName; } } @@ -327,12 +327,12 @@ MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { const auto updateUserSetAttribute = [this](const MFnDependencyNode& fnNode, const MFnAttribute& fnAttribute, - const RuleAttribute& ruleAttribute, std::wstring fqAttrName, - const PrtAttributeType attrType) { + const RuleAttribute& ruleAttribute, const PrtAttributeType attrType) { const AttributeMapUPtr defaultAttributeValues = getDefaultAttributeValues( mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, *inPrtMesh, mRandomSeed); const MPlug plug(fnNode.object(), fnAttribute.object()); bool isDefaultValue = false; + const std::wstring fqAttrName = ruleAttribute.fqName; switch (attrType) { case PrtAttributeType::BOOL: { @@ -405,11 +405,11 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { MStatus PRTModifierAction::updateUI(const MObject& node) { const auto updateUIFromAttributes = [this](const MFnDependencyNode& fnNode, const MFnAttribute& fnAttribute, - const RuleAttribute& ruleAttribute, std::wstring fqAttrName, - const PrtAttributeType attrType) { + const RuleAttribute& ruleAttribute, const PrtAttributeType attrType) { const AttributeMapUPtr defaultAttributeValues = getDefaultAttributeValues( mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, *inPrtMesh, mRandomSeed); MPlug plug(fnNode.object(), fnAttribute.object()); + const std::wstring fqAttrName = ruleAttribute.fqName; switch (attrType) { case PrtAttributeType::BOOL: { From 9c6e3733ef4f15c87edd603938920548fa66eed9 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 6 Jan 2022 17:12:09 +0100 Subject: [PATCH 183/668] Remove declaration for the Shading engine This caused a warning in the mel console. The shading engine variable is already used (and thereby declared) earlier in synchronouslyCreateShadingEngine(). Since the synchronous code is executed before the other lines, removing these optional declarations fixes these warnings. Another alternative solution would be to force the variable to be declared synchronously with MELScriptBuilder.executeSync(string output) --- src/serlio/materials/ArnoldMaterialNode.cpp | 1 - src/serlio/materials/StingrayMaterialNode.cpp | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/serlio/materials/ArnoldMaterialNode.cpp b/src/serlio/materials/ArnoldMaterialNode.cpp index 9b96120c..a0622d02 100644 --- a/src/serlio/materials/ArnoldMaterialNode.cpp +++ b/src/serlio/materials/ArnoldMaterialNode.cpp @@ -336,7 +336,6 @@ MStatus ArnoldMaterialNode::compute(const MPlug& plug, MDataBlock& data) { scriptBuilder.getUndoState(MEL_UNDO_STATE); scriptBuilder.setUndoState(false); - scriptBuilder.declString(MEL_VARIABLE_SHADING_ENGINE); scriptBuilder.declString(MEL_VAR_SHADER_NODE); scriptBuilder.declString(MEL_VAR_MAP_FILE); scriptBuilder.declString(MEL_VAR_MAP_NODE); diff --git a/src/serlio/materials/StingrayMaterialNode.cpp b/src/serlio/materials/StingrayMaterialNode.cpp index eb85e556..538b9883 100644 --- a/src/serlio/materials/StingrayMaterialNode.cpp +++ b/src/serlio/materials/StingrayMaterialNode.cpp @@ -198,8 +198,6 @@ MStatus StingrayMaterialNode::compute(const MPlug& plug, MDataBlock& data) { scriptBuilder.getUndoState(MEL_UNDO_STATE); scriptBuilder.setUndoState(false); - scriptBuilder.declString(MEL_VARIABLE_SHADING_ENGINE); - // declare MEL variables required by appendToMaterialScriptBuilder() scriptBuilder.declString(MEL_VAR_SHADER_NODE); scriptBuilder.declString(MEL_VAR_MAP_FILE); From a8c6cdb9c4a721db3c96fe04df27991db4b89d9e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 6 Jan 2022 19:47:21 +0100 Subject: [PATCH 184/668] Cleanup: remove redundant flags These are overwritten later anyways --- src/serlio/scripts/AEserlioTemplate.mel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index e029a79c..c2e68b64 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -151,7 +151,7 @@ global proc prtAttributeControl(string $attr, string $varname){ $nrOfColumns = 5; } - rowColumnLayout -numberOfColumns $nrOfColumns -bgc 0.4 0.4 0.4 -noBackground true -adjustableColumn 2 ($control + "_column_layout"); + rowColumnLayout -numberOfColumns $nrOfColumns -adjustableColumn 2 ($control + "_column_layout"); $label = `niceName $attr`; From 8896b842bf3cca21b382b55e7476e7c75652f7fd Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 6 Jan 2022 19:48:10 +0100 Subject: [PATCH 185/668] Cleanup: add helper functions for setting the edit highlights --- src/serlio/scripts/AEserlioTemplate.mel | 30 +++++++++++++++---------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index c2e68b64..1a3478f9 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -46,6 +46,20 @@ proc int prtIsColorAttr(string $attr){ return `addAttr -q -uac ($attr)`; } +proc int prtAttributeExists(string $attr){ + string $node = plugNode($attr); + string $attributeName = plugAttr($attr); + return`attributeExists $attributeName $node`; +} + +proc prtSetSymbolButtonVisibility(int $visible, string $control){ + symbolButton -edit -visible $visible $control; +} + +proc prtHighlightBackground(int $highlighted, float $grayScaleColor, string $control){ + control -edit -bgc $grayScaleColor $grayScaleColor$grayScaleColor -ebg $highlighted $control; +} + global proc prtReloadRPK(string $attr) { $node = prtGetNodeName($attr); $attrCurrentRule = $node + ".currentRule_Package"; @@ -116,23 +130,15 @@ global proc prtFileBrowseReplaceRPK(string $attr, string $varname, string $filte } global proc prtUpdateEditHighlights(string $attr){ - string $node = plugNode($attr); - string $attributeName = plugAttr($attr); - if (!`attributeExists $attributeName $node`) + if (!prtAttributeExists($attr)) return; int $userSet = `getAttr ($attr + "_user_set")`; string $control = prtControlName($attr); - symbolButton -edit -visible $userSet ($control + "_user_set"); - - if ($userSet){ - rowColumnLayout -edit -bgc 0.32 0.32 0.32 ($control + "_column_layout"); - symbolButton -edit -visible true ($control + "_force_default_reset"); - } else { - rowColumnLayout -edit -bgc 0.4 0.4 0.4 -noBackground true ($control + "_column_layout"); - symbolButton -edit -visible false ($control + "_force_default_reset"); - } + prtSetSymbolButtonVisibility($userSet, $control + "_user_set"); + prtSetSymbolButtonVisibility($userSet, $control + "_force_default_reset"); + prtHighlightBackground($userSet, 0.32, $control + "_column_layout"); } global proc prtSetAttrUserSetAndUpdateUI(string $attr){ From 5f76a4e712f8553ecfef8e4531cafabb1e643f6b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 6 Jan 2022 19:48:50 +0100 Subject: [PATCH 186/668] Cleanup: set background of controls Otherwise they share the same backgroundcolor as our highlight --- src/serlio/scripts/AEserlioTemplate.mel | 1 + 1 file changed, 1 insertion(+) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 1a3478f9..fda62def 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -139,6 +139,7 @@ global proc prtUpdateEditHighlights(string $attr){ prtSetSymbolButtonVisibility($userSet, $control + "_user_set"); prtSetSymbolButtonVisibility($userSet, $control + "_force_default_reset"); prtHighlightBackground($userSet, 0.32, $control + "_column_layout"); + prtHighlightBackground(false, 0.4, $control); } global proc prtSetAttrUserSetAndUpdateUI(string $attr){ From 663228dd52d44b9835fbea43e24d85e9713a3889 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 6 Jan 2022 19:51:08 +0100 Subject: [PATCH 187/668] Cleanup: rename for better clarity --- src/serlio/scripts/AEserlioTemplate.mel | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index fda62def..30da0f0d 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -56,8 +56,8 @@ proc prtSetSymbolButtonVisibility(int $visible, string $control){ symbolButton -edit -visible $visible $control; } -proc prtHighlightBackground(int $highlighted, float $grayScaleColor, string $control){ - control -edit -bgc $grayScaleColor $grayScaleColor$grayScaleColor -ebg $highlighted $control; +proc prtSetBackgroundColor(int $enabled, float $grayScaleColor, string $control){ + control -edit -bgc $grayScaleColor $grayScaleColor $grayScaleColor -ebg $enabled $control; } global proc prtReloadRPK(string $attr) { @@ -138,8 +138,8 @@ global proc prtUpdateEditHighlights(string $attr){ prtSetSymbolButtonVisibility($userSet, $control + "_user_set"); prtSetSymbolButtonVisibility($userSet, $control + "_force_default_reset"); - prtHighlightBackground($userSet, 0.32, $control + "_column_layout"); - prtHighlightBackground(false, 0.4, $control); + prtSetBackgroundColor($userSet, 0.32, $control + "_column_layout"); + prtSetBackgroundColor(false, 0.4, $control); } global proc prtSetAttrUserSetAndUpdateUI(string $attr){ From 4122d4c21613bcfbbacb0b7d637efb964d1665d2 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 7 Jan 2022 10:44:04 +0100 Subject: [PATCH 188/668] Clenaup: remove unused define --- src/serlio/modifiers/PRTModifierAction.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index a39c8d5a..225a76f3 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -37,10 +37,6 @@ #include -#define CHECK_STATUS(st) \ - if ((st) != MS::kSuccess) { \ - break; \ - } namespace { constexpr bool DBG = false; From 8fa3f79cf762c8e6770203d052be5905f540f02a Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 7 Jan 2022 11:11:04 +0100 Subject: [PATCH 189/668] Cleanup: refactor addParameter using helper functions This also removes the unhandled throw --- src/serlio/modifiers/PRTModifierAction.cpp | 51 +++++++++++----------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 225a76f3..149d9224 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -221,6 +221,22 @@ MStatus iterateThroughAttributesAndApply(const MObject& node, const RuleAttribut } return MStatus::kSuccess; } + +MStatus addHiddenBoolParameter(MFnDependencyNode& node, MFnAttribute& tAttr, const MString& suffix) { + MStatus stat; + + MFnNumericAttribute nAttr; + MObject attr = + nAttr.create(tAttr.name() + suffix, tAttr.shortName() + suffix, MFnNumericData::kBoolean, false, &stat); + MCHECK(stat); + + if (!(node.hasAttribute(nAttr.shortName()))) { + MCHECK(nAttr.setHidden(true)); + MCHECK(nAttr.setStorable(true)); + stat = node.addAttribute(attr); + } + return stat; +} } // namespace PRTModifierAction::PRTModifierAction() { @@ -897,39 +913,24 @@ T PRTModifierAction::getPlugValueAndRemoveAttr(MFnDependencyNode& node, const MS MStatus PRTModifierAction::addParameter(MFnDependencyNode& node, MObject& attr, MFnAttribute& tAttr) { if (!(node.hasAttribute(tAttr.shortName()))) { + MStatus stat; + MCHECK(tAttr.setKeyable(true)); MCHECK(tAttr.setHidden(false)); MCHECK(tAttr.setStorable(true)); - MCHECK(node.addAttribute(attr)); + stat = node.addAttribute(attr); + if (stat != MS::kSuccess) + return stat; // add hidden user_set attribute - MStatus stat; - MFnNumericAttribute nAttrUserSet; - MObject attrUserSet = nAttrUserSet.create(tAttr.name() + ATTRIBUTE_USER_SET_SUFFIX, - tAttr.shortName() + ATTRIBUTE_USER_SET_SUFFIX, - MFnNumericData::kBoolean, false, &stat); + stat = addHiddenBoolParameter(node, tAttr, ATTRIBUTE_USER_SET_SUFFIX); if (stat != MS::kSuccess) - throw stat; - - if (!(node.hasAttribute(nAttrUserSet.shortName()))) { - MCHECK(nAttrUserSet.setHidden(true)); - MCHECK(nAttrUserSet.setStorable(true)); - MCHECK(node.addAttribute(attrUserSet)); - } + return stat; // add hidden force_default attribute - MFnNumericAttribute nAttrForceDefault; - MObject attrForceDefault = nAttrForceDefault.create(tAttr.name() + ATTRIBUTE_FORCE_DEFAULT_SUFFIX, - tAttr.shortName() + ATTRIBUTE_FORCE_DEFAULT_SUFFIX, - MFnNumericData::kBoolean, false, &stat); - - if (!(node.hasAttribute(nAttrForceDefault.shortName()))) { - MCHECK(nAttrForceDefault.setHidden(true)); - MCHECK(nAttrForceDefault.setStorable(true)); - MCHECK(node.addAttribute(attrForceDefault)); - } - if (stat != MS::kSuccess) - throw stat; + stat = addHiddenBoolParameter(node, tAttr, ATTRIBUTE_FORCE_DEFAULT_SUFFIX); + + return stat; } return MS::kSuccess; } From 9bd5c334529d18ad5ced9f2f6ce7e63734ccdf58 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 7 Jan 2022 11:11:57 +0100 Subject: [PATCH 190/668] Cleanup: replace throw with MCHECK --- src/serlio/modifiers/PRTModifierAction.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 149d9224..ec49e093 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -944,8 +944,7 @@ MStatus PRTModifierAction::addBoolParameter(MFnDependencyNode& node, MObject& at attr = nAttr.create(ruleAttr.mayaFullName.c_str(), ruleAttr.mayaBriefName.c_str(), MFnNumericData::kBoolean, defaultValue, &stat); nAttr.setNiceNameOverride(ruleAttr.mayaNiceName.c_str()); - if (stat != MS::kSuccess) - throw stat; + MCHECK(stat); MCHECK(addParameter(node, attr, nAttr)); @@ -964,8 +963,7 @@ MStatus PRTModifierAction::addFloatParameter(MFnDependencyNode& node, MObject& a attr = nAttr.create(ruleAttr.mayaFullName.c_str(), ruleAttr.mayaBriefName.c_str(), MFnNumericData::kDouble, defaultValue, &stat); nAttr.setNiceNameOverride(ruleAttr.mayaNiceName.c_str()); - if (stat != MS::kSuccess) - throw stat; + MCHECK(stat); if (!prtu::isnan(min)) { MCHECK(nAttr.setMin(min)); From 941f02620abf25d538c054e4253ee9450dc96510 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 7 Jan 2022 11:12:49 +0100 Subject: [PATCH 191/668] Cleanup: return status instead of kSuccess during parameter creation --- src/serlio/modifiers/PRTModifierAction.cpp | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index ec49e093..39b19482 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -946,12 +946,12 @@ MStatus PRTModifierAction::addBoolParameter(MFnDependencyNode& node, MObject& at nAttr.setNiceNameOverride(ruleAttr.mayaNiceName.c_str()); MCHECK(stat); - MCHECK(addParameter(node, attr, nAttr)); + stat = addParameter(node, attr, nAttr); MPlug plug(node.object(), attr); MCHECK(plug.setValue(plugValue)); - return MS::kSuccess; + return stat; } MStatus PRTModifierAction::addFloatParameter(MFnDependencyNode& node, MObject& attr, const RuleAttribute& ruleAttr, @@ -973,12 +973,12 @@ MStatus PRTModifierAction::addFloatParameter(MFnDependencyNode& node, MObject& a MCHECK(nAttr.setMax(max)); } - MCHECK(addParameter(node, attr, nAttr)); + stat = addParameter(node, attr, nAttr); MPlug plug(node.object(), attr); MCHECK(plug.setValue(plugValue)); - return MS::kSuccess; + return stat; } MStatus PRTModifierAction::addEnumParameter(const prt::Annotation* annot, MFnDependencyNode& node, MObject& attr, @@ -1032,12 +1032,12 @@ MStatus PRTModifierAction::addEnumParameter(const prt::Annotation* annot, MFnDep MCHECK(e.fill(annot)); - MCHECK(addParameter(node, attr, e.mAttr)); + stat = addParameter(node, attr, e.mAttr); MPlug plug(node.object(), attr); MCHECK(plug.setValue(plugValue)); - return MS::kSuccess; + return stat; } MStatus PRTModifierAction::addFileParameter(MFnDependencyNode& node, MObject& attr, const RuleAttribute& ruleAttr, @@ -1055,12 +1055,12 @@ MStatus PRTModifierAction::addFileParameter(MFnDependencyNode& node, MObject& at MCHECK(sAttr.setNiceNameOverride(ruleAttr.mayaNiceName.c_str())); MCHECK(stat); MCHECK(sAttr.setUsedAsFilename(true)); - MCHECK(addParameter(node, attr, sAttr)); + stat = addParameter(node, attr, sAttr); MPlug plug(node.object(), attr); MCHECK(plug.setValue(plugValue)); - return MS::kSuccess; + return stat; } MStatus PRTModifierAction::addColorParameter(MFnDependencyNode& node, MObject& attr, const RuleAttribute& ruleAttr, @@ -1082,12 +1082,12 @@ MStatus PRTModifierAction::addColorParameter(MFnDependencyNode& node, MObject& a nAttr.setDefault(color[0], color[1], color[2]); MCHECK(stat); - MCHECK(addParameter(node, attr, nAttr)); + stat = addParameter(node, attr, nAttr); MPlug plug(node.object(), attr); MCHECK(plug.setValue(plugValue)); - return MS::kSuccess; + return stat; } MStatus PRTModifierAction::addStrParameter(MFnDependencyNode& node, MObject& attr, const RuleAttribute& ruleAttr, @@ -1104,7 +1104,7 @@ MStatus PRTModifierAction::addStrParameter(MFnDependencyNode& node, MObject& att sAttr.setNiceNameOverride(ruleAttr.mayaNiceName.c_str()); MCHECK(stat); - MCHECK(addParameter(node, attr, sAttr)); + stat = addParameter(node, attr, sAttr); MPlug plug(node.object(), attr); MCHECK(plug.setValue(plugValue)); @@ -1112,5 +1112,5 @@ MStatus PRTModifierAction::addStrParameter(MFnDependencyNode& node, MObject& att if (DBG) LOG_DBG << sAttr.name().asWChar() << " = " << plugValue.asWChar(); - return MS::kSuccess; + return stat; } From db528fb35164cb6bf122b21ff4b4c57213731542 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 7 Jan 2022 11:19:02 +0100 Subject: [PATCH 192/668] Cleanup: remove redundant status assignment --- src/serlio/modifiers/PRTModifierAction.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 39b19482..ec7f39d0 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -928,9 +928,7 @@ MStatus PRTModifierAction::addParameter(MFnDependencyNode& node, MObject& attr, return stat; // add hidden force_default attribute - stat = addHiddenBoolParameter(node, tAttr, ATTRIBUTE_FORCE_DEFAULT_SUFFIX); - - return stat; + return addHiddenBoolParameter(node, tAttr, ATTRIBUTE_FORCE_DEFAULT_SUFFIX); } return MS::kSuccess; } From fece5fc7e9ea7e8c4bd0008d34df5cd68b521dba Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 10 Jan 2022 10:25:07 +0100 Subject: [PATCH 193/668] Compare color values instead of string values This fixes a bug where the default color in prt is a non-valid color (i.e. empty string) --- src/serlio/modifiers/PRTModifierAction.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index ec7f39d0..8b1b0829 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -364,7 +364,7 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { break; } case PrtAttributeType::COLOR: { - const wchar_t* defColStr = defaultAttributeValues->getString(fqAttrName.c_str()); + const prtu::Color defCol = prtu::parseColor(defaultAttributeValues->getString(fqAttrName.c_str())); MObject rgb; MCHECK(plug.getValue(rgb)); @@ -372,9 +372,8 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { prtu::Color col; MCHECK(fRGB.getData3Float(col[0], col[1], col[2])); - const std::wstring colStr = prtu::getColorString(col); - isDefaultValue = std::wcscmp(colStr.c_str(), defColStr) == 0; + isDefaultValue = defCol == col; break; } case PrtAttributeType::STRING: { From 5ebab1d25418f5b68a24ef25457fe29febde262b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 10 Jan 2022 10:26:41 +0100 Subject: [PATCH 194/668] Only update user_set to false, if force_reset is used --- src/serlio/modifiers/PRTModifierAction.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 8b1b0829..8525ce8c 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -404,8 +404,8 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { if (getAndResetForceDefault(fnNode, fnAttribute)) { setIsUserSet(fnNode, fnAttribute, false); } - else { - setIsUserSet(fnNode, fnAttribute, !isDefaultValue); + else if (!isDefaultValue){ + setIsUserSet(fnNode, fnAttribute, true); } }; From 0abe2fdd64cf06213cfecf966595fc60afe0fdbf Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 10 Jan 2022 10:48:21 +0100 Subject: [PATCH 195/668] Fill enum before querying the default value --- src/serlio/modifiers/PRTModifierAction.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 8525ce8c..041c3ef6 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -980,9 +980,12 @@ MStatus PRTModifierAction::addFloatParameter(MFnDependencyNode& node, MObject& a MStatus PRTModifierAction::addEnumParameter(const prt::Annotation* annot, MFnDependencyNode& node, MObject& attr, const RuleAttribute& ruleAttr, bool defaultValue, PRTModifierEnum& e) { + PRTModifierEnum tmpEnum; + tmpEnum.fill(annot); + short idx = 0; - for (int i = static_cast(e.mBVals.length()); --i >= 0;) { - if ((e.mBVals[i] != 0) == defaultValue) { + for (int i = static_cast(tmpEnum.mBVals.length()); --i >= 0;) { + if ((tmpEnum.mBVals[i] != 0) == defaultValue) { idx = static_cast(i); break; } @@ -993,9 +996,12 @@ MStatus PRTModifierAction::addEnumParameter(const prt::Annotation* annot, MFnDep MStatus PRTModifierAction::addEnumParameter(const prt::Annotation* annot, MFnDependencyNode& node, MObject& attr, const RuleAttribute& ruleAttr, double defaultValue, PRTModifierEnum& e) { + PRTModifierEnum tmpEnum; + tmpEnum.fill(annot); + short idx = 0; - for (int i = static_cast(e.mFVals.length()); --i >= 0;) { - if (e.mFVals[i] == defaultValue) { + for (int i = static_cast(tmpEnum.mFVals.length()); --i >= 0;) { + if (tmpEnum.mFVals[i] == defaultValue) { idx = static_cast(i); break; } @@ -1007,9 +1013,12 @@ MStatus PRTModifierAction::addEnumParameter(const prt::Annotation* annot, MFnDep MStatus PRTModifierAction::addEnumParameter(const prt::Annotation* annot, MFnDependencyNode& node, MObject& attr, const RuleAttribute& ruleAttr, const MString& defaultValue, PRTModifierEnum& e) { + PRTModifierEnum tmpEnum; + tmpEnum.fill(annot); + short idx = 0; - for (int i = static_cast(e.mSVals.length()); --i >= 0;) { - if (e.mSVals[i] == defaultValue) { + for (int i = static_cast(tmpEnum.mSVals.length()); --i >= 0;) { + if (tmpEnum.mSVals[i] == defaultValue) { idx = static_cast(i); break; } From 58ca6e16e717e5ca8d4258b5a962ff2e41dba1a3 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 10 Jan 2022 11:38:55 +0100 Subject: [PATCH 196/668] Only update user_set highlights when user_set changes This allows us to remove an old hack where we reassign the current attribute value in order to force a UI refresh --- src/serlio/modifiers/PRTModifierAction.cpp | 46 ++++++---------------- src/serlio/scripts/AEserlioTemplate.mel | 12 +++--- 2 files changed, 16 insertions(+), 42 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index ec7f39d0..8425e665 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -431,13 +431,8 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { const bool isDefaultValue = defBoolVal == boolVal; - if (getIsUserSet(fnNode, fnAttribute)) { - plug.setBool(boolVal); - } - else { - if (!isDefaultValue) { - plug.setBool(defBoolVal); - } + if (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue) { + plug.setBool(defBoolVal); } break; } @@ -448,13 +443,8 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { const bool isDefaultValue = defDoubleVal == doubleVal; - if (getIsUserSet(fnNode, fnAttribute)) { - plug.setDouble(doubleVal); - } - else { - if (!isDefaultValue) { - plug.setDouble(defDoubleVal); - } + if (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue) { + plug.setDouble(defDoubleVal); } break; } @@ -476,13 +466,8 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { const bool isDefaultValue = std::wcscmp(colStr.c_str(), defColStr) == 0; - if (getIsUserSet(fnNode, fnAttribute)) { - plug.setMObject(rgb); - } - else { - if (!isDefaultValue) { - plug.setMObject(defaultColorObj); - } + if (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue) { + plug.setMObject(defaultColorObj); } break; } @@ -494,13 +479,8 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { const bool isDefaultValue = std::wcscmp(stringVal.asWChar(), defStringVal) == 0; - if (getIsUserSet(fnNode, fnAttribute)) { - plug.setString(stringVal); - } - else { - if (!isDefaultValue) { - plug.setString(defStringVal); - } + if (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue) { + plug.setString(defStringVal); } break; } @@ -513,13 +493,9 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { MCHECK(plug.getValue(enumVal)); const bool isDefaultValue = defEnumVal == enumVal; - if (getIsUserSet(fnNode, fnAttribute)) { - plug.setShort(enumVal); - } - else { - if (!isDefaultValue) { - plug.setShort(defEnumVal); - } + + if (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue) { + plug.setShort(defEnumVal); } break; } diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 30da0f0d..312a589e 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -142,10 +142,8 @@ global proc prtUpdateEditHighlights(string $attr){ prtSetBackgroundColor(false, 0.4, $control); } -global proc prtSetAttrUserSetAndUpdateUI(string $attr){ +global proc prtForceDefault(string $attr){ setAttr ($attr + "_force_default") true; - setAttr ($attr + "_user_set") false; - prtUpdateEditHighlights($attr); } global proc prtAttributeControl(string $attr, string $varname){ @@ -173,7 +171,7 @@ global proc prtAttributeControl(string $attr, string $varname){ if ($isFileAttribute){ symbolButton -image "navButtonBrowse.xpm" -c ("prtShowFileDialog(\"" + $attr + "\",\"All Files (*.*)\" )") ($control + "_browse"); } - symbolButton -image "undo_s.png" -visible false -annotation "reset rule attribute to default value" -c ("prtSetAttrUserSetAndUpdateUI (\"" + $attr +"\")") ($control + "_force_default_reset"); + symbolButton -image "undo_s.png" -visible false -annotation "reset rule attribute to default value" -c ("prtForceDefault (\"" + $attr +"\")") ($control + "_force_default_reset"); if(prtIsStringAttr($attr)){ connectControl -index 2 $control $attr; @@ -181,7 +179,7 @@ global proc prtAttributeControl(string $attr, string $varname){ connectControl $control $attr; } string $changeCommand = "prtUpdateEditHighlights " + $attr; - scriptJob -rp -p $control -ac $attr $changeCommand; + scriptJob -rp -p $control -ac ($attr + "_user_set") $changeCommand; setParent ..; setUITemplate -ppt; @@ -202,9 +200,9 @@ global proc prtAttributeControlReplace(string $attr, string $varname){ attrControlGrp -edit -attribute $attr $control; connectControl $control $attr; } - symbolButton -edit -c ("prtSetAttrUserSetAndUpdateUI (\"" + $attr +"\")") ($control + "_force_default_reset"); + symbolButton -edit -c ("prtForceDefault (\"" + $attr +"\")") ($control + "_force_default_reset"); string $changeCommand = "prtUpdateEditHighlights " + $attr; - scriptJob -rp -p $control -ac $attr $changeCommand; + scriptJob -rp -p $control -ac ($attr + "_user_set") $changeCommand; } prtUpdateEditHighlights($attr); } From b9b29f3d5ca51d9b349ee4e8af6c5fc5ca6f16e8 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 10 Jan 2022 16:56:50 +0100 Subject: [PATCH 197/668] Use current AttributeMap to generate default attribtue values --- src/serlio/modifiers/PRTModifierAction.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 0977b0b3..83516dab 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -61,7 +61,7 @@ const AttributeMapUPtr AttributeMapUPtr getDefaultAttributeValues(const std::wstring& ruleFile, const std::wstring& startRule, const prt::ResolveMap& resolveMap, prt::CacheObject& cache, - const PRTMesh& prtMesh, const int32_t seed) { + const PRTMesh& prtMesh, const int32_t seed, const AttributeMapUPtr& attributeMap) { AttributeMapBuilderUPtr mayaCallbacksAttributeBuilder(prt::AttributeMapBuilder::create()); MayaCallbacks mayaCallbacks(MObject::kNullObj, MObject::kNullObj, mayaCallbacksAttributeBuilder); @@ -70,7 +70,7 @@ AttributeMapUPtr getDefaultAttributeValues(const std::wstring& ruleFile, const s isb->setGeometry(prtMesh.vertexCoords(), prtMesh.vcCount(), prtMesh.indices(), prtMesh.indicesCount(), prtMesh.faceCounts(), prtMesh.faceCountsCount()); - isb->setAttributes(ruleFile.c_str(), startRule.c_str(), seed, L"", EMPTY_ATTRIBUTES.get(), &resolveMap); + isb->setAttributes(ruleFile.c_str(), startRule.c_str(), seed, L"", attributeMap.get(), &resolveMap); const InitialShapeUPtr shape(isb->createInitialShapeAndReset()); const InitialShapeNOPtrVector shapes = {shape.get()}; @@ -341,7 +341,7 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { const auto updateUserSetAttribute = [this](const MFnDependencyNode& fnNode, const MFnAttribute& fnAttribute, const RuleAttribute& ruleAttribute, const PrtAttributeType attrType) { const AttributeMapUPtr defaultAttributeValues = getDefaultAttributeValues( - mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, *inPrtMesh, mRandomSeed); + mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, *inPrtMesh, mRandomSeed, mGenerateAttrs); const MPlug plug(fnNode.object(), fnAttribute.object()); bool isDefaultValue = false; const std::wstring fqAttrName = ruleAttribute.fqName; @@ -417,8 +417,8 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { MStatus PRTModifierAction::updateUI(const MObject& node) { const auto updateUIFromAttributes = [this](const MFnDependencyNode& fnNode, const MFnAttribute& fnAttribute, const RuleAttribute& ruleAttribute, const PrtAttributeType attrType) { - const AttributeMapUPtr defaultAttributeValues = getDefaultAttributeValues( - mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, *inPrtMesh, mRandomSeed); + const AttributeMapUPtr defaultAttributeValues = getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, + *inPrtMesh, mRandomSeed, mGenerateAttrs); MPlug plug(fnNode.object(), fnAttribute.object()); const std::wstring fqAttrName = ruleAttribute.fqName; @@ -560,7 +560,7 @@ MStatus PRTModifierAction::updateRuleFiles(const MObject& node, const MString& r mStartRule = prtu::detectStartRule(info); mGenerateAttrs = getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, - *inPrtMesh, mRandomSeed); + *inPrtMesh, mRandomSeed, EMPTY_ATTRIBUTES); if (DBG) LOG_DBG << "default attrs: " << prtu::objectToXML(mGenerateAttrs); From 5a608036a0d9940e581b9b150f7945f618cafbb8 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 10 Jan 2022 17:27:27 +0100 Subject: [PATCH 198/668] Add support for dynamic default values of enums --- src/serlio/modifiers/PRTModifierAction.cpp | 53 ++++++++++++++++++++-- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 83516dab..53488d42 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -237,6 +237,53 @@ MStatus addHiddenBoolParameter(MFnDependencyNode& node, MFnAttribute& tAttr, con } return stat; } + +short getDefaultEnumValue(const AttributeMapUPtr& defaultAttributeValues, const MFnEnumAttribute& eAttr, + const RuleAttribute& ruleAttr) { + const std::wstring fqAttrName = ruleAttr.fqName; + const prt::AnnotationArgumentType ruleAttrType = ruleAttr.mType; + + short minVal; + short maxVal; + MCHECK(eAttr.getMin(minVal)); + MCHECK(eAttr.getMax(maxVal)); + + switch (ruleAttrType) { + case prt::AAT_STR: { + const wchar_t* defStringVal = defaultAttributeValues->getString(fqAttrName.c_str()); + + for (short currIdx = minVal; currIdx <= maxVal; currIdx++) { + const wchar_t* currString = eAttr.fieldName(currIdx).asWChar(); + if (std::wcscmp(currString, defStringVal) == 0) + return currIdx; + } + break; + } + case prt::AAT_FLOAT: { + const auto defDoubleVal = defaultAttributeValues->getFloat(fqAttrName.c_str()); + + for (short currIdx = minVal; currIdx <= maxVal; currIdx++) { + const double currDouble = eAttr.fieldName(currIdx).asDouble(); + if (currDouble == defDoubleVal) + return currIdx; + } + break; + } + case prt::AAT_BOOL: { + const auto defBoolVal = defaultAttributeValues->getBool(fqAttrName.c_str()); + + for (short currIdx = minVal; currIdx <= maxVal; currIdx++) { + const int currBool = eAttr.fieldName(currIdx).asInt() != 0; + if (currBool == defBoolVal) + return currIdx; + } + break; + } + default: + LOG_ERR << "Cannot handle attribute type " << ruleAttrType << " for attr " << fqAttrName; + } + return 0; +} } // namespace PRTModifierAction::PRTModifierAction() { @@ -388,9 +435,8 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { case PrtAttributeType::ENUM: { MFnEnumAttribute eAttr(fnAttribute.object()); - short defEnumVal; + const short defEnumVal = getDefaultEnumValue(defaultAttributeValues, eAttr, ruleAttribute); short enumVal; - MCHECK(eAttr.getDefault(defEnumVal)); MCHECK(plug.getValue(enumVal)); isDefaultValue = defEnumVal == enumVal; @@ -486,9 +532,8 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { case PrtAttributeType::ENUM: { MFnEnumAttribute eAttr(fnAttribute.object()); - short defEnumVal; + const short defEnumVal = getDefaultEnumValue(defaultAttributeValues, eAttr, ruleAttribute); short enumVal; - MCHECK(eAttr.getDefault(defEnumVal)); MCHECK(plug.getValue(enumVal)); const bool isDefaultValue = defEnumVal == enumVal; From 6c12943118dba2d3eba51064a190bcb23d99161a Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 10 Jan 2022 17:37:52 +0100 Subject: [PATCH 199/668] Only update user-set attributes if we didn't update the rule file Setting user-set variables during the first setup of the rule-file is redundant and may use out-of date attributes --- src/serlio/modifiers/PRTModifierNode.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierNode.cpp b/src/serlio/modifiers/PRTModifierNode.cpp index 7db37fa1..568d022a 100644 --- a/src/serlio/modifiers/PRTModifierNode.cpp +++ b/src/serlio/modifiers/PRTModifierNode.cpp @@ -94,6 +94,8 @@ MStatus PRTModifierNode::compute(const MPlug& plug, MDataBlock& data) { MDataHandle currentRulePkgData = data.inputValue(currentRulePkg, &status); MCheckStatus(status, "ERROR getting currentRulePkg"); + const bool ruleFileWasChanged = rulePkgData.asString() != currentRulePkgData.asString(); + // Copy the inMesh to the outMesh, so you can // perform operations directly on outMesh // @@ -104,14 +106,14 @@ MStatus PRTModifierNode::compute(const MPlug& plug, MDataBlock& data) { // Set the mesh object and component List on the factory fPRTModifierAction.setMesh(iMesh, oMesh); - fPRTModifierAction.updateUserSetAttributes(thisMObject()); + if (!ruleFileWasChanged) + fPRTModifierAction.updateUserSetAttributes(thisMObject()); MDataHandle randomSeed = data.inputValue(mRandomSeed, &status); fPRTModifierAction.setRandomSeed(randomSeed.asInt()); - if (rulePkgData.asString() != currentRulePkgData.asString()) { + if (ruleFileWasChanged) fPRTModifierAction.updateRuleFiles(thisMObject(), rulePkgData.asString()); - } status = fPRTModifierAction.fillAttributesFromNode(thisMObject()); if (status != MStatus::kSuccess) From 60d23e441d2fb70fcbda443ddae6c9f19d70c440 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 10 Jan 2022 17:45:47 +0100 Subject: [PATCH 200/668] Return early in updateUserSetAttributes, if we force the default value --- src/serlio/modifiers/PRTModifierAction.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 53488d42..5b5ad70e 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -389,6 +389,12 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { const RuleAttribute& ruleAttribute, const PrtAttributeType attrType) { const AttributeMapUPtr defaultAttributeValues = getDefaultAttributeValues( mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, *inPrtMesh, mRandomSeed, mGenerateAttrs); + + if (getAndResetForceDefault(fnNode, fnAttribute)) { + setIsUserSet(fnNode, fnAttribute, false); + return; + } + const MPlug plug(fnNode.object(), fnAttribute.object()); bool isDefaultValue = false; const std::wstring fqAttrName = ruleAttribute.fqName; @@ -447,12 +453,8 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { break; } - if (getAndResetForceDefault(fnNode, fnAttribute)) { - setIsUserSet(fnNode, fnAttribute, false); - } - else if (!isDefaultValue){ + if (!isDefaultValue) setIsUserSet(fnNode, fnAttribute, true); - } }; iterateThroughAttributesAndApply(node, mRuleAttributes, updateUserSetAttribute); From 833994a1b0a5e38642960ec685f76aab77adacf2 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 11 Jan 2022 14:06:00 +0100 Subject: [PATCH 201/668] Reserve correct length for uv arrays in SerializedGeometry This optimization reduces the amount of necessary memory allocations for filling the uv arrays in the serialized geometry. --- src/codec/encoder/MayaEncoder.cpp | 35 +++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index f59de908..6758a62c 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -80,11 +80,22 @@ struct SerializedGeometry { std::vector uvCounts; std::vector uvIndices; - SerializedGeometry(uint32_t numCounts, uint32_t numIndices, uint32_t uvSets) + SerializedGeometry(uint32_t numCounts, uint32_t numIndices, uint32_t uvSets, const std::vector& numUvs, + const std::vector& numUvCounts, const std::vector& numUvIndices) : uvs(uvSets), uvCounts(uvSets), uvIndices(uvSets) { counts.reserve(numCounts); vertexIndices.reserve(numIndices); normalIndices.reserve(numIndices); + + assert(numUvCounts.size() == uvSets); + assert(numUvCounts.size() == uvSets); + assert(numUvIndices.size() == uvSets); + + for (uint32_t uvSet = 0; uvSet < uvSets; uvSet++) { + uvs[uvSet].reserve(numUvs[uvSet]); + uvCounts[uvSet].reserve(numUvCounts[uvSet]); + uvIndices[uvSet].reserve(numUvIndices[uvSet]); + } } bool isEmpty() const { @@ -430,7 +441,27 @@ SerializedGeometry serializeGeometry(const prtx::GeometryPtrVector& geometries, } ++matsIt; } - SerializedGeometry sg(numCounts, numIndices, maxNumUVSets); + + std::vector numUvs(maxNumUVSets); + std::vector numUvCounts(maxNumUVSets); + std::vector numUvIndices(maxNumUVSets); + + for (const auto& geo : geometries) { + const prtx::MeshPtrVector& meshes = geo->getMeshes(); + for (const auto& mesh : meshes) { + const uint32_t numUVSets = mesh->getUVSetsCount(); + + for (uint32_t uvSet = 0; uvSet < numUVSets; uvSet++) { + numUvs[uvSet] += static_cast(mesh->getUVCoords(uvSet).size()); + + const auto& faceUVCounts = mesh->getFaceUVCounts(uvSet); + numUvCounts[uvSet] += static_cast(faceUVCounts.size()); + numUvIndices[uvSet] = std::accumulate(faceUVCounts.begin(), faceUVCounts.end(), numUvIndices[uvSet]); + } + } + } + + SerializedGeometry sg(numCounts, numIndices, maxNumUVSets, numUvs, numUvCounts, numUvIndices); // PASS 2: copy uint32_t vertexIndexBase = 0u; From 429b4a7f214c4e6164523b78008ec5672241f5ad Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 11 Jan 2022 15:56:36 +0100 Subject: [PATCH 202/668] Update prt version to 2.6 --- src/common.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common.cmake b/src/common.cmake index 18276c69..5d7e925d 100644 --- a/src/common.cmake +++ b/src/common.cmake @@ -42,7 +42,7 @@ if (NOT prt_DIR) set(PRT_TC "gcc93") endif () - set(PRT_VERSION "2.5.7799") + set(PRT_VERSION "2.6.8135") set(PRT_CLS "${PRT_OS}-${PRT_TC}-x86_64-rel-opt") set(PRT_URL "https://github.com/esri/cityengine-sdk/releases/download/${PRT_VERSION}/esri_ce_sdk-${PRT_VERSION}-${PRT_CLS}.zip") From 8ff8ad16f2b4a2e4e805021ef7916937953e0d2f Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 13 Jan 2022 11:24:13 +0100 Subject: [PATCH 203/668] Update custom logger to print to std::cout Also removed redundant name field/print --- src/serlio/utils/LogHandler.h | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/serlio/utils/LogHandler.h b/src/serlio/utils/LogHandler.h index d078fa20..fb1d40b5 100644 --- a/src/serlio/utils/LogHandler.h +++ b/src/serlio/utils/LogHandler.h @@ -101,10 +101,8 @@ struct PRTLogger : Logger { class LogHandler : public prt::LogHandler { public: - explicit LogHandler(const std::wstring& name) : mName(name) {} - void handleLogEvent(const wchar_t* msg, prt::LogLevel) override { - std::wcout << L"[" << mName << L"] " << msg << std::endl; + std::cout << prtu::toOSNarrowFromUTF16(msg) << std::endl; } const prt::LogLevel* getLevels(size_t* count) override { @@ -116,13 +114,6 @@ class LogHandler : public prt::LogHandler { *dateTime = true; *level = true; } - - void setName(const std::wstring& n) { - mName = n; - } - -private: - std::wstring mName; }; using LogHandlerPtr = std::unique_ptr; From 66ee1afe312ea8d6839eaeabc20e58d0200ed657 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 13 Jan 2022 12:05:31 +0100 Subject: [PATCH 204/668] Cleanup: remove unused streamlogger --- src/serlio/utils/LogHandler.h | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/src/serlio/utils/LogHandler.h b/src/serlio/utils/LogHandler.h index fb1d40b5..9d6179a2 100644 --- a/src/serlio/utils/LogHandler.h +++ b/src/serlio/utils/LogHandler.h @@ -35,38 +35,6 @@ namespace logging { struct Logger {}; - -const std::string LEVELS[] = {"trace", "debug", "info", "warning", "error", "fatal"}; -const std::wstring WLEVELS[] = {L"trace", L"debug", L"info", L"warning", L"error", L"fatal"}; - -// log to std streams -template -struct StreamLogger : Logger { - explicit StreamLogger(std::wostream& out = std::wcout) : Logger(), mOut(out) { - mOut << prefix(); - } - virtual ~StreamLogger() { - mOut << std::endl; - } - StreamLogger& operator<<(std::wostream& (*x)(std::wostream&)) { - mOut << x; - return *this; - } - StreamLogger& operator<<(const std::string& x) { - std::copy(x.begin(), x.end(), std::ostream_iterator(mOut)); - return *this; - } - template - StreamLogger& operator<<(const T& x) { - mOut << x; - return *this; - } - static std::wstring prefix() { - return L"[" + WLEVELS[L] + L"] "; - } - std::wostream& mOut; -}; - // log through the prt logger template struct PRTLogger : Logger { From 19e9f94fe36a97924b3ca3d7a8763a51f4bd30c1 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 13 Jan 2022 12:05:58 +0100 Subject: [PATCH 205/668] Cleanup: rename unique ptr to UPtr --- src/serlio/utils/LogHandler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/utils/LogHandler.h b/src/serlio/utils/LogHandler.h index 9d6179a2..0b75dddd 100644 --- a/src/serlio/utils/LogHandler.h +++ b/src/serlio/utils/LogHandler.h @@ -84,7 +84,7 @@ class LogHandler : public prt::LogHandler { } }; -using LogHandlerPtr = std::unique_ptr; +using LogHandlerUPtr = std::unique_ptr; } // namespace logging From 6add12027555e22c876c5bd2058bcc8f53f93912 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 13 Jan 2022 12:09:02 +0100 Subject: [PATCH 206/668] Use custom Loghandler instead of prt::ConsoleLogHandler --- src/serlio/PRTContext.cpp | 10 +++------- src/serlio/PRTContext.h | 3 ++- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/serlio/PRTContext.cpp b/src/serlio/PRTContext.cpp index be96c508..0a66e479 100644 --- a/src/serlio/PRTContext.cpp +++ b/src/serlio/PRTContext.cpp @@ -19,8 +19,6 @@ #include "PRTContext.h" -#include "utils/LogHandler.h" - #include namespace { @@ -48,8 +46,8 @@ PRTContext& PRTContext::get() { PRTContext::PRTContext(const std::vector& addExtDirs) : mPluginRootPath(prtu::getPluginRoot()) { if (ENABLE_LOG_CONSOLE) { - theLogHandler = prt::ConsoleLogHandler::create(prt::LogHandler::ALL, prt::LogHandler::ALL_COUNT); - prt::addLogHandler(theLogHandler); + theLogHandler = std::make_unique(); + prt::addLogHandler(theLogHandler.get()); } if (ENABLE_LOG_FILE) { @@ -98,9 +96,7 @@ PRTContext::~PRTContext() { thePRT.reset(); if (ENABLE_LOG_CONSOLE && (theLogHandler != nullptr)) { - prt::removeLogHandler(theLogHandler); - theLogHandler->destroy(); - theLogHandler = nullptr; + prt::removeLogHandler(theLogHandler.get()); } if (ENABLE_LOG_FILE && (theFileLogHandler != nullptr)) { diff --git a/src/serlio/PRTContext.h b/src/serlio/PRTContext.h index 80c815e8..f408aa32 100644 --- a/src/serlio/PRTContext.h +++ b/src/serlio/PRTContext.h @@ -21,6 +21,7 @@ #include "serlioPlugin.h" +#include "utils/LogHandler.h" #include "utils/ResolveMapCache.h" #include "utils/Utilities.h" @@ -47,7 +48,7 @@ struct SRL_TEST_EXPORTS_API PRTContext final { const std::filesystem::path mPluginRootPath; // the path where serlio dso resides ObjectUPtr thePRT; CacheObjectUPtr theCache; - prt::ConsoleLogHandler* theLogHandler = nullptr; + logging::LogHandlerUPtr theLogHandler; prt::FileLogHandler* theFileLogHandler = nullptr; ResolveMapCacheUPtr mResolveMapCache; }; From 735f7c053eaa3be32531000ba4ac1c1a27281ab0 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 13 Jan 2022 13:49:08 +0100 Subject: [PATCH 207/668] Cleanup: use parenthesis for checking equality --- src/serlio/modifiers/PRTModifierAction.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 041c3ef6..0c8eb645 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -352,7 +352,7 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { bool boolVal; MCHECK(plug.getValue(boolVal)); - isDefaultValue = defBoolVal == boolVal; + isDefaultValue = (defBoolVal == boolVal); break; } case PrtAttributeType::FLOAT: { @@ -360,7 +360,7 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { double doubleVal; MCHECK(plug.getValue(doubleVal)); - isDefaultValue = defDoubleVal == doubleVal; + isDefaultValue = (defDoubleVal == doubleVal); break; } case PrtAttributeType::COLOR: { @@ -373,7 +373,7 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { prtu::Color col; MCHECK(fRGB.getData3Float(col[0], col[1], col[2])); - isDefaultValue = defCol == col; + isDefaultValue = (defCol == col); break; } case PrtAttributeType::STRING: { @@ -382,7 +382,7 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { MString stringVal; MCHECK(plug.getValue(stringVal)); - isDefaultValue = std::wcscmp(stringVal.asWChar(), defStringVal) == 0; + isDefaultValue = (std::wcscmp(stringVal.asWChar(), defStringVal) == 0); break; } case PrtAttributeType::ENUM: { @@ -393,7 +393,7 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { MCHECK(eAttr.getDefault(defEnumVal)); MCHECK(plug.getValue(enumVal)); - isDefaultValue = defEnumVal == enumVal; + isDefaultValue = (defEnumVal == enumVal); break; } @@ -428,7 +428,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { bool boolVal; MCHECK(plug.getValue(boolVal)); - const bool isDefaultValue = defBoolVal == boolVal; + const bool isDefaultValue = (defBoolVal == boolVal); if (getIsUserSet(fnNode, fnAttribute)) { plug.setBool(boolVal); @@ -445,7 +445,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { double doubleVal; MCHECK(plug.getValue(doubleVal)); - const bool isDefaultValue = defDoubleVal == doubleVal; + const bool isDefaultValue = (defDoubleVal == doubleVal); if (getIsUserSet(fnNode, fnAttribute)) { plug.setDouble(doubleVal); @@ -473,7 +473,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { MObject defaultColorObj = fdefaultColor.create(MFnNumericData::Type::k3Float); fdefaultColor.setData3Float(defaultColor[0], defaultColor[1], defaultColor[2]); - const bool isDefaultValue = std::wcscmp(colStr.c_str(), defColStr) == 0; + const bool isDefaultValue = (std::wcscmp(colStr.c_str(), defColStr) == 0); if (getIsUserSet(fnNode, fnAttribute)) { plug.setMObject(rgb); @@ -491,7 +491,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { MString stringVal; MCHECK(plug.getValue(stringVal)); - const bool isDefaultValue = std::wcscmp(stringVal.asWChar(), defStringVal) == 0; + const bool isDefaultValue = (std::wcscmp(stringVal.asWChar(), defStringVal) == 0); if (getIsUserSet(fnNode, fnAttribute)) { plug.setString(stringVal); @@ -511,7 +511,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { MCHECK(eAttr.getDefault(defEnumVal)); MCHECK(plug.getValue(enumVal)); - const bool isDefaultValue = defEnumVal == enumVal; + const bool isDefaultValue = (defEnumVal == enumVal); if (getIsUserSet(fnNode, fnAttribute)) { plug.setShort(enumVal); } From d1fc216bb400016e790653a3f9f408b0b0d11c4e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 13 Jan 2022 13:52:44 +0100 Subject: [PATCH 208/668] Cleanup: use parentheses for equality check --- src/serlio/modifiers/PRTModifierNode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierNode.cpp b/src/serlio/modifiers/PRTModifierNode.cpp index 568d022a..98203f4f 100644 --- a/src/serlio/modifiers/PRTModifierNode.cpp +++ b/src/serlio/modifiers/PRTModifierNode.cpp @@ -94,7 +94,7 @@ MStatus PRTModifierNode::compute(const MPlug& plug, MDataBlock& data) { MDataHandle currentRulePkgData = data.inputValue(currentRulePkg, &status); MCheckStatus(status, "ERROR getting currentRulePkg"); - const bool ruleFileWasChanged = rulePkgData.asString() != currentRulePkgData.asString(); + const bool ruleFileWasChanged = (rulePkgData.asString() != currentRulePkgData.asString()); // Copy the inMesh to the outMesh, so you can // perform operations directly on outMesh From ce5010baec54e283c63388ef20020fe09fd80375 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 13 Jan 2022 13:54:58 +0100 Subject: [PATCH 209/668] Cleanup: use correct variable type --- src/serlio/modifiers/PRTModifierAction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 5b5ad70e..3aeb7148 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -273,7 +273,7 @@ short getDefaultEnumValue(const AttributeMapUPtr& defaultAttributeValues, const const auto defBoolVal = defaultAttributeValues->getBool(fqAttrName.c_str()); for (short currIdx = minVal; currIdx <= maxVal; currIdx++) { - const int currBool = eAttr.fieldName(currIdx).asInt() != 0; + const bool currBool = (eAttr.fieldName(currIdx).asInt() != 0); if (currBool == defBoolVal) return currIdx; } From 3be1c5bb6f69a79bff45d7c3641464b45d380ee9 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 13 Jan 2022 14:09:39 +0100 Subject: [PATCH 210/668] Cleanup: use const ref of object instead of const ref of unique_ptr --- src/serlio/modifiers/PRTModifierAction.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 3aeb7148..be47330d 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -61,7 +61,7 @@ const AttributeMapUPtr AttributeMapUPtr getDefaultAttributeValues(const std::wstring& ruleFile, const std::wstring& startRule, const prt::ResolveMap& resolveMap, prt::CacheObject& cache, - const PRTMesh& prtMesh, const int32_t seed, const AttributeMapUPtr& attributeMap) { + const PRTMesh& prtMesh, const int32_t seed, const prt::AttributeMap& attributeMap) { AttributeMapBuilderUPtr mayaCallbacksAttributeBuilder(prt::AttributeMapBuilder::create()); MayaCallbacks mayaCallbacks(MObject::kNullObj, MObject::kNullObj, mayaCallbacksAttributeBuilder); @@ -70,7 +70,7 @@ AttributeMapUPtr getDefaultAttributeValues(const std::wstring& ruleFile, const s isb->setGeometry(prtMesh.vertexCoords(), prtMesh.vcCount(), prtMesh.indices(), prtMesh.indicesCount(), prtMesh.faceCounts(), prtMesh.faceCountsCount()); - isb->setAttributes(ruleFile.c_str(), startRule.c_str(), seed, L"", attributeMap.get(), &resolveMap); + isb->setAttributes(ruleFile.c_str(), startRule.c_str(), seed, L"", &attributeMap, &resolveMap); const InitialShapeUPtr shape(isb->createInitialShapeAndReset()); const InitialShapeNOPtrVector shapes = {shape.get()}; @@ -238,7 +238,7 @@ MStatus addHiddenBoolParameter(MFnDependencyNode& node, MFnAttribute& tAttr, con return stat; } -short getDefaultEnumValue(const AttributeMapUPtr& defaultAttributeValues, const MFnEnumAttribute& eAttr, +short getDefaultEnumValue(const prt::AttributeMap& defaultAttributeValues, const MFnEnumAttribute& eAttr, const RuleAttribute& ruleAttr) { const std::wstring fqAttrName = ruleAttr.fqName; const prt::AnnotationArgumentType ruleAttrType = ruleAttr.mType; @@ -250,7 +250,7 @@ short getDefaultEnumValue(const AttributeMapUPtr& defaultAttributeValues, const switch (ruleAttrType) { case prt::AAT_STR: { - const wchar_t* defStringVal = defaultAttributeValues->getString(fqAttrName.c_str()); + const wchar_t* defStringVal = defaultAttributeValues.getString(fqAttrName.c_str()); for (short currIdx = minVal; currIdx <= maxVal; currIdx++) { const wchar_t* currString = eAttr.fieldName(currIdx).asWChar(); @@ -260,7 +260,7 @@ short getDefaultEnumValue(const AttributeMapUPtr& defaultAttributeValues, const break; } case prt::AAT_FLOAT: { - const auto defDoubleVal = defaultAttributeValues->getFloat(fqAttrName.c_str()); + const auto defDoubleVal = defaultAttributeValues.getFloat(fqAttrName.c_str()); for (short currIdx = minVal; currIdx <= maxVal; currIdx++) { const double currDouble = eAttr.fieldName(currIdx).asDouble(); @@ -270,7 +270,7 @@ short getDefaultEnumValue(const AttributeMapUPtr& defaultAttributeValues, const break; } case prt::AAT_BOOL: { - const auto defBoolVal = defaultAttributeValues->getBool(fqAttrName.c_str()); + const auto defBoolVal = defaultAttributeValues.getBool(fqAttrName.c_str()); for (short currIdx = minVal; currIdx <= maxVal; currIdx++) { const bool currBool = (eAttr.fieldName(currIdx).asInt() != 0); @@ -388,7 +388,7 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { const auto updateUserSetAttribute = [this](const MFnDependencyNode& fnNode, const MFnAttribute& fnAttribute, const RuleAttribute& ruleAttribute, const PrtAttributeType attrType) { const AttributeMapUPtr defaultAttributeValues = getDefaultAttributeValues( - mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, *inPrtMesh, mRandomSeed, mGenerateAttrs); + mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, *inPrtMesh, mRandomSeed, *mGenerateAttrs); if (getAndResetForceDefault(fnNode, fnAttribute)) { setIsUserSet(fnNode, fnAttribute, false); @@ -441,7 +441,7 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { case PrtAttributeType::ENUM: { MFnEnumAttribute eAttr(fnAttribute.object()); - const short defEnumVal = getDefaultEnumValue(defaultAttributeValues, eAttr, ruleAttribute); + const short defEnumVal = getDefaultEnumValue(*defaultAttributeValues, eAttr, ruleAttribute); short enumVal; MCHECK(plug.getValue(enumVal)); @@ -466,7 +466,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { const auto updateUIFromAttributes = [this](const MFnDependencyNode& fnNode, const MFnAttribute& fnAttribute, const RuleAttribute& ruleAttribute, const PrtAttributeType attrType) { const AttributeMapUPtr defaultAttributeValues = getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, - *inPrtMesh, mRandomSeed, mGenerateAttrs); + *inPrtMesh, mRandomSeed, *mGenerateAttrs); MPlug plug(fnNode.object(), fnAttribute.object()); const std::wstring fqAttrName = ruleAttribute.fqName; @@ -534,7 +534,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { case PrtAttributeType::ENUM: { MFnEnumAttribute eAttr(fnAttribute.object()); - const short defEnumVal = getDefaultEnumValue(defaultAttributeValues, eAttr, ruleAttribute); + const short defEnumVal = getDefaultEnumValue(*defaultAttributeValues, eAttr, ruleAttribute); short enumVal; MCHECK(plug.getValue(enumVal)); @@ -607,7 +607,7 @@ MStatus PRTModifierAction::updateRuleFiles(const MObject& node, const MString& r mStartRule = prtu::detectStartRule(info); mGenerateAttrs = getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, - *inPrtMesh, mRandomSeed, EMPTY_ATTRIBUTES); + *inPrtMesh, mRandomSeed, *EMPTY_ATTRIBUTES); if (DBG) LOG_DBG << "default attrs: " << prtu::objectToXML(mGenerateAttrs); From a0210716f546a78d79d9f260403c1d3a15d62522 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 14 Jan 2022 13:33:57 +0100 Subject: [PATCH 211/668] Cleanup: use helper function for getting initial default index for enums --- src/serlio/modifiers/PRTModifierAction.cpp | 82 ++++++++++++++-------- 1 file changed, 52 insertions(+), 30 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index be47330d..e7372730 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -284,6 +284,49 @@ short getDefaultEnumValue(const prt::AttributeMap& defaultAttributeValues, const } return 0; } + +struct PRTEnumDefaultValue { + bool mBool = false; + double mDouble = std::numeric_limits::quiet_NaN(); + MString mString = ""; +}; +short getDefaultEnumIdx(const prt::Annotation* annot, const PRTEnumDefaultValue& defaultValue) { + short idx = 0; + for (size_t arg = 0; arg < annot->getNumArguments(); arg++) { + + const wchar_t* key = annot->getArgument(arg)->getKey(); + if (std::wcscmp(key, NULL_KEY) != 0) { + continue; + } + + switch (annot->getArgument(arg)->getType()) { + case prt::AAT_BOOL: { + bool val = annot->getArgument(arg)->getBool(); + if (val == defaultValue.mBool) + return idx; + idx++; + break; + } + case prt::AAT_FLOAT: { + double val = annot->getArgument(arg)->getFloat(); + if (val == defaultValue.mDouble) + return idx; + idx++; + break; + } + case prt::AAT_STR: { + const MString val = annot->getArgument(arg)->getStr(); + if (val == defaultValue.mString) + return idx; + idx++; + break; + } + default: + break; + } + } + return 0; +} } // namespace PRTModifierAction::PRTModifierAction() { @@ -1003,32 +1046,18 @@ MStatus PRTModifierAction::addFloatParameter(MFnDependencyNode& node, MObject& a MStatus PRTModifierAction::addEnumParameter(const prt::Annotation* annot, MFnDependencyNode& node, MObject& attr, const RuleAttribute& ruleAttr, bool defaultValue, PRTModifierEnum& e) { - PRTModifierEnum tmpEnum; - tmpEnum.fill(annot); - - short idx = 0; - for (int i = static_cast(tmpEnum.mBVals.length()); --i >= 0;) { - if ((tmpEnum.mBVals[i] != 0) == defaultValue) { - idx = static_cast(i); - break; - } - } + PRTEnumDefaultValue defaultEnumVal; + defaultEnumVal.mBool = defaultValue; + const short idx = getDefaultEnumIdx(annot, defaultEnumVal); return addEnumParameter(annot, node, attr, ruleAttr, idx, e); } MStatus PRTModifierAction::addEnumParameter(const prt::Annotation* annot, MFnDependencyNode& node, MObject& attr, const RuleAttribute& ruleAttr, double defaultValue, PRTModifierEnum& e) { - PRTModifierEnum tmpEnum; - tmpEnum.fill(annot); - - short idx = 0; - for (int i = static_cast(tmpEnum.mFVals.length()); --i >= 0;) { - if (tmpEnum.mFVals[i] == defaultValue) { - idx = static_cast(i); - break; - } - } + PRTEnumDefaultValue defaultEnumVal; + defaultEnumVal.mDouble = defaultValue; + const short idx = getDefaultEnumIdx(annot, defaultEnumVal); return addEnumParameter(annot, node, attr, ruleAttr, idx, e); } @@ -1036,16 +1065,9 @@ MStatus PRTModifierAction::addEnumParameter(const prt::Annotation* annot, MFnDep MStatus PRTModifierAction::addEnumParameter(const prt::Annotation* annot, MFnDependencyNode& node, MObject& attr, const RuleAttribute& ruleAttr, const MString& defaultValue, PRTModifierEnum& e) { - PRTModifierEnum tmpEnum; - tmpEnum.fill(annot); - - short idx = 0; - for (int i = static_cast(tmpEnum.mSVals.length()); --i >= 0;) { - if (tmpEnum.mSVals[i] == defaultValue) { - idx = static_cast(i); - break; - } - } + PRTEnumDefaultValue defaultEnumVal; + defaultEnumVal.mString = defaultValue; + const short idx = getDefaultEnumIdx(annot, defaultEnumVal); return addEnumParameter(annot, node, attr, ruleAttr, idx, e); } From ba56246b8147a8f7aa2ae5b6af66427f3f27bed3 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 14 Jan 2022 15:41:14 +0100 Subject: [PATCH 212/668] Cleanup: fixed copy-paste typo --- src/codec/encoder/MayaEncoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index 6758a62c..0e79fdc5 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -87,7 +87,7 @@ struct SerializedGeometry { vertexIndices.reserve(numIndices); normalIndices.reserve(numIndices); - assert(numUvCounts.size() == uvSets); + assert(numUvs.size() == uvSets); assert(numUvCounts.size() == uvSets); assert(numUvIndices.size() == uvSets); From d1ec0ce44d6efdca61bae1a696bc4c4e88340b14 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 14 Jan 2022 15:44:17 +0100 Subject: [PATCH 213/668] Cleanup: fix c-lang format --- src/codec/encoder/MayaEncoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index 0e79fdc5..655db442 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -589,7 +589,7 @@ void MayaEncoder::convertGeometry(const prtx::InitialShape& initialShape, const prtx::EncodePreparator::InstanceVector& instances, IMayaCallbacks* cb) { if (instances.empty()) return; - + const bool emitMaterials = getOptions()->getBool(EO_EMIT_MATERIALS); const bool emitReports = getOptions()->getBool(EO_EMIT_REPORTS); From 21b8d67171c023b706752090cf0fb911eee235e9 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Fri, 14 Jan 2022 15:46:40 +0100 Subject: [PATCH 214/668] cleanup: avoid duplicate code when handling the enum attribute types --- src/serlio/modifiers/PRTModifierAction.cpp | 54 ++++++---------------- src/serlio/modifiers/PRTModifierAction.h | 6 --- 2 files changed, 14 insertions(+), 46 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 46c66532..13f2bfc6 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -36,6 +36,7 @@ #include "maya/MFnTypedAttribute.h" #include +#include namespace { @@ -285,11 +286,8 @@ short getDefaultEnumValue(const prt::AttributeMap& defaultAttributeValues, const return 0; } -struct PRTEnumDefaultValue { - bool mBool = false; - double mDouble = std::numeric_limits::quiet_NaN(); - MString mString = ""; -}; +using PRTEnumDefaultValue = std::variant; + short getDefaultEnumIdx(const prt::Annotation* annot, const PRTEnumDefaultValue& defaultValue) { short idx = 0; for (size_t arg = 0; arg < annot->getNumArguments(); arg++) { @@ -302,21 +300,21 @@ short getDefaultEnumIdx(const prt::Annotation* annot, const PRTEnumDefaultValue& switch (annot->getArgument(arg)->getType()) { case prt::AAT_BOOL: { bool val = annot->getArgument(arg)->getBool(); - if (val == defaultValue.mBool) + if (val == std::get(defaultValue)) return idx; idx++; break; } case prt::AAT_FLOAT: { double val = annot->getArgument(arg)->getFloat(); - if (val == defaultValue.mDouble) + if (val == std::get(defaultValue)) return idx; idx++; break; } case prt::AAT_STR: { const MString val = annot->getArgument(arg)->getStr(); - if (val == defaultValue.mString) + if (val == std::get(defaultValue)) return idx; idx++; break; @@ -765,7 +763,8 @@ MStatus PRTModifierAction::createNodeAttributes(const MObject& nodeObj, const pr const bool value = mGenerateAttrs->getBool(fqName.c_str()); if (attrTrait.first == AttributeTrait::ENUM) { mEnums.emplace_front(); - MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, value, mEnums.front())); + const short enumIndex = getDefaultEnumIdx(attrTrait.second.mAnnot, value); + MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, enumIndex, mEnums.front())); } else { MCHECK(addBoolParameter(node, attr, p, value)); @@ -778,7 +777,8 @@ MStatus PRTModifierAction::createNodeAttributes(const MObject& nodeObj, const pr switch (attrTrait.first) { case AttributeTrait::ENUM: { mEnums.emplace_front(); - MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, value, mEnums.front())); + const short enumIndex = getDefaultEnumIdx(attrTrait.second.mAnnot, value); + MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, enumIndex, mEnums.front())); break; } case AttributeTrait::RANGE: { @@ -826,10 +826,12 @@ MStatus PRTModifierAction::createNodeAttributes(const MObject& nodeObj, const pr const MString mvalue(value.c_str()); switch (attrTrait.first) { - case AttributeTrait::ENUM: + case AttributeTrait::ENUM: { mEnums.emplace_front(); - MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, mvalue, mEnums.front())); + const short enumIndex = getDefaultEnumIdx(attrTrait.second.mAnnot, mvalue); + MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, enumIndex, mEnums.front())); break; + } case AttributeTrait::FILE: case AttributeTrait::DIR: MCHECK(addFileParameter(node, attr, p, mvalue, attrTrait.second.mString)); @@ -1044,34 +1046,6 @@ MStatus PRTModifierAction::addFloatParameter(MFnDependencyNode& node, MObject& a return stat; } -MStatus PRTModifierAction::addEnumParameter(const prt::Annotation* annot, MFnDependencyNode& node, MObject& attr, - const RuleAttribute& ruleAttr, bool defaultValue, PRTModifierEnum& e) { - PRTEnumDefaultValue defaultEnumVal; - defaultEnumVal.mBool = defaultValue; - const short idx = getDefaultEnumIdx(annot, defaultEnumVal); - - return addEnumParameter(annot, node, attr, ruleAttr, idx, e); -} - -MStatus PRTModifierAction::addEnumParameter(const prt::Annotation* annot, MFnDependencyNode& node, MObject& attr, - const RuleAttribute& ruleAttr, double defaultValue, PRTModifierEnum& e) { - PRTEnumDefaultValue defaultEnumVal; - defaultEnumVal.mDouble = defaultValue; - const short idx = getDefaultEnumIdx(annot, defaultEnumVal); - - return addEnumParameter(annot, node, attr, ruleAttr, idx, e); -} - -MStatus PRTModifierAction::addEnumParameter(const prt::Annotation* annot, MFnDependencyNode& node, MObject& attr, - const RuleAttribute& ruleAttr, const MString& defaultValue, - PRTModifierEnum& e) { - PRTEnumDefaultValue defaultEnumVal; - defaultEnumVal.mString = defaultValue; - const short idx = getDefaultEnumIdx(annot, defaultEnumVal); - - return addEnumParameter(annot, node, attr, ruleAttr, idx, e); -} - MStatus PRTModifierAction::addEnumParameter(const prt::Annotation* annot, MFnDependencyNode& node, MObject& attr, const RuleAttribute& ruleAttr, short defaultValue, PRTModifierEnum& e) { MStatus stat; diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index f639e1c8..ab164a67 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -118,12 +118,6 @@ class PRTModifierAction : public polyModifierFty { const MString& defaultValue); static MStatus addFileParameter(MFnDependencyNode& node, MObject& attr, const RuleAttribute& name, const MString& defaultValue, const std::wstring& ext); - static MStatus addEnumParameter(const prt::Annotation* annot, MFnDependencyNode& node, MObject& attr, - const RuleAttribute& name, bool defaultValue, PRTModifierEnum& e); - static MStatus addEnumParameter(const prt::Annotation* annot, MFnDependencyNode& node, MObject& attr, - const RuleAttribute& name, double defaultValue, PRTModifierEnum& e); - static MStatus addEnumParameter(const prt::Annotation* annot, MFnDependencyNode& node, MObject& attr, - const RuleAttribute& name, const MString& defaultValue, PRTModifierEnum& e); static MStatus addEnumParameter(const prt::Annotation* annot, MFnDependencyNode& node, MObject& attr, const RuleAttribute& name, short defaultValue, PRTModifierEnum& e); static MStatus addColorParameter(MFnDependencyNode& node, MObject& attr, const RuleAttribute& name, From 50fb09805cb411810ec94e0b700fac36b4170bcf Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Fri, 14 Jan 2022 16:25:39 +0100 Subject: [PATCH 215/668] run clang-format --- src/serlio/modifiers/PRTModifierAction.cpp | 23 ++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 13f2bfc6..065add67 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -62,7 +62,8 @@ const AttributeMapUPtr AttributeMapUPtr getDefaultAttributeValues(const std::wstring& ruleFile, const std::wstring& startRule, const prt::ResolveMap& resolveMap, prt::CacheObject& cache, - const PRTMesh& prtMesh, const int32_t seed, const prt::AttributeMap& attributeMap) { + const PRTMesh& prtMesh, const int32_t seed, + const prt::AttributeMap& attributeMap) { AttributeMapBuilderUPtr mayaCallbacksAttributeBuilder(prt::AttributeMapBuilder::create()); MayaCallbacks mayaCallbacks(MObject::kNullObj, MObject::kNullObj, mayaCallbacksAttributeBuilder); @@ -345,7 +346,8 @@ MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { AttributeMapBuilderSPtr aBuilder(prt::AttributeMapBuilder::create(), PRTDestroyer()); const auto fillAttributeFromNode = [this, aBuilder](const MFnDependencyNode& fnNode, - const MFnAttribute& fnAttribute, const RuleAttribute& ruleAttribute, + const MFnAttribute& fnAttribute, + const RuleAttribute& ruleAttribute, const PrtAttributeType attrType) mutable { MPlug plug(fnNode.object(), fnAttribute.object()); const std::wstring fqAttrName = ruleAttribute.fqName; @@ -407,8 +409,7 @@ MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { aBuilder->setBool(fqAttrName.c_str(), eAttr.fieldName(enumVal).asInt() != 0); break; default: - LOG_ERR << "Cannot handle attribute type " << ruleAttrType << " for attr " - << fqAttrName; + LOG_ERR << "Cannot handle attribute type " << ruleAttrType << " for attr " << fqAttrName; } } break; @@ -428,8 +429,9 @@ MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { const auto updateUserSetAttribute = [this](const MFnDependencyNode& fnNode, const MFnAttribute& fnAttribute, const RuleAttribute& ruleAttribute, const PrtAttributeType attrType) { - const AttributeMapUPtr defaultAttributeValues = getDefaultAttributeValues( - mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, *inPrtMesh, mRandomSeed, *mGenerateAttrs); + const AttributeMapUPtr defaultAttributeValues = + getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, + *inPrtMesh, mRandomSeed, *mGenerateAttrs); if (getAndResetForceDefault(fnNode, fnAttribute)) { setIsUserSet(fnNode, fnAttribute, false); @@ -506,7 +508,8 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { MStatus PRTModifierAction::updateUI(const MObject& node) { const auto updateUIFromAttributes = [this](const MFnDependencyNode& fnNode, const MFnAttribute& fnAttribute, const RuleAttribute& ruleAttribute, const PrtAttributeType attrType) { - const AttributeMapUPtr defaultAttributeValues = getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, + const AttributeMapUPtr defaultAttributeValues = + getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, *inPrtMesh, mRandomSeed, *mGenerateAttrs); MPlug plug(fnNode.object(), fnAttribute.object()); const std::wstring fqAttrName = ruleAttribute.fqName; @@ -520,7 +523,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { const bool isDefaultValue = (defBoolVal == boolVal); if (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue) { - plug.setBool(defBoolVal); + plug.setBool(defBoolVal); } break; } @@ -555,7 +558,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { const bool isDefaultValue = (std::wcscmp(colStr.c_str(), defColStr) == 0); if (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue) { - plug.setMObject(defaultColorObj); + plug.setMObject(defaultColorObj); } break; } @@ -568,7 +571,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { const bool isDefaultValue = (std::wcscmp(stringVal.asWChar(), defStringVal) == 0); if (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue) { - plug.setString(defStringVal); + plug.setString(defStringVal); } break; } From 44b4aac34b7ef6c94a882805d2921a367feabe0c Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 17 Jan 2022 09:13:46 +0100 Subject: [PATCH 216/668] Cleanup: move serialized geometry counting loop into constructor --- src/codec/encoder/MayaEncoder.cpp | 154 +++++++++++++++--------------- 1 file changed, 79 insertions(+), 75 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index 655db442..26db45aa 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -69,40 +69,6 @@ constexpr bool DBG = false; constexpr const wchar_t* ENC_NAME = L"Autodesk(tm) Maya(tm) Encoder"; constexpr const wchar_t* ENC_DESCRIPTION = L"Encodes geometry into the Maya format."; -struct SerializedGeometry { - prtx::DoubleVector coords; - prtx::DoubleVector normals; - std::vector counts; - std::vector vertexIndices; - std::vector normalIndices; - - std::vector uvs; - std::vector uvCounts; - std::vector uvIndices; - - SerializedGeometry(uint32_t numCounts, uint32_t numIndices, uint32_t uvSets, const std::vector& numUvs, - const std::vector& numUvCounts, const std::vector& numUvIndices) - : uvs(uvSets), uvCounts(uvSets), uvIndices(uvSets) { - counts.reserve(numCounts); - vertexIndices.reserve(numIndices); - normalIndices.reserve(numIndices); - - assert(numUvs.size() == uvSets); - assert(numUvCounts.size() == uvSets); - assert(numUvIndices.size() == uvSets); - - for (uint32_t uvSet = 0; uvSet < uvSets; uvSet++) { - uvs[uvSet].reserve(numUvs[uvSet]); - uvCounts[uvSet].reserve(numUvCounts[uvSet]); - uvIndices[uvSet].reserve(numUvIndices[uvSet]); - } - } - - bool isEmpty() const { - return coords.empty() || counts.empty() || vertexIndices.empty(); - } -}; - const prtx::EncodePreparator::PreparationFlags PREP_FLAGS = prtx::EncodePreparator::PreparationFlags() .instancing(false) @@ -411,59 +377,97 @@ uint32_t scanValidTextures(const prtx::MaterialPtr& mat) { return highestUVSet + 1; } -const prtx::DoubleVector EMPTY_UVS; -const prtx::IndexVector EMPTY_IDX; - -} // namespace +struct SerializedGeometry { + prtx::DoubleVector coords; + prtx::DoubleVector normals; + std::vector counts; + std::vector vertexIndices; + std::vector normalIndices; -namespace detail { + std::vector uvs; + std::vector uvCounts; + std::vector uvIndices; -SerializedGeometry serializeGeometry(const prtx::GeometryPtrVector& geometries, - const std::vector& materials) { - // PASS 1: scan - uint32_t numCounts = 0; - uint32_t numIndices = 0; - uint32_t maxNumUVSets = 0; - auto matsIt = materials.cbegin(); - for (const auto& geo : geometries) { - const prtx::MeshPtrVector& meshes = geo->getMeshes(); - const prtx::MaterialPtrVector& mats = *matsIt; - auto matIt = mats.cbegin(); - for (const auto& mesh : meshes) { - numCounts += mesh->getFaceCount(); - const auto& vtxCnts = mesh->getFaceVertexCounts(); - numIndices = std::accumulate(vtxCnts.begin(), vtxCnts.end(), numIndices); - - const prtx::MaterialPtr& mat = *matIt; - const uint32_t requiredUVSetsByMaterial = scanValidTextures(mat); - maxNumUVSets = std::max(maxNumUVSets, std::max(mesh->getUVSetsCount(), requiredUVSetsByMaterial)); - ++matIt; + SerializedGeometry(const prtx::GeometryPtrVector& geometries, + const std::vector& materials) { + // Allocate memory for geometry + uint32_t numCounts = 0; + uint32_t numIndices = 0; + uint32_t maxNumUVSets = 0; + + auto matsIt = materials.cbegin(); + for (const auto& geo : geometries) { + const prtx::MeshPtrVector& meshes = geo->getMeshes(); + const prtx::MaterialPtrVector& mats = *matsIt; + auto matIt = mats.cbegin(); + for (const auto& mesh : meshes) { + numCounts += mesh->getFaceCount(); + const auto& vtxCnts = mesh->getFaceVertexCounts(); + numIndices = std::accumulate(vtxCnts.begin(), vtxCnts.end(), numIndices); + + const prtx::MaterialPtr& mat = *matIt; + const uint32_t requiredUVSetsByMaterial = scanValidTextures(mat); + maxNumUVSets = std::max(maxNumUVSets, std::max(mesh->getUVSetsCount(), requiredUVSetsByMaterial)); + ++matIt; + } + ++matsIt; } - ++matsIt; - } - std::vector numUvs(maxNumUVSets); - std::vector numUvCounts(maxNumUVSets); - std::vector numUvIndices(maxNumUVSets); + counts.reserve(numCounts); + vertexIndices.reserve(numIndices); + normalIndices.reserve(numIndices); - for (const auto& geo : geometries) { - const prtx::MeshPtrVector& meshes = geo->getMeshes(); - for (const auto& mesh : meshes) { - const uint32_t numUVSets = mesh->getUVSetsCount(); + // Allocate memory for uvs + std::vector numUvs(maxNumUVSets); + std::vector numUvCounts(maxNumUVSets); + std::vector numUvIndices(maxNumUVSets); + + for (const auto& geo : geometries) { + const prtx::MeshPtrVector& meshes = geo->getMeshes(); + for (const auto& mesh : meshes) { + const uint32_t numUVSets = mesh->getUVSetsCount(); - for (uint32_t uvSet = 0; uvSet < numUVSets; uvSet++) { - numUvs[uvSet] += static_cast(mesh->getUVCoords(uvSet).size()); + for (uint32_t uvSet = 0; uvSet < numUVSets; uvSet++) { + numUvs[uvSet] += static_cast(mesh->getUVCoords(uvSet).size()); - const auto& faceUVCounts = mesh->getFaceUVCounts(uvSet); - numUvCounts[uvSet] += static_cast(faceUVCounts.size()); - numUvIndices[uvSet] = std::accumulate(faceUVCounts.begin(), faceUVCounts.end(), numUvIndices[uvSet]); + const auto& faceUVCounts = mesh->getFaceUVCounts(uvSet); + numUvCounts[uvSet] += static_cast(faceUVCounts.size()); + numUvIndices[uvSet] = + std::accumulate(faceUVCounts.begin(), faceUVCounts.end(), numUvIndices[uvSet]); + } } } + + std::vector uvs(maxNumUVSets); + std::vector uvCounts(maxNumUVSets); + std::vector uvIndices(maxNumUVSets); + + for (uint32_t uvSet = 0; uvSet < maxNumUVSets; uvSet++) { + uvs[uvSet].reserve(numUvs[uvSet]); + uvCounts[uvSet].reserve(numUvCounts[uvSet]); + uvIndices[uvSet].reserve(numUvIndices[uvSet]); + } } - SerializedGeometry sg(numCounts, numIndices, maxNumUVSets, numUvs, numUvCounts, numUvIndices); + bool isEmpty() const { + return coords.empty() || counts.empty() || vertexIndices.empty(); + } +}; + +const prtx::DoubleVector EMPTY_UVS; +const prtx::IndexVector EMPTY_IDX; + +} // namespace + +namespace detail { + +SerializedGeometry serializeGeometry(const prtx::GeometryPtrVector& geometries, + const std::vector& materials) { + // Initialize serialized geometry + SerializedGeometry sg(geometries, materials); + const uint32_t maxNumUVSets = static_cast(sg.uvs.size()); - // PASS 2: copy + // Copy data into serialized geometry uint32_t vertexIndexBase = 0u; uint32_t normalIndexBase = 0u; std::vector uvIndexBases(maxNumUVSets, 0u); From fb19734c33a755d0cade3d904b0458957ee840b9 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 17 Jan 2022 12:16:24 +0100 Subject: [PATCH 217/668] fix regression: local variables are shadowing struct members --- src/codec/encoder/MayaEncoder.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index 26db45aa..f6debb7e 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -438,9 +438,9 @@ struct SerializedGeometry { } } - std::vector uvs(maxNumUVSets); - std::vector uvCounts(maxNumUVSets); - std::vector uvIndices(maxNumUVSets); + uvs.resize(maxNumUVSets); + uvCounts.resize(maxNumUVSets); + uvIndices.resize(maxNumUVSets); for (uint32_t uvSet = 0; uvSet < maxNumUVSets; uvSet++) { uvs[uvSet].reserve(numUvs[uvSet]); From 6fe6ac3da242c89585c18f2cfd57b135f9fdd438 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 17 Jan 2022 12:22:43 +0100 Subject: [PATCH 218/668] turn SerializedGeometry into class (rule of thumb: member values are not independent of each other -> class) --- src/codec/encoder/MayaEncoder.cpp | 78 ++++++++++++++++--------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index f6debb7e..848ad659 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -377,17 +377,8 @@ uint32_t scanValidTextures(const prtx::MaterialPtr& mat) { return highestUVSet + 1; } -struct SerializedGeometry { - prtx::DoubleVector coords; - prtx::DoubleVector normals; - std::vector counts; - std::vector vertexIndices; - std::vector normalIndices; - - std::vector uvs; - std::vector uvCounts; - std::vector uvIndices; - +class SerializedGeometry { +public: SerializedGeometry(const prtx::GeometryPtrVector& geometries, const std::vector& materials) { // Allocate memory for geometry @@ -413,9 +404,9 @@ struct SerializedGeometry { ++matsIt; } - counts.reserve(numCounts); - vertexIndices.reserve(numIndices); - normalIndices.reserve(numIndices); + mCounts.reserve(numCounts); + mVertexIndices.reserve(numIndices); + mNormalIndices.reserve(numIndices); // Allocate memory for uvs std::vector numUvs(maxNumUVSets); @@ -438,20 +429,31 @@ struct SerializedGeometry { } } - uvs.resize(maxNumUVSets); - uvCounts.resize(maxNumUVSets); - uvIndices.resize(maxNumUVSets); + mUvs.resize(maxNumUVSets); + mUvCounts.resize(maxNumUVSets); + mUvIndices.resize(maxNumUVSets); for (uint32_t uvSet = 0; uvSet < maxNumUVSets; uvSet++) { - uvs[uvSet].reserve(numUvs[uvSet]); - uvCounts[uvSet].reserve(numUvCounts[uvSet]); - uvIndices[uvSet].reserve(numUvIndices[uvSet]); + mUvs[uvSet].reserve(numUvs[uvSet]); + mUvCounts[uvSet].reserve(numUvCounts[uvSet]); + mUvIndices[uvSet].reserve(numUvIndices[uvSet]); } } bool isEmpty() const { - return coords.empty() || counts.empty() || vertexIndices.empty(); + return mCoords.empty() || mCounts.empty() || mVertexIndices.empty(); } + +public: + prtx::DoubleVector mCoords; + prtx::DoubleVector mNormals; + std::vector mCounts; + std::vector mVertexIndices; + std::vector mNormalIndices; + + std::vector mUvs; + std::vector mUvCounts; + std::vector mUvIndices; }; const prtx::DoubleVector EMPTY_UVS; @@ -465,7 +467,7 @@ SerializedGeometry serializeGeometry(const prtx::GeometryPtrVector& geometries, const std::vector& materials) { // Initialize serialized geometry SerializedGeometry sg(geometries, materials); - const uint32_t maxNumUVSets = static_cast(sg.uvs.size()); + const uint32_t maxNumUVSets = static_cast(sg.mUvs.size()); // Copy data into serialized geometry uint32_t vertexIndexBase = 0u; @@ -476,11 +478,11 @@ SerializedGeometry serializeGeometry(const prtx::GeometryPtrVector& geometries, for (const auto& mesh : meshes) { // append points const prtx::DoubleVector& verts = mesh->getVertexCoords(); - sg.coords.insert(sg.coords.end(), verts.begin(), verts.end()); + sg.mCoords.insert(sg.mCoords.end(), verts.begin(), verts.end()); // append normals const prtx::DoubleVector& norms = mesh->getVertexNormalsCoords(); - sg.normals.insert(sg.normals.end(), norms.begin(), norms.end()); + sg.mNormals.insert(sg.mNormals.end(), norms.begin(), norms.end()); // append uv sets (uv coords, counts, indices) with special cases: // - if mesh has no uv sets but maxNumUVSets is > 0, insert "0" uv face counts to keep in sync @@ -492,18 +494,18 @@ SerializedGeometry serializeGeometry(const prtx::GeometryPtrVector& geometries, if (DBG) log_debug("-- mesh: numUVSets = %1%") % numUVSets; - for (uint32_t uvSet = 0; uvSet < sg.uvs.size(); uvSet++) { + for (uint32_t uvSet = 0; uvSet < sg.mUvs.size(); uvSet++) { // append texture coordinates const prtx::DoubleVector& uvs = (uvSet < numUVSets) ? mesh->getUVCoords(uvSet) : EMPTY_UVS; const auto& src = uvs.empty() ? uvs0 : uvs; - auto& tgt = sg.uvs[uvSet]; + auto& tgt = sg.mUvs[uvSet]; tgt.insert(tgt.end(), src.begin(), src.end()); // append uv face counts const prtx::IndexVector& faceUVCounts = (uvSet < numUVSets && !uvs.empty()) ? mesh->getFaceUVCounts(uvSet) : faceUVCounts0; assert(faceUVCounts.size() == mesh->getFaceCount()); - auto& tgtCnts = sg.uvCounts[uvSet]; + auto& tgtCnts = sg.mUvCounts[uvSet]; tgtCnts.insert(tgtCnts.end(), faceUVCounts.begin(), faceUVCounts.end()); if (DBG) log_debug(" -- uvset %1%: face counts size = %2%") % uvSet % faceUVCounts.size(); @@ -518,7 +520,7 @@ SerializedGeometry serializeGeometry(const prtx::GeometryPtrVector& geometries, log_debug(" fi %1%: faceUVCnt = %2%, faceVtxCnt = %3%") % fi % faceUVCnt % mesh->getFaceVertexCount(fi); for (uint32_t vi = 0; vi < faceUVCnt; vi++) - sg.uvIndices[uvSet].push_back(uvIndexBases[uvSet] + faceUVIdx[vi]); + sg.mUvIndices[uvSet].push_back(uvIndexBases[uvSet] + faceUVIdx[vi]); } uvIndexBases[uvSet] += static_cast(src.size()) / 2; @@ -527,12 +529,12 @@ SerializedGeometry serializeGeometry(const prtx::GeometryPtrVector& geometries, // append counts and indices for vertices and vertex normals for (uint32_t fi = 0, faceCount = mesh->getFaceCount(); fi < faceCount; ++fi) { const uint32_t vtxCnt = mesh->getFaceVertexCount(fi); - sg.counts.push_back(vtxCnt); + sg.mCounts.push_back(vtxCnt); const uint32_t* vtxIdx = mesh->getFaceVertexIndices(fi); const uint32_t* nrmIdx = mesh->getFaceVertexNormalIndices(fi); for (uint32_t vi = 0; vi < vtxCnt; vi++) { - sg.vertexIndices.push_back(vertexIndexBase + vtxIdx[vi]); - sg.normalIndices.push_back(normalIndexBase + nrmIdx[vi]); + sg.mVertexIndices.push_back(vertexIndexBase + vtxIdx[vi]); + sg.mNormalIndices.push_back(normalIndexBase + nrmIdx[vi]); } } @@ -667,16 +669,16 @@ void MayaEncoder::convertGeometry(const prtx::InitialShape& initialShape, assert(reportAttrMaps.v.empty() || reportAttrMaps.v.size() == faceRanges.size() - 1); assert(shapeIDs.size() == faceRanges.size() - 1); - auto puvs = toPtrVec(sg.uvs); - auto puvCounts = toPtrVec(sg.uvCounts); - auto puvIndices = toPtrVec(sg.uvIndices); + auto puvs = toPtrVec(sg.mUvs); + auto puvCounts = toPtrVec(sg.mUvCounts); + auto puvIndices = toPtrVec(sg.mUvIndices); - cb->addMesh(initialShape.getName(), sg.coords.data(), sg.coords.size(), sg.normals.data(), sg.normals.size(), - sg.counts.data(), sg.counts.size(), sg.vertexIndices.data(), sg.vertexIndices.size(), - sg.normalIndices.data(), sg.normalIndices.size(), + cb->addMesh(initialShape.getName(), sg.mCoords.data(), sg.mCoords.size(), sg.mNormals.data(), sg.mNormals.size(), + sg.mCounts.data(), sg.mCounts.size(), sg.mVertexIndices.data(), sg.mVertexIndices.size(), + sg.mNormalIndices.data(), sg.mNormalIndices.size(), puvs.first.data(), puvs.second.data(), puvCounts.first.data(), puvCounts.second.data(), - puvIndices.first.data(), puvIndices.second.data(), sg.uvs.size(), + puvIndices.first.data(), puvIndices.second.data(), sg.mUvs.size(), faceRanges.data(), faceRanges.size(), matAttrMaps.v.empty() ? nullptr : matAttrMaps.v.data(), reportAttrMaps.v.empty() ? nullptr : reportAttrMaps.v.data(), shapeIDs.data()); From a4d4e54cd6e065794e2d749dd03ea7d6feef49fa Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 17 Jan 2022 12:32:03 +0100 Subject: [PATCH 219/668] cleanup: move serializeGeometry function into class for consistency --- src/codec/encoder/MayaEncoder.cpp | 176 +++++++++++++++--------------- 1 file changed, 86 insertions(+), 90 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index 848ad659..a5dde597 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -377,6 +377,9 @@ uint32_t scanValidTextures(const prtx::MaterialPtr& mat) { return highestUVSet + 1; } +const prtx::DoubleVector EMPTY_UVS; +const prtx::IndexVector EMPTY_IDX; + class SerializedGeometry { public: SerializedGeometry(const prtx::GeometryPtrVector& geometries, @@ -438,12 +441,94 @@ class SerializedGeometry { mUvCounts[uvSet].reserve(numUvCounts[uvSet]); mUvIndices[uvSet].reserve(numUvIndices[uvSet]); } + + serialize(geometries, materials); } bool isEmpty() const { return mCoords.empty() || mCounts.empty() || mVertexIndices.empty(); } +private: + void serialize(const prtx::GeometryPtrVector& geometries, + const std::vector& materials) { + const uint32_t maxNumUVSets = static_cast(mUvs.size()); + + // Copy data into serialized geometry + uint32_t vertexIndexBase = 0u; + uint32_t normalIndexBase = 0u; + std::vector uvIndexBases(maxNumUVSets, 0u); + for (const auto& geo : geometries) { + const prtx::MeshPtrVector& meshes = geo->getMeshes(); + for (const auto& mesh : meshes) { + // append points + const prtx::DoubleVector& verts = mesh->getVertexCoords(); + mCoords.insert(mCoords.end(), verts.begin(), verts.end()); + + // append normals + const prtx::DoubleVector& norms = mesh->getVertexNormalsCoords(); + mNormals.insert(mNormals.end(), norms.begin(), norms.end()); + + // append uv sets (uv coords, counts, indices) with special cases: + // - if mesh has no uv sets but maxNumUVSets is > 0, insert "0" uv face counts to keep in sync + // - if mesh has less uv sets than maxNumUVSets, copy uv set 0 to the missing higher sets + const uint32_t numUVSets = mesh->getUVSetsCount(); + const prtx::DoubleVector& uvs0 = (numUVSets > 0) ? mesh->getUVCoords(0) : EMPTY_UVS; + const prtx::IndexVector faceUVCounts0 = + (numUVSets > 0) ? mesh->getFaceUVCounts(0) : prtx::IndexVector(mesh->getFaceCount(), 0); + if (DBG) + log_debug("-- mesh: numUVSets = %1%") % numUVSets; + + for (uint32_t uvSet = 0; uvSet < mUvs.size(); uvSet++) { + // append texture coordinates + const prtx::DoubleVector& uvs = (uvSet < numUVSets) ? mesh->getUVCoords(uvSet) : EMPTY_UVS; + const auto& src = uvs.empty() ? uvs0 : uvs; + auto& tgt = mUvs[uvSet]; + tgt.insert(tgt.end(), src.begin(), src.end()); + + // append uv face counts + const prtx::IndexVector& faceUVCounts = + (uvSet < numUVSets && !uvs.empty()) ? mesh->getFaceUVCounts(uvSet) : faceUVCounts0; + assert(faceUVCounts.size() == mesh->getFaceCount()); + auto& tgtCnts = mUvCounts[uvSet]; + tgtCnts.insert(tgtCnts.end(), faceUVCounts.begin(), faceUVCounts.end()); + if (DBG) + log_debug(" -- uvset %1%: face counts size = %2%") % uvSet % faceUVCounts.size(); + + // append uv vertex indices + for (uint32_t fi = 0, faceCount = static_cast(faceUVCounts.size()); fi < faceCount; ++fi) { + const uint32_t* faceUVIdx0 = (numUVSets > 0) ? mesh->getFaceUVIndices(fi, 0) : EMPTY_IDX.data(); + const uint32_t* faceUVIdx = + (uvSet < numUVSets && !uvs.empty()) ? mesh->getFaceUVIndices(fi, uvSet) : faceUVIdx0; + const uint32_t faceUVCnt = faceUVCounts[fi]; + if (DBG) + log_debug(" fi %1%: faceUVCnt = %2%, faceVtxCnt = %3%") % fi % faceUVCnt % + mesh->getFaceVertexCount(fi); + for (uint32_t vi = 0; vi < faceUVCnt; vi++) + mUvIndices[uvSet].push_back(uvIndexBases[uvSet] + faceUVIdx[vi]); + } + + uvIndexBases[uvSet] += static_cast(src.size()) / 2; + } // for all uv sets + + // append counts and indices for vertices and vertex normals + for (uint32_t fi = 0, faceCount = mesh->getFaceCount(); fi < faceCount; ++fi) { + const uint32_t vtxCnt = mesh->getFaceVertexCount(fi); + mCounts.push_back(vtxCnt); + const uint32_t* vtxIdx = mesh->getFaceVertexIndices(fi); + const uint32_t* nrmIdx = mesh->getFaceVertexNormalIndices(fi); + for (uint32_t vi = 0; vi < vtxCnt; vi++) { + mVertexIndices.push_back(vertexIndexBase + vtxIdx[vi]); + mNormalIndices.push_back(normalIndexBase + nrmIdx[vi]); + } + } + + vertexIndexBase += (uint32_t)verts.size() / 3u; + normalIndexBase += (uint32_t)norms.size() / 3u; + } // for all meshes + } // for all geometries + } + public: prtx::DoubleVector mCoords; prtx::DoubleVector mNormals; @@ -456,97 +541,8 @@ class SerializedGeometry { std::vector mUvIndices; }; -const prtx::DoubleVector EMPTY_UVS; -const prtx::IndexVector EMPTY_IDX; - } // namespace -namespace detail { - -SerializedGeometry serializeGeometry(const prtx::GeometryPtrVector& geometries, - const std::vector& materials) { - // Initialize serialized geometry - SerializedGeometry sg(geometries, materials); - const uint32_t maxNumUVSets = static_cast(sg.mUvs.size()); - - // Copy data into serialized geometry - uint32_t vertexIndexBase = 0u; - uint32_t normalIndexBase = 0u; - std::vector uvIndexBases(maxNumUVSets, 0u); - for (const auto& geo : geometries) { - const prtx::MeshPtrVector& meshes = geo->getMeshes(); - for (const auto& mesh : meshes) { - // append points - const prtx::DoubleVector& verts = mesh->getVertexCoords(); - sg.mCoords.insert(sg.mCoords.end(), verts.begin(), verts.end()); - - // append normals - const prtx::DoubleVector& norms = mesh->getVertexNormalsCoords(); - sg.mNormals.insert(sg.mNormals.end(), norms.begin(), norms.end()); - - // append uv sets (uv coords, counts, indices) with special cases: - // - if mesh has no uv sets but maxNumUVSets is > 0, insert "0" uv face counts to keep in sync - // - if mesh has less uv sets than maxNumUVSets, copy uv set 0 to the missing higher sets - const uint32_t numUVSets = mesh->getUVSetsCount(); - const prtx::DoubleVector& uvs0 = (numUVSets > 0) ? mesh->getUVCoords(0) : EMPTY_UVS; - const prtx::IndexVector faceUVCounts0 = - (numUVSets > 0) ? mesh->getFaceUVCounts(0) : prtx::IndexVector(mesh->getFaceCount(), 0); - if (DBG) - log_debug("-- mesh: numUVSets = %1%") % numUVSets; - - for (uint32_t uvSet = 0; uvSet < sg.mUvs.size(); uvSet++) { - // append texture coordinates - const prtx::DoubleVector& uvs = (uvSet < numUVSets) ? mesh->getUVCoords(uvSet) : EMPTY_UVS; - const auto& src = uvs.empty() ? uvs0 : uvs; - auto& tgt = sg.mUvs[uvSet]; - tgt.insert(tgt.end(), src.begin(), src.end()); - - // append uv face counts - const prtx::IndexVector& faceUVCounts = - (uvSet < numUVSets && !uvs.empty()) ? mesh->getFaceUVCounts(uvSet) : faceUVCounts0; - assert(faceUVCounts.size() == mesh->getFaceCount()); - auto& tgtCnts = sg.mUvCounts[uvSet]; - tgtCnts.insert(tgtCnts.end(), faceUVCounts.begin(), faceUVCounts.end()); - if (DBG) - log_debug(" -- uvset %1%: face counts size = %2%") % uvSet % faceUVCounts.size(); - - // append uv vertex indices - for (uint32_t fi = 0, faceCount = static_cast(faceUVCounts.size()); fi < faceCount; ++fi) { - const uint32_t* faceUVIdx0 = (numUVSets > 0) ? mesh->getFaceUVIndices(fi, 0) : EMPTY_IDX.data(); - const uint32_t* faceUVIdx = - (uvSet < numUVSets && !uvs.empty()) ? mesh->getFaceUVIndices(fi, uvSet) : faceUVIdx0; - const uint32_t faceUVCnt = faceUVCounts[fi]; - if (DBG) - log_debug(" fi %1%: faceUVCnt = %2%, faceVtxCnt = %3%") % fi % faceUVCnt % - mesh->getFaceVertexCount(fi); - for (uint32_t vi = 0; vi < faceUVCnt; vi++) - sg.mUvIndices[uvSet].push_back(uvIndexBases[uvSet] + faceUVIdx[vi]); - } - - uvIndexBases[uvSet] += static_cast(src.size()) / 2; - } // for all uv sets - - // append counts and indices for vertices and vertex normals - for (uint32_t fi = 0, faceCount = mesh->getFaceCount(); fi < faceCount; ++fi) { - const uint32_t vtxCnt = mesh->getFaceVertexCount(fi); - sg.mCounts.push_back(vtxCnt); - const uint32_t* vtxIdx = mesh->getFaceVertexIndices(fi); - const uint32_t* nrmIdx = mesh->getFaceVertexNormalIndices(fi); - for (uint32_t vi = 0; vi < vtxCnt; vi++) { - sg.mVertexIndices.push_back(vertexIndexBase + vtxIdx[vi]); - sg.mNormalIndices.push_back(normalIndexBase + nrmIdx[vi]); - } - } - - vertexIndexBase += (uint32_t)verts.size() / 3u; - normalIndexBase += (uint32_t)norms.size() / 3u; - } // for all meshes - } // for all geometries - - return sg; -} -} // namespace detail - MayaEncoder::MayaEncoder(const std::wstring& id, const prt::AttributeMap* options, prt::Callbacks* callbacks) : prtx::GeometryEncoder(id, options, callbacks) {} @@ -616,7 +612,7 @@ void MayaEncoder::convertGeometry(const prtx::InitialShape& initialShape, shapeIDs.push_back(inst.getShapeId()); } - const SerializedGeometry sg = detail::serializeGeometry(geometries, materials); + const SerializedGeometry sg(geometries, materials); if (sg.isEmpty()) return; From 5c858e6de9be8ffac54c494def3c768cb23fc843 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 17 Jan 2022 12:33:05 +0100 Subject: [PATCH 220/668] cleanup: move scanValidTextures function into class for consistency --- src/codec/encoder/MayaEncoder.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index a5dde597..c552324f 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -363,20 +363,6 @@ const std::vector TEXTURE_UV_MAPPINGS = []() -> std::vectorgetTextureArray(t.key); - if (ta.size() > t.index && ta[t.index]->isValid()) - highestUVSet = std::max(highestUVSet, t.uvSet); - } - if (highestUVSet < 0) - return 0; - else - return highestUVSet + 1; -} - const prtx::DoubleVector EMPTY_UVS; const prtx::IndexVector EMPTY_IDX; @@ -529,6 +515,20 @@ class SerializedGeometry { } // for all geometries } + // return the highest required uv set (where a valid texture is present) + uint32_t scanValidTextures(const prtx::MaterialPtr& mat) { + int8_t highestUVSet = -1; + for (const auto& t : TEXTURE_UV_MAPPINGS) { + const auto& ta = mat->getTextureArray(t.key); + if (ta.size() > t.index && ta[t.index]->isValid()) + highestUVSet = std::max(highestUVSet, t.uvSet); + } + if (highestUVSet < 0) + return 0; + else + return highestUVSet + 1; + } + public: prtx::DoubleVector mCoords; prtx::DoubleVector mNormals; From cfce0dc6f6cb006a331cababd822099c95a4173f Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 17 Jan 2022 12:34:55 +0100 Subject: [PATCH 221/668] cleanup: extract reserveMemory function --- src/codec/encoder/MayaEncoder.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index c552324f..c86d8f08 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -370,6 +370,17 @@ class SerializedGeometry { public: SerializedGeometry(const prtx::GeometryPtrVector& geometries, const std::vector& materials) { + reserveMemory(geometries, materials); + serialize(geometries, materials); + } + + bool isEmpty() const { + return mCoords.empty() || mCounts.empty() || mVertexIndices.empty(); + } + +private: + void reserveMemory(const prtx::GeometryPtrVector& geometries, + const std::vector& materials) { // Allocate memory for geometry uint32_t numCounts = 0; uint32_t numIndices = 0; @@ -427,15 +438,8 @@ class SerializedGeometry { mUvCounts[uvSet].reserve(numUvCounts[uvSet]); mUvIndices[uvSet].reserve(numUvIndices[uvSet]); } - - serialize(geometries, materials); - } - - bool isEmpty() const { - return mCoords.empty() || mCounts.empty() || mVertexIndices.empty(); } -private: void serialize(const prtx::GeometryPtrVector& geometries, const std::vector& materials) { const uint32_t maxNumUVSets = static_cast(mUvs.size()); From 56caeadac5f0bd7c90cf3d8672fab2c5d983a37e Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 17 Jan 2022 12:38:03 +0100 Subject: [PATCH 222/668] cleanup: avoid looping two times through geo/meshes --- src/codec/encoder/MayaEncoder.cpp | 36 ++++++++++++------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index c86d8f08..6128c1b1 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -381,11 +381,14 @@ class SerializedGeometry { private: void reserveMemory(const prtx::GeometryPtrVector& geometries, const std::vector& materials) { - // Allocate memory for geometry uint32_t numCounts = 0; uint32_t numIndices = 0; uint32_t maxNumUVSets = 0; + std::vector numUvs(maxNumUVSets); + std::vector numUvCounts(maxNumUVSets); + std::vector numUvIndices(maxNumUVSets); + auto matsIt = materials.cbegin(); for (const auto& geo : geometries) { const prtx::MeshPtrVector& meshes = geo->getMeshes(); @@ -396,28 +399,7 @@ class SerializedGeometry { const auto& vtxCnts = mesh->getFaceVertexCounts(); numIndices = std::accumulate(vtxCnts.begin(), vtxCnts.end(), numIndices); - const prtx::MaterialPtr& mat = *matIt; - const uint32_t requiredUVSetsByMaterial = scanValidTextures(mat); - maxNumUVSets = std::max(maxNumUVSets, std::max(mesh->getUVSetsCount(), requiredUVSetsByMaterial)); - ++matIt; - } - ++matsIt; - } - - mCounts.reserve(numCounts); - mVertexIndices.reserve(numIndices); - mNormalIndices.reserve(numIndices); - - // Allocate memory for uvs - std::vector numUvs(maxNumUVSets); - std::vector numUvCounts(maxNumUVSets); - std::vector numUvIndices(maxNumUVSets); - - for (const auto& geo : geometries) { - const prtx::MeshPtrVector& meshes = geo->getMeshes(); - for (const auto& mesh : meshes) { const uint32_t numUVSets = mesh->getUVSetsCount(); - for (uint32_t uvSet = 0; uvSet < numUVSets; uvSet++) { numUvs[uvSet] += static_cast(mesh->getUVCoords(uvSet).size()); @@ -426,9 +408,19 @@ class SerializedGeometry { numUvIndices[uvSet] = std::accumulate(faceUVCounts.begin(), faceUVCounts.end(), numUvIndices[uvSet]); } + + const prtx::MaterialPtr& mat = *matIt; + const uint32_t requiredUVSetsByMaterial = scanValidTextures(mat); + maxNumUVSets = std::max(maxNumUVSets, std::max(mesh->getUVSetsCount(), requiredUVSetsByMaterial)); + ++matIt; } + ++matsIt; } + mCounts.reserve(numCounts); + mVertexIndices.reserve(numIndices); + mNormalIndices.reserve(numIndices); + mUvs.resize(maxNumUVSets); mUvCounts.resize(maxNumUVSets); mUvIndices.resize(maxNumUVSets); From 3656bac00cb78a7c7eb06ec1a1de25204c8e5413 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 17 Jan 2022 12:40:10 +0100 Subject: [PATCH 223/668] cleanup: switch to constexpr for DBG guards --- src/codec/encoder/MayaEncoder.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index 6128c1b1..88e5a86d 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -474,7 +474,7 @@ class SerializedGeometry { assert(faceUVCounts.size() == mesh->getFaceCount()); auto& tgtCnts = mUvCounts[uvSet]; tgtCnts.insert(tgtCnts.end(), faceUVCounts.begin(), faceUVCounts.end()); - if (DBG) + if constexpr (DBG) log_debug(" -- uvset %1%: face counts size = %2%") % uvSet % faceUVCounts.size(); // append uv vertex indices @@ -483,7 +483,7 @@ class SerializedGeometry { const uint32_t* faceUVIdx = (uvSet < numUVSets && !uvs.empty()) ? mesh->getFaceUVIndices(fi, uvSet) : faceUVIdx0; const uint32_t faceUVCnt = faceUVCounts[fi]; - if (DBG) + if constexpr (DBG) log_debug(" fi %1%: faceUVCnt = %2%, faceVtxCnt = %3%") % fi % faceUVCnt % mesh->getFaceVertexCount(fi); for (uint32_t vi = 0; vi < faceUVCnt; vi++) @@ -544,10 +544,10 @@ MayaEncoder::MayaEncoder(const std::wstring& id, const prt::AttributeMap* option void MayaEncoder::init(prtx::GenerateContext&) { prt::Callbacks* cb = getCallbacks(); - if (DBG) + if constexpr (DBG) srl_log_debug(L"MayaEncoder::init: cb = %x") % (size_t)cb; auto* oh = dynamic_cast(cb); - if (DBG) + if constexpr (DBG) srl_log_debug(L" oh = %x") % (size_t)oh; if (oh == nullptr) throw prtx::StatusException(prt::STATUS_ILLEGAL_CALLBACK_OBJECT); @@ -613,7 +613,7 @@ void MayaEncoder::convertGeometry(const prtx::InitialShape& initialShape, if (sg.isEmpty()) return; - if (DBG) { + if constexpr (DBG) { log_debug("resolvemap: %s") % prtx::PRTUtils::objectToXML(initialShape.getResolveMap()); log_debug("encoder #materials = %s") % materials.size(); } @@ -645,7 +645,7 @@ void MayaEncoder::convertGeometry(const prtx::InitialShape& initialShape, if (emitReports) { convertReportsToAttributeMap(amb, *repIt); reportAttrMaps.v.push_back(amb->createAttributeMapAndReset()); - if (DBG) + if constexpr (DBG) log_debug("report attr map: %1%") % prtx::PRTUtils::objectToXML(reportAttrMaps.v.back()); } @@ -675,7 +675,7 @@ void MayaEncoder::convertGeometry(const prtx::InitialShape& initialShape, faceRanges.data(), faceRanges.size(), matAttrMaps.v.empty() ? nullptr : matAttrMaps.v.data(), reportAttrMaps.v.empty() ? nullptr : reportAttrMaps.v.data(), shapeIDs.data()); - if (DBG) + if constexpr (DBG) srl_log_debug(L"MayaEncoder::convertGeometry: end"); } From 24f3f8aabcf084951ff827da0bb660cd7a3dbc02 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 17 Jan 2022 12:49:04 +0100 Subject: [PATCH 224/668] cleanup: fallback values for uvs/indices only temporarily needed, move into function --- src/codec/encoder/MayaEncoder.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index 88e5a86d..c1dac8f5 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -363,9 +363,6 @@ const std::vector TEXTURE_UV_MAPPINGS = []() -> std::vector& materials) { const uint32_t maxNumUVSets = static_cast(mUvs.size()); + const prtx::DoubleVector EMPTY_UVS; + const prtx::IndexVector EMPTY_IDX; + // Copy data into serialized geometry uint32_t vertexIndexBase = 0u; uint32_t normalIndexBase = 0u; From cee8231c4b5cf667cc8c48a8c763eb580eeff6bf Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 17 Jan 2022 13:42:54 +0100 Subject: [PATCH 225/668] Revert "cleanup: avoid looping two times through geo/meshes" This reverts commit 56caeadac5f0bd7c90cf3d8672fab2c5d983a37e. --- src/codec/encoder/MayaEncoder.cpp | 36 +++++++++++++++++++------------ 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index c1dac8f5..138208f5 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -378,14 +378,11 @@ class SerializedGeometry { private: void reserveMemory(const prtx::GeometryPtrVector& geometries, const std::vector& materials) { + // Allocate memory for geometry uint32_t numCounts = 0; uint32_t numIndices = 0; uint32_t maxNumUVSets = 0; - std::vector numUvs(maxNumUVSets); - std::vector numUvCounts(maxNumUVSets); - std::vector numUvIndices(maxNumUVSets); - auto matsIt = materials.cbegin(); for (const auto& geo : geometries) { const prtx::MeshPtrVector& meshes = geo->getMeshes(); @@ -396,16 +393,6 @@ class SerializedGeometry { const auto& vtxCnts = mesh->getFaceVertexCounts(); numIndices = std::accumulate(vtxCnts.begin(), vtxCnts.end(), numIndices); - const uint32_t numUVSets = mesh->getUVSetsCount(); - for (uint32_t uvSet = 0; uvSet < numUVSets; uvSet++) { - numUvs[uvSet] += static_cast(mesh->getUVCoords(uvSet).size()); - - const auto& faceUVCounts = mesh->getFaceUVCounts(uvSet); - numUvCounts[uvSet] += static_cast(faceUVCounts.size()); - numUvIndices[uvSet] = - std::accumulate(faceUVCounts.begin(), faceUVCounts.end(), numUvIndices[uvSet]); - } - const prtx::MaterialPtr& mat = *matIt; const uint32_t requiredUVSetsByMaterial = scanValidTextures(mat); maxNumUVSets = std::max(maxNumUVSets, std::max(mesh->getUVSetsCount(), requiredUVSetsByMaterial)); @@ -418,6 +405,27 @@ class SerializedGeometry { mVertexIndices.reserve(numIndices); mNormalIndices.reserve(numIndices); + // Allocate memory for uvs + std::vector numUvs(maxNumUVSets); + std::vector numUvCounts(maxNumUVSets); + std::vector numUvIndices(maxNumUVSets); + + for (const auto& geo : geometries) { + const prtx::MeshPtrVector& meshes = geo->getMeshes(); + for (const auto& mesh : meshes) { + const uint32_t numUVSets = mesh->getUVSetsCount(); + + for (uint32_t uvSet = 0; uvSet < numUVSets; uvSet++) { + numUvs[uvSet] += static_cast(mesh->getUVCoords(uvSet).size()); + + const auto& faceUVCounts = mesh->getFaceUVCounts(uvSet); + numUvCounts[uvSet] += static_cast(faceUVCounts.size()); + numUvIndices[uvSet] = + std::accumulate(faceUVCounts.begin(), faceUVCounts.end(), numUvIndices[uvSet]); + } + } + } + mUvs.resize(maxNumUVSets); mUvCounts.resize(maxNumUVSets); mUvIndices.resize(maxNumUVSets); From ed0087d8d484abef1de88ae44f72f8ee37bcd9e5 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 17 Jan 2022 13:51:09 +0100 Subject: [PATCH 226/668] cleanup: hide texture-to-uv-set mapping helper structures --- src/codec/encoder/MayaEncoder.cpp | 57 ++++++++++++++++--------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index 138208f5..9413a7ff 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -335,34 +335,6 @@ struct AttributeMapNOPtrVectorOwner { } }; -struct TextureUVMapping { - std::wstring key; - uint8_t index; - int8_t uvSet; -}; - -const std::vector TEXTURE_UV_MAPPINGS = []() -> std::vector { - // clang-format off - return { - // shader key | idx | uv set | CGA key - { L"diffuseMap", 0, 0 }, // colormap - { L"bumpMap", 0, 1 }, // bumpmap - { L"diffuseMap", 1, 2 }, // dirtmap - { L"specularMap", 0, 3 }, // specularmap - { L"opacityMap", 0, 4 }, // opacitymap - { L"normalMap", 0, 5 } // normalmap - -#if PRT_VERSION_MAJOR > 1 - , - { L"emissiveMap", 0, 6 }, // emissivemap - { L"occlusionMap", 0, 7 }, // occlusionmap - { L"roughnessMap", 0, 8 }, // roughnessmap - { L"metallicMap", 0, 9 } // metallicmap -#endif - }; - // clang-format on -}(); - class SerializedGeometry { public: SerializedGeometry(const prtx::GeometryPtrVector& geometries, @@ -519,8 +491,37 @@ class SerializedGeometry { } // for all geometries } + struct TextureUVMapping { + std::wstring key; + uint8_t index; + int8_t uvSet; + }; + // return the highest required uv set (where a valid texture is present) uint32_t scanValidTextures(const prtx::MaterialPtr& mat) { + + // clang-format off + static const std::vector TEXTURE_UV_MAPPINGS = []() -> std::vector { + return { + // shader key | idx | uv set | CGA key + { L"diffuseMap", 0, 0 }, // colormap + { L"bumpMap", 0, 1 }, // bumpmap + { L"diffuseMap", 1, 2 }, // dirtmap + { L"specularMap", 0, 3 }, // specularmap + { L"opacityMap", 0, 4 }, // opacitymap + { L"normalMap", 0, 5 } // normalmap + + #if PRT_VERSION_MAJOR > 1 + , + { L"emissiveMap", 0, 6 }, // emissivemap + { L"occlusionMap", 0, 7 }, // occlusionmap + { L"roughnessMap", 0, 8 }, // roughnessmap + { L"metallicMap", 0, 9 } // metallicmap + #endif + }; + }(); + // clang-format on + int8_t highestUVSet = -1; for (const auto& t : TEXTURE_UV_MAPPINGS) { const auto& ta = mat->getTextureArray(t.key); From 4aa155ddf6453ba7238fb7c4aa00fc351255ecab Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 17 Jan 2022 13:51:42 +0100 Subject: [PATCH 227/668] cleanup: run clang-format --- src/codec/encoder/MayaEncoder.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index 9413a7ff..5dfa8684 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -36,6 +36,7 @@ #include "prt/prt.h" #include +#include #include #include #include @@ -43,7 +44,6 @@ #include #include #include -#include // PRT version < 2.1 #if ((PRT_VERSION_MAJOR <= 2) && ((PRT_VERSION_MAJOR < 2) || (PRT_VERSION_MINOR < 1))) @@ -349,7 +349,7 @@ class SerializedGeometry { private: void reserveMemory(const prtx::GeometryPtrVector& geometries, - const std::vector& materials) { + const std::vector& materials) { // Allocate memory for geometry uint32_t numCounts = 0; uint32_t numIndices = 0; @@ -409,8 +409,7 @@ class SerializedGeometry { } } - void serialize(const prtx::GeometryPtrVector& geometries, - const std::vector& materials) { + void serialize(const prtx::GeometryPtrVector& geometries, const std::vector& materials) { const uint32_t maxNumUVSets = static_cast(mUvs.size()); const prtx::DoubleVector EMPTY_UVS; @@ -458,7 +457,8 @@ class SerializedGeometry { log_debug(" -- uvset %1%: face counts size = %2%") % uvSet % faceUVCounts.size(); // append uv vertex indices - for (uint32_t fi = 0, faceCount = static_cast(faceUVCounts.size()); fi < faceCount; ++fi) { + for (uint32_t fi = 0, faceCount = static_cast(faceUVCounts.size()); fi < faceCount; + ++fi) { const uint32_t* faceUVIdx0 = (numUVSets > 0) ? mesh->getFaceUVIndices(fi, 0) : EMPTY_IDX.data(); const uint32_t* faceUVIdx = (uvSet < numUVSets && !uvs.empty()) ? mesh->getFaceUVIndices(fi, uvSet) : faceUVIdx0; From ed405df634487f82c97405cd1d1fecd29d4061a3 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 18 Jan 2022 15:37:45 +0100 Subject: [PATCH 228/668] Extend PRTModifierEnum to store the valuesAttr for dynamic enums --- src/serlio/modifiers/PRTModifierAction.cpp | 4 ++++ src/serlio/modifiers/PRTModifierAction.h | 1 + 2 files changed, 5 insertions(+) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 065add67..45a1ce16 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -53,6 +53,7 @@ constexpr const wchar_t* NULL_KEY = L"#NULL#"; constexpr const wchar_t* MIN_KEY = L"min"; constexpr const wchar_t* MAX_KEY = L"max"; constexpr const wchar_t* RESTRICTED_KEY = L"restricted"; +constexpr const wchar_t* VALUES_ATTR_KEY = L"valuesAttr"; constexpr const wchar_t* ATTRIBUTE_USER_SET_SUFFIX = L"_user_set"; constexpr const wchar_t* ATTRIBUTE_FORCE_DEFAULT_SUFFIX = L"_force_default"; @@ -919,6 +920,9 @@ MStatus PRTModifierEnum::fill(const prt::Annotation* annot) { if (std::wcscmp(key, RESTRICTED_KEY) == 0) { mRestricted = annot->getArgument(arg)->getBool(); } + if (std::wcscmp(key, VALUES_ATTR_KEY) == 0) { + mValuesAttr = annot->getArgument(arg)->getStr(); + } continue; } diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index ab164a67..8b6f7367 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -58,6 +58,7 @@ class PRTModifierEnum { MDoubleArray mFVals; MIntArray mBVals; bool mRestricted = true; + MString mValuesAttr = ""; }; // class PRTModifierEnum class PRTModifierAction : public polyModifierFty { From 8e3175a40c3b8cd2ee38f324756595250028dfe5 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 18 Jan 2022 15:38:40 +0100 Subject: [PATCH 229/668] Add helper function to extract import path from fqRuleName --- src/serlio/utils/Utilities.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index 15fd62f1..ac54a0d1 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -196,6 +196,13 @@ SRL_TEST_EXPORTS_API inline std::wstring removeImport(const std::wstring& fqRule return removePrefix(fqRuleName, IMPORT_DELIMITER); } +SRL_TEST_EXPORTS_API inline std::wstring getImport(const std::wstring& fqRuleName) { + const std::wstring ruleWithoutStyle = removeStyle(fqRuleName); + const auto sepPos = ruleWithoutStyle.find(IMPORT_DELIMITER); + if (sepPos == std::wstring::npos || sepPos == 0) + return {}; + return fqRuleName.substr(0, sepPos); +} } // namespace prtu inline void replace_all_not_of(std::wstring& s, const std::wstring& allowedChars) { From 9bff0ac42b05d627c9b0e6f0eb88857db8f273ff Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 18 Jan 2022 15:42:12 +0100 Subject: [PATCH 230/668] Update dynamic enums based on string arrays --- src/serlio/modifiers/PRTModifierAction.cpp | 53 ++++++++++++++++++++++ src/serlio/modifiers/PRTModifierAction.h | 1 + 2 files changed, 54 insertions(+) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 45a1ce16..d3639ead 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -421,6 +421,7 @@ MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { } }; + updateDynamicEnums(); iterateThroughAttributesAndApply(node, mRuleAttributes, fillAttributeFromNode); mGenerateAttrs.reset(aBuilder->createAttributeMap()); @@ -601,6 +602,58 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { return MStatus::kSuccess; } +MStatus PRTModifierAction::updateDynamicEnums() { + auto reverseLookupAttribute = [ruleAttributes = mRuleAttributes](const std::wstring& mayaFullAttrName) { + auto it = std::find_if(ruleAttributes.begin(), ruleAttributes.end(), + [&mayaFullAttrName](const auto& ra) { return (ra.mayaFullName == mayaFullAttrName); }); + if (it != ruleAttributes.end()) + return *it; + return RULE_NOT_FOUND; + }; + + for (auto& e : mEnums) { + if (e.mValuesAttr != "") { + + const MString fullAttrName = e.mAttr.name(); + const RuleAttribute ruleAttr = reverseLookupAttribute(fullAttrName.asWChar()); + + const std::wstring attrStyle = prtu::getStyle(ruleAttr.fqName).c_str(); + std::wstring attrImport = prtu::getImport(ruleAttr.fqName).c_str(); + if (!attrImport.empty()) + attrImport += prtu::IMPORT_DELIMITER; + + const std::wstring prefix = attrStyle + prtu::STYLE_DELIMITER + attrImport; + + const wchar_t* valuesAttr = (MString(prefix.c_str()) + e.mValuesAttr).asWChar(); + prt::Attributable::PrimitiveType type = mGenerateAttrs->getType(valuesAttr); + + switch (type) { + case prt::Attributable::PT_STRING_ARRAY: { + size_t* arr_length; + const wchar_t *const *stringArray = mGenerateAttrs->getStringArray(valuesAttr, arr_length); + e.mSVals.clear(); + + for (size_t i = 0; i < *arr_length; i++) { + if (wcslen(stringArray[i]) == 0) + continue; + + std::wstring currString = stringArray[i]; + + //remove newlines from strings, because they break the maya UI + currString.erase(std::remove(currString.begin(), currString.end(), '\n'), currString.end()); + + MString mCurrString = MString(currString.c_str()); + e.mSVals.append(mCurrString); + e.mAttr.addField(mCurrString, e.mSVals.length()); + } + break; + } + } + } + } + return MStatus(); +} + // Sets the mesh object for the action to operate on void PRTModifierAction::setMesh(MObject& _inMesh, MObject& _outMesh) { inMesh = _inMesh; diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 8b6f7367..3e871499 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -106,6 +106,7 @@ class PRTModifierAction : public polyModifierFty { AttributeMapUPtr mGenerateAttrs; std::list mEnums; + MStatus updateDynamicEnums(); // std::map mBriefName2prtAttr; MStatus createNodeAttributes(const MObject& node, const prt::RuleFileInfo* info); void removeUnusedAttribs(MFnDependencyNode& node); From 354b17eaa4844efc4ee7f000bafefd3d209fb51e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 18 Jan 2022 16:54:11 +0100 Subject: [PATCH 231/668] Also remove nested imports when using removePrefix --- src/serlio/utils/Utilities.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index 15fd62f1..058be6fe 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -178,7 +178,7 @@ SRL_TEST_EXPORTS_API inline std::wstring getStyle(const std::wstring& fqRuleName } SRL_TEST_EXPORTS_API inline std::wstring removePrefix(const std::wstring& fqRuleName, wchar_t delim) { - const auto sepPos = fqRuleName.find(delim); + const auto sepPos = fqRuleName.rfind(delim); if (sepPos == std::wstring::npos) return fqRuleName; if (sepPos == fqRuleName.length() - 1) From 08c08482d6e1c38ec974afd66ba0a68b459bb8a0 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 18 Jan 2022 17:25:30 +0100 Subject: [PATCH 232/668] Cleanup: use constexpr if (DBG) --- src/codec/encoder/MayaEncoder.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index 5dfa8684..ac6ef32b 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -189,13 +189,13 @@ const std::set MATERIAL_ATTRIBUTE_BLACKLIST = { void convertMaterialToAttributeMap(prtx::PRTUtils::AttributeMapBuilderPtr& aBuilder, const prtx::Material& prtxAttr, const prtx::WStringVector& keys) { - if (DBG) + if constexpr (DBG) srl_log_debug(L"-- converting material: %1%") % prtxAttr.name(); for (const auto& key : keys) { if (MATERIAL_ATTRIBUTE_BLACKLIST.count(key) > 0) continue; - if (DBG) + if constexpr (DBG) srl_log_debug(L" key: %1%") % key; switch (prtxAttr.getType(key)) { @@ -264,7 +264,7 @@ void convertMaterialToAttributeMap(prtx::PRTUtils::AttributeMapBuilderPtr& aBuil } default: - if (DBG) + if constexpr (DBG) srl_log_debug(L"ignored atttribute '%s' with type %d") % key % prtxAttr.getType(key); break; } @@ -437,7 +437,7 @@ class SerializedGeometry { const prtx::DoubleVector& uvs0 = (numUVSets > 0) ? mesh->getUVCoords(0) : EMPTY_UVS; const prtx::IndexVector faceUVCounts0 = (numUVSets > 0) ? mesh->getFaceUVCounts(0) : prtx::IndexVector(mesh->getFaceCount(), 0); - if (DBG) + if constexpr (DBG) log_debug("-- mesh: numUVSets = %1%") % numUVSets; for (uint32_t uvSet = 0; uvSet < mUvs.size(); uvSet++) { From 76c1f1fb77254cc8b38d2903e04df266aae6a639 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 18 Jan 2022 17:28:44 +0100 Subject: [PATCH 233/668] Cleanup: use consistent log function --- src/codec/encoder/MayaEncoder.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index ac6ef32b..2f2ee824 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -438,7 +438,7 @@ class SerializedGeometry { const prtx::IndexVector faceUVCounts0 = (numUVSets > 0) ? mesh->getFaceUVCounts(0) : prtx::IndexVector(mesh->getFaceCount(), 0); if constexpr (DBG) - log_debug("-- mesh: numUVSets = %1%") % numUVSets; + srl_log_debug("-- mesh: numUVSets = %1%") % numUVSets; for (uint32_t uvSet = 0; uvSet < mUvs.size(); uvSet++) { // append texture coordinates @@ -454,7 +454,7 @@ class SerializedGeometry { auto& tgtCnts = mUvCounts[uvSet]; tgtCnts.insert(tgtCnts.end(), faceUVCounts.begin(), faceUVCounts.end()); if constexpr (DBG) - log_debug(" -- uvset %1%: face counts size = %2%") % uvSet % faceUVCounts.size(); + srl_log_debug(" -- uvset %1%: face counts size = %2%") % uvSet % faceUVCounts.size(); // append uv vertex indices for (uint32_t fi = 0, faceCount = static_cast(faceUVCounts.size()); fi < faceCount; @@ -464,7 +464,7 @@ class SerializedGeometry { (uvSet < numUVSets && !uvs.empty()) ? mesh->getFaceUVIndices(fi, uvSet) : faceUVIdx0; const uint32_t faceUVCnt = faceUVCounts[fi]; if constexpr (DBG) - log_debug(" fi %1%: faceUVCnt = %2%, faceVtxCnt = %3%") % fi % faceUVCnt % + srl_log_debug(" fi %1%: faceUVCnt = %2%, faceVtxCnt = %3%") % fi % faceUVCnt % mesh->getFaceVertexCount(fi); for (uint32_t vi = 0; vi < faceUVCnt; vi++) mUvIndices[uvSet].push_back(uvIndexBases[uvSet] + faceUVIdx[vi]); @@ -623,8 +623,8 @@ void MayaEncoder::convertGeometry(const prtx::InitialShape& initialShape, return; if constexpr (DBG) { - log_debug("resolvemap: %s") % prtx::PRTUtils::objectToXML(initialShape.getResolveMap()); - log_debug("encoder #materials = %s") % materials.size(); + srl_log_debug("resolvemap: %s") % prtx::PRTUtils::objectToXML(initialShape.getResolveMap()); + srl_log_debug("encoder #materials = %s") % materials.size(); } uint32_t faceCount = 0; @@ -655,7 +655,7 @@ void MayaEncoder::convertGeometry(const prtx::InitialShape& initialShape, convertReportsToAttributeMap(amb, *repIt); reportAttrMaps.v.push_back(amb->createAttributeMapAndReset()); if constexpr (DBG) - log_debug("report attr map: %1%") % prtx::PRTUtils::objectToXML(reportAttrMaps.v.back()); + srl_log_debug("report attr map: %1%") % prtx::PRTUtils::objectToXML(reportAttrMaps.v.back()); } faceCount += m->getFaceCount(); From 559522a018bf9bd94922d549ca0cdc56238d5676 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 19 Jan 2022 10:34:58 +0100 Subject: [PATCH 234/668] Disable rpk upacking in ResolveMap cache --- src/serlio/PRTContext.cpp | 3 +-- src/serlio/utils/ResolveMapCache.cpp | 16 +--------------- src/serlio/utils/ResolveMapCache.h | 4 +--- 3 files changed, 3 insertions(+), 20 deletions(-) diff --git a/src/serlio/PRTContext.cpp b/src/serlio/PRTContext.cpp index 0a66e479..37cf3c5e 100644 --- a/src/serlio/PRTContext.cpp +++ b/src/serlio/PRTContext.cpp @@ -84,8 +84,7 @@ PRTContext::PRTContext(const std::vector& addExtDirs) : mPluginRoo } else { theCache.reset(prt::CacheObject::create(prt::CacheObject::CACHE_TYPE_DEFAULT)); - std::filesystem::path serlioTmpDir = prtu::getProcessTempDir(SRL_TMP_PREFIX); - mResolveMapCache = std::make_unique(serlioTmpDir.wstring()); + mResolveMapCache = std::make_unique(); } } diff --git a/src/serlio/utils/ResolveMapCache.cpp b/src/serlio/utils/ResolveMapCache.cpp index 9e032979..d452af48 100644 --- a/src/serlio/utils/ResolveMapCache.cpp +++ b/src/serlio/utils/ResolveMapCache.cpp @@ -33,13 +33,6 @@ std::mutex resolveMapCacheMutex; } // namespace -ResolveMapCache::~ResolveMapCache() { - if (!mRPKUnpackPath.empty()) - std::filesystem::remove_all(mRPKUnpackPath); - if (DBG) - LOG_DBG << "Removed RPK unpack directory"; -} - ResolveMapCache::LookupResult ResolveMapCache::get(const std::wstring& rpk) { std::lock_guard lock(resolveMapCacheMutex); @@ -59,10 +52,6 @@ ResolveMapCache::LookupResult ResolveMapCache::get(const std::wstring& rpk) { LOG_DBG << "rpk: cache timestamp: " << it->second.mTimeStamp; if (it->second.mTimeStamp != timeStamp) { mCache.erase(it); - std::filesystem::path filename = std::filesystem::path(rpk).filename(); - - if (!mRPKUnpackPath.empty() && !filename.empty()) - std::filesystem::remove_all(mRPKUnpackPath / filename); if (DBG) LOG_DBG << "RPK change detected, forcing reload and clearing cache for " << rpk; @@ -82,15 +71,12 @@ ResolveMapCache::LookupResult ResolveMapCache::get(const std::wstring& rpk) { prt::Status status = prt::STATUS_UNSPECIFIED_ERROR; if (DBG) LOG_DBG << "createResolveMap from " << rpk; - const std::wstring mRPKUnpackPathString = mRPKUnpackPath.wstring(); - rmce.mResolveMap.reset(prt::createResolveMap(rpkURI.c_str(), mRPKUnpackPathString.c_str(), &status), + rmce.mResolveMap.reset(prt::createResolveMap(rpkURI.c_str(), nullptr, &status), PRTDestroyer()); if (status != prt::STATUS_OK) return LOOKUP_FAILURE; it = mCache.emplace(rpk, std::move(rmce)).first; - if (DBG) - LOG_DBG << "Upacked RPK " << rpk << " to " << mRPKUnpackPath; } return {it->second.mResolveMap, cs}; diff --git a/src/serlio/utils/ResolveMapCache.h b/src/serlio/utils/ResolveMapCache.h index 12971bde..a1424f2b 100644 --- a/src/serlio/utils/ResolveMapCache.h +++ b/src/serlio/utils/ResolveMapCache.h @@ -29,7 +29,7 @@ class ResolveMapCache { public: using KeyType = std::wstring; - explicit ResolveMapCache(const std::wstring& unpackPath) : mRPKUnpackPath{unpackPath} {} + ResolveMapCache() = default; ResolveMapCache(const ResolveMapCache&) = delete; ResolveMapCache(ResolveMapCache&&) = delete; ResolveMapCache& operator=(ResolveMapCache const&) = delete; @@ -47,8 +47,6 @@ class ResolveMapCache { }; using Cache = std::map; Cache mCache; - - const std::filesystem::path mRPKUnpackPath; }; using ResolveMapCacheUPtr = std::unique_ptr; \ No newline at end of file From e27226d572365648f7836c93d86c608c10439daf Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 19 Jan 2022 15:21:57 +0100 Subject: [PATCH 235/668] Cleanup: remove unused destructor --- src/serlio/utils/ResolveMapCache.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/serlio/utils/ResolveMapCache.h b/src/serlio/utils/ResolveMapCache.h index a1424f2b..ae345e78 100644 --- a/src/serlio/utils/ResolveMapCache.h +++ b/src/serlio/utils/ResolveMapCache.h @@ -34,7 +34,6 @@ class ResolveMapCache { ResolveMapCache(ResolveMapCache&&) = delete; ResolveMapCache& operator=(ResolveMapCache const&) = delete; ResolveMapCache& operator=(ResolveMapCache&&) = delete; - ~ResolveMapCache(); enum class CacheStatus { HIT, MISS }; using LookupResult = std::pair; From 26b64513959dda54d0b99c9de5d9c420f24215cb Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 20 Jan 2022 14:07:48 +0100 Subject: [PATCH 236/668] Add TextureEncoder from puma plugin --- src/codec/CMakeLists.txt | 3 +- src/codec/encoder/TextureEncoder.cpp | 199 +++++++++++++++++++++++++++ src/codec/encoder/TextureEncoder.h | 37 +++++ 3 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 src/codec/encoder/TextureEncoder.cpp create mode 100644 src/codec/encoder/TextureEncoder.h diff --git a/src/codec/CMakeLists.txt b/src/codec/CMakeLists.txt index 2990e427..1242246f 100644 --- a/src/codec/CMakeLists.txt +++ b/src/codec/CMakeLists.txt @@ -6,7 +6,8 @@ cmake_policy(SET CMP0015 NEW) add_library(${CODEC_TARGET} SHARED CodecMain.cpp - encoder/MayaEncoder.cpp) + encoder/MayaEncoder.cpp + encoder/TextureEncoder.cpp) if (CMAKE_GENERATOR MATCHES "Visual Studio.+") target_sources(${CODEC_TARGET} diff --git a/src/codec/encoder/TextureEncoder.cpp b/src/codec/encoder/TextureEncoder.cpp new file mode 100644 index 00000000..a78c65ef --- /dev/null +++ b/src/codec/encoder/TextureEncoder.cpp @@ -0,0 +1,199 @@ +/** + * Serlio - Esri CityEngine Plugin for Autodesk Maya + * + * See https://github.com/esri/serlio for build and usage instructions. + * + * Copyright (c) 2012-2019 Esri R&D Center Zurich + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TextureEncoder.h" + +#include "prtx/Exception.h" +#include "prtx/ExtensionManager.h" +#include "prtx/PRTUtils.h" +#include "prtx/Texture.h" +#include "prtx/URI.h" + +#include "prt/EncoderInfo.h" + +#include +#include +#include +#include + +namespace TextureEncoder { + +namespace IDs { + +const std::wstring PNG = L"com.esri.prt.codecs.PNGEncoder"; +const std::wstring JPG = L"com.esri.prt.codecs.JPGEncoder"; +const std::wstring TIF = L"com.esri.prt.codecs.TIFFEncoder"; +const std::wstring DXT = L"com.esri.prt.codecs.DxtEncoder"; +const std::wstring RAW = L"com.esri.prt.codecs.RAWEncoder"; + +} // namespace IDs + +// key names for texture encoder options provided by PRT +namespace OptionNames { + +constexpr const wchar_t* NAME = L"textureName"; +constexpr const wchar_t* FLIPH = L"textureFlipH"; +constexpr const wchar_t* BASE64 = L"textureBase64"; +constexpr const wchar_t* MAXDIM = L"textureMaxDimension"; +constexpr const wchar_t* SCALING = L"textureScalingFactor"; +constexpr const wchar_t* FIXED_DIMENSIONS = L"textureFixedDimensions"; + +constexpr const wchar_t* JPG_QUALITY = L"textureQuality"; +constexpr const wchar_t* JPG_FORCE_JFIF = L"forceJFIFHeaders"; +constexpr const wchar_t* PNG_COMPRESSION = L"textureCompression"; + +constexpr const wchar_t* EXISTING_FILES = L"existingFiles"; +constexpr const wchar_t* EXISTING_FILES_OVERWRITE = L"OVERWRITE"; +constexpr const wchar_t* EXISTING_FILES_SKIP = L"SKIP"; + +} // namespace OptionNames + +std::wstring const& selectEncoderID(Format format) { + switch (format) { + case Format::JPG: + return IDs::JPG; + case Format::PNG: + return IDs::PNG; + case Format::TIF: + return IDs::TIF; + default: + throw std::invalid_argument("unsupported format"); + } +} + +std::wstring const getBestMatchingEncoder(const prtx::Texture& tex) { + const prtx::URIPtr& uri = tex.getURI(); + + std::wstring const extension = uri->getExtension(); + if (!extension.empty()) { + std::vector availableEncoders; + prtx::ExtensionManager::instance().listEncoderIds(availableEncoders); + + std::wstring bestId; + double bestMerit = std::numeric_limits::lowest(); + for (const auto& encId : availableEncoders) { + prtx::PRTUtils::EncoderInfoUPtr encInfo(prtx::ExtensionManager::instance().createEncoderInfo(encId)); + if (encInfo->getType() == prt::CT_TEXTURE) { + const std::wstring_view extensions(encInfo->getExtensions()); + const bool hasCompatibleFileExtension = (extensions.find(extension + L';') != std::wstring_view::npos); + if (hasCompatibleFileExtension && (encInfo->getMerit() > bestMerit)) { + bestId = encId; + bestMerit = encInfo->getMerit(); + } + } + } + + if (!bestId.empty()) + return bestId; + } + + // fallback behavior + if (extension == L".png") + return IDs::PNG; + else if (tex.getFormat() == prtx::Texture::RGB8 || tex.getFormat() == prtx::Texture::GREY8) + return IDs::JPG; + else + return IDs::PNG; +} + +std::wstring getExtensionForEncoder(std::wstring const& textureEncoderID, std::wstring const& currentExt) { + const prtx::PRTUtils::EncoderInfoUPtr encoderInfo( + prtx::ExtensionManager::instance().createEncoderInfo(textureEncoderID)); + const std::wstring_view extensions(encoderInfo->getExtensions()); + const bool hasCompatibleFileExtension = (extensions.find(currentExt + L';') != std::wstring_view::npos); + return hasCompatibleFileExtension ? currentExt : std::wstring(extensions.substr(0, extensions.find_first_of(L';'))); +} + +std::wstring replaceExtension(std::wstring const& texName, std::wstring const& extension) { + const prtx::URIPtr nameUri = prtx::URIUtils::createFileURI(prtx::URIUtils::percentEncode(L"/" + texName)); + const prtx::URIPtr newExtensionUri = prtx::URIUtils::replaceExtension(nameUri, extension); + return newExtensionUri->getPath(); +} + +std::wstring getBaseName(const prtx::URIPtr& texURI, const std::wstring& namePrefix) { + const std::wstring queryBaseName = texURI->getQuery(prtx::URI::QUERY_TEXTURE_NAME); + const std::wstring uriBaseName = queryBaseName.empty() ? texURI->getBaseName() : queryBaseName; + + if (texURI->getScheme() == prtx::URI::SCHEME_MEMORY && namePrefix.size() > 0) + return namePrefix + L'_' + uriBaseName; + + return uriBaseName; +} + +std::wstring constructNameForTexture(const prtx::TexturePtr& texture, const std::wstring& namePrefix) { + if (!texture || !texture->isValid()) + throw prtx::StatusException(prt::STATUS_ILLEGAL_VALUE); + + prtx::URIPtr texURI = texture->getURI(); + std::wstring const baseName = getBaseName(texURI, namePrefix); + std::wstring const extension = texURI->getExtension(); + return baseName + extension; +} + +prtx::PRTUtils::AttributeMapUPtr getEncOpts(const std::wstring& encoderId, const std::wstring& filename, + prt::SimpleOutputCallbacks::OpenMode openMode) { + prtx::PRTUtils::AttributeMapBuilderPtr builder(prt::AttributeMapBuilder::create()); + builder->setString(OptionNames::NAME, filename.c_str()); + builder->setBool(OptionNames::FLIPH, true); + auto const evExistingFiles = openMode == prt::SimpleOutputCallbacks::OPENMODE_ALWAYS + ? OptionNames::EXISTING_FILES_OVERWRITE + : OptionNames::EXISTING_FILES_SKIP; + builder->setString(OptionNames::EXISTING_FILES, evExistingFiles); + + const prtx::PRTUtils::EncoderInfoPtr encInfo(prtx::ExtensionManager::instance().createEncoderInfo(encoderId)); + prtx::PRTUtils::AttributeMapPtr rawEncOpts{builder->createAttributeMap()}; + const prt::AttributeMap* validOpts = nullptr; + encInfo->createValidatedOptionsAndStates(rawEncOpts.get(), &validOpts); + return prtx::PRTUtils::AttributeMapUPtr(validOpts); +} + +std::wstring encode(const prtx::TexturePtr& texture, prt::SimpleOutputCallbacks* soh, + prtx::NamePreparator& namePreparator, const prtx::NamePreparator::NamespacePtr& namespaceFilenames, + const std::wstring& memTexFileNamePrefix, const Format& targetFormat) { + if (!texture || !texture->isValid()) + throw prtx::StatusException(prt::STATUS_ILLEGAL_VALUE); + + std::wstring textureEncoderID; + if (targetFormat == Format::AUTO) + textureEncoderID = getBestMatchingEncoder(*texture); + else + textureEncoderID = selectEncoderID(targetFormat); + + const std::wstring texName = constructNameForTexture(texture, memTexFileNamePrefix); + const std::wstring extension = getExtensionForEncoder(textureEncoderID, texture->getURI()->getExtension()); + const std::wstring texNameWithExtension = replaceExtension(texName, extension); + const std::wstring uniqueName = namePreparator.legalizedAndUniquified( + texNameWithExtension.substr(1), prtx::NamePreparator::ENTITY_FILE, namespaceFilenames); + + prtx::PRTUtils::AttributeMapUPtr encOpts = + getEncOpts(textureEncoderID, uniqueName, prt::SimpleOutputCallbacks::OpenMode::OPENMODE_ALWAYS); + prtx::EncoderPtr texEnc = prtx::ExtensionManager::instance().createEncoder(textureEncoderID, encOpts.get(), soh); + texEnc->encode({texture}); + + prt::Status status = prt::STATUS_UNSPECIFIED_ERROR; + wchar_t const* const validatedName = encOpts->getString(OptionNames::NAME, &status); + if (status != prt::STATUS_OK) + throw prtx::StatusException(status); + if (validatedName == nullptr) + throw prtx::StatusException(prt::STATUS_ENCODE_FAILED); + + return std::wstring(validatedName); +} + +} // namespace TextureEncoder diff --git a/src/codec/encoder/TextureEncoder.h b/src/codec/encoder/TextureEncoder.h new file mode 100644 index 00000000..2c2fcea7 --- /dev/null +++ b/src/codec/encoder/TextureEncoder.h @@ -0,0 +1,37 @@ +/** + * Serlio - Esri CityEngine Plugin for Autodesk Maya + * + * See https://github.com/esri/serlio for build and usage instructions. + * + * Copyright (c) 2012-2019 Esri R&D Center Zurich + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "prtx/NamePreparator.h" +#include "prtx/Texture.h" + +#include "prt/Callbacks.h" + +#include + +namespace TextureEncoder { + +enum class Format : uint8_t { AUTO, JPG, PNG, TIF }; + +std::wstring encode(const prtx::TexturePtr& tex, prt::SimpleOutputCallbacks* soh, prtx::NamePreparator& namePreparator, + const prtx::NamePreparator::NamespacePtr& namespaceFilenames, + const std::wstring& memTexFileNamePrefix, const Format& targetFormat = Format::AUTO); + +} // namespace TextureEncoder From 095334f10c4591665caff65e00d2fb0e9045647e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 20 Jan 2022 14:09:29 +0100 Subject: [PATCH 237/668] Add AssetCache and its dependencies from Puma stduuid is a requirement for AssetCache in order to guarantee unique asset file names without running into issues with the windows file system --- src/serlio/CMakeLists.txt | 2 + src/serlio/utils/AssetCache.cpp | 102 + src/serlio/utils/AssetCache.h | 37 + src/serlio/utils/stduuid/gsl/gsl | 29 + src/serlio/utils/stduuid/gsl/gsl_algorithm | 63 + src/serlio/utils/stduuid/gsl/gsl_assert | 145 ++ src/serlio/utils/stduuid/gsl/gsl_byte | 181 ++ src/serlio/utils/stduuid/gsl/gsl_util | 158 ++ src/serlio/utils/stduuid/gsl/multi_span | 2242 ++++++++++++++++++++ src/serlio/utils/stduuid/gsl/pointers | 193 ++ src/serlio/utils/stduuid/gsl/span | 766 +++++++ src/serlio/utils/stduuid/gsl/string_span | 730 +++++++ src/serlio/utils/stduuid/uuid.h | 873 ++++++++ 13 files changed, 5521 insertions(+) create mode 100644 src/serlio/utils/AssetCache.cpp create mode 100644 src/serlio/utils/AssetCache.h create mode 100644 src/serlio/utils/stduuid/gsl/gsl create mode 100644 src/serlio/utils/stduuid/gsl/gsl_algorithm create mode 100644 src/serlio/utils/stduuid/gsl/gsl_assert create mode 100644 src/serlio/utils/stduuid/gsl/gsl_byte create mode 100644 src/serlio/utils/stduuid/gsl/gsl_util create mode 100644 src/serlio/utils/stduuid/gsl/multi_span create mode 100644 src/serlio/utils/stduuid/gsl/pointers create mode 100644 src/serlio/utils/stduuid/gsl/span create mode 100644 src/serlio/utils/stduuid/gsl/string_span create mode 100644 src/serlio/utils/stduuid/uuid.h diff --git a/src/serlio/CMakeLists.txt b/src/serlio/CMakeLists.txt index 151cc845..69f413ad 100644 --- a/src/serlio/CMakeLists.txt +++ b/src/serlio/CMakeLists.txt @@ -21,6 +21,7 @@ add_library(${SERLIO_TARGET} SHARED materials/MaterialUtils.cpp materials/StingrayMaterialNode.cpp materials/MaterialCommand.cpp + utils/AssetCache.cpp utils/Utilities.cpp utils/ResolveMapCache.cpp utils/MayaUtilities.cpp @@ -46,6 +47,7 @@ if (CMAKE_GENERATOR MATCHES "Visual Studio.+") materials/MaterialUtils.h materials/StingrayMaterialNode.h materials/MaterialCommand.h + utils/AssetCache.h utils/Utilities.h utils/ResolveMapCache.h utils/MayaUtilities.h diff --git a/src/serlio/utils/AssetCache.cpp b/src/serlio/utils/AssetCache.cpp new file mode 100644 index 00000000..8a74ea08 --- /dev/null +++ b/src/serlio/utils/AssetCache.cpp @@ -0,0 +1,102 @@ +/** + * Serlio - Esri CityEngine Plugin for Autodesk Maya + * + * See https://github.com/esri/serlio for build and usage instructions. + * + * Copyright (c) 2012-2019 Esri R&D Center Zurich + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AssetCache.h" + +#include "utils/LogHandler.h" +#include "utils/stduuid/uuid.h" + +#include +#include +#include +#include +#include + +namespace { + +bool writeCacheEntry(const std::filesystem::path& assetPath, const uint8_t* buffer, size_t size) noexcept { + std::ofstream stream(assetPath, std::ofstream::binary | std::ofstream::trunc); + if (!stream) + return false; + stream.write(reinterpret_cast(buffer), size); + if (!stream) + return false; + stream.close(); + return true; +} + +void removeCacheEntry(const std::filesystem::path& expiredAssetPath) { + if (std::error_code removeError; !std::filesystem::remove(expiredAssetPath, removeError)) + LOG_WRN << "Failed to delete expired asset cache entry " << expiredAssetPath << ": " << removeError.message(); +} + +} // namespace + +AssetCache::AssetCache(const std::filesystem::path& cacheRootPath) : mCacheRootPath(cacheRootPath) {} + +std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileName, const uint8_t* buffer, size_t size) { + assert(uri != nullptr); + + const std::string_view bufferView(reinterpret_cast(buffer), size); + const size_t hash = std::hash{}(bufferView); + + const auto it = std::find_if(mCache.begin(), mCache.end(), + [&uri](const auto& p) { return (std::wcscmp(p.first.c_str(), uri) == 0); }); + + // reuse cached asset if uri and hash match + if ((it != mCache.end()) && (it->second.second == hash)) { + const std::filesystem::path& assetPath = it->second.first; + return assetPath; + } + + const std::filesystem::path newAssetPath = getCachedPath(fileName); + if (newAssetPath.empty()) { + LOG_ERR << "Invalid URI, cannot cache the asset: " << uri; + return {}; + } + + if (!writeCacheEntry(newAssetPath, buffer, size)) { + LOG_ERR << "Failed to put asset into cache, skipping asset: " << newAssetPath; + return {}; + } + + if (it == mCache.end()) { + mCache.emplace(uri, std::make_pair(newAssetPath, hash)); + } + else { + // handle hash mismatch + removeCacheEntry(it->second.first); + it->second = std::make_pair(newAssetPath, hash); + } + + return newAssetPath; +} + +std::filesystem::path AssetCache::getCachedPath(const wchar_t* fileName) const { + // we use an UUID to have unique entries (filenames might clash across different RPKs) + const uuids::uuid uuid = uuids::uuid_system_generator{}(); + std::wstring cachedAssetName = uuids::to_wstring(uuid); + + // we append the filename constructed by the encoder from the URI + assert(fileName != nullptr); + cachedAssetName.append(L"_").append(fileName); + + const std::filesystem::path cachedAssetPath = mCacheRootPath / cachedAssetName; + return cachedAssetPath; +} diff --git a/src/serlio/utils/AssetCache.h b/src/serlio/utils/AssetCache.h new file mode 100644 index 00000000..affbd290 --- /dev/null +++ b/src/serlio/utils/AssetCache.h @@ -0,0 +1,37 @@ +/** + * Serlio - Esri CityEngine Plugin for Autodesk Maya + * + * See https://github.com/esri/serlio for build and usage instructions. + * + * Copyright (c) 2012-2019 Esri R&D Center Zurich + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +class AssetCache { +public: + explicit AssetCache(const std::filesystem::path& cacheRootPath); + + std::filesystem::path put(const wchar_t* uri, const wchar_t* fileName, const uint8_t* buffer, size_t size); + +private: + std::filesystem::path getCachedPath(const wchar_t* fileName) const; + + std::unordered_map> mCache; + const std::filesystem::path mCacheRootPath; +}; diff --git a/src/serlio/utils/stduuid/gsl/gsl b/src/serlio/utils/stduuid/gsl/gsl new file mode 100644 index 00000000..55862ebd --- /dev/null +++ b/src/serlio/utils/stduuid/gsl/gsl @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_GSL_H +#define GSL_GSL_H + +#include // copy +#include // Ensures/Expects +#include // byte +#include // finally()/narrow()/narrow_cast()... +#include // multi_span, strided_span... +#include // owner, not_null +#include // span +#include // zstring, string_span, zstring_builder... + +#endif // GSL_GSL_H diff --git a/src/serlio/utils/stduuid/gsl/gsl_algorithm b/src/serlio/utils/stduuid/gsl/gsl_algorithm new file mode 100644 index 00000000..710792fb --- /dev/null +++ b/src/serlio/utils/stduuid/gsl/gsl_algorithm @@ -0,0 +1,63 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_ALGORITHM_H +#define GSL_ALGORITHM_H + +#include // for Expects +#include // for dynamic_extent, span + +#include // for copy_n +#include // for ptrdiff_t +#include // for is_assignable + +#ifdef _MSC_VER +#pragma warning(push) + +// turn off some warnings that are noisy about our Expects statements +#pragma warning(disable : 4127) // conditional expression is constant +#pragma warning(disable : 4996) // unsafe use of std::copy_n + +// blanket turn off warnings from CppCoreCheck for now +// so people aren't annoyed by them when running the tool. +// more targeted suppressions will be added in a future update to the GSL +#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) +#endif // _MSC_VER + +namespace gsl +{ + +template +void copy(span src, span dest) +{ + static_assert(std::is_assignable::value, + "Elements of source span can not be assigned to elements of destination span"); + static_assert(SrcExtent == dynamic_extent || DestExtent == dynamic_extent || + (SrcExtent <= DestExtent), + "Source range is longer than target range"); + + Expects(dest.size() >= src.size()); + std::copy_n(src.data(), src.size(), dest.data()); +} + +} // namespace gsl + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +#endif // GSL_ALGORITHM_H diff --git a/src/serlio/utils/stduuid/gsl/gsl_assert b/src/serlio/utils/stduuid/gsl/gsl_assert new file mode 100644 index 00000000..131fa8b1 --- /dev/null +++ b/src/serlio/utils/stduuid/gsl/gsl_assert @@ -0,0 +1,145 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_CONTRACTS_H +#define GSL_CONTRACTS_H + +#include +#include // for logic_error + +// +// Temporary until MSVC STL supports no-exceptions mode. +// Currently terminate is a no-op in this mode, so we add termination behavior back +// +#if defined(_MSC_VER) && defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS +#define GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND +#endif + +// +// There are three configuration options for this GSL implementation's behavior +// when pre/post conditions on the GSL types are violated: +// +// 1. GSL_TERMINATE_ON_CONTRACT_VIOLATION: std::terminate will be called (default) +// 2. GSL_THROW_ON_CONTRACT_VIOLATION: a gsl::fail_fast exception will be thrown +// 3. GSL_UNENFORCED_ON_CONTRACT_VIOLATION: nothing happens +// +#if !(defined(GSL_THROW_ON_CONTRACT_VIOLATION) || defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) || \ + defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION)) +#define GSL_TERMINATE_ON_CONTRACT_VIOLATION +#endif + +#define GSL_STRINGIFY_DETAIL(x) #x +#define GSL_STRINGIFY(x) GSL_STRINGIFY_DETAIL(x) + +#if defined(__clang__) || defined(__GNUC__) +#define GSL_LIKELY(x) __builtin_expect(!!(x), 1) +#define GSL_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define GSL_LIKELY(x) (!!(x)) +#define GSL_UNLIKELY(x) (!!(x)) +#endif + +// +// GSL_ASSUME(cond) +// +// Tell the optimizer that the predicate cond must hold. It is unspecified +// whether or not cond is actually evaluated. +// +#ifdef _MSC_VER +#define GSL_ASSUME(cond) __assume(cond) +#elif defined(__GNUC__) +#define GSL_ASSUME(cond) ((cond) ? static_cast(0) : __builtin_unreachable()) +#else +#define GSL_ASSUME(cond) static_cast((cond) ? 0 : 0) +#endif + +// +// GSL.assert: assertions +// + +namespace gsl +{ +struct fail_fast : public std::logic_error +{ + explicit fail_fast(char const* const message) : std::logic_error(message) {} +}; + +namespace details +{ +#if defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND) + + typedef void (__cdecl *terminate_handler)(); + + inline gsl::details::terminate_handler& get_terminate_handler() noexcept + { + static terminate_handler handler = &abort; + return handler; + } + +#endif + + [[noreturn]] inline void terminate() noexcept + { +#if defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND) + (*gsl::details::get_terminate_handler())(); +#else + std::terminate(); +#endif + } + +#if defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) + + template + [[noreturn]] void throw_exception(Exception&&) + { + gsl::details::terminate(); + } + +#else + + template + [[noreturn]] void throw_exception(Exception&& exception) + { + throw std::forward(exception); + } + +#endif + +} // namespace details +} // namespace gsl + +#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) + +#define GSL_CONTRACT_CHECK(type, cond) \ + (GSL_LIKELY(cond) ? static_cast(0) \ + : gsl::details::throw_exception(gsl::fail_fast( \ + "GSL: " type " failure at " __FILE__ ": " GSL_STRINGIFY(__LINE__)))) + +#elif defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) + +#define GSL_CONTRACT_CHECK(type, cond) \ + (GSL_LIKELY(cond) ? static_cast(0) : gsl::details::terminate()) + +#elif defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION) + +#define GSL_CONTRACT_CHECK(type, cond) GSL_ASSUME(cond) + +#endif + +#define Expects(cond) GSL_CONTRACT_CHECK("Precondition", cond) +#define Ensures(cond) GSL_CONTRACT_CHECK("Postcondition", cond) + +#endif // GSL_CONTRACTS_H diff --git a/src/serlio/utils/stduuid/gsl/gsl_byte b/src/serlio/utils/stduuid/gsl/gsl_byte new file mode 100644 index 00000000..e8611733 --- /dev/null +++ b/src/serlio/utils/stduuid/gsl/gsl_byte @@ -0,0 +1,181 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_BYTE_H +#define GSL_BYTE_H + +#include + +#ifdef _MSC_VER + +#pragma warning(push) + +// don't warn about function style casts in byte related operators +#pragma warning(disable : 26493) + +#ifndef GSL_USE_STD_BYTE +// this tests if we are under MSVC and the standard lib has std::byte and it is enabled +#if defined(_HAS_STD_BYTE) && _HAS_STD_BYTE + +#define GSL_USE_STD_BYTE 1 + +#else // defined(_HAS_STD_BYTE) && _HAS_STD_BYTE + +#define GSL_USE_STD_BYTE 0 + +#endif // defined(_HAS_STD_BYTE) && _HAS_STD_BYTE +#endif // GSL_USE_STD_BYTE + +#else // _MSC_VER + +#ifndef GSL_USE_STD_BYTE +// this tests if we are under GCC or Clang with enough -std:c++1z power to get us std::byte +#if defined(__cplusplus) && (__cplusplus >= 201703L) + +#define GSL_USE_STD_BYTE 1 +#include + +#else // defined(__cplusplus) && (__cplusplus >= 201703L) + +#define GSL_USE_STD_BYTE 0 + +#endif //defined(__cplusplus) && (__cplusplus >= 201703L) +#endif // GSL_USE_STD_BYTE + +#endif // _MSC_VER + +// Use __may_alias__ attribute on gcc and clang +#if defined __clang__ || (__GNUC__ > 5) +#define byte_may_alias __attribute__((__may_alias__)) +#else // defined __clang__ || defined __GNUC__ +#define byte_may_alias +#endif // defined __clang__ || defined __GNUC__ + +namespace gsl +{ +#if GSL_USE_STD_BYTE + + +using std::byte; +using std::to_integer; + +#else // GSL_USE_STD_BYTE + +// This is a simple definition for now that allows +// use of byte within span<> to be standards-compliant +enum class byte_may_alias byte : unsigned char +{ +}; + +template ::value>> +constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept +{ + return b = byte(static_cast(b) << shift); +} + +template ::value>> +constexpr byte operator<<(byte b, IntegerType shift) noexcept +{ + return byte(static_cast(b) << shift); +} + +template ::value>> +constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept +{ + return b = byte(static_cast(b) >> shift); +} + +template ::value>> +constexpr byte operator>>(byte b, IntegerType shift) noexcept +{ + return byte(static_cast(b) >> shift); +} + +constexpr byte& operator|=(byte& l, byte r) noexcept +{ + return l = byte(static_cast(l) | static_cast(r)); +} + +constexpr byte operator|(byte l, byte r) noexcept +{ + return byte(static_cast(l) | static_cast(r)); +} + +constexpr byte& operator&=(byte& l, byte r) noexcept +{ + return l = byte(static_cast(l) & static_cast(r)); +} + +constexpr byte operator&(byte l, byte r) noexcept +{ + return byte(static_cast(l) & static_cast(r)); +} + +constexpr byte& operator^=(byte& l, byte r) noexcept +{ + return l = byte(static_cast(l) ^ static_cast(r)); +} + +constexpr byte operator^(byte l, byte r) noexcept +{ + return byte(static_cast(l) ^ static_cast(r)); +} + +constexpr byte operator~(byte b) noexcept { return byte(~static_cast(b)); } + +template ::value>> +constexpr IntegerType to_integer(byte b) noexcept +{ + return static_cast(b); +} + +#endif // GSL_USE_STD_BYTE + +template +constexpr byte to_byte_impl(T t) noexcept +{ + static_assert( + E, "gsl::to_byte(t) must be provided an unsigned char, otherwise data loss may occur. " + "If you are calling to_byte with an integer contant use: gsl::to_byte() version."); + return static_cast(t); +} +template <> +constexpr byte to_byte_impl(unsigned char t) noexcept +{ + return byte(t); +} + +template +constexpr byte to_byte(T t) noexcept +{ + return to_byte_impl::value, T>(t); +} + +template +constexpr byte to_byte() noexcept +{ + static_assert(I >= 0 && I <= 255, + "gsl::byte only has 8 bits of storage, values must be in range 0-255"); + return static_cast(I); +} + +} // namespace gsl + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +#endif // GSL_BYTE_H diff --git a/src/serlio/utils/stduuid/gsl/gsl_util b/src/serlio/utils/stduuid/gsl/gsl_util new file mode 100644 index 00000000..93d60217 --- /dev/null +++ b/src/serlio/utils/stduuid/gsl/gsl_util @@ -0,0 +1,158 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_UTIL_H +#define GSL_UTIL_H + +#include // for Expects + +#include +#include // for ptrdiff_t, size_t +#include // for exception +#include // for initializer_list +#include // for is_signed, integral_constant +#include // for forward + +#if defined(_MSC_VER) + +#pragma warning(push) +#pragma warning(disable : 4127) // conditional expression is constant + +#if _MSC_VER < 1910 +#pragma push_macro("constexpr") +#define constexpr /*constexpr*/ +#endif // _MSC_VER < 1910 +#endif // _MSC_VER + +namespace gsl +{ +// +// GSL.util: utilities +// + +// index type for all container indexes/subscripts/sizes +using index = std::ptrdiff_t; + +// final_action allows you to ensure something gets run at the end of a scope +template +class final_action +{ +public: + explicit final_action(F f) noexcept : f_(std::move(f)) {} + + final_action(final_action&& other) noexcept : f_(std::move(other.f_)), invoke_(other.invoke_) + { + other.invoke_ = false; + } + + final_action(const final_action&) = delete; + final_action& operator=(const final_action&) = delete; + final_action& operator=(final_action&&) = delete; + + ~final_action() noexcept + { + if (invoke_) f_(); + } + +private: + F f_; + bool invoke_ {true}; +}; + +// finally() - convenience function to generate a final_action +template + +final_action finally(const F& f) noexcept +{ + return final_action(f); +} + +template +final_action finally(F&& f) noexcept +{ + return final_action(std::forward(f)); +} + +// narrow_cast(): a searchable way to do narrowing casts of values +template +constexpr T narrow_cast(U&& u) noexcept +{ + return static_cast(std::forward(u)); +} + +struct narrowing_error : public std::exception +{ +}; + +namespace details +{ + template + struct is_same_signedness + : public std::integral_constant::value == std::is_signed::value> + { + }; +} + +// narrow() : a checked version of narrow_cast() that throws if the cast changed the value +template +T narrow(U u) +{ + T t = narrow_cast(u); + if (static_cast(t) != u) gsl::details::throw_exception(narrowing_error()); + if (!details::is_same_signedness::value && ((t < T{}) != (u < U{}))) + gsl::details::throw_exception(narrowing_error()); + return t; +} + +// +// at() - Bounds-checked way of accessing builtin arrays, std::array, std::vector +// +template +constexpr T& at(T (&arr)[N], const index i) +{ + Expects(i >= 0 && i < narrow_cast(N)); + return arr[static_cast(i)]; +} + +template +constexpr auto at(Cont& cont, const index i) -> decltype(cont[cont.size()]) +{ + Expects(i >= 0 && i < narrow_cast(cont.size())); + using size_type = decltype(cont.size()); + return cont[static_cast(i)]; +} + +template +constexpr T at(const std::initializer_list cont, const index i) +{ + Expects(i >= 0 && i < narrow_cast(cont.size())); + return *(cont.begin() + i); +} + +} // namespace gsl + +#if defined(_MSC_VER) +#if _MSC_VER < 1910 +#undef constexpr +#pragma pop_macro("constexpr") + +#endif // _MSC_VER < 1910 + +#pragma warning(pop) + +#endif // _MSC_VER + +#endif // GSL_UTIL_H diff --git a/src/serlio/utils/stduuid/gsl/multi_span b/src/serlio/utils/stduuid/gsl/multi_span new file mode 100644 index 00000000..9c0c27b3 --- /dev/null +++ b/src/serlio/utils/stduuid/gsl/multi_span @@ -0,0 +1,2242 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_MULTI_SPAN_H +#define GSL_MULTI_SPAN_H + +#include // for Expects +#include // for byte +#include // for narrow_cast + +#include // for transform, lexicographical_compare +#include // for array +#include +#include // for ptrdiff_t, size_t, nullptr_t +#include // for PTRDIFF_MAX +#include // for divides, multiplies, minus, negate, plus +#include // for initializer_list +#include // for iterator, random_access_iterator_tag +#include // for numeric_limits +#include +#include +#include +#include // for basic_string +#include // for enable_if_t, remove_cv_t, is_same, is_co... +#include + +#ifdef _MSC_VER + +// turn off some warnings that are noisy about our Expects statements +#pragma warning(push) +#pragma warning(disable : 4127) // conditional expression is constant +#pragma warning(disable : 4702) // unreachable code + +#if _MSC_VER < 1910 +#pragma push_macro("constexpr") +#define constexpr /*constexpr*/ + +#endif // _MSC_VER < 1910 +#endif // _MSC_VER + +// GCC 7 does not like the signed unsigned missmatch (size_t ptrdiff_t) +// While there is a conversion from signed to unsigned, it happens at +// compiletime, so the compiler wouldn't have to warn indiscriminently, but +// could check if the source value actually doesn't fit into the target type +// and only warn in those cases. +#if __GNUC__ > 6 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" +#endif + +#ifdef GSL_THROW_ON_CONTRACT_VIOLATION +#define GSL_NOEXCEPT /*noexcept*/ +#else +#define GSL_NOEXCEPT noexcept +#endif // GSL_THROW_ON_CONTRACT_VIOLATION + +namespace gsl +{ + +/* +** begin definitions of index and bounds +*/ +namespace details +{ + template + struct SizeTypeTraits + { + static const SizeType max_value = std::numeric_limits::max(); + }; + + template + class are_integral : public std::integral_constant + { + }; + + template + class are_integral + : public std::integral_constant::value && are_integral::value> + { + }; +} + +template +class multi_span_index final +{ + static_assert(Rank > 0, "Rank must be greater than 0!"); + + template + friend class multi_span_index; + +public: + static const std::size_t rank = Rank; + using value_type = std::ptrdiff_t; + using size_type = value_type; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_lvalue_reference_t>; + + constexpr multi_span_index() GSL_NOEXCEPT {} + + constexpr multi_span_index(const value_type (&values)[Rank]) GSL_NOEXCEPT + { + std::copy(values, values + Rank, elems); + } + + template ::value>> + constexpr multi_span_index(Ts... ds) GSL_NOEXCEPT : elems{narrow_cast(ds)...} + { + } + + constexpr multi_span_index(const multi_span_index& other) GSL_NOEXCEPT = default; + + constexpr multi_span_index& operator=(const multi_span_index& rhs) GSL_NOEXCEPT = default; + + // Preconditions: component_idx < rank + constexpr reference operator[](std::size_t component_idx) + { + Expects(component_idx < Rank); // Component index must be less than rank + return elems[component_idx]; + } + + // Preconditions: component_idx < rank + constexpr const_reference operator[](std::size_t component_idx) const GSL_NOEXCEPT + { + Expects(component_idx < Rank); // Component index must be less than rank + return elems[component_idx]; + } + + constexpr bool operator==(const multi_span_index& rhs) const GSL_NOEXCEPT + { + return std::equal(elems, elems + rank, rhs.elems); + } + + constexpr bool operator!=(const multi_span_index& rhs) const GSL_NOEXCEPT { return !(*this == rhs); } + + constexpr multi_span_index operator+() const GSL_NOEXCEPT { return *this; } + + constexpr multi_span_index operator-() const GSL_NOEXCEPT + { + multi_span_index ret = *this; + std::transform(ret, ret + rank, ret, std::negate{}); + return ret; + } + + constexpr multi_span_index operator+(const multi_span_index& rhs) const GSL_NOEXCEPT + { + multi_span_index ret = *this; + ret += rhs; + return ret; + } + + constexpr multi_span_index operator-(const multi_span_index& rhs) const GSL_NOEXCEPT + { + multi_span_index ret = *this; + ret -= rhs; + return ret; + } + + constexpr multi_span_index& operator+=(const multi_span_index& rhs) GSL_NOEXCEPT + { + std::transform(elems, elems + rank, rhs.elems, elems, std::plus{}); + return *this; + } + + constexpr multi_span_index& operator-=(const multi_span_index& rhs) GSL_NOEXCEPT + { + std::transform(elems, elems + rank, rhs.elems, elems, std::minus{}); + return *this; + } + + constexpr multi_span_index operator*(value_type v) const GSL_NOEXCEPT + { + multi_span_index ret = *this; + ret *= v; + return ret; + } + + constexpr multi_span_index operator/(value_type v) const GSL_NOEXCEPT + { + multi_span_index ret = *this; + ret /= v; + return ret; + } + + friend constexpr multi_span_index operator*(value_type v, const multi_span_index& rhs) GSL_NOEXCEPT + { + return rhs * v; + } + + constexpr multi_span_index& operator*=(value_type v) GSL_NOEXCEPT + { + std::transform(elems, elems + rank, elems, + [v](value_type x) { return std::multiplies{}(x, v); }); + return *this; + } + + constexpr multi_span_index& operator/=(value_type v) GSL_NOEXCEPT + { + std::transform(elems, elems + rank, elems, + [v](value_type x) { return std::divides{}(x, v); }); + return *this; + } + +private: + value_type elems[Rank] = {}; +}; + +#if !defined(_MSC_VER) || _MSC_VER >= 1910 + +struct static_bounds_dynamic_range_t +{ + template ::value>> + constexpr operator T() const GSL_NOEXCEPT + { + return narrow_cast(-1); + } +}; + +constexpr bool operator==(static_bounds_dynamic_range_t, static_bounds_dynamic_range_t) GSL_NOEXCEPT +{ + return true; +} + +constexpr bool operator!=(static_bounds_dynamic_range_t, static_bounds_dynamic_range_t) GSL_NOEXCEPT +{ + return false; +} + +template ::value>> +constexpr bool operator==(static_bounds_dynamic_range_t, T other) GSL_NOEXCEPT +{ + return narrow_cast(-1) == other; +} + +template ::value>> +constexpr bool operator==(T left, static_bounds_dynamic_range_t right) GSL_NOEXCEPT +{ + return right == left; +} + +template ::value>> +constexpr bool operator!=(static_bounds_dynamic_range_t, T other) GSL_NOEXCEPT +{ + return narrow_cast(-1) != other; +} + +template ::value>> +constexpr bool operator!=(T left, static_bounds_dynamic_range_t right) GSL_NOEXCEPT +{ + return right != left; +} + +constexpr static_bounds_dynamic_range_t dynamic_range{}; +#else +const std::ptrdiff_t dynamic_range = -1; +#endif + +struct generalized_mapping_tag +{ +}; +struct contiguous_mapping_tag : generalized_mapping_tag +{ +}; + +namespace details +{ + + template + struct LessThan + { + static const bool value = Left < Right; + }; + + template + struct BoundsRanges + { + using size_type = std::ptrdiff_t; + static const size_type Depth = 0; + static const size_type DynamicNum = 0; + static const size_type CurrentRange = 1; + static const size_type TotalSize = 1; + + // TODO : following signature is for work around VS bug + template + BoundsRanges(const OtherRange&, bool /* firstLevel */) + { + } + + BoundsRanges(const std::ptrdiff_t* const) {} + BoundsRanges() = default; + + template + void serialize(T&) const + { + } + + template + size_type linearize(const T&) const + { + return 0; + } + + template + size_type contains(const T&) const + { + return -1; + } + + size_type elementNum(std::size_t) const GSL_NOEXCEPT { return 0; } + + size_type totalSize() const GSL_NOEXCEPT { return TotalSize; } + + bool operator==(const BoundsRanges&) const GSL_NOEXCEPT { return true; } + }; + + template + struct BoundsRanges : BoundsRanges + { + using Base = BoundsRanges; + using size_type = std::ptrdiff_t; + static const std::size_t Depth = Base::Depth + 1; + static const std::size_t DynamicNum = Base::DynamicNum + 1; + static const size_type CurrentRange = dynamic_range; + static const size_type TotalSize = dynamic_range; + + private: + size_type m_bound; + + public: + BoundsRanges(const std::ptrdiff_t* const arr) + : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) + { + Expects(0 <= *arr); + } + + BoundsRanges() : m_bound(0) {} + + template + BoundsRanges(const BoundsRanges& other, + bool /* firstLevel */ = true) + : Base(static_cast&>(other), false) + , m_bound(other.totalSize()) + { + } + + template + void serialize(T& arr) const + { + arr[Dim] = elementNum(); + this->Base::template serialize(arr); + } + + template + size_type linearize(const T& arr) const + { + const size_type index = this->Base::totalSize() * arr[Dim]; + Expects(index < m_bound); + return index + this->Base::template linearize(arr); + } + + template + size_type contains(const T& arr) const + { + const ptrdiff_t last = this->Base::template contains(arr); + if (last == -1) return -1; + const ptrdiff_t cur = this->Base::totalSize() * arr[Dim]; + return cur < m_bound ? cur + last : -1; + } + + size_type totalSize() const GSL_NOEXCEPT { return m_bound; } + + size_type elementNum() const GSL_NOEXCEPT { return totalSize() / this->Base::totalSize(); } + + size_type elementNum(std::size_t dim) const GSL_NOEXCEPT + { + if (dim > 0) + return this->Base::elementNum(dim - 1); + else + return elementNum(); + } + + bool operator==(const BoundsRanges& rhs) const GSL_NOEXCEPT + { + return m_bound == rhs.m_bound && + static_cast(*this) == static_cast(rhs); + } + }; + + template + struct BoundsRanges : BoundsRanges + { + using Base = BoundsRanges; + using size_type = std::ptrdiff_t; + static const std::size_t Depth = Base::Depth + 1; + static const std::size_t DynamicNum = Base::DynamicNum; + static const size_type CurrentRange = CurRange; + static const size_type TotalSize = + Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize; + + BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) {} + BoundsRanges() = default; + + template + BoundsRanges(const BoundsRanges& other, + bool firstLevel = true) + : Base(static_cast&>(other), false) + { + (void) firstLevel; + } + + template + void serialize(T& arr) const + { + arr[Dim] = elementNum(); + this->Base::template serialize(arr); + } + + template + size_type linearize(const T& arr) const + { + Expects(arr[Dim] >= 0 && arr[Dim] < CurrentRange); // Index is out of range + return this->Base::totalSize() * arr[Dim] + + this->Base::template linearize(arr); + } + + template + size_type contains(const T& arr) const + { + if (arr[Dim] >= CurrentRange) return -1; + const size_type last = this->Base::template contains(arr); + if (last == -1) return -1; + return this->Base::totalSize() * arr[Dim] + last; + } + + size_type totalSize() const GSL_NOEXCEPT { return CurrentRange * this->Base::totalSize(); } + + size_type elementNum() const GSL_NOEXCEPT { return CurrentRange; } + + size_type elementNum(std::size_t dim) const GSL_NOEXCEPT + { + if (dim > 0) + return this->Base::elementNum(dim - 1); + else + return elementNum(); + } + + bool operator==(const BoundsRanges& rhs) const GSL_NOEXCEPT + { + return static_cast(*this) == static_cast(rhs); + } + }; + + template + struct BoundsRangeConvertible + : public std::integral_constant= TargetType::TotalSize || + TargetType::TotalSize == dynamic_range || + SourceType::TotalSize == dynamic_range || + TargetType::TotalSize == 0)> + { + }; + + template + struct TypeListIndexer + { + const TypeChain& obj_; + TypeListIndexer(const TypeChain& obj) : obj_(obj) {} + + template + const TypeChain& getObj(std::true_type) + { + return obj_; + } + + template + auto getObj(std::false_type) + -> decltype(TypeListIndexer(static_cast(obj_)).template get()) + { + return TypeListIndexer(static_cast(obj_)).template get(); + } + + template + auto get() -> decltype(getObj(std::integral_constant())) + { + return getObj(std::integral_constant()); + } + }; + + template + TypeListIndexer createTypeListIndexer(const TypeChain& obj) + { + return TypeListIndexer(obj); + } + + template 1), + typename Ret = std::enable_if_t>> + constexpr Ret shift_left(const multi_span_index& other) GSL_NOEXCEPT + { + Ret ret{}; + for (std::size_t i = 0; i < Rank - 1; ++i) { + ret[i] = other[i + 1]; + } + return ret; + } +} + +template +class bounds_iterator; + +template +class static_bounds +{ +public: + static_bounds(const details::BoundsRanges&) {} +}; + +template +class static_bounds +{ + using MyRanges = details::BoundsRanges; + + MyRanges m_ranges; + constexpr static_bounds(const MyRanges& range) : m_ranges(range) {} + + template + friend class static_bounds; + +public: + static const std::size_t rank = MyRanges::Depth; + static const std::size_t dynamic_rank = MyRanges::DynamicNum; + static const std::ptrdiff_t static_size = MyRanges::TotalSize; + + using size_type = std::ptrdiff_t; + using index_type = multi_span_index; + using const_index_type = std::add_const_t; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; + using difference_type = std::ptrdiff_t; + using sliced_type = static_bounds; + using mapping_type = contiguous_mapping_tag; + + constexpr static_bounds(const static_bounds&) = default; + + template + struct BoundsRangeConvertible2; + + template > + static auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret; + + template + static auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type; + + template + struct BoundsRangeConvertible2 + : decltype(helpBoundsRangeConvertible( + SourceType(), TargetType(), + std::integral_constant())) + { + }; + + template + struct BoundsRangeConvertible2 : std::true_type + { + }; + + template + struct BoundsRangeConvertible + : decltype(helpBoundsRangeConvertible( + SourceType(), TargetType(), + std::integral_constant::value || + TargetType::CurrentRange == dynamic_range || + SourceType::CurrentRange == dynamic_range)>())) + { + }; + + template + struct BoundsRangeConvertible : std::true_type + { + }; + + template , + details::BoundsRanges>::value>> + constexpr static_bounds(const static_bounds& other) : m_ranges(other.m_ranges) + { + Expects((MyRanges::DynamicNum == 0 && details::BoundsRanges::DynamicNum == 0) || + MyRanges::DynamicNum > 0 || other.m_ranges.totalSize() >= m_ranges.totalSize()); + } + + constexpr static_bounds(std::initializer_list il) + : m_ranges(il.begin()) + { + // Size of the initializer list must match the rank of the array + Expects((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || + MyRanges::DynamicNum == il.size()); + // Size of the range must be less than the max element of the size type + Expects(m_ranges.totalSize() <= PTRDIFF_MAX); + } + + constexpr static_bounds() = default; + + constexpr sliced_type slice() const GSL_NOEXCEPT + { + return sliced_type{static_cast&>(m_ranges)}; + } + + constexpr size_type stride() const GSL_NOEXCEPT { return rank > 1 ? slice().size() : 1; } + + constexpr size_type size() const GSL_NOEXCEPT { return m_ranges.totalSize(); } + + constexpr size_type total_size() const GSL_NOEXCEPT { return m_ranges.totalSize(); } + + constexpr size_type linearize(const index_type& idx) const { return m_ranges.linearize(idx); } + + constexpr bool contains(const index_type& idx) const GSL_NOEXCEPT + { + return m_ranges.contains(idx) != -1; + } + + constexpr size_type operator[](std::size_t idx) const GSL_NOEXCEPT + { + return m_ranges.elementNum(idx); + } + + template + constexpr size_type extent() const GSL_NOEXCEPT + { + static_assert(Dim < rank, + "dimension should be less than rank (dimension count starts from 0)"); + return details::createTypeListIndexer(m_ranges).template get().elementNum(); + } + + template + constexpr size_type extent(IntType dim) const GSL_NOEXCEPT + { + static_assert(std::is_integral::value, + "Dimension parameter must be supplied as an integral type."); + auto real_dim = narrow_cast(dim); + Expects(real_dim < rank); + + return m_ranges.elementNum(real_dim); + } + + constexpr index_type index_bounds() const GSL_NOEXCEPT + { + size_type extents[rank] = {}; + m_ranges.serialize(extents); + return {extents}; + } + + template + constexpr bool operator==(const static_bounds& rhs) const GSL_NOEXCEPT + { + return this->size() == rhs.size(); + } + + template + constexpr bool operator!=(const static_bounds& rhs) const GSL_NOEXCEPT + { + return !(*this == rhs); + } + + constexpr const_iterator begin() const GSL_NOEXCEPT + { + return const_iterator(*this, index_type{}); + } + + constexpr const_iterator end() const GSL_NOEXCEPT + { + return const_iterator(*this, this->index_bounds()); + } +}; + +template +class strided_bounds +{ + template + friend class strided_bounds; + +public: + static const std::size_t rank = Rank; + using value_type = std::ptrdiff_t; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_const_t; + using size_type = value_type; + using difference_type = value_type; + using index_type = multi_span_index; + using const_index_type = std::add_const_t; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; + static const value_type dynamic_rank = rank; + static const value_type static_size = dynamic_range; + using sliced_type = std::conditional_t, void>; + using mapping_type = generalized_mapping_tag; + + constexpr strided_bounds(const strided_bounds&) GSL_NOEXCEPT = default; + + constexpr strided_bounds& operator=(const strided_bounds&) GSL_NOEXCEPT = default; + + constexpr strided_bounds(const value_type (&values)[rank], index_type strides) + : m_extents(values), m_strides(std::move(strides)) + { + } + + constexpr strided_bounds(const index_type& extents, const index_type& strides) GSL_NOEXCEPT + : m_extents(extents), + m_strides(strides) + { + } + + constexpr index_type strides() const GSL_NOEXCEPT { return m_strides; } + + constexpr size_type total_size() const GSL_NOEXCEPT + { + size_type ret = 0; + for (std::size_t i = 0; i < rank; ++i) { + ret += (m_extents[i] - 1) * m_strides[i]; + } + return ret + 1; + } + + constexpr size_type size() const GSL_NOEXCEPT + { + size_type ret = 1; + for (std::size_t i = 0; i < rank; ++i) { + ret *= m_extents[i]; + } + return ret; + } + + constexpr bool contains(const index_type& idx) const GSL_NOEXCEPT + { + for (std::size_t i = 0; i < rank; ++i) { + if (idx[i] < 0 || idx[i] >= m_extents[i]) return false; + } + return true; + } + + constexpr size_type linearize(const index_type& idx) const GSL_NOEXCEPT + { + size_type ret = 0; + for (std::size_t i = 0; i < rank; i++) { + Expects(idx[i] < m_extents[i]); // index is out of bounds of the array + ret += idx[i] * m_strides[i]; + } + return ret; + } + + constexpr size_type stride() const GSL_NOEXCEPT { return m_strides[0]; } + + template 1), typename Ret = std::enable_if_t> + constexpr sliced_type slice() const + { + return {details::shift_left(m_extents), details::shift_left(m_strides)}; + } + + template + constexpr size_type extent() const GSL_NOEXCEPT + { + static_assert(Dim < Rank, + "dimension should be less than rank (dimension count starts from 0)"); + return m_extents[Dim]; + } + + constexpr index_type index_bounds() const GSL_NOEXCEPT { return m_extents; } + constexpr const_iterator begin() const GSL_NOEXCEPT + { + return const_iterator{*this, index_type{}}; + } + + constexpr const_iterator end() const GSL_NOEXCEPT + { + return const_iterator{*this, index_bounds()}; + } + +private: + index_type m_extents; + index_type m_strides; +}; + +template +struct is_bounds : std::integral_constant +{ +}; +template +struct is_bounds> : std::integral_constant +{ +}; +template +struct is_bounds> : std::integral_constant +{ +}; + +template +class bounds_iterator +{ +public: + static const std::size_t rank = IndexType::rank; + using iterator_category = std::random_access_iterator_tag; + using value_type = IndexType; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + using index_type = value_type; + using index_size_type = typename IndexType::value_type; + template + explicit bounds_iterator(const Bounds& bnd, value_type curr) GSL_NOEXCEPT + : boundary_(bnd.index_bounds()), + curr_(std::move(curr)) + { + static_assert(is_bounds::value, "Bounds type must be provided"); + } + + constexpr reference operator*() const GSL_NOEXCEPT { return curr_; } + + constexpr pointer operator->() const GSL_NOEXCEPT { return &curr_; } + + constexpr bounds_iterator& operator++() GSL_NOEXCEPT + { + for (std::size_t i = rank; i-- > 0;) { + if (curr_[i] < boundary_[i] - 1) { + curr_[i]++; + return *this; + } + curr_[i] = 0; + } + // If we're here we've wrapped over - set to past-the-end. + curr_ = boundary_; + return *this; + } + + constexpr bounds_iterator operator++(int) GSL_NOEXCEPT + { + auto ret = *this; + ++(*this); + return ret; + } + + constexpr bounds_iterator& operator--() GSL_NOEXCEPT + { + if (!less(curr_, boundary_)) { + // if at the past-the-end, set to last element + for (std::size_t i = 0; i < rank; ++i) { + curr_[i] = boundary_[i] - 1; + } + return *this; + } + for (std::size_t i = rank; i-- > 0;) { + if (curr_[i] >= 1) { + curr_[i]--; + return *this; + } + curr_[i] = boundary_[i] - 1; + } + // If we're here the preconditions were violated + // "pre: there exists s such that r == ++s" + Expects(false); + return *this; + } + + constexpr bounds_iterator operator--(int) GSL_NOEXCEPT + { + auto ret = *this; + --(*this); + return ret; + } + + constexpr bounds_iterator operator+(difference_type n) const GSL_NOEXCEPT + { + bounds_iterator ret{*this}; + return ret += n; + } + + constexpr bounds_iterator& operator+=(difference_type n) GSL_NOEXCEPT + { + auto linear_idx = linearize(curr_) + n; + std::remove_const_t stride = 0; + stride[rank - 1] = 1; + for (std::size_t i = rank - 1; i-- > 0;) { + stride[i] = stride[i + 1] * boundary_[i + 1]; + } + for (std::size_t i = 0; i < rank; ++i) { + curr_[i] = linear_idx / stride[i]; + linear_idx = linear_idx % stride[i]; + } + // index is out of bounds of the array + Expects(!less(curr_, index_type{}) && !less(boundary_, curr_)); + return *this; + } + + constexpr bounds_iterator operator-(difference_type n) const GSL_NOEXCEPT + { + bounds_iterator ret{*this}; + return ret -= n; + } + + constexpr bounds_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; } + + constexpr difference_type operator-(const bounds_iterator& rhs) const GSL_NOEXCEPT + { + return linearize(curr_) - linearize(rhs.curr_); + } + + constexpr value_type operator[](difference_type n) const GSL_NOEXCEPT { return *(*this + n); } + + constexpr bool operator==(const bounds_iterator& rhs) const GSL_NOEXCEPT + { + return curr_ == rhs.curr_; + } + + constexpr bool operator!=(const bounds_iterator& rhs) const GSL_NOEXCEPT + { + return !(*this == rhs); + } + + constexpr bool operator<(const bounds_iterator& rhs) const GSL_NOEXCEPT + { + return less(curr_, rhs.curr_); + } + + constexpr bool operator<=(const bounds_iterator& rhs) const GSL_NOEXCEPT + { + return !(rhs < *this); + } + + constexpr bool operator>(const bounds_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; } + + constexpr bool operator>=(const bounds_iterator& rhs) const GSL_NOEXCEPT + { + return !(rhs > *this); + } + + void swap(bounds_iterator& rhs) GSL_NOEXCEPT + { + std::swap(boundary_, rhs.boundary_); + std::swap(curr_, rhs.curr_); + } + +private: + constexpr bool less(index_type& one, index_type& other) const GSL_NOEXCEPT + { + for (std::size_t i = 0; i < rank; ++i) { + if (one[i] < other[i]) return true; + } + return false; + } + + constexpr index_size_type linearize(const value_type& idx) const GSL_NOEXCEPT + { + // TODO: Smarter impl. + // Check if past-the-end + index_size_type multiplier = 1; + index_size_type res = 0; + if (!less(idx, boundary_)) { + res = 1; + for (std::size_t i = rank; i-- > 0;) { + res += (idx[i] - 1) * multiplier; + multiplier *= boundary_[i]; + } + } + else + { + for (std::size_t i = rank; i-- > 0;) { + res += idx[i] * multiplier; + multiplier *= boundary_[i]; + } + } + return res; + } + + value_type boundary_; + std::remove_const_t curr_; +}; + +template +bounds_iterator operator+(typename bounds_iterator::difference_type n, + const bounds_iterator& rhs) GSL_NOEXCEPT +{ + return rhs + n; +} + +namespace details +{ + template + constexpr std::enable_if_t< + std::is_same::value, + typename Bounds::index_type> + make_stride(const Bounds& bnd) GSL_NOEXCEPT + { + return bnd.strides(); + } + + // Make a stride vector from bounds, assuming contiguous memory. + template + constexpr std::enable_if_t< + std::is_same::value, + typename Bounds::index_type> + make_stride(const Bounds& bnd) GSL_NOEXCEPT + { + auto extents = bnd.index_bounds(); + typename Bounds::size_type stride[Bounds::rank] = {}; + + stride[Bounds::rank - 1] = 1; + for (std::size_t i = 1; i < Bounds::rank; ++i) { + stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i]; + } + return {stride}; + } + + template + void verifyBoundsReshape(const BoundsSrc& src, const BoundsDest& dest) + { + static_assert(is_bounds::value && is_bounds::value, + "The src type and dest type must be bounds"); + static_assert(std::is_same::value, + "The source type must be a contiguous bounds"); + static_assert(BoundsDest::static_size == dynamic_range || + BoundsSrc::static_size == dynamic_range || + BoundsDest::static_size == BoundsSrc::static_size, + "The source bounds must have same size as dest bounds"); + Expects(src.size() == dest.size()); + } + +} // namespace details + +template +class contiguous_span_iterator; +template +class general_span_iterator; + +template +struct dim_t +{ + static const std::ptrdiff_t value = DimSize; +}; +template <> +struct dim_t +{ + static const std::ptrdiff_t value = dynamic_range; + const std::ptrdiff_t dvalue; + constexpr dim_t(std::ptrdiff_t size) GSL_NOEXCEPT : dvalue(size) {} +}; + +template = 0)>> +constexpr dim_t dim() GSL_NOEXCEPT +{ + return dim_t(); +} + +template > +constexpr dim_t dim(std::ptrdiff_t n) GSL_NOEXCEPT +{ + return dim_t<>(n); +} + +template +class multi_span; +template +class strided_span; + +namespace details +{ + template + struct SpanTypeTraits + { + using value_type = T; + using size_type = std::size_t; + }; + + template + struct SpanTypeTraits::type> + { + using value_type = typename Traits::span_traits::value_type; + using size_type = typename Traits::span_traits::size_type; + }; + + template + struct SpanArrayTraits + { + using type = multi_span; + using value_type = T; + using bounds_type = static_bounds; + using pointer = T*; + using reference = T&; + }; + template + struct SpanArrayTraits : SpanArrayTraits + { + }; + + template + BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size + { + Expects(totalSize >= 0 && totalSize <= PTRDIFF_MAX); + return BoundsType{totalSize}; + } + template + BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size + { + Expects(BoundsType::static_size <= totalSize); + return {}; + } + template + BoundsType newBoundsHelper(std::ptrdiff_t totalSize) + { + static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1"); + return newBoundsHelperImpl( + totalSize, std::integral_constant()); + } + + struct Sep + { + }; + + template + T static_as_multi_span_helper(Sep, Args... args) + { + return T{narrow_cast(args)...}; + } + template + std::enable_if_t< + !std::is_same>::value && !std::is_same::value, T> + static_as_multi_span_helper(Arg, Args... args) + { + return static_as_multi_span_helper(args...); + } + template + T static_as_multi_span_helper(dim_t val, Args... args) + { + return static_as_multi_span_helper(args..., val.dvalue); + } + + template + struct static_as_multi_span_static_bounds_helper + { + using type = static_bounds<(Dimensions::value)...>; + }; + + template + struct is_multi_span_oracle : std::false_type + { + }; + + template + struct is_multi_span_oracle> + : std::true_type + { + }; + + template + struct is_multi_span_oracle> : std::true_type + { + }; + + template + struct is_multi_span : is_multi_span_oracle> + { + }; +} + +template +class multi_span +{ + // TODO do we still need this? + template + friend class multi_span; + +public: + using bounds_type = static_bounds; + static const std::size_t Rank = bounds_type::rank; + using size_type = typename bounds_type::size_type; + using index_type = typename bounds_type::index_type; + using value_type = ValueType; + using const_value_type = std::add_const_t; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; + using iterator = contiguous_span_iterator; + using const_span = multi_span; + using const_iterator = contiguous_span_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using sliced_type = + std::conditional_t>; + +private: + pointer data_; + bounds_type bounds_; + + friend iterator; + friend const_iterator; + +public: + // default constructor - same as constructing from nullptr_t + constexpr multi_span() GSL_NOEXCEPT : multi_span(nullptr, bounds_type{}) + { + static_assert(bounds_type::dynamic_rank != 0 || + (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), + "Default construction of multi_span only possible " + "for dynamic or fixed, zero-length spans."); + } + + // construct from nullptr - get an empty multi_span + constexpr multi_span(std::nullptr_t) GSL_NOEXCEPT : multi_span(nullptr, bounds_type{}) + { + static_assert(bounds_type::dynamic_rank != 0 || + (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), + "nullptr_t construction of multi_span only possible " + "for dynamic or fixed, zero-length spans."); + } + + // construct from nullptr with size of 0 (helps with template function calls) + template ::value>> + constexpr multi_span(std::nullptr_t, IntType size) GSL_NOEXCEPT + : multi_span(nullptr, bounds_type{}) + { + static_assert(bounds_type::dynamic_rank != 0 || + (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), + "nullptr_t construction of multi_span only possible " + "for dynamic or fixed, zero-length spans."); + Expects(size == 0); + } + + // construct from a single element + constexpr multi_span(reference data) GSL_NOEXCEPT : multi_span(&data, bounds_type{1}) + { + static_assert(bounds_type::dynamic_rank > 0 || bounds_type::static_size == 0 || + bounds_type::static_size == 1, + "Construction from a single element only possible " + "for dynamic or fixed spans of length 0 or 1."); + } + + // prevent constructing from temporaries for single-elements + constexpr multi_span(value_type&&) = delete; + + // construct from pointer + length + constexpr multi_span(pointer ptr, size_type size) GSL_NOEXCEPT + : multi_span(ptr, bounds_type{size}) + { + } + + // construct from pointer + length - multidimensional + constexpr multi_span(pointer data, bounds_type bounds) GSL_NOEXCEPT : data_(data), + bounds_(std::move(bounds)) + { + Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0); + } + + // construct from begin,end pointer pair + template ::value && + details::LessThan::value>> + constexpr multi_span(pointer begin, Ptr end) + : multi_span(begin, + details::newBoundsHelper(static_cast(end) - begin)) + { + Expects(begin != nullptr && end != nullptr && begin <= static_cast(end)); + } + + // construct from n-dimensions static array + template > + constexpr multi_span(T (&arr)[N]) + : multi_span(reinterpret_cast(arr), bounds_type{typename Helper::bounds_type{}}) + { + static_assert(std::is_convertible::value, + "Cannot convert from source type to target multi_span type."); + static_assert(std::is_convertible::value, + "Cannot construct a multi_span from an array with fewer elements."); + } + + // construct from n-dimensions dynamic array (e.g. new int[m][4]) + // (precedence will be lower than the 1-dimension pointer) + template > + constexpr multi_span(T* const& data, size_type size) + : multi_span(reinterpret_cast(data), typename Helper::bounds_type{size}) + { + static_assert(std::is_convertible::value, + "Cannot convert from source type to target multi_span type."); + } + + // construct from std::array + template + constexpr multi_span(std::array& arr) + : multi_span(arr.data(), bounds_type{static_bounds{}}) + { + static_assert( + std::is_convertible(*)[]>::value, + "Cannot convert from source type to target multi_span type."); + static_assert(std::is_convertible, bounds_type>::value, + "You cannot construct a multi_span from a std::array of smaller size."); + } + + // construct from const std::array + template + constexpr multi_span(const std::array& arr) + : multi_span(arr.data(), bounds_type{static_bounds{}}) + { + static_assert(std::is_convertible(*)[]>::value, + "Cannot convert from source type to target multi_span type."); + static_assert(std::is_convertible, bounds_type>::value, + "You cannot construct a multi_span from a std::array of smaller size."); + } + + // prevent constructing from temporary std::array + template + constexpr multi_span(std::array&& arr) = delete; + + // construct from containers + // future: could use contiguous_iterator_traits to identify only contiguous containers + // type-requirements: container must have .size(), operator[] which are value_type compatible + template ::value && + std::is_convertible::value && + std::is_same().size(), + *std::declval().data())>, + DataType>::value>> + constexpr multi_span(Cont& cont) + : multi_span(static_cast(cont.data()), + details::newBoundsHelper(narrow_cast(cont.size()))) + { + } + + // prevent constructing from temporary containers + template ::value && + std::is_convertible::value && + std::is_same().size(), + *std::declval().data())>, + DataType>::value>> + explicit constexpr multi_span(Cont&& cont) = delete; + + // construct from a convertible multi_span + template , + typename = std::enable_if_t::value && + std::is_convertible::value>> + constexpr multi_span(multi_span other) GSL_NOEXCEPT + : data_(other.data_), + bounds_(other.bounds_) + { + } + + // trivial copy and move + constexpr multi_span(const multi_span&) = default; + constexpr multi_span(multi_span&&) = default; + + // trivial assignment + constexpr multi_span& operator=(const multi_span&) = default; + constexpr multi_span& operator=(multi_span&&) = default; + + // first() - extract the first Count elements into a new multi_span + template + constexpr multi_span first() const GSL_NOEXCEPT + { + static_assert(Count >= 0, "Count must be >= 0."); + static_assert(bounds_type::static_size == dynamic_range || + Count <= bounds_type::static_size, + "Count is out of bounds."); + + Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); + return {this->data(), Count}; + } + + // first() - extract the first count elements into a new multi_span + constexpr multi_span first(size_type count) const GSL_NOEXCEPT + { + Expects(count >= 0 && count <= this->size()); + return {this->data(), count}; + } + + // last() - extract the last Count elements into a new multi_span + template + constexpr multi_span last() const GSL_NOEXCEPT + { + static_assert(Count >= 0, "Count must be >= 0."); + static_assert(bounds_type::static_size == dynamic_range || + Count <= bounds_type::static_size, + "Count is out of bounds."); + + Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); + return {this->data() + this->size() - Count, Count}; + } + + // last() - extract the last count elements into a new multi_span + constexpr multi_span last(size_type count) const GSL_NOEXCEPT + { + Expects(count >= 0 && count <= this->size()); + return {this->data() + this->size() - count, count}; + } + + // subspan() - create a subview of Count elements starting at Offset + template + constexpr multi_span subspan() const GSL_NOEXCEPT + { + static_assert(Count >= 0, "Count must be >= 0."); + static_assert(Offset >= 0, "Offset must be >= 0."); + static_assert(bounds_type::static_size == dynamic_range || + ((Offset <= bounds_type::static_size) && + Count <= bounds_type::static_size - Offset), + "You must describe a sub-range within bounds of the multi_span."); + + Expects(bounds_type::static_size != dynamic_range || + (Offset <= this->size() && Count <= this->size() - Offset)); + return {this->data() + Offset, Count}; + } + + // subspan() - create a subview of count elements starting at offset + // supplying dynamic_range for count will consume all available elements from offset + constexpr multi_span + subspan(size_type offset, size_type count = dynamic_range) const GSL_NOEXCEPT + { + Expects((offset >= 0 && offset <= this->size()) && + (count == dynamic_range || (count <= this->size() - offset))); + return {this->data() + offset, count == dynamic_range ? this->length() - offset : count}; + } + + // section - creates a non-contiguous, strided multi_span from a contiguous one + constexpr strided_span section(index_type origin, + index_type extents) const GSL_NOEXCEPT + { + size_type size = this->bounds().total_size() - this->bounds().linearize(origin); + return {&this->operator[](origin), size, + strided_bounds{extents, details::make_stride(bounds())}}; + } + + // length of the multi_span in elements + constexpr size_type size() const GSL_NOEXCEPT { return bounds_.size(); } + + // length of the multi_span in elements + constexpr size_type length() const GSL_NOEXCEPT { return this->size(); } + + // length of the multi_span in bytes + constexpr size_type size_bytes() const GSL_NOEXCEPT + { + return narrow_cast(sizeof(value_type)) * this->size(); + } + + // length of the multi_span in bytes + constexpr size_type length_bytes() const GSL_NOEXCEPT { return this->size_bytes(); } + + constexpr bool empty() const GSL_NOEXCEPT { return this->size() == 0; } + + static constexpr std::size_t rank() { return Rank; } + + template + constexpr size_type extent() const GSL_NOEXCEPT + { + static_assert(Dim < Rank, + "Dimension should be less than rank (dimension count starts from 0)."); + return bounds_.template extent(); + } + + template + constexpr size_type extent(IntType dim) const GSL_NOEXCEPT + { + return bounds_.extent(dim); + } + + constexpr bounds_type bounds() const GSL_NOEXCEPT { return bounds_; } + + constexpr pointer data() const GSL_NOEXCEPT { return data_; } + + template + constexpr reference operator()(FirstIndex idx) + { + return this->operator[](narrow_cast(idx)); + } + + template + constexpr reference operator()(FirstIndex firstIndex, OtherIndices... indices) + { + index_type idx = {narrow_cast(firstIndex), + narrow_cast(indices)...}; + return this->operator[](idx); + } + + constexpr reference operator[](const index_type& idx) const GSL_NOEXCEPT + { + return data_[bounds_.linearize(idx)]; + } + + template 1), typename Ret = std::enable_if_t> + constexpr Ret operator[](size_type idx) const GSL_NOEXCEPT + { + Expects(idx >= 0 && idx < bounds_.size()); // index is out of bounds of the array + const size_type ridx = idx * bounds_.stride(); + + // index is out of bounds of the underlying data + Expects(ridx < bounds_.total_size()); + return Ret{data_ + ridx, bounds_.slice()}; + } + + constexpr iterator begin() const GSL_NOEXCEPT { return iterator{this, true}; } + + constexpr iterator end() const GSL_NOEXCEPT { return iterator{this, false}; } + + constexpr const_iterator cbegin() const GSL_NOEXCEPT + { + return const_iterator{reinterpret_cast(this), true}; + } + + constexpr const_iterator cend() const GSL_NOEXCEPT + { + return const_iterator{reinterpret_cast(this), false}; + } + + constexpr reverse_iterator rbegin() const GSL_NOEXCEPT { return reverse_iterator{end()}; } + + constexpr reverse_iterator rend() const GSL_NOEXCEPT { return reverse_iterator{begin()}; } + + constexpr const_reverse_iterator crbegin() const GSL_NOEXCEPT + { + return const_reverse_iterator{cend()}; + } + + constexpr const_reverse_iterator crend() const GSL_NOEXCEPT + { + return const_reverse_iterator{cbegin()}; + } + + template , std::remove_cv_t>::value>> + constexpr bool + operator==(const multi_span& other) const GSL_NOEXCEPT + { + return bounds_.size() == other.bounds_.size() && + (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); + } + + template , std::remove_cv_t>::value>> + constexpr bool + operator!=(const multi_span& other) const GSL_NOEXCEPT + { + return !(*this == other); + } + + template , std::remove_cv_t>::value>> + constexpr bool + operator<(const multi_span& other) const GSL_NOEXCEPT + { + return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); + } + + template , std::remove_cv_t>::value>> + constexpr bool + operator<=(const multi_span& other) const GSL_NOEXCEPT + { + return !(other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool + operator>(const multi_span& other) const GSL_NOEXCEPT + { + return (other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool + operator>=(const multi_span& other) const GSL_NOEXCEPT + { + return !(*this < other); + } +}; + +// +// Free functions for manipulating spans +// + +// reshape a multi_span into a different dimensionality +// DimCount and Enabled here are workarounds for a bug in MSVC 2015 +template 0), typename = std::enable_if_t> +constexpr auto as_multi_span(SpanType s, Dimensions2... dims) + -> multi_span +{ + static_assert(details::is_multi_span::value, + "Variadic as_multi_span() is for reshaping existing spans."); + using BoundsType = + typename multi_span::bounds_type; + auto tobounds = details::static_as_multi_span_helper(dims..., details::Sep{}); + details::verifyBoundsReshape(s.bounds(), tobounds); + return {s.data(), tobounds}; +} + +// convert a multi_span to a multi_span +template +multi_span as_bytes(multi_span s) GSL_NOEXCEPT +{ + static_assert(std::is_trivial>::value, + "The value_type of multi_span must be a trivial type."); + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +// convert a multi_span to a multi_span (a writeable byte multi_span) +// this is not currently a portable function that can be relied upon to work +// on all implementations. It should be considered an experimental extension +// to the standard GSL interface. +template +multi_span as_writeable_bytes(multi_span s) GSL_NOEXCEPT +{ + static_assert(std::is_trivial>::value, + "The value_type of multi_span must be a trivial type."); + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +// convert a multi_span to a multi_span +// this is not currently a portable function that can be relied upon to work +// on all implementations. It should be considered an experimental extension +// to the standard GSL interface. +template +constexpr auto +as_multi_span(multi_span s) GSL_NOEXCEPT -> multi_span< + const U, static_cast( + multi_span::bounds_type::static_size != dynamic_range + ? (static_cast( + multi_span::bounds_type::static_size) / + sizeof(U)) + : dynamic_range)> +{ + using ConstByteSpan = multi_span; + static_assert( + std::is_trivial>::value && + (ConstByteSpan::bounds_type::static_size == dynamic_range || + ConstByteSpan::bounds_type::static_size % narrow_cast(sizeof(U)) == 0), + "Target type must be a trivial type and its size must match the byte array size"); + + Expects((s.size_bytes() % narrow_cast(sizeof(U))) == 0 && + (s.size_bytes() / narrow_cast(sizeof(U))) < PTRDIFF_MAX); + return {reinterpret_cast(s.data()), + s.size_bytes() / narrow_cast(sizeof(U))}; +} + +// convert a multi_span to a multi_span +// this is not currently a portable function that can be relied upon to work +// on all implementations. It should be considered an experimental extension +// to the standard GSL interface. +template +constexpr auto as_multi_span(multi_span s) GSL_NOEXCEPT + -> multi_span( + multi_span::bounds_type::static_size != dynamic_range + ? static_cast( + multi_span::bounds_type::static_size) / + sizeof(U) + : dynamic_range)> +{ + using ByteSpan = multi_span; + static_assert( + std::is_trivial>::value && + (ByteSpan::bounds_type::static_size == dynamic_range || + ByteSpan::bounds_type::static_size % sizeof(U) == 0), + "Target type must be a trivial type and its size must match the byte array size"); + + Expects((s.size_bytes() % sizeof(U)) == 0); + return {reinterpret_cast(s.data()), + s.size_bytes() / narrow_cast(sizeof(U))}; +} + +template +constexpr auto as_multi_span(T* const& ptr, dim_t... args) + -> multi_span, Dimensions...> +{ + return {reinterpret_cast*>(ptr), + details::static_as_multi_span_helper>(args..., + details::Sep{})}; +} + +template +constexpr auto as_multi_span(T* arr, std::ptrdiff_t len) -> + typename details::SpanArrayTraits::type +{ + return {reinterpret_cast*>(arr), len}; +} + +template +constexpr auto as_multi_span(T (&arr)[N]) -> typename details::SpanArrayTraits::type +{ + return {arr}; +} + +template +constexpr multi_span as_multi_span(const std::array& arr) +{ + return {arr}; +} + +template +constexpr multi_span as_multi_span(const std::array&&) = delete; + +template +constexpr multi_span as_multi_span(std::array& arr) +{ + return {arr}; +} + +template +constexpr multi_span as_multi_span(T* begin, T* end) +{ + return {begin, end}; +} + +template +constexpr auto as_multi_span(Cont& arr) -> std::enable_if_t< + !details::is_multi_span>::value, + multi_span, dynamic_range>> +{ + Expects(arr.size() < PTRDIFF_MAX); + return {arr.data(), narrow_cast(arr.size())}; +} + +template +constexpr auto as_multi_span(Cont&& arr) -> std::enable_if_t< + !details::is_multi_span>::value, + multi_span, dynamic_range>> = delete; + +// from basic_string which doesn't have nonconst .data() member like other contiguous containers +template +constexpr auto as_multi_span(std::basic_string& str) + -> multi_span +{ + Expects(str.size() < PTRDIFF_MAX); + return {&str[0], narrow_cast(str.size())}; +} + +// strided_span is an extension that is not strictly part of the GSL at this time. +// It is kept here while the multidimensional interface is still being defined. +template +class strided_span +{ +public: + using bounds_type = strided_bounds; + using size_type = typename bounds_type::size_type; + using index_type = typename bounds_type::index_type; + using value_type = ValueType; + using const_value_type = std::add_const_t; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; + using iterator = general_span_iterator; + using const_strided_span = strided_span; + using const_iterator = general_span_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using sliced_type = + std::conditional_t>; + +private: + pointer data_; + bounds_type bounds_; + + friend iterator; + friend const_iterator; + template + friend class strided_span; + +public: + // from raw data + constexpr strided_span(pointer ptr, size_type size, bounds_type bounds) + : data_(ptr), bounds_(std::move(bounds)) + { + Expects((bounds_.size() > 0 && ptr != nullptr) || bounds_.size() == 0); + // Bounds cross data boundaries + Expects(this->bounds().total_size() <= size); + (void) size; + } + + // from static array of size N + template + constexpr strided_span(value_type (&values)[N], bounds_type bounds) + : strided_span(values, N, std::move(bounds)) + { + } + + // from array view + template ::value, + typename = std::enable_if_t> + constexpr strided_span(multi_span av, bounds_type bounds) + : strided_span(av.data(), av.bounds().total_size(), std::move(bounds)) + { + } + + // convertible + template ::value>> + constexpr strided_span(const strided_span& other) + : data_(other.data_), bounds_(other.bounds_) + { + } + + // convert from bytes + template + constexpr strided_span< + typename std::enable_if::value, OtherValueType>::type, + Rank> + as_strided_span() const + { + static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && + (sizeof(OtherValueType) % sizeof(value_type) == 0), + "OtherValueType should have a size to contain a multiple of ValueTypes"); + auto d = narrow_cast(sizeof(OtherValueType) / sizeof(value_type)); + + size_type size = this->bounds().total_size() / d; + return {const_cast(reinterpret_cast(this->data())), + size, + bounds_type{resize_extent(this->bounds().index_bounds(), d), + resize_stride(this->bounds().strides(), d)}}; + } + + constexpr strided_span section(index_type origin, index_type extents) const + { + size_type size = this->bounds().total_size() - this->bounds().linearize(origin); + return {&this->operator[](origin), size, + bounds_type{extents, details::make_stride(bounds())}}; + } + + constexpr reference operator[](const index_type& idx) const + { + return data_[bounds_.linearize(idx)]; + } + + template 1), typename Ret = std::enable_if_t> + constexpr Ret operator[](size_type idx) const + { + Expects(idx < bounds_.size()); // index is out of bounds of the array + const size_type ridx = idx * bounds_.stride(); + + // index is out of bounds of the underlying data + Expects(ridx < bounds_.total_size()); + return {data_ + ridx, bounds_.slice().total_size(), bounds_.slice()}; + } + + constexpr bounds_type bounds() const GSL_NOEXCEPT { return bounds_; } + + template + constexpr size_type extent() const GSL_NOEXCEPT + { + static_assert(Dim < Rank, + "dimension should be less than Rank (dimension count starts from 0)"); + return bounds_.template extent(); + } + + constexpr size_type size() const GSL_NOEXCEPT { return bounds_.size(); } + + constexpr pointer data() const GSL_NOEXCEPT { return data_; } + + constexpr explicit operator bool() const GSL_NOEXCEPT { return data_ != nullptr; } + + constexpr iterator begin() const { return iterator{this, true}; } + + constexpr iterator end() const { return iterator{this, false}; } + + constexpr const_iterator cbegin() const + { + return const_iterator{reinterpret_cast(this), true}; + } + + constexpr const_iterator cend() const + { + return const_iterator{reinterpret_cast(this), false}; + } + + constexpr reverse_iterator rbegin() const { return reverse_iterator{end()}; } + + constexpr reverse_iterator rend() const { return reverse_iterator{begin()}; } + + constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator{cend()}; } + + constexpr const_reverse_iterator crend() const { return const_reverse_iterator{cbegin()}; } + + template , std::remove_cv_t>::value>> + constexpr bool + operator==(const strided_span& other) const GSL_NOEXCEPT + { + return bounds_.size() == other.bounds_.size() && + (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); + } + + template , std::remove_cv_t>::value>> + constexpr bool + operator!=(const strided_span& other) const GSL_NOEXCEPT + { + return !(*this == other); + } + + template , std::remove_cv_t>::value>> + constexpr bool + operator<(const strided_span& other) const GSL_NOEXCEPT + { + return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); + } + + template , std::remove_cv_t>::value>> + constexpr bool + operator<=(const strided_span& other) const GSL_NOEXCEPT + { + return !(other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool + operator>(const strided_span& other) const GSL_NOEXCEPT + { + return (other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool + operator>=(const strided_span& other) const GSL_NOEXCEPT + { + return !(*this < other); + } + +private: + static index_type resize_extent(const index_type& extent, std::ptrdiff_t d) + { + // The last dimension of the array needs to contain a multiple of new type elements + Expects(extent[Rank - 1] >= d && (extent[Rank - 1] % d == 0)); + + index_type ret = extent; + ret[Rank - 1] /= d; + + return ret; + } + + template > + static index_type resize_stride(const index_type& strides, std::ptrdiff_t, void* = nullptr) + { + // Only strided arrays with regular strides can be resized + Expects(strides[Rank - 1] == 1); + + return strides; + } + + template 1), typename = std::enable_if_t> + static index_type resize_stride(const index_type& strides, std::ptrdiff_t d) + { + // Only strided arrays with regular strides can be resized + Expects(strides[Rank - 1] == 1); + // The strides must have contiguous chunks of + // memory that can contain a multiple of new type elements + Expects(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0)); + + for (std::size_t i = Rank - 1; i > 0; --i) { + // Only strided arrays with regular strides can be resized + Expects((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0)); + } + + index_type ret = strides / d; + ret[Rank - 1] = 1; + + return ret; + } +}; + +template +class contiguous_span_iterator +{ +public: + using iterator_category = std::random_access_iterator_tag; + using value_type = typename Span::value_type; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + +private: + template + friend class multi_span; + + pointer data_; + const Span* m_validator; + void validateThis() const + { + // iterator is out of range of the array + Expects(data_ >= m_validator->data_ && data_ < m_validator->data_ + m_validator->size()); + } + contiguous_span_iterator(const Span* container, bool isbegin) + : data_(isbegin ? container->data_ : container->data_ + container->size()) + , m_validator(container) + { + } + +public: + reference operator*() const GSL_NOEXCEPT + { + validateThis(); + return *data_; + } + pointer operator->() const GSL_NOEXCEPT + { + validateThis(); + return data_; + } + contiguous_span_iterator& operator++() GSL_NOEXCEPT + { + ++data_; + return *this; + } + contiguous_span_iterator operator++(int) GSL_NOEXCEPT + { + auto ret = *this; + ++(*this); + return ret; + } + contiguous_span_iterator& operator--() GSL_NOEXCEPT + { + --data_; + return *this; + } + contiguous_span_iterator operator--(int) GSL_NOEXCEPT + { + auto ret = *this; + --(*this); + return ret; + } + contiguous_span_iterator operator+(difference_type n) const GSL_NOEXCEPT + { + contiguous_span_iterator ret{*this}; + return ret += n; + } + contiguous_span_iterator& operator+=(difference_type n) GSL_NOEXCEPT + { + data_ += n; + return *this; + } + contiguous_span_iterator operator-(difference_type n) const GSL_NOEXCEPT + { + contiguous_span_iterator ret{*this}; + return ret -= n; + } + contiguous_span_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; } + difference_type operator-(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT + { + Expects(m_validator == rhs.m_validator); + return data_ - rhs.data_; + } + reference operator[](difference_type n) const GSL_NOEXCEPT { return *(*this + n); } + bool operator==(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT + { + Expects(m_validator == rhs.m_validator); + return data_ == rhs.data_; + } + bool operator!=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT + { + return !(*this == rhs); + } + bool operator<(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT + { + Expects(m_validator == rhs.m_validator); + return data_ < rhs.data_; + } + bool operator<=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT + { + return !(rhs < *this); + } + bool operator>(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; } + bool operator>=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT + { + return !(rhs > *this); + } + void swap(contiguous_span_iterator& rhs) GSL_NOEXCEPT + { + std::swap(data_, rhs.data_); + std::swap(m_validator, rhs.m_validator); + } +}; + +template +contiguous_span_iterator operator+(typename contiguous_span_iterator::difference_type n, + const contiguous_span_iterator& rhs) GSL_NOEXCEPT +{ + return rhs + n; +} + +template +class general_span_iterator +{ +public: + using iterator_category = std::random_access_iterator_tag; + using value_type = typename Span::value_type; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + +private: + template + friend class strided_span; + + const Span* m_container; + typename Span::bounds_type::iterator m_itr; + general_span_iterator(const Span* container, bool isbegin) + : m_container(container) + , m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) + { + } + +public: + reference operator*() GSL_NOEXCEPT { return (*m_container)[*m_itr]; } + pointer operator->() GSL_NOEXCEPT { return &(*m_container)[*m_itr]; } + general_span_iterator& operator++() GSL_NOEXCEPT + { + ++m_itr; + return *this; + } + general_span_iterator operator++(int) GSL_NOEXCEPT + { + auto ret = *this; + ++(*this); + return ret; + } + general_span_iterator& operator--() GSL_NOEXCEPT + { + --m_itr; + return *this; + } + general_span_iterator operator--(int) GSL_NOEXCEPT + { + auto ret = *this; + --(*this); + return ret; + } + general_span_iterator operator+(difference_type n) const GSL_NOEXCEPT + { + general_span_iterator ret{*this}; + return ret += n; + } + general_span_iterator& operator+=(difference_type n) GSL_NOEXCEPT + { + m_itr += n; + return *this; + } + general_span_iterator operator-(difference_type n) const GSL_NOEXCEPT + { + general_span_iterator ret{*this}; + return ret -= n; + } + general_span_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; } + difference_type operator-(const general_span_iterator& rhs) const GSL_NOEXCEPT + { + Expects(m_container == rhs.m_container); + return m_itr - rhs.m_itr; + } + value_type operator[](difference_type n) const GSL_NOEXCEPT { return (*m_container)[m_itr[n]]; } + + bool operator==(const general_span_iterator& rhs) const GSL_NOEXCEPT + { + Expects(m_container == rhs.m_container); + return m_itr == rhs.m_itr; + } + bool operator!=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(*this == rhs); } + bool operator<(const general_span_iterator& rhs) const GSL_NOEXCEPT + { + Expects(m_container == rhs.m_container); + return m_itr < rhs.m_itr; + } + bool operator<=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(rhs < *this); } + bool operator>(const general_span_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; } + bool operator>=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(rhs > *this); } + void swap(general_span_iterator& rhs) GSL_NOEXCEPT + { + std::swap(m_itr, rhs.m_itr); + std::swap(m_container, rhs.m_container); + } +}; + +template +general_span_iterator operator+(typename general_span_iterator::difference_type n, + const general_span_iterator& rhs) GSL_NOEXCEPT +{ + return rhs + n; +} + +} // namespace gsl + +#undef GSL_NOEXCEPT + +#ifdef _MSC_VER +#if _MSC_VER < 1910 + +#undef constexpr +#pragma pop_macro("constexpr") +#endif // _MSC_VER < 1910 + +#pragma warning(pop) + +#endif // _MSC_VER + +#if __GNUC__ > 6 +#pragma GCC diagnostic pop +#endif // __GNUC__ > 6 + +#endif // GSL_MULTI_SPAN_H diff --git a/src/serlio/utils/stduuid/gsl/pointers b/src/serlio/utils/stduuid/gsl/pointers new file mode 100644 index 00000000..69499d6f --- /dev/null +++ b/src/serlio/utils/stduuid/gsl/pointers @@ -0,0 +1,193 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_POINTERS_H +#define GSL_POINTERS_H + +#include // for Ensures, Expects + +#include // for forward +#include // for ptrdiff_t, nullptr_t, ostream, size_t +#include // for shared_ptr, unique_ptr +#include // for hash +#include // for enable_if_t, is_convertible, is_assignable + +#if defined(_MSC_VER) && _MSC_VER < 1910 +#pragma push_macro("constexpr") +#define constexpr /*constexpr*/ + +#endif // defined(_MSC_VER) && _MSC_VER < 1910 + +namespace gsl +{ + +// +// GSL.owner: ownership pointers +// +using std::unique_ptr; +using std::shared_ptr; + +// +// owner +// +// owner is designed as a bridge for code that must deal directly with owning pointers for some reason +// +// T must be a pointer type +// - disallow construction from any type other than pointer type +// +template ::value>> +using owner = T; + +// +// not_null +// +// Restricts a pointer or smart pointer to only hold non-null values. +// +// Has zero size overhead over T. +// +// If T is a pointer (i.e. T == U*) then +// - allow construction from U* +// - disallow construction from nullptr_t +// - disallow default construction +// - ensure construction from null U* fails +// - allow implicit conversion to U* +// +template +class not_null +{ +public: + static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); + + template ::value>> + constexpr explicit not_null(U&& u) : ptr_(std::forward(u)) + { + Expects(ptr_ != nullptr); + } + + template ::value>> + constexpr explicit not_null(T u) : ptr_(u) + { + Expects(ptr_ != nullptr); + } + + template ::value>> + constexpr not_null(const not_null& other) : not_null(other.get()) + { + } + + not_null(not_null&& other) = default; + not_null(const not_null& other) = default; + not_null& operator=(const not_null& other) = default; + + constexpr T get() const + { + Ensures(ptr_ != nullptr); + return ptr_; + } + + constexpr operator T() const { return get(); } + constexpr T operator->() const { return get(); } + constexpr decltype(auto) operator*() const { return *get(); } + + // prevents compilation when someone attempts to assign a null pointer constant + not_null(std::nullptr_t) = delete; + not_null& operator=(std::nullptr_t) = delete; + + // unwanted operators...pointers only point to single objects! + not_null& operator++() = delete; + not_null& operator--() = delete; + not_null operator++(int) = delete; + not_null operator--(int) = delete; + not_null& operator+=(std::ptrdiff_t) = delete; + not_null& operator-=(std::ptrdiff_t) = delete; + void operator[](std::ptrdiff_t) const = delete; + +private: + T ptr_; +}; + +template +std::ostream& operator<<(std::ostream& os, const not_null& val) +{ + os << val.get(); + return os; +} + +template +auto operator==(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() == rhs.get()) +{ + return lhs.get() == rhs.get(); +} + +template +auto operator!=(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() != rhs.get()) +{ + return lhs.get() != rhs.get(); +} + +template +auto operator<(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() < rhs.get()) +{ + return lhs.get() < rhs.get(); +} + +template +auto operator<=(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() <= rhs.get()) +{ + return lhs.get() <= rhs.get(); +} + +template +auto operator>(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() > rhs.get()) +{ + return lhs.get() > rhs.get(); +} + +template +auto operator>=(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() >= rhs.get()) +{ + return lhs.get() >= rhs.get(); +} + +// more unwanted operators +template +std::ptrdiff_t operator-(const not_null&, const not_null&) = delete; +template +not_null operator-(const not_null&, std::ptrdiff_t) = delete; +template +not_null operator+(const not_null&, std::ptrdiff_t) = delete; +template +not_null operator+(std::ptrdiff_t, const not_null&) = delete; + +} // namespace gsl + +namespace std +{ +template +struct hash> +{ + std::size_t operator()(const gsl::not_null& value) const { return hash{}(value); } +}; + +} // namespace std + +#if defined(_MSC_VER) && _MSC_VER < 1910 +#undef constexpr +#pragma pop_macro("constexpr") + +#endif // defined(_MSC_VER) && _MSC_VER < 1910 + +#endif // GSL_POINTERS_H diff --git a/src/serlio/utils/stduuid/gsl/span b/src/serlio/utils/stduuid/gsl/span new file mode 100644 index 00000000..dcb78a61 --- /dev/null +++ b/src/serlio/utils/stduuid/gsl/span @@ -0,0 +1,766 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_SPAN_H +#define GSL_SPAN_H + +#include // for Expects +#include // for byte +#include // for narrow_cast, narrow + +#include // for lexicographical_compare +#include // for array +#include // for ptrdiff_t, size_t, nullptr_t +#include // for reverse_iterator, distance, random_access_... +#include +#include +#include // for enable_if_t, declval, is_convertible, inte... +#include + +#ifdef _MSC_VER +#pragma warning(push) + +// turn off some warnings that are noisy about our Expects statements +#pragma warning(disable : 4127) // conditional expression is constant +#pragma warning(disable : 4702) // unreachable code + +// blanket turn off warnings from CppCoreCheck for now +// so people aren't annoyed by them when running the tool. +// more targeted suppressions will be added in a future update to the GSL +#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) + +#if _MSC_VER < 1910 +#pragma push_macro("constexpr") +#define constexpr /*constexpr*/ +#define GSL_USE_STATIC_CONSTEXPR_WORKAROUND + +#endif // _MSC_VER < 1910 +#else // _MSC_VER + +// See if we have enough C++17 power to use a static constexpr data member +// without needing an out-of-line definition +#if !(defined(__cplusplus) && (__cplusplus >= 201703L)) +#define GSL_USE_STATIC_CONSTEXPR_WORKAROUND +#endif // !(defined(__cplusplus) && (__cplusplus >= 201703L)) + +#endif // _MSC_VER + +// GCC 7 does not like the signed unsigned missmatch (size_t ptrdiff_t) +// While there is a conversion from signed to unsigned, it happens at +// compiletime, so the compiler wouldn't have to warn indiscriminently, but +// could check if the source value actually doesn't fit into the target type +// and only warn in those cases. +#if __GNUC__ > 6 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" +#endif + +namespace gsl +{ + +// [views.constants], constants +constexpr const std::ptrdiff_t dynamic_extent = -1; + +template +class span; + +// implementation details +namespace details +{ + template + struct is_span_oracle : std::false_type + { + }; + + template + struct is_span_oracle> : std::true_type + { + }; + + template + struct is_span : public is_span_oracle> + { + }; + + template + struct is_std_array_oracle : std::false_type + { + }; + + template + struct is_std_array_oracle> : std::true_type + { + }; + + template + struct is_std_array : public is_std_array_oracle> + { + }; + + template + struct is_allowed_extent_conversion + : public std::integral_constant + { + }; + + template + struct is_allowed_element_type_conversion + : public std::integral_constant::value> + { + }; + + template + class span_iterator + { + using element_type_ = typename Span::element_type; + + public: + +#ifdef _MSC_VER + // Tell Microsoft standard library that span_iterators are checked. + using _Unchecked_type = typename Span::pointer; +#endif + + using iterator_category = std::random_access_iterator_tag; + using value_type = std::remove_cv_t; + using difference_type = typename Span::index_type; + + using reference = std::conditional_t&; + using pointer = std::add_pointer_t; + + span_iterator() = default; + + constexpr span_iterator(const Span* span, typename Span::index_type idx) noexcept + : span_(span), index_(idx) + {} + + friend span_iterator; + template* = nullptr> + constexpr span_iterator(const span_iterator& other) noexcept + : span_iterator(other.span_, other.index_) + { + } + + constexpr reference operator*() const + { + Expects(index_ != span_->size()); + return *(span_->data() + index_); + } + + constexpr pointer operator->() const + { + Expects(index_ != span_->size()); + return span_->data() + index_; + } + + constexpr span_iterator& operator++() + { + Expects(0 <= index_ && index_ != span_->size()); + ++index_; + return *this; + } + + constexpr span_iterator operator++(int) + { + auto ret = *this; + ++(*this); + return ret; + } + + constexpr span_iterator& operator--() + { + Expects(index_ != 0 && index_ <= span_->size()); + --index_; + return *this; + } + + constexpr span_iterator operator--(int) + { + auto ret = *this; + --(*this); + return ret; + } + + constexpr span_iterator operator+(difference_type n) const + { + auto ret = *this; + return ret += n; + } + + friend constexpr span_iterator operator+(difference_type n, span_iterator const& rhs) + { + return rhs + n; + } + + constexpr span_iterator& operator+=(difference_type n) + { + Expects((index_ + n) >= 0 && (index_ + n) <= span_->size()); + index_ += n; + return *this; + } + + constexpr span_iterator operator-(difference_type n) const + { + auto ret = *this; + return ret -= n; + } + + constexpr span_iterator& operator-=(difference_type n) { return *this += -n; } + + constexpr difference_type operator-(span_iterator rhs) const + { + Expects(span_ == rhs.span_); + return index_ - rhs.index_; + } + + constexpr reference operator[](difference_type n) const + { + return *(*this + n); + } + + constexpr friend bool operator==(span_iterator lhs, + span_iterator rhs) noexcept + { + return lhs.span_ == rhs.span_ && lhs.index_ == rhs.index_; + } + + constexpr friend bool operator!=(span_iterator lhs, + span_iterator rhs) noexcept + { + return !(lhs == rhs); + } + + constexpr friend bool operator<(span_iterator lhs, + span_iterator rhs) noexcept + { + return lhs.index_ < rhs.index_; + } + + constexpr friend bool operator<=(span_iterator lhs, + span_iterator rhs) noexcept + { + return !(rhs < lhs); + } + + constexpr friend bool operator>(span_iterator lhs, + span_iterator rhs) noexcept + { + return rhs < lhs; + } + + constexpr friend bool operator>=(span_iterator lhs, + span_iterator rhs) noexcept + { + return !(rhs > lhs); + } + +#ifdef _MSC_VER + // MSVC++ iterator debugging support; allows STL algorithms in 15.8+ + // to unwrap span_iterator to a pointer type after a range check in STL + // algorithm calls + friend constexpr void _Verify_range(span_iterator lhs, + span_iterator rhs) noexcept + { // test that [lhs, rhs) forms a valid range inside an STL algorithm + Expects(lhs.span_ == rhs.span_ // range spans have to match + && lhs.index_ <= rhs.index_); // range must not be transposed + } + + constexpr void _Verify_offset(const difference_type n) const noexcept + { // test that the iterator *this + n is a valid range in an STL + // algorithm call + Expects((index_ + n) >= 0 && (index_ + n) <= span_->size()); + } + + constexpr pointer _Unwrapped() const noexcept + { // after seeking *this to a high water mark, or using one of the + // _Verify_xxx functions above, unwrap this span_iterator to a raw + // pointer + return span_->data() + index_; + } + + // Tell the STL that span_iterator should not be unwrapped if it can't + // validate in advance, even in release / optimized builds: +#if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND) + static constexpr const bool _Unwrap_when_unverified = false; +#else + static constexpr bool _Unwrap_when_unverified = false; +#endif + constexpr void _Seek_to(const pointer p) noexcept + { // adjust the position of *this to previously verified location p + // after _Unwrapped + index_ = p - span_->data(); + } +#endif + + protected: + const Span* span_ = nullptr; + std::ptrdiff_t index_ = 0; + }; + + template + class extent_type + { + public: + using index_type = std::ptrdiff_t; + + static_assert(Ext >= 0, "A fixed-size span must be >= 0 in size."); + + constexpr extent_type() noexcept {} + + template + constexpr extent_type(extent_type ext) + { + static_assert(Other == Ext || Other == dynamic_extent, + "Mismatch between fixed-size extent and size of initializing data."); + Expects(ext.size() == Ext); + } + + constexpr extent_type(index_type size) { Expects(size == Ext); } + + constexpr index_type size() const noexcept { return Ext; } + }; + + template <> + class extent_type + { + public: + using index_type = std::ptrdiff_t; + + template + explicit constexpr extent_type(extent_type ext) : size_(ext.size()) + { + } + + explicit constexpr extent_type(index_type size) : size_(size) { Expects(size >= 0); } + + constexpr index_type size() const noexcept { return size_; } + + private: + index_type size_; + }; + + template + struct calculate_subspan_type + { + using type = span; + }; +} // namespace details + +// [span], class template span +template +class span +{ +public: + // constants and types + using element_type = ElementType; + using value_type = std::remove_cv_t; + using index_type = std::ptrdiff_t; + using pointer = element_type*; + using reference = element_type&; + + using iterator = details::span_iterator, false>; + using const_iterator = details::span_iterator, true>; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + using size_type = index_type; + +#if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND) + static constexpr const index_type extent { Extent }; +#else + static constexpr index_type extent { Extent }; +#endif + + // [span.cons], span constructors, copy, assignment, and destructor + template " SFINAE, + // since "std::enable_if_t" is ill-formed when Extent is greater than 0. + class = std::enable_if_t<(Dependent || Extent <= 0)>> + constexpr span() noexcept : storage_(nullptr, details::extent_type<0>()) + { + } + + constexpr span(pointer ptr, index_type count) : storage_(ptr, count) {} + + constexpr span(pointer firstElem, pointer lastElem) + : storage_(firstElem, std::distance(firstElem, lastElem)) + { + } + + template + constexpr span(element_type (&arr)[N]) noexcept + : storage_(KnownNotNull{&arr[0]}, details::extent_type()) + { + } + + template > + constexpr span(std::array& arr) noexcept + : storage_(&arr[0], details::extent_type()) + { + } + + template + constexpr span(const std::array, N>& arr) noexcept + : storage_(&arr[0], details::extent_type()) + { + } + + // NB: the SFINAE here uses .data() as a incomplete/imperfect proxy for the requirement + // on Container to be a contiguous sequence container. + template ::value && !details::is_std_array::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> + constexpr span(Container& cont) : span(cont.data(), narrow(cont.size())) + { + } + + template ::value && !details::is_span::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> + constexpr span(const Container& cont) : span(cont.data(), narrow(cont.size())) + { + } + + constexpr span(const span& other) noexcept = default; + + template < + class OtherElementType, std::ptrdiff_t OtherExtent, + class = std::enable_if_t< + details::is_allowed_extent_conversion::value && + details::is_allowed_element_type_conversion::value>> + constexpr span(const span& other) + : storage_(other.data(), details::extent_type(other.size())) + { + } + + ~span() noexcept = default; + constexpr span& operator=(const span& other) noexcept = default; + + // [span.sub], span subviews + template + constexpr span first() const + { + Expects(Count >= 0 && Count <= size()); + return {data(), Count}; + } + + template + constexpr span last() const + { + Expects(Count >= 0 && size() - Count >= 0); + return {data() + (size() - Count), Count}; + } + + template + constexpr auto subspan() const -> typename details::calculate_subspan_type::type + { + Expects((Offset >= 0 && size() - Offset >= 0) && + (Count == dynamic_extent || (Count >= 0 && Offset + Count <= size()))); + + return {data() + Offset, Count == dynamic_extent ? size() - Offset : Count}; + } + + constexpr span first(index_type count) const + { + Expects(count >= 0 && count <= size()); + return {data(), count}; + } + + constexpr span last(index_type count) const + { + return make_subspan(size() - count, dynamic_extent, subspan_selector{}); + } + + constexpr span subspan(index_type offset, + index_type count = dynamic_extent) const + { + return make_subspan(offset, count, subspan_selector{}); + } + + + // [span.obs], span observers + constexpr index_type size() const noexcept { return storage_.size(); } + constexpr index_type size_bytes() const noexcept + { + return size() * narrow_cast(sizeof(element_type)); + } + constexpr bool empty() const noexcept { return size() == 0; } + + // [span.elem], span element access + constexpr reference operator[](index_type idx) const + { + Expects(idx >= 0 && idx < storage_.size()); + return data()[idx]; + } + + constexpr reference at(index_type idx) const { return this->operator[](idx); } + constexpr reference operator()(index_type idx) const { return this->operator[](idx); } + constexpr pointer data() const noexcept { return storage_.data(); } + + // [span.iter], span iterator support + constexpr iterator begin() const noexcept { return {this, 0}; } + constexpr iterator end() const noexcept { return {this, size()}; } + + constexpr const_iterator cbegin() const noexcept { return {this, 0}; } + constexpr const_iterator cend() const noexcept { return {this, size()}; } + + constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } + constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } + + constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{cend()}; } + constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator{cbegin()}; } + +#ifdef _MSC_VER + // Tell MSVC how to unwrap spans in range-based-for + constexpr pointer _Unchecked_begin() const noexcept { return data(); } + constexpr pointer _Unchecked_end() const noexcept { return data() + size(); } +#endif // _MSC_VER + +private: + + // Needed to remove unnecessary null check in subspans + struct KnownNotNull + { + pointer p; + }; + + // this implementation detail class lets us take advantage of the + // empty base class optimization to pay for only storage of a single + // pointer in the case of fixed-size spans + template + class storage_type : public ExtentType + { + public: + // KnownNotNull parameter is needed to remove unnecessary null check + // in subspans and constructors from arrays + template + constexpr storage_type(KnownNotNull data, OtherExtentType ext) : ExtentType(ext), data_(data.p) + { + Expects(ExtentType::size() >= 0); + } + + + template + constexpr storage_type(pointer data, OtherExtentType ext) : ExtentType(ext), data_(data) + { + Expects(ExtentType::size() >= 0); + Expects(data || ExtentType::size() == 0); + } + + constexpr pointer data() const noexcept { return data_; } + + private: + pointer data_; + }; + + storage_type> storage_; + + // The rest is needed to remove unnecessary null check + // in subspans and constructors from arrays + constexpr span(KnownNotNull ptr, index_type count) : storage_(ptr, count) {} + + template + class subspan_selector {}; + + template + span make_subspan(index_type offset, + index_type count, + subspan_selector) const + { + span tmp(*this); + return tmp.subspan(offset, count); + } + + span make_subspan(index_type offset, + index_type count, + subspan_selector) const + { + Expects(offset >= 0 && size() - offset >= 0); + if (count == dynamic_extent) + { + return { KnownNotNull{ data() + offset }, size() - offset }; + } + + Expects(count >= 0 && size() - offset >= count); + return { KnownNotNull{ data() + offset }, count }; + } +}; + +#if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND) +template +constexpr const typename span::index_type span::extent; +#endif + + +// [span.comparison], span comparison operators +template +constexpr bool operator==(span l, + span r) +{ + return std::equal(l.begin(), l.end(), r.begin(), r.end()); +} + +template +constexpr bool operator!=(span l, + span r) +{ + return !(l == r); +} + +template +constexpr bool operator<(span l, + span r) +{ + return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); +} + +template +constexpr bool operator<=(span l, + span r) +{ + return !(l > r); +} + +template +constexpr bool operator>(span l, + span r) +{ + return r < l; +} + +template +constexpr bool operator>=(span l, + span r) +{ + return !(l < r); +} + +namespace details +{ + // if we only supported compilers with good constexpr support then + // this pair of classes could collapse down to a constexpr function + + // we should use a narrow_cast<> to go to std::size_t, but older compilers may not see it as + // constexpr + // and so will fail compilation of the template + template + struct calculate_byte_size + : std::integral_constant(sizeof(ElementType) * + static_cast(Extent))> + { + }; + + template + struct calculate_byte_size + : std::integral_constant + { + }; +} + +// [span.objectrep], views of object representation +template +span::value> +as_bytes(span s) noexcept +{ + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +template ::value>> +span::value> +as_writeable_bytes(span s) noexcept +{ + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +// +// make_span() - Utility functions for creating spans +// +template +constexpr span make_span(ElementType* ptr, typename span::index_type count) +{ + return span(ptr, count); +} + +template +constexpr span make_span(ElementType* firstElem, ElementType* lastElem) +{ + return span(firstElem, lastElem); +} + +template +constexpr span make_span(ElementType (&arr)[N]) noexcept +{ + return span(arr); +} + +template +constexpr span make_span(Container& cont) +{ + return span(cont); +} + +template +constexpr span make_span(const Container& cont) +{ + return span(cont); +} + +template +constexpr span make_span(Ptr& cont, std::ptrdiff_t count) +{ + return span(cont, count); +} + +template +constexpr span make_span(Ptr& cont) +{ + return span(cont); +} + +// Specialization of gsl::at for span +template +constexpr ElementType& at(span s, index i) +{ + // No bounds checking here because it is done in span::operator[] called below + return s[i]; +} + +} // namespace gsl + +#ifdef _MSC_VER +#if _MSC_VER < 1910 +#undef constexpr +#pragma pop_macro("constexpr") + +#endif // _MSC_VER < 1910 + +#pragma warning(pop) +#endif // _MSC_VER + +#if __GNUC__ > 6 +#pragma GCC diagnostic pop +#endif // __GNUC__ > 6 + +#endif // GSL_SPAN_H diff --git a/src/serlio/utils/stduuid/gsl/string_span b/src/serlio/utils/stduuid/gsl/string_span new file mode 100644 index 00000000..c08f2467 --- /dev/null +++ b/src/serlio/utils/stduuid/gsl/string_span @@ -0,0 +1,730 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_STRING_SPAN_H +#define GSL_STRING_SPAN_H + +#include // for Ensures, Expects +#include // for narrow_cast +#include // for operator!=, operator==, dynamic_extent + +#include // for equal, lexicographical_compare +#include // for array +#include // for ptrdiff_t, size_t, nullptr_t +#include // for PTRDIFF_MAX +#include +#include // for basic_string, allocator, char_traits +#include // for declval, is_convertible, enable_if_t, add_... + +#ifdef _MSC_VER +#pragma warning(push) + +// blanket turn off warnings from CppCoreCheck for now +// so people aren't annoyed by them when running the tool. +// more targeted suppressions will be added in a future update to the GSL +#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) + +#if _MSC_VER < 1910 +#pragma push_macro("constexpr") +#define constexpr /*constexpr*/ + +#endif // _MSC_VER < 1910 +#endif // _MSC_VER + +// In order to test the library, we need it to throw exceptions that we can catch +#ifdef GSL_THROW_ON_CONTRACT_VIOLATION +#define GSL_NOEXCEPT /*noexcept*/ +#else +#define GSL_NOEXCEPT noexcept +#endif // GSL_THROW_ON_CONTRACT_VIOLATION + +namespace gsl +{ +// +// czstring and wzstring +// +// These are "tag" typedefs for C-style strings (i.e. null-terminated character arrays) +// that allow static analysis to help find bugs. +// +// There are no additional features/semantics that we can find a way to add inside the +// type system for these types that will not either incur significant runtime costs or +// (sometimes needlessly) break existing programs when introduced. +// + +template +using basic_zstring = CharT*; + +template +using czstring = basic_zstring; + +template +using cwzstring = basic_zstring; + +template +using cu16zstring = basic_zstring; + +template +using cu32zstring = basic_zstring; + +template +using zstring = basic_zstring; + +template +using wzstring = basic_zstring; + +template +using u16zstring = basic_zstring; + +template +using u32zstring = basic_zstring; + +namespace details +{ + template + std::ptrdiff_t string_length(const CharT* str, std::ptrdiff_t n) + { + if (str == nullptr || n <= 0) return 0; + + const span str_span{str, n}; + + std::ptrdiff_t len = 0; + while (len < n && str_span[len]) len++; + + return len; + } +} + +// +// ensure_sentinel() +// +// Provides a way to obtain an span from a contiguous sequence +// that ends with a (non-inclusive) sentinel value. +// +// Will fail-fast if sentinel cannot be found before max elements are examined. +// +template +span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX) +{ + auto cur = seq; + while ((cur - seq) < max && *cur != Sentinel) ++cur; + Ensures(*cur == Sentinel); + return {seq, cur - seq}; +} + +// +// ensure_z - creates a span for a zero terminated strings. +// Will fail fast if a null-terminator cannot be found before +// the limit of size_type. +// +template +span ensure_z(CharT* const& sz, std::ptrdiff_t max = PTRDIFF_MAX) +{ + return ensure_sentinel(sz, max); +} + +template +span ensure_z(CharT (&sz)[N]) +{ + return ensure_z(&sz[0], static_cast(N)); +} + +template +span::type, dynamic_extent> +ensure_z(Cont& cont) +{ + return ensure_z(cont.data(), static_cast(cont.size())); +} + +template +class basic_string_span; + +namespace details +{ + template + struct is_basic_string_span_oracle : std::false_type + { + }; + + template + struct is_basic_string_span_oracle> : std::true_type + { + }; + + template + struct is_basic_string_span : is_basic_string_span_oracle> + { + }; +} + +// +// string_span and relatives +// +template +class basic_string_span +{ +public: + using element_type = CharT; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_lvalue_reference_t>; + using impl_type = span; + + using index_type = typename impl_type::index_type; + using iterator = typename impl_type::iterator; + using const_iterator = typename impl_type::const_iterator; + using reverse_iterator = typename impl_type::reverse_iterator; + using const_reverse_iterator = typename impl_type::const_reverse_iterator; + + // default (empty) + constexpr basic_string_span() GSL_NOEXCEPT = default; + + // copy + constexpr basic_string_span(const basic_string_span& other) GSL_NOEXCEPT = default; + + // assign + constexpr basic_string_span& operator=(const basic_string_span& other) GSL_NOEXCEPT = default; + + constexpr basic_string_span(pointer ptr, index_type length) : span_(ptr, length) {} + constexpr basic_string_span(pointer firstElem, pointer lastElem) : span_(firstElem, lastElem) {} + + // From static arrays - if 0-terminated, remove 0 from the view + // All other containers allow 0s within the length, so we do not remove them + template + constexpr basic_string_span(element_type (&arr)[N]) : span_(remove_z(arr)) + { + } + + template > + constexpr basic_string_span(std::array& arr) GSL_NOEXCEPT : span_(arr) + { + } + + template > + constexpr basic_string_span(const std::array& arr) GSL_NOEXCEPT + : span_(arr) + { + } + + // Container signature should work for basic_string after C++17 version exists + template + constexpr basic_string_span(std::basic_string& str) + : span_(&str[0], narrow_cast(str.length())) + { + } + + template + constexpr basic_string_span(const std::basic_string& str) + : span_(&str[0], str.length()) + { + } + + // from containers. Containers must have a pointer type and data() function signatures + template ::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> + constexpr basic_string_span(Container& cont) : span_(cont) + { + } + + template ::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> + constexpr basic_string_span(const Container& cont) : span_(cont) + { + } + + // from string_span + template < + class OtherValueType, std::ptrdiff_t OtherExtent, + class = std::enable_if_t::impl_type, impl_type>::value>> + constexpr basic_string_span(basic_string_span other) + : span_(other.data(), other.length()) + { + } + + template + constexpr basic_string_span first() const + { + return {span_.template first()}; + } + + constexpr basic_string_span first(index_type count) const + { + return {span_.first(count)}; + } + + template + constexpr basic_string_span last() const + { + return {span_.template last()}; + } + + constexpr basic_string_span last(index_type count) const + { + return {span_.last(count)}; + } + + template + constexpr basic_string_span subspan() const + { + return {span_.template subspan()}; + } + + constexpr basic_string_span + subspan(index_type offset, index_type count = dynamic_extent) const + { + return {span_.subspan(offset, count)}; + } + + constexpr reference operator[](index_type idx) const { return span_[idx]; } + constexpr reference operator()(index_type idx) const { return span_[idx]; } + + constexpr pointer data() const { return span_.data(); } + + constexpr index_type length() const GSL_NOEXCEPT { return span_.size(); } + constexpr index_type size() const GSL_NOEXCEPT { return span_.size(); } + constexpr index_type size_bytes() const GSL_NOEXCEPT { return span_.size_bytes(); } + constexpr index_type length_bytes() const GSL_NOEXCEPT { return span_.length_bytes(); } + constexpr bool empty() const GSL_NOEXCEPT { return size() == 0; } + + constexpr iterator begin() const GSL_NOEXCEPT { return span_.begin(); } + constexpr iterator end() const GSL_NOEXCEPT { return span_.end(); } + + constexpr const_iterator cbegin() const GSL_NOEXCEPT { return span_.cbegin(); } + constexpr const_iterator cend() const GSL_NOEXCEPT { return span_.cend(); } + + constexpr reverse_iterator rbegin() const GSL_NOEXCEPT { return span_.rbegin(); } + constexpr reverse_iterator rend() const GSL_NOEXCEPT { return span_.rend(); } + + constexpr const_reverse_iterator crbegin() const GSL_NOEXCEPT { return span_.crbegin(); } + constexpr const_reverse_iterator crend() const GSL_NOEXCEPT { return span_.crend(); } + +private: + static impl_type remove_z(pointer const& sz, std::ptrdiff_t max) + { + return {sz, details::string_length(sz, max)}; + } + + template + static impl_type remove_z(element_type (&sz)[N]) + { + return remove_z(&sz[0], narrow_cast(N)); + } + + impl_type span_; +}; + +template +using string_span = basic_string_span; + +template +using cstring_span = basic_string_span; + +template +using wstring_span = basic_string_span; + +template +using cwstring_span = basic_string_span; + +template +using u16string_span = basic_string_span; + +template +using cu16string_span = basic_string_span; + +template +using u32string_span = basic_string_span; + +template +using cu32string_span = basic_string_span; + +// +// to_string() allow (explicit) conversions from string_span to string +// + +template +std::basic_string::type> +to_string(basic_string_span view) +{ + return {view.data(), static_cast(view.length())}; +} + +template , + typename Allocator = std::allocator, typename gCharT, std::ptrdiff_t Extent> +std::basic_string to_basic_string(basic_string_span view) +{ + return {view.data(), static_cast(view.length())}; +} + +template +basic_string_span::value> +as_bytes(basic_string_span s) noexcept +{ + return { reinterpret_cast(s.data()), s.size_bytes() }; +} + +template ::value>> +basic_string_span::value> +as_writeable_bytes(basic_string_span s) noexcept +{ + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +// zero-terminated string span, used to convert +// zero-terminated spans to legacy strings +template +class basic_zstring_span +{ +public: + using value_type = CharT; + using const_value_type = std::add_const_t; + + using pointer = std::add_pointer_t; + using const_pointer = std::add_pointer_t; + + using zstring_type = basic_zstring; + using const_zstring_type = basic_zstring; + + using impl_type = span; + using string_span_type = basic_string_span; + + constexpr basic_zstring_span(impl_type s) GSL_NOEXCEPT : span_(s) + { + // expects a zero-terminated span + Expects(s[s.size() - 1] == '\0'); + } + + // copy + constexpr basic_zstring_span(const basic_zstring_span& other) = default; + + // move + constexpr basic_zstring_span(basic_zstring_span&& other) = default; + + // assign + constexpr basic_zstring_span& operator=(const basic_zstring_span& other) = default; + + // move assign + constexpr basic_zstring_span& operator=(basic_zstring_span&& other) = default; + + constexpr bool empty() const GSL_NOEXCEPT { return span_.size() == 0; } + + constexpr string_span_type as_string_span() const GSL_NOEXCEPT + { + auto sz = span_.size(); + return { span_.data(), sz > 1 ? sz - 1 : 0 }; + } + constexpr string_span_type ensure_z() const GSL_NOEXCEPT { return gsl::ensure_z(span_); } + + constexpr const_zstring_type assume_z() const GSL_NOEXCEPT { return span_.data(); } + +private: + impl_type span_; +}; + +template +using zstring_span = basic_zstring_span; + +template +using wzstring_span = basic_zstring_span; + +template +using u16zstring_span = basic_zstring_span; + +template +using u32zstring_span = basic_zstring_span; + +template +using czstring_span = basic_zstring_span; + +template +using cwzstring_span = basic_zstring_span; + +template +using cu16zstring_span = basic_zstring_span; + +template +using cu32zstring_span = basic_zstring_span; + +// operator == +template ::value || + std::is_convertible>>::value>> +bool operator==(const gsl::basic_string_span& one, const T& other) GSL_NOEXCEPT +{ + const gsl::basic_string_span> tmp(other); + return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end()); +} + +template ::value && + std::is_convertible>>::value>> +bool operator==(const T& one, const gsl::basic_string_span& other) GSL_NOEXCEPT +{ + gsl::basic_string_span> tmp(one); + return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end()); +} + +// operator != +template , Extent>>::value>> +bool operator!=(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT +{ + return !(one == other); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename = std::enable_if_t< + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> +bool operator!=(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT +{ + return !(one == other); +} + +// operator< +template , Extent>>::value>> +bool operator<(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT +{ + const gsl::basic_string_span, Extent> tmp(other); + return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename = std::enable_if_t< + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> +bool operator<(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT +{ + gsl::basic_string_span, Extent> tmp(one); + return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); +} + +#ifndef _MSC_VER + +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename DataType = typename T::value_type, + typename = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator<(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT +{ + gsl::basic_string_span, Extent> tmp(other); + return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename DataType = typename T::value_type, + typename = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator<(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT +{ + gsl::basic_string_span, Extent> tmp(one); + return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); +} +#endif + +// operator <= +template , Extent>>::value>> +bool operator<=(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT +{ + return !(other < one); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename = std::enable_if_t< + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> +bool operator<=(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT +{ + return !(other < one); +} + +#ifndef _MSC_VER + +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename DataType = typename T::value_type, + typename = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator<=(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT +{ + return !(other < one); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename DataType = typename T::value_type, + typename = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator<=(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT +{ + return !(other < one); +} +#endif + +// operator> +template , Extent>>::value>> +bool operator>(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT +{ + return other < one; +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename = std::enable_if_t< + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> +bool operator>(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT +{ + return other < one; +} + +#ifndef _MSC_VER + +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename DataType = typename T::value_type, + typename = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator>(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT +{ + return other < one; +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename DataType = typename T::value_type, + typename = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator>(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT +{ + return other < one; +} +#endif + +// operator >= +template , Extent>>::value>> +bool operator>=(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT +{ + return !(one < other); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename = std::enable_if_t< + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> +bool operator>=(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT +{ + return !(one < other); +} + +#ifndef _MSC_VER + +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename DataType = typename T::value_type, + typename = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator>=(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT +{ + return !(one < other); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename DataType = typename T::value_type, + typename = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator>=(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT +{ + return !(one < other); +} +#endif +} // namespace gsl + +#undef GSL_NOEXCEPT + +#ifdef _MSC_VER +#pragma warning(pop) + +#if _MSC_VER < 1910 +#undef constexpr +#pragma pop_macro("constexpr") + +#endif // _MSC_VER < 1910 +#endif // _MSC_VER + +#endif // GSL_STRING_SPAN_H diff --git a/src/serlio/utils/stduuid/uuid.h b/src/serlio/utils/stduuid/uuid.h new file mode 100644 index 00000000..89565c81 --- /dev/null +++ b/src/serlio/utils/stduuid/uuid.h @@ -0,0 +1,873 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#elif defined(__linux__) || defined(__unix__) +#include +#elif defined(__APPLE__) +#include +#endif + +namespace uuids +{ + namespace detail + { + template + constexpr inline unsigned char hex2char(TChar const ch) + { + if (ch >= static_cast('0') && ch <= static_cast('9')) + return ch - static_cast('0'); + if (ch >= static_cast('a') && ch <= static_cast('f')) + return 10 + ch - static_cast('a'); + if (ch >= static_cast('A') && ch <= static_cast('F')) + return 10 + ch - static_cast('A'); + return 0; + } + + template + constexpr inline bool is_hex(TChar const ch) + { + return + (ch >= static_cast('0') && ch <= static_cast('9')) || + (ch >= static_cast('a') && ch <= static_cast('f')) || + (ch >= static_cast('A') && ch <= static_cast('F')); + } + + template + constexpr inline unsigned char hexpair2char(TChar const a, TChar const b) + { + return (hex2char(a) << 4) | hex2char(b); + } + + class sha1 + { + public: + using digest32_t = uint32_t[5]; + using digest8_t = uint8_t[20]; + + static constexpr unsigned int block_bytes = 64; + + inline static uint32_t left_rotate(uint32_t value, size_t const count) + { + return (value << count) ^ (value >> (32 - count)); + } + + sha1() { reset(); } + + void reset() + { + m_digest[0] = 0x67452301; + m_digest[1] = 0xEFCDAB89; + m_digest[2] = 0x98BADCFE; + m_digest[3] = 0x10325476; + m_digest[4] = 0xC3D2E1F0; + m_blockByteIndex = 0; + m_byteCount = 0; + } + + void process_byte(uint8_t octet) + { + this->m_block[this->m_blockByteIndex++] = octet; + ++this->m_byteCount; + if (m_blockByteIndex == block_bytes) + { + this->m_blockByteIndex = 0; + process_block(); + } + } + + void process_block(void const * const start, void const * const end) + { + const uint8_t* begin = static_cast(start); + const uint8_t* finish = static_cast(end); + while (begin != finish) + { + process_byte(*begin); + begin++; + } + } + + void process_bytes(void const * const data, size_t const len) + { + const uint8_t* block = static_cast(data); + process_block(block, block + len); + } + + uint32_t const * get_digest(digest32_t digest) + { + size_t const bitCount = this->m_byteCount * 8; + process_byte(0x80); + if (this->m_blockByteIndex > 56) { + while (m_blockByteIndex != 0) { + process_byte(0); + } + while (m_blockByteIndex < 56) { + process_byte(0); + } + } + else { + while (m_blockByteIndex < 56) { + process_byte(0); + } + } + process_byte(0); + process_byte(0); + process_byte(0); + process_byte(0); + process_byte(static_cast((bitCount >> 24) & 0xFF)); + process_byte(static_cast((bitCount >> 16) & 0xFF)); + process_byte(static_cast((bitCount >> 8) & 0xFF)); + process_byte(static_cast((bitCount) & 0xFF)); + + memcpy(digest, m_digest, 5 * sizeof(uint32_t)); + return digest; + } + + uint8_t const * get_digest_bytes(digest8_t digest) + { + digest32_t d32; + get_digest(d32); + size_t di = 0; + digest[di++] = ((d32[0] >> 24) & 0xFF); + digest[di++] = ((d32[0] >> 16) & 0xFF); + digest[di++] = ((d32[0] >> 8) & 0xFF); + digest[di++] = ((d32[0]) & 0xFF); + + digest[di++] = ((d32[1] >> 24) & 0xFF); + digest[di++] = ((d32[1] >> 16) & 0xFF); + digest[di++] = ((d32[1] >> 8) & 0xFF); + digest[di++] = ((d32[1]) & 0xFF); + + digest[di++] = ((d32[2] >> 24) & 0xFF); + digest[di++] = ((d32[2] >> 16) & 0xFF); + digest[di++] = ((d32[2] >> 8) & 0xFF); + digest[di++] = ((d32[2]) & 0xFF); + + digest[di++] = ((d32[3] >> 24) & 0xFF); + digest[di++] = ((d32[3] >> 16) & 0xFF); + digest[di++] = ((d32[3] >> 8) & 0xFF); + digest[di++] = ((d32[3]) & 0xFF); + + digest[di++] = ((d32[4] >> 24) & 0xFF); + digest[di++] = ((d32[4] >> 16) & 0xFF); + digest[di++] = ((d32[4] >> 8) & 0xFF); + digest[di++] = ((d32[4]) & 0xFF); + + return digest; + } + + private: + void process_block() + { + uint32_t w[80]; + for (size_t i = 0; i < 16; i++) { + w[i] = (m_block[i * 4 + 0] << 24); + w[i] |= (m_block[i * 4 + 1] << 16); + w[i] |= (m_block[i * 4 + 2] << 8); + w[i] |= (m_block[i * 4 + 3]); + } + for (size_t i = 16; i < 80; i++) { + w[i] = left_rotate((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1); + } + + uint32_t a = m_digest[0]; + uint32_t b = m_digest[1]; + uint32_t c = m_digest[2]; + uint32_t d = m_digest[3]; + uint32_t e = m_digest[4]; + + for (std::size_t i = 0; i < 80; ++i) + { + uint32_t f = 0; + uint32_t k = 0; + + if (i < 20) { + f = (b & c) | (~b & d); + k = 0x5A827999; + } + else if (i < 40) { + f = b ^ c ^ d; + k = 0x6ED9EBA1; + } + else if (i < 60) { + f = (b & c) | (b & d) | (c & d); + k = 0x8F1BBCDC; + } + else { + f = b ^ c ^ d; + k = 0xCA62C1D6; + } + uint32_t temp = left_rotate(a, 5) + f + e + k + w[i]; + e = d; + d = c; + c = left_rotate(b, 30); + b = a; + a = temp; + } + + m_digest[0] += a; + m_digest[1] += b; + m_digest[2] += c; + m_digest[3] += d; + m_digest[4] += e; + } + + private: + digest32_t m_digest; + uint8_t m_block[64]; + size_t m_blockByteIndex; + size_t m_byteCount; + }; + } + + // UUID format https://tools.ietf.org/html/rfc4122 + // Field NDR Data Type Octet # Note + // -------------------------------------------------------------------------------------------------------------------------- + // time_low unsigned long 0 - 3 The low field of the timestamp. + // time_mid unsigned short 4 - 5 The middle field of the timestamp. + // time_hi_and_version unsigned short 6 - 7 The high field of the timestamp multiplexed with the version number. + // clock_seq_hi_and_reserved unsigned small 8 The high field of the clock sequence multiplexed with the variant. + // clock_seq_low unsigned small 9 The low field of the clock sequence. + // node character 10 - 15 The spatially unique node identifier. + // -------------------------------------------------------------------------------------------------------------------------- + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | time_low | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | time_mid | time_hi_and_version | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // |clk_seq_hi_res | clk_seq_low | node (0-1) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | node (2-5) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + // indicated by a bit pattern in octet 8, marked with N in xxxxxxxx-xxxx-xxxx-Nxxx-xxxxxxxxxxxx + enum class uuid_variant + { + // NCS backward compatibility (with the obsolete Apollo Network Computing System 1.5 UUID format) + // N bit pattern: 0xxx + // > the first 6 octets of the UUID are a 48-bit timestamp (the number of 4 microsecond units of time since 1 Jan 1980 UTC); + // > the next 2 octets are reserved; + // > the next octet is the "address family"; + // > the final 7 octets are a 56-bit host ID in the form specified by the address family + ncs, + + // RFC 4122/DCE 1.1 + // N bit pattern: 10xx + // > big-endian byte order + rfc, + + // Microsoft Corporation backward compatibility + // N bit pattern: 110x + // > little endian byte order + // > formely used in the Component Object Model (COM) library + microsoft, + + // reserved for possible future definition + // N bit pattern: 111x + reserved + }; + + struct uuid_error : public std::runtime_error + { + explicit uuid_error(std::string_view message) + : std::runtime_error(message.data()) + { + } + + explicit uuid_error(char const * message) + : std::runtime_error(message) + { + } + }; + + // indicated by a bit pattern in octet 6, marked with M in xxxxxxxx-xxxx-Mxxx-xxxx-xxxxxxxxxxxx + enum class uuid_version + { + none = 0, // only possible for nil or invalid uuids + time_based = 1, // The time-based version specified in RFC 4122 + dce_security = 2, // DCE Security version, with embedded POSIX UIDs. + name_based_md5 = 3, // The name-based version specified in RFS 4122 with MD5 hashing + random_number_based = 4, // The randomly or pseudo-randomly generated version specified in RFS 4122 + name_based_sha1 = 5 // The name-based version specified in RFS 4122 with SHA1 hashing + }; + + struct uuid + { + struct uuid_const_iterator + { + using self_type = uuid_const_iterator; + using value_type = uint8_t; + using reference = uint8_t const &; + using pointer = uint8_t const *; + using iterator_category = std::random_access_iterator_tag; + using difference_type = ptrdiff_t; + + protected: + pointer ptr = nullptr; + size_t index = 0; + + bool compatible(self_type const & other) const noexcept + { + return ptr == other.ptr; + } + + public: + constexpr explicit uuid_const_iterator(pointer ptr, size_t const index) : + ptr(ptr), index(index) + { + } + + uuid_const_iterator(uuid_const_iterator const & o) = default; + uuid_const_iterator& operator=(uuid_const_iterator const & o) = default; + ~uuid_const_iterator() = default; + + self_type & operator++ () + { + if (index >= 16) + throw std::out_of_range("Iterator cannot be incremented past the end of the data."); + ++index; + return *this; + } + + self_type operator++ (int) + { + self_type tmp = *this; + ++*this; + return tmp; + } + + bool operator== (self_type const & other) const + { + assert(compatible(other)); + return index == other.index; + } + + bool operator!= (self_type const & other) const + { + return !(*this == other); + } + + reference operator* () const + { + if (ptr == nullptr) + throw std::bad_function_call(); + return *(ptr + index); + } + + reference operator-> () const + { + if (ptr == nullptr) + throw std::bad_function_call(); + return *(ptr + index); + } + + uuid_const_iterator() = default; + + self_type & operator--() + { + if (index <= 0) + throw std::out_of_range("Iterator cannot be decremented past the beginning of the data."); + --index; + return *this; + } + + self_type operator--(int) + { + self_type tmp = *this; + --*this; + return tmp; + } + + self_type operator+(difference_type offset) const + { + self_type tmp = *this; + return tmp += offset; + } + + self_type operator-(difference_type offset) const + { + self_type tmp = *this; + return tmp -= offset; + } + + difference_type operator-(self_type const & other) const + { + assert(compatible(other)); + return (index - other.index); + } + + bool operator<(self_type const & other) const + { + assert(compatible(other)); + return index < other.index; + } + + bool operator>(self_type const & other) const + { + return other < *this; + } + + bool operator<=(self_type const & other) const + { + return !(other < *this); + } + + bool operator>=(self_type const & other) const + { + return !(*this < other); + } + + self_type & operator+=(difference_type const offset) + { + if (static_cast(index) + offset < 0 || + static_cast(index) + offset > 16) + throw std::out_of_range("Iterator cannot be incremented outside data bounds."); + + index += offset; + return *this; + } + + self_type & operator-=(difference_type const offset) + { + return *this += -offset; + } + + value_type const & operator[](difference_type const offset) const + { + return (*(*this + offset)); + } + }; + + using value_type = uint8_t; + + public: + constexpr uuid() noexcept : data({}) {}; + + explicit uuid(gsl::span bytes) + { + std::copy(std::cbegin(bytes), std::cend(bytes), std::begin(data)); + } + + template + explicit uuid(ForwardIterator first, ForwardIterator last) + { + if (std::distance(first, last) == 16) + std::copy(first, last, std::begin(data)); + } + + constexpr uuid_variant variant() const noexcept + { + if ((data[8] & 0x80) == 0x00) + return uuid_variant::ncs; + else if ((data[8] & 0xC0) == 0x80) + return uuid_variant::rfc; + else if ((data[8] & 0xE0) == 0xC0) + return uuid_variant::microsoft; + else + return uuid_variant::reserved; + } + + constexpr uuid_version version() const noexcept + { + if ((data[6] & 0xF0) == 0x10) + return uuid_version::time_based; + else if ((data[6] & 0xF0) == 0x20) + return uuid_version::dce_security; + else if ((data[6] & 0xF0) == 0x30) + return uuid_version::name_based_md5; + else if ((data[6] & 0xF0) == 0x40) + return uuid_version::random_number_based; + else if ((data[6] & 0xF0) == 0x50) + return uuid_version::name_based_sha1; + else + return uuid_version::none; + } + + constexpr std::size_t size() const noexcept { return 16; } + + constexpr bool is_nil() const noexcept + { + for (size_t i = 0; i < data.size(); ++i) if (data[i] != 0) return false; + return true; + } + + void swap(uuid & other) noexcept + { + data.swap(other.data); + } + + constexpr uuid_const_iterator begin() const noexcept { return uuid_const_iterator(&data[0], 0); } + constexpr uuid_const_iterator end() const noexcept { return uuid_const_iterator(&data[0], 16); } + + inline gsl::span as_bytes() const + { + return gsl::span(reinterpret_cast(data.data()), 16); + } + + template + static uuid from_string(TChar const * const str, size_t const size) + { + TChar digit = 0; + bool firstDigit = true; + int hasBraces = 0; + size_t index = 0; + std::array data{ { 0 } }; + + if (str == nullptr || size == 0) + throw uuid_error{ "Wrong uuid format" }; + + if (str[0] == static_cast('{')) + hasBraces = 1; + if (hasBraces && str[size - 1] != static_cast('}')) + throw uuid_error{ "Wrong uuid format" }; + + for (size_t i = hasBraces; i < size - hasBraces; ++i) + { + if (str[i] == static_cast('-')) continue; + + if (index >= 16 || !detail::is_hex(str[i])) + { + throw uuid_error{ "Wrong uuid format" }; + } + + if (firstDigit) + { + digit = str[i]; + firstDigit = false; + } + else + { + data[index++] = detail::hexpair2char(digit, str[i]); + firstDigit = true; + } + } + + if (index < 16) + { + throw uuid_error{ "Wrong uuid format" }; + } + + return uuid{ std::cbegin(data), std::cend(data) }; + } + + static uuid from_string(std::string_view str) + { + return from_string(str.data(), str.size()); + } + + static uuid from_string(std::wstring_view str) + { + return from_string(str.data(), str.size()); + } + + private: + std::array data{ { 0 } }; + + friend bool operator==(uuid const & lhs, uuid const & rhs) noexcept; + friend bool operator<(uuid const & lhs, uuid const & rhs) noexcept; + + template + friend std::basic_ostream & operator<<(std::basic_ostream &s, uuid const & id); + }; + + inline bool operator== (uuid const& lhs, uuid const& rhs) noexcept + { + return lhs.data == rhs.data; + } + + inline bool operator!= (uuid const& lhs, uuid const& rhs) noexcept + { + return !(lhs == rhs); + } + + inline bool operator< (uuid const& lhs, uuid const& rhs) noexcept + { + return lhs.data < rhs.data; + } + + template + std::basic_ostream & operator<<(std::basic_ostream &s, uuid const & id) + { + return s << std::hex << std::setfill(static_cast('0')) + << std::setw(2) << (int)id.data[0] + << std::setw(2) << (int)id.data[1] + << std::setw(2) << (int)id.data[2] + << std::setw(2) << (int)id.data[3] + << '-' + << std::setw(2) << (int)id.data[4] + << std::setw(2) << (int)id.data[5] + << '-' + << std::setw(2) << (int)id.data[6] + << std::setw(2) << (int)id.data[7] + << '-' + << std::setw(2) << (int)id.data[8] + << std::setw(2) << (int)id.data[9] + << '-' + << std::setw(2) << (int)id.data[10] + << std::setw(2) << (int)id.data[11] + << std::setw(2) << (int)id.data[12] + << std::setw(2) << (int)id.data[13] + << std::setw(2) << (int)id.data[14] + << std::setw(2) << (int)id.data[15]; + } + + inline std::string to_string(uuid const & id) + { + std::stringstream sstr; + sstr << id; + return sstr.str(); + } + + inline std::wstring to_wstring(uuid const & id) + { + std::wstringstream sstr; + sstr << id; + return sstr.str(); + } + + inline void swap(uuids::uuid & lhs, uuids::uuid & rhs) + { + lhs.swap(rhs); + } + + class uuid_system_generator + { + public: + using result_type = uuid; + + uuid operator()() + { +#ifdef _WIN32 + + GUID newId; + ::CoCreateGuid(&newId); + + std::array bytes = + { { + (unsigned char)((newId.Data1 >> 24) & 0xFF), + (unsigned char)((newId.Data1 >> 16) & 0xFF), + (unsigned char)((newId.Data1 >> 8) & 0xFF), + (unsigned char)((newId.Data1) & 0xFF), + + (unsigned char)((newId.Data2 >> 8) & 0xFF), + (unsigned char)((newId.Data2) & 0xFF), + + (unsigned char)((newId.Data3 >> 8) & 0xFF), + (unsigned char)((newId.Data3) & 0xFF), + + newId.Data4[0], + newId.Data4[1], + newId.Data4[2], + newId.Data4[3], + newId.Data4[4], + newId.Data4[5], + newId.Data4[6], + newId.Data4[7] + } }; + + return uuid{ std::begin(bytes), std::end(bytes) }; + +#elif defined(__linux__) || defined(__unix__) + + uuid_t id; + uuid_generate(id); + + std::array bytes = + { { + id[0], + id[1], + id[2], + id[3], + id[4], + id[5], + id[6], + id[7], + id[8], + id[9], + id[10], + id[11], + id[12], + id[13], + id[14], + id[15] + } }; + + return uuid{ std::begin(bytes), std::end(bytes) }; + +#elif defined(__APPLE__) + auto newId = CFUUIDCreate(NULL); + auto bytes = CFUUIDGetUUIDBytes(newId); + CFRelease(newId); + + std::array arrbytes = + { { + bytes.byte0, + bytes.byte1, + bytes.byte2, + bytes.byte3, + bytes.byte4, + bytes.byte5, + bytes.byte6, + bytes.byte7, + bytes.byte8, + bytes.byte9, + bytes.byte10, + bytes.byte11, + bytes.byte12, + bytes.byte13, + bytes.byte14, + bytes.byte15 + } }; + return uuid{ std::begin(arrbytes), std::end(arrbytes) }; +#elif + return uuid{}; +#endif + } + }; + + template + class basic_uuid_random_generator + { + public: + using result_type = uuid; + + basic_uuid_random_generator() + :generator(new UniformRandomNumberGenerator) + { + std::random_device rd; + generator->seed(rd()); + } + + explicit basic_uuid_random_generator(UniformRandomNumberGenerator& gen) : + generator(&gen, [](auto) {}) {} + explicit basic_uuid_random_generator(UniformRandomNumberGenerator* gen) : + generator(gen, [](auto) {}) {} + + uuid operator()() + { + uint8_t bytes[16]; + for (int i = 0; i < 16; i += 4) + *reinterpret_cast(bytes + i) = distribution(*generator); + + // variant must be 10xxxxxx + bytes[8] &= 0xBF; + bytes[8] |= 0x80; + + // version must be 0100xxxx + bytes[6] &= 0x4F; + bytes[6] |= 0x40; + + return uuid{std::begin(bytes), std::end(bytes)}; + } + + private: + std::uniform_int_distribution distribution; + std::shared_ptr generator; + }; + + using uuid_random_generator = basic_uuid_random_generator; + + class uuid_name_generator + { + public: + using result_type = uuid; + + explicit uuid_name_generator(uuid const& namespace_uuid) noexcept + : nsuuid(namespace_uuid) + {} + + uuid operator()(std::string_view name) + { + reset(); + process_characters(name.data(), name.size()); + return make_uuid(); + } + + uuid operator()(std::wstring_view name) + { + reset(); + process_characters(name.data(), name.size()); + return make_uuid(); + } + + private: + void reset() + { + hasher.reset(); + uint8_t bytes[16]; + std::copy(std::begin(nsuuid), std::end(nsuuid), bytes); + hasher.process_bytes(bytes, 16); + } + + template ::value>> + void process_characters(char_type const * const characters, size_t const count) + { + for (size_t i = 0; i < count; i++) + { + uint32_t c = characters[i]; + hasher.process_byte(static_cast((c >> 0) & 0xFF)); + hasher.process_byte(static_cast((c >> 8) & 0xFF)); + hasher.process_byte(static_cast((c >> 16) & 0xFF)); + hasher.process_byte(static_cast((c >> 24) & 0xFF)); + } + } + + void process_characters(const char * const characters, size_t const count) + { + hasher.process_bytes(characters, count); + } + + uuid make_uuid() + { + detail::sha1::digest8_t digest; + hasher.get_digest_bytes(digest); + + // variant must be 0b10xxxxxx + digest[8] &= 0xBF; + digest[8] |= 0x80; + + // version must be 0b0101xxxx + digest[6] &= 0x5F; + digest[6] |= 0x50; + + return uuid{ digest, digest + 16 }; + } + + private: + uuid nsuuid; + detail::sha1 hasher; + }; +} + +namespace std +{ + template <> + struct hash + { + using argument_type = uuids::uuid; + using result_type = std::size_t; + + result_type operator()(argument_type const &uuid) const + { + std::hash hasher; + return static_cast(hasher(uuids::to_string(uuid))); + } + }; +} From c16e1949c0121b2b877b26340084564ea451e915 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 20 Jan 2022 14:10:13 +0100 Subject: [PATCH 238/668] Add an AssetCache to the PRTContext --- src/serlio/PRTContext.cpp | 32 ++++++++++++++++++++++++++++++-- src/serlio/PRTContext.h | 2 ++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/serlio/PRTContext.cpp b/src/serlio/PRTContext.cpp index 37cf3c5e..91ebd4ac 100644 --- a/src/serlio/PRTContext.cpp +++ b/src/serlio/PRTContext.cpp @@ -25,7 +25,7 @@ namespace { constexpr bool DBG = false; -constexpr const wchar_t* SRL_TMP_PREFIX = L"serlio_"; +constexpr const wchar_t* SRL_TMP_FOLDER = L"serlio/asset_cache"; constexpr const wchar_t* PRT_EXT_SUBDIR = L"ext"; constexpr prt::LogLevel PRT_LOG_LEVEL = prt::LOG_INFO; constexpr bool ENABLE_LOG_CONSOLE = true; @@ -37,6 +37,31 @@ bool verifyMayaEncoder() { return static_cast(mayaEncOpts); } +std::filesystem::path getAssetCacheDir() { + return std::filesystem::temp_directory_path() / SRL_TMP_FOLDER; +} + +std::filesystem::path createAndGetAssetCacheDir() { + std::filesystem::path assetCacheDir = getAssetCacheDir(); + try { + std::filesystem::create_directories(assetCacheDir); + } + catch (std::exception& e) { + LOG_ERR << "Error while creating the asset cache directory at " << assetCacheDir << ": " << e.what(); + } + + return assetCacheDir; +} + +void cleanAssetCache() { + std::filesystem::path assetCacheDir = getAssetCacheDir(); + try { + std::filesystem::remove_all(assetCacheDir); + } + catch (std::exception& e) { + LOG_ERR << "Error while cleaning the asset cache at " << assetCacheDir << ": " << e.what(); + } +} } // namespace PRTContext& PRTContext::get() { @@ -44,7 +69,8 @@ PRTContext& PRTContext::get() { return prtCtx; } -PRTContext::PRTContext(const std::vector& addExtDirs) : mPluginRootPath(prtu::getPluginRoot()) { +PRTContext::PRTContext(const std::vector& addExtDirs) + : mPluginRootPath(prtu::getPluginRoot()), mAssetCache(createAndGetAssetCacheDir()) { if (ENABLE_LOG_CONSOLE) { theLogHandler = std::make_unique(); prt::addLogHandler(theLogHandler.get()); @@ -94,6 +120,8 @@ PRTContext::~PRTContext() { theCache.reset(); thePRT.reset(); + cleanAssetCache(); + if (ENABLE_LOG_CONSOLE && (theLogHandler != nullptr)) { prt::removeLogHandler(theLogHandler.get()); } diff --git a/src/serlio/PRTContext.h b/src/serlio/PRTContext.h index f408aa32..95c53a6f 100644 --- a/src/serlio/PRTContext.h +++ b/src/serlio/PRTContext.h @@ -21,6 +21,7 @@ #include "serlioPlugin.h" +#include "utils/AssetCache.h" #include "utils/LogHandler.h" #include "utils/ResolveMapCache.h" #include "utils/Utilities.h" @@ -46,6 +47,7 @@ struct SRL_TEST_EXPORTS_API PRTContext final { } const std::filesystem::path mPluginRootPath; // the path where serlio dso resides + AssetCache mAssetCache; ObjectUPtr thePRT; CacheObjectUPtr theCache; logging::LogHandlerUPtr theLogHandler; From 295e8eb7ea500b5c2a71b8057fa01827297ac378 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 20 Jan 2022 14:18:03 +0100 Subject: [PATCH 239/668] Add addAsset function to Callbacks --- src/codec/encoder/IMayaCallbacks.h | 12 +++++++++++ src/serlio/modifiers/MayaCallbacks.cpp | 28 ++++++++++++++++++++++++++ src/serlio/modifiers/MayaCallbacks.h | 4 ++++ 3 files changed, 44 insertions(+) diff --git a/src/codec/encoder/IMayaCallbacks.h b/src/codec/encoder/IMayaCallbacks.h index 3bf29f22..8f6d9c8b 100644 --- a/src/codec/encoder/IMayaCallbacks.h +++ b/src/codec/encoder/IMayaCallbacks.h @@ -68,4 +68,16 @@ class IMayaCallbacks : public prt::Callbacks { const int32_t* shapeIDs ) = 0; // clang-format on + + /** + * Writes an asset (e.g. in-memory texture) to an implementation-defined path. Assets with same uri will be assumed + * to contain identical data. + * + * @param uri the original asset within the RPK + * @param fileName local fileName derived from the URI by the asset encoder. can be used to cache the asset. + * @param [out] result file system path of the locally cached asset. Expected to be valid for the whole process + * life-time. + */ + virtual void addAsset(const wchar_t* uri, const wchar_t* fileName, const uint8_t* buffer, size_t size, + wchar_t* result, size_t& resultSize) = 0; }; diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 5fee20f6..37ef54b1 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -25,6 +25,8 @@ #include "utils/LogHandler.h" #include "utils/MayaUtilities.h" #include "utils/Utilities.h" +#include "utils/AssetCache.h" +#include "PRTContext.h" #include "prt/StringUtils.h" @@ -438,6 +440,32 @@ prt::Status MayaCallbacks::attrString(size_t /*isIndex*/, int32_t /*shapeID*/, c return prt::STATUS_OK; } +void MayaCallbacks::addAsset(const wchar_t* uri, const wchar_t* fileName, const uint8_t* buffer, size_t size, + wchar_t* result, size_t& resultSize) { + if (uri == nullptr || std::wcslen(uri) == 0 || fileName == nullptr || std::wcslen(fileName) == 0) { + LOG_WRN << "Skipping asset caching for invalid uri '" << uri << "' or filename '" << fileName << '"'; + resultSize = 0; + return; + } + + const std::filesystem::path& assetPath = PRTContext::get().mAssetCache.put(uri, fileName, buffer, size); + if (assetPath.empty()) { + resultSize = 0; + return; + } + + const std::wstring pathStr = assetPath.generic_wstring(); + + if (resultSize <= pathStr.size()) { // also check for null-terminator + resultSize = pathStr.size() + 1; // ask for space for null-terminator + return; + } + + wcsncpy_s(result, resultSize, pathStr.c_str(), resultSize); + result[resultSize - 1] = 0x0; + resultSize = pathStr.length() + 1; +} + // PRT version >= 2.3 #if PRT_VERSION_GTE(2, 3) diff --git a/src/serlio/modifiers/MayaCallbacks.h b/src/serlio/modifiers/MayaCallbacks.h index f421ed60..10190575 100644 --- a/src/serlio/modifiers/MayaCallbacks.h +++ b/src/serlio/modifiers/MayaCallbacks.h @@ -115,6 +115,10 @@ class MayaCallbacks : public IMayaCallbacks { const int32_t* shapeIDs) override; // clang-format on + void addAsset(const wchar_t* uri, const wchar_t* fileName, const uint8_t* buffer, size_t size, wchar_t* result, + size_t& resultSize) override; + + private: MObject outMeshObj; MObject inMeshObj; From b4dd3061b4b0dd1c2d88684e03858921b007ceee Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 20 Jan 2022 14:25:25 +0100 Subject: [PATCH 240/668] Add function to get the path for an asset in the assetcache --- src/codec/encoder/MayaEncoder.cpp | 69 +++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index 2f2ee824..4bd6bc6c 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -19,8 +19,10 @@ #include "encoder/MayaEncoder.h" #include "encoder/IMayaCallbacks.h" +#include "encoder/TextureEncoder.h" #include "prtx/Attributable.h" +#include "prtx/DataBackend.h" #include "prtx/Exception.h" #include "prtx/ExtensionManager.h" #include "prtx/GenerateContext.h" @@ -33,6 +35,7 @@ #include "prtx/ShapeIterator.h" #include "prtx/URI.h" +#include "prt/MemoryOutputCallbacks.h" #include "prt/prt.h" #include @@ -103,6 +106,72 @@ std::wstring uriToPath(const prtx::TexturePtr& t) { return t->getURI()->getPath(); } +template +std::basic_string callAPI(FUNC f, OBJ& obj, ARGS&&... args) { + std::vector buffer(1024, 0x0); + size_t size = buffer.size(); + std::invoke(f, obj, args..., buffer.data(), size); + if (size == 0) + return {}; // error case + else if (size > buffer.size()) { + buffer.resize(size); + std::invoke(f, obj, args..., buffer.data(), size); + } + return {buffer.data()}; +} + +std::wstring getTexturePath(const prtx::TexturePtr& texture, IMayaCallbacks* callbacks, prt::Cache* cache) { + if (!texture || !texture->isValid()) + return L""; + + const prtx::URIPtr& uri = texture->getURI(); + const std::wstring& uriStr = uri->wstring(); + const std::wstring& scheme = uri->getScheme(); + + if (!uri->isComposite() && (scheme == prtx::URI::SCHEME_FILE || scheme == prtx::URI::SCHEME_UNC)) { + // textures from the local file system or a mounted share on Windows can be directly passed to Serlio + return uri->getNativeFormat(); + } + else if (uri->isComposite() && (scheme == prtx::URI::SCHEME_RPK)) { + // textures from within an RPK can be directly copied out, no need for encoding + // just need to make sure we have useful filename for embedded texture blocks without names + + const prtx::BinaryVectorPtr data = prtx::DataBackend::resolveBinaryData(cache, uriStr); + const std::wstring fileName = uri->getBaseName() + uri->getExtension(); + const std::wstring assetPath = callAPI(&IMayaCallbacks::addAsset, *callbacks, uriStr.c_str(), + fileName.c_str(), data->data(), data->size()); + return assetPath; + } + else { + // all other textures (builtin or from memory) need to be extracted and potentially re-encoded + try { + prtx::PRTUtils::MemoryOutputCallbacksUPtr moc(prt::MemoryOutputCallbacks::create()); + + prtx::AsciiFileNamePreparator namePrep; + const prtx::NamePreparator::NamespacePtr& namePrepNamespace = namePrep.newNamespace(); + const std::wstring validatedFilename = + TextureEncoder::encode(texture, moc.get(), namePrep, namePrepNamespace, {}); + + if (moc->getNumBlocks() == 1) { + size_t bufferSize = 0; + const uint8_t* buffer = moc->getBlock(0, &bufferSize); + + const std::wstring assetPath = callAPI(&IMayaCallbacks::addAsset, *callbacks, uriStr.c_str(), + validatedFilename.c_str(), buffer, bufferSize); + if (!assetPath.empty()) + return assetPath; + else + srl_log_warn("Received invalid asset path while trying to write asset with URI: %1%") % uriStr; + } + } + catch (std::exception& e) { + srl_log_warn("Failed to encode or write texture at %1% to the local filesystem: %2%") % uriStr % e.what(); + } + } + + return {}; +} + // we blacklist all CGA-style material attribute keys, see prtx/Material.h const std::set MATERIAL_ATTRIBUTE_BLACKLIST = { L"ambient.b", From 309a1143998fddfa9ffa9a500cc51dcad4744c1e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 20 Jan 2022 14:26:22 +0100 Subject: [PATCH 241/668] Replace uriToPath with getTexturePath --- src/codec/encoder/MayaEncoder.cpp | 36 +++++++++++++++++++------------ src/codec/encoder/MayaEncoder.h | 3 ++- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index 4bd6bc6c..bbe3191a 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -102,10 +102,6 @@ std::pair, std::vector> toPtrVec(const std::vector return std::make_pair(pv, ps); } -std::wstring uriToPath(const prtx::TexturePtr& t) { - return t->getURI()->getPath(); -} - template std::basic_string callAPI(FUNC f, OBJ& obj, ARGS&&... args) { std::vector buffer(1024, 0x0); @@ -257,7 +253,7 @@ const std::set MATERIAL_ATTRIBUTE_BLACKLIST = { }; void convertMaterialToAttributeMap(prtx::PRTUtils::AttributeMapBuilderPtr& aBuilder, const prtx::Material& prtxAttr, - const prtx::WStringVector& keys) { + const prtx::WStringVector& keys, IMayaCallbacks* cb, prt::Cache* cache) { if constexpr (DBG) srl_log_debug(L"-- converting material: %1%") % prtxAttr.name(); for (const auto& key : keys) { @@ -316,19 +312,30 @@ void convertMaterialToAttributeMap(prtx::PRTUtils::AttributeMapBuilderPtr& aBuil case prtx::Material::PT_TEXTURE: { const auto& t = prtxAttr.getTexture(key); - const std::wstring p = uriToPath(t); - aBuilder->setString(key.c_str(), p.c_str()); + const std::wstring p = getTexturePath(t, cb, cache); + if (p.length() > 0) { + aBuilder->setString(key.c_str(), p.c_str()); + } break; } case prtx::Material::PT_TEXTURE_ARRAY: { const auto& ta = prtxAttr.getTextureArray(key); - prtx::WStringVector pa(ta.size()); - std::transform(ta.begin(), ta.end(), pa.begin(), uriToPath); + prtx::WStringVector texPaths; + texPaths.reserve(ta.size()); + + for (const auto& tex : ta) { + const std::wstring texPath = getTexturePath(tex, cb, cache); + if (!texPath.empty()) + texPaths.push_back(texPath); + } + + if (texPaths.size() > 0) { + std::vector pTexPaths = toPtrVec(texPaths); + aBuilder->setStringArray(key.c_str(), pTexPaths.data(), pTexPaths.size()); + } - std::vector ppa = toPtrVec(pa); - aBuilder->setStringArray(key.c_str(), ppa.data(), ppa.size()); break; } @@ -658,11 +665,12 @@ void MayaEncoder::encode(prtx::GenerateContext& context, size_t initialShapeInde prtx::EncodePreparator::InstanceVector instances; encPrep->fetchFinalizedInstances(instances, PREP_FLAGS); - convertGeometry(initialShape, instances, cb); + convertGeometry(initialShape, instances, cb, context.getCache()); } void MayaEncoder::convertGeometry(const prtx::InitialShape& initialShape, - const prtx::EncodePreparator::InstanceVector& instances, IMayaCallbacks* cb) { + const prtx::EncodePreparator::InstanceVector& instances, IMayaCallbacks* cb, + prt::Cache* cache) { if (instances.empty()) return; @@ -716,7 +724,7 @@ void MayaEncoder::convertGeometry(const prtx::InitialShape& initialShape, faceRanges.push_back(faceCount); if (emitMaterials) { - convertMaterialToAttributeMap(amb, *(mat.get()), mat->getKeys()); + convertMaterialToAttributeMap(amb, *(mat.get()), mat->getKeys(), cb, cache); matAttrMaps.v.push_back(amb->createAttributeMapAndReset()); } diff --git a/src/codec/encoder/MayaEncoder.h b/src/codec/encoder/MayaEncoder.h index b7769bc8..593edd13 100644 --- a/src/codec/encoder/MayaEncoder.h +++ b/src/codec/encoder/MayaEncoder.h @@ -50,7 +50,8 @@ class MayaEncoder : public prtx::GeometryEncoder { private: void convertGeometry(const prtx::InitialShape& initialShape, - const prtx::EncodePreparator::InstanceVector& instances, IMayaCallbacks* callbacks); + const prtx::EncodePreparator::InstanceVector& instances, IMayaCallbacks* callbacks, + prt::Cache* cache); }; class MayaEncoderFactory : public prtx::EncoderFactory, public prtx::Singleton { From 3a0a0054572e9add2be5cb79b54aa64a1d25b9ba Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 20 Jan 2022 14:33:29 +0100 Subject: [PATCH 242/668] Cleanup: remove unused helper function --- src/serlio/utils/Utilities.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/serlio/utils/Utilities.cpp b/src/serlio/utils/Utilities.cpp index 7e588323..4c3b7284 100644 --- a/src/serlio/utils/Utilities.cpp +++ b/src/serlio/utils/Utilities.cpp @@ -168,18 +168,6 @@ std::wstring toFileURI(const std::wstring& p) { return schema + u16String; } -std::filesystem::path getProcessTempDir(const std::wstring& prefix) { - std::filesystem::path tmpPath = std::filesystem::temp_directory_path(); - - std::wstring n = prefix; -#ifdef _WIN32 - n += std::to_wstring(::_getpid()); // prevent warning in win32 -#else - n += std::to_wstring(::getpid()); -#endif - return tmpPath / n; -} - time_t getFileModificationTime(const std::wstring& p) { std::wstring pn = std::filesystem::path(p).make_preferred().wstring(); From 369390baa37e503a7cc0439fe3d65a74395aaa38 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 21 Jan 2022 09:43:20 +0100 Subject: [PATCH 243/668] Add cases for all types of dynamic enums --- src/serlio/modifiers/PRTModifierAction.cpp | 68 +++++++++++++++++++++- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index d3639ead..c2f6dc10 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -629,11 +629,11 @@ MStatus PRTModifierAction::updateDynamicEnums() { switch (type) { case prt::Attributable::PT_STRING_ARRAY: { - size_t* arr_length; - const wchar_t *const *stringArray = mGenerateAttrs->getStringArray(valuesAttr, arr_length); + size_t arr_length = 0; + const wchar_t *const *stringArray = mGenerateAttrs->getStringArray(valuesAttr, &arr_length); e.mSVals.clear(); - for (size_t i = 0; i < *arr_length; i++) { + for (size_t i = 0; i < arr_length; i++) { if (wcslen(stringArray[i]) == 0) continue; @@ -648,6 +648,68 @@ MStatus PRTModifierAction::updateDynamicEnums() { } break; } + case prt::Attributable::PT_FLOAT_ARRAY: { + size_t arr_length = 0; + const double* doubleArray = mGenerateAttrs->getFloatArray(valuesAttr, &arr_length); + e.mFVals.clear(); + + for (size_t i = 0; i < arr_length; i++) { + double currDouble = doubleArray[i]; + + e.mFVals.append(currDouble); + MString mCurrString = std::to_wstring(currDouble).c_str(); + e.mAttr.addField(mCurrString, e.mFVals.length()); + } + break; + } + case prt::Attributable::PT_BOOL_ARRAY: { + size_t arr_length = 0; + const bool* boolArray = mGenerateAttrs->getBoolArray(valuesAttr, &arr_length); + e.mBVals.clear(); + + for (size_t i = 0; i < arr_length; i++) { + bool currBool = boolArray[i]; + + e.mBVals.append(currBool); + MString mCurrString = std::to_wstring(currBool).c_str(); + e.mAttr.addField(mCurrString, e.mBVals.length()); + } + break; + } + case prt::Attributable::PT_STRING: { + size_t arr_length = 0; + const wchar_t* currString = mGenerateAttrs->getString(valuesAttr); + + e.mSVals.clear(); + e.mSVals.append(currString); + + MString mCurrString = currString; + e.mAttr.addField(mCurrString, e.mBVals.length()); + break; + } + case prt::Attributable::PT_FLOAT: { + size_t arr_length = 0; + const bool currFloat = mGenerateAttrs->getFloat(valuesAttr); + + e.mFVals.clear(); + e.mFVals.append(currFloat); + + MString mCurrString = std::to_wstring(currFloat).c_str(); + e.mAttr.addField(mCurrString, e.mBVals.length()); + + break; + } + case prt::Attributable::PT_BOOL: { + size_t arr_length = 0; + const bool currBool = mGenerateAttrs->getBool(valuesAttr); + + e.mBVals.clear(); + e.mBVals.append(currBool); + + MString mCurrString = std::to_wstring(currBool).c_str(); + e.mAttr.addField(mCurrString, e.mBVals.length()); + break; + } } } } From 9f234ceffe30d639c9108cd06488482383fc5f76 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 21 Jan 2022 09:44:27 +0100 Subject: [PATCH 244/668] Cleanup: remove unused enum fields --- src/serlio/modifiers/PRTModifierAction.cpp | 57 +++++++--------------- src/serlio/modifiers/PRTModifierAction.h | 3 -- 2 files changed, 17 insertions(+), 43 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index c2f6dc10..b57063dd 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -631,48 +631,42 @@ MStatus PRTModifierAction::updateDynamicEnums() { case prt::Attributable::PT_STRING_ARRAY: { size_t arr_length = 0; const wchar_t *const *stringArray = mGenerateAttrs->getStringArray(valuesAttr, &arr_length); - e.mSVals.clear(); - for (size_t i = 0; i < arr_length; i++) { - if (wcslen(stringArray[i]) == 0) + for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { + if (wcslen(stringArray[enumIndex]) == 0) continue; - std::wstring currString = stringArray[i]; + std::wstring currString = stringArray[enumIndex]; //remove newlines from strings, because they break the maya UI currString.erase(std::remove(currString.begin(), currString.end(), '\n'), currString.end()); MString mCurrString = MString(currString.c_str()); - e.mSVals.append(mCurrString); - e.mAttr.addField(mCurrString, e.mSVals.length()); + e.mAttr.addField(mCurrString, enumIndex); } break; } case prt::Attributable::PT_FLOAT_ARRAY: { size_t arr_length = 0; const double* doubleArray = mGenerateAttrs->getFloatArray(valuesAttr, &arr_length); - e.mFVals.clear(); - for (size_t i = 0; i < arr_length; i++) { - double currDouble = doubleArray[i]; + for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { + double currDouble = doubleArray[enumIndex]; - e.mFVals.append(currDouble); MString mCurrString = std::to_wstring(currDouble).c_str(); - e.mAttr.addField(mCurrString, e.mFVals.length()); + e.mAttr.addField(mCurrString, enumIndex); } break; } case prt::Attributable::PT_BOOL_ARRAY: { size_t arr_length = 0; const bool* boolArray = mGenerateAttrs->getBoolArray(valuesAttr, &arr_length); - e.mBVals.clear(); - for (size_t i = 0; i < arr_length; i++) { - bool currBool = boolArray[i]; + for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { + bool currBool = boolArray[enumIndex]; - e.mBVals.append(currBool); MString mCurrString = std::to_wstring(currBool).c_str(); - e.mAttr.addField(mCurrString, e.mBVals.length()); + e.mAttr.addField(mCurrString, enumIndex); } break; } @@ -680,22 +674,16 @@ MStatus PRTModifierAction::updateDynamicEnums() { size_t arr_length = 0; const wchar_t* currString = mGenerateAttrs->getString(valuesAttr); - e.mSVals.clear(); - e.mSVals.append(currString); - MString mCurrString = currString; - e.mAttr.addField(mCurrString, e.mBVals.length()); + e.mAttr.addField(mCurrString, 0); break; } case prt::Attributable::PT_FLOAT: { size_t arr_length = 0; const bool currFloat = mGenerateAttrs->getFloat(valuesAttr); - e.mFVals.clear(); - e.mFVals.append(currFloat); - MString mCurrString = std::to_wstring(currFloat).c_str(); - e.mAttr.addField(mCurrString, e.mBVals.length()); + e.mAttr.addField(mCurrString, 0); break; } @@ -703,11 +691,8 @@ MStatus PRTModifierAction::updateDynamicEnums() { size_t arr_length = 0; const bool currBool = mGenerateAttrs->getBool(valuesAttr); - e.mBVals.clear(); - e.mBVals.append(currBool); - MString mCurrString = std::to_wstring(currBool).c_str(); - e.mAttr.addField(mCurrString, e.mBVals.length()); + e.mAttr.addField(mCurrString, 0); break; } } @@ -1028,6 +1013,7 @@ MStatus PRTModifierEnum::fill(const prt::Annotation* annot) { mRestricted = true; MStatus stat; + uint32_t enumIndex = 0; for (size_t arg = 0; arg < annot->getNumArguments(); arg++) { const wchar_t* key = annot->getArgument(arg)->getKey(); @@ -1044,26 +1030,17 @@ MStatus PRTModifierEnum::fill(const prt::Annotation* annot) { switch (annot->getArgument(arg)->getType()) { case prt::AAT_BOOL: { bool val = annot->getArgument(arg)->getBool(); - MCHECK(mAttr.addField(MString(std::to_wstring(val).c_str()), mBVals.length())); - mBVals.append(val); - mFVals.append(std::numeric_limits::quiet_NaN()); - mSVals.append(""); + MCHECK(mAttr.addField(MString(std::to_wstring(val).c_str()), enumIndex++)); break; } case prt::AAT_FLOAT: { double val = annot->getArgument(arg)->getFloat(); - MCHECK(mAttr.addField(MString(std::to_wstring(val).c_str()), mFVals.length())); - mBVals.append(false); - mFVals.append(val); - mSVals.append(""); + MCHECK(mAttr.addField(MString(std::to_wstring(val).c_str()), enumIndex++)); break; } case prt::AAT_STR: { const wchar_t* val = annot->getArgument(arg)->getStr(); - MCHECK(mAttr.addField(MString(val), mSVals.length())); - mBVals.append(false); - mFVals.append(std::numeric_limits::quiet_NaN()); - mSVals.append(MString(val)); + MCHECK(mAttr.addField(MString(val), enumIndex++)); break; } default: diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 3e871499..7d89eb51 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -54,9 +54,6 @@ class PRTModifierEnum { MFnEnumAttribute mAttr; private: - MStringArray mSVals; - MDoubleArray mFVals; - MIntArray mBVals; bool mRestricted = true; MString mValuesAttr = ""; }; // class PRTModifierEnum From 299b8a4f615eb8846aaf587ec36cf066b7df7ec0 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 21 Jan 2022 09:46:52 +0100 Subject: [PATCH 245/668] Cleanup: use const where possible --- src/serlio/modifiers/PRTModifierAction.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index b57063dd..9d88f2d3 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -641,7 +641,7 @@ MStatus PRTModifierAction::updateDynamicEnums() { //remove newlines from strings, because they break the maya UI currString.erase(std::remove(currString.begin(), currString.end(), '\n'), currString.end()); - MString mCurrString = MString(currString.c_str()); + const MString mCurrString = MString(currString.c_str()); e.mAttr.addField(mCurrString, enumIndex); } break; @@ -651,9 +651,9 @@ MStatus PRTModifierAction::updateDynamicEnums() { const double* doubleArray = mGenerateAttrs->getFloatArray(valuesAttr, &arr_length); for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { - double currDouble = doubleArray[enumIndex]; + const double currDouble = doubleArray[enumIndex]; - MString mCurrString = std::to_wstring(currDouble).c_str(); + const MString mCurrString = std::to_wstring(currDouble).c_str(); e.mAttr.addField(mCurrString, enumIndex); } break; @@ -663,9 +663,9 @@ MStatus PRTModifierAction::updateDynamicEnums() { const bool* boolArray = mGenerateAttrs->getBoolArray(valuesAttr, &arr_length); for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { - bool currBool = boolArray[enumIndex]; + const bool currBool = boolArray[enumIndex]; - MString mCurrString = std::to_wstring(currBool).c_str(); + const MString mCurrString = std::to_wstring(currBool).c_str(); e.mAttr.addField(mCurrString, enumIndex); } break; @@ -674,7 +674,7 @@ MStatus PRTModifierAction::updateDynamicEnums() { size_t arr_length = 0; const wchar_t* currString = mGenerateAttrs->getString(valuesAttr); - MString mCurrString = currString; + const MString mCurrString = currString; e.mAttr.addField(mCurrString, 0); break; } @@ -682,7 +682,7 @@ MStatus PRTModifierAction::updateDynamicEnums() { size_t arr_length = 0; const bool currFloat = mGenerateAttrs->getFloat(valuesAttr); - MString mCurrString = std::to_wstring(currFloat).c_str(); + const MString mCurrString = std::to_wstring(currFloat).c_str(); e.mAttr.addField(mCurrString, 0); break; @@ -691,7 +691,7 @@ MStatus PRTModifierAction::updateDynamicEnums() { size_t arr_length = 0; const bool currBool = mGenerateAttrs->getBool(valuesAttr); - MString mCurrString = std::to_wstring(currBool).c_str(); + const MString mCurrString = std::to_wstring(currBool).c_str(); e.mAttr.addField(mCurrString, 0); break; } From a8741d7e6aa350c7bc9820c7a327c8a48fd4517c Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 21 Jan 2022 09:48:15 +0100 Subject: [PATCH 246/668] Cleanup: remove redundant MString constructor usage --- src/serlio/modifiers/PRTModifierAction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 9d88f2d3..ba48b2f4 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -641,7 +641,7 @@ MStatus PRTModifierAction::updateDynamicEnums() { //remove newlines from strings, because they break the maya UI currString.erase(std::remove(currString.begin(), currString.end(), '\n'), currString.end()); - const MString mCurrString = MString(currString.c_str()); + const MString mCurrString = currString.c_str(); e.mAttr.addField(mCurrString, enumIndex); } break; From bf64f08f9e2ac28f5628f2587de22ff0938b5bad Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 21 Jan 2022 09:51:16 +0100 Subject: [PATCH 247/668] Omit skipping empty string enum fields --- src/serlio/modifiers/PRTModifierAction.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index ba48b2f4..17dc50a3 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -633,9 +633,6 @@ MStatus PRTModifierAction::updateDynamicEnums() { const wchar_t *const *stringArray = mGenerateAttrs->getStringArray(valuesAttr, &arr_length); for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { - if (wcslen(stringArray[enumIndex]) == 0) - continue; - std::wstring currString = stringArray[enumIndex]; //remove newlines from strings, because they break the maya UI From de8324ef0bdbdb4848d40c3891823ad78fc545d6 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 21 Jan 2022 09:54:04 +0100 Subject: [PATCH 248/668] Cleanup: avoid comparing with empty string Use builtin MString::length function instead --- src/serlio/modifiers/PRTModifierAction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 17dc50a3..d6fb61ff 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -612,7 +612,7 @@ MStatus PRTModifierAction::updateDynamicEnums() { }; for (auto& e : mEnums) { - if (e.mValuesAttr != "") { + if (e.mValuesAttr.length() > 0) { const MString fullAttrName = e.mAttr.name(); const RuleAttribute ruleAttr = reverseLookupAttribute(fullAttrName.asWChar()); From 0e5ab03de62b606dcc0c36eb40d7678d3ec5ff17 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 21 Jan 2022 09:56:38 +0100 Subject: [PATCH 249/668] Cleanup: remove unused variable --- src/serlio/modifiers/PRTModifierAction.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index d6fb61ff..5df8fe09 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -668,7 +668,6 @@ MStatus PRTModifierAction::updateDynamicEnums() { break; } case prt::Attributable::PT_STRING: { - size_t arr_length = 0; const wchar_t* currString = mGenerateAttrs->getString(valuesAttr); const MString mCurrString = currString; @@ -676,7 +675,6 @@ MStatus PRTModifierAction::updateDynamicEnums() { break; } case prt::Attributable::PT_FLOAT: { - size_t arr_length = 0; const bool currFloat = mGenerateAttrs->getFloat(valuesAttr); const MString mCurrString = std::to_wstring(currFloat).c_str(); @@ -685,7 +683,6 @@ MStatus PRTModifierAction::updateDynamicEnums() { break; } case prt::Attributable::PT_BOOL: { - size_t arr_length = 0; const bool currBool = mGenerateAttrs->getBool(valuesAttr); const MString mCurrString = std::to_wstring(currBool).c_str(); From 91a697cdca0f10c2756ffee87100e417a5dad197 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 21 Jan 2022 09:57:11 +0100 Subject: [PATCH 250/668] Cleanup: remove redundant intermediate variable --- src/serlio/modifiers/PRTModifierAction.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 5df8fe09..cfc94812 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -668,9 +668,8 @@ MStatus PRTModifierAction::updateDynamicEnums() { break; } case prt::Attributable::PT_STRING: { - const wchar_t* currString = mGenerateAttrs->getString(valuesAttr); + const MString mCurrString = mGenerateAttrs->getString(valuesAttr); - const MString mCurrString = currString; e.mAttr.addField(mCurrString, 0); break; } From 9c7fd7f71884b22480e02e4a719fdd3195bbe077 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 21 Jan 2022 10:07:26 +0100 Subject: [PATCH 251/668] Cleanup: make dynamic enum update call during UI update --- src/serlio/modifiers/PRTModifierAction.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index cfc94812..b7931919 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -421,7 +421,6 @@ MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { } }; - updateDynamicEnums(); iterateThroughAttributesAndApply(node, mRuleAttributes, fillAttributeFromNode); mGenerateAttrs.reset(aBuilder->createAttributeMap()); @@ -597,6 +596,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { } }; + updateDynamicEnums(); iterateThroughAttributesAndApply(node, mRuleAttributes, updateUIFromAttributes); return MStatus::kSuccess; @@ -611,6 +611,11 @@ MStatus PRTModifierAction::updateDynamicEnums() { return RULE_NOT_FOUND; }; + const AttributeMapUPtr defaultAttributeValues = + getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, *inPrtMesh, + mRandomSeed, *mGenerateAttrs); + + for (auto& e : mEnums) { if (e.mValuesAttr.length() > 0) { @@ -625,12 +630,12 @@ MStatus PRTModifierAction::updateDynamicEnums() { const std::wstring prefix = attrStyle + prtu::STYLE_DELIMITER + attrImport; const wchar_t* valuesAttr = (MString(prefix.c_str()) + e.mValuesAttr).asWChar(); - prt::Attributable::PrimitiveType type = mGenerateAttrs->getType(valuesAttr); + prt::Attributable::PrimitiveType type = defaultAttributeValues->getType(valuesAttr); switch (type) { case prt::Attributable::PT_STRING_ARRAY: { size_t arr_length = 0; - const wchar_t *const *stringArray = mGenerateAttrs->getStringArray(valuesAttr, &arr_length); + const wchar_t* const* stringArray = defaultAttributeValues->getStringArray(valuesAttr, &arr_length); for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { std::wstring currString = stringArray[enumIndex]; @@ -645,7 +650,7 @@ MStatus PRTModifierAction::updateDynamicEnums() { } case prt::Attributable::PT_FLOAT_ARRAY: { size_t arr_length = 0; - const double* doubleArray = mGenerateAttrs->getFloatArray(valuesAttr, &arr_length); + const double* doubleArray = defaultAttributeValues->getFloatArray(valuesAttr, &arr_length); for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { const double currDouble = doubleArray[enumIndex]; @@ -657,7 +662,7 @@ MStatus PRTModifierAction::updateDynamicEnums() { } case prt::Attributable::PT_BOOL_ARRAY: { size_t arr_length = 0; - const bool* boolArray = mGenerateAttrs->getBoolArray(valuesAttr, &arr_length); + const bool* boolArray = defaultAttributeValues->getBoolArray(valuesAttr, &arr_length); for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { const bool currBool = boolArray[enumIndex]; @@ -668,13 +673,13 @@ MStatus PRTModifierAction::updateDynamicEnums() { break; } case prt::Attributable::PT_STRING: { - const MString mCurrString = mGenerateAttrs->getString(valuesAttr); + const MString mCurrString = defaultAttributeValues->getString(valuesAttr); e.mAttr.addField(mCurrString, 0); break; } case prt::Attributable::PT_FLOAT: { - const bool currFloat = mGenerateAttrs->getFloat(valuesAttr); + const bool currFloat = defaultAttributeValues->getFloat(valuesAttr); const MString mCurrString = std::to_wstring(currFloat).c_str(); e.mAttr.addField(mCurrString, 0); @@ -682,7 +687,7 @@ MStatus PRTModifierAction::updateDynamicEnums() { break; } case prt::Attributable::PT_BOOL: { - const bool currBool = mGenerateAttrs->getBool(valuesAttr); + const bool currBool = defaultAttributeValues->getBool(valuesAttr); const MString mCurrString = std::to_wstring(currBool).c_str(); e.mAttr.addField(mCurrString, 0); From 3d786129d7c607f897b4a1bff6ef91753f4f4c57 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 21 Jan 2022 10:12:59 +0100 Subject: [PATCH 252/668] Cleanup: make helper function for reverseLookupAttribute --- src/serlio/modifiers/PRTModifierAction.cpp | 29 ++++++++-------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index b7931919..2b1986a0 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -169,27 +169,27 @@ std::list getNodeAttributesCorrespondingToCGA(const MFnDependencyNode& const RuleAttribute RULE_NOT_FOUND{}; +const RuleAttribute reverseLookupAttribute(const std::wstring& mayaFullAttrName, const RuleAttributes& ruleAttributes) { + auto it = std::find_if(ruleAttributes.begin(), ruleAttributes.end(), + [&mayaFullAttrName](const auto& ra) { return (ra.mayaFullName == mayaFullAttrName); }); + if (it != ruleAttributes.end()) + return *it; + return RULE_NOT_FOUND; +} + template MStatus iterateThroughAttributesAndApply(const MObject& node, const RuleAttributes& mRuleAttributes, T attrFunction) { MStatus stat; const MFnDependencyNode fNode(node, &stat); MCHECK(stat); - auto reverseLookupAttribute = [mRuleAttributes](const std::wstring& mayaFullAttrName) { - auto it = std::find_if(mRuleAttributes.begin(), mRuleAttributes.end(), - [&mayaFullAttrName](const auto& ra) { return (ra.mayaFullName == mayaFullAttrName); }); - if (it != mRuleAttributes.end()) - return *it; - return RULE_NOT_FOUND; - }; - const std::list cgaAttributes = getNodeAttributesCorrespondingToCGA(fNode); for (const auto& attrObj : cgaAttributes) { MFnAttribute fnAttr(attrObj); const MString fullAttrName = fnAttr.name(); - const RuleAttribute ruleAttr = reverseLookupAttribute(fullAttrName.asWChar()); + const RuleAttribute ruleAttr = reverseLookupAttribute(fullAttrName.asWChar(), mRuleAttributes); assert(!ruleAttr.fqName.empty()); // poor mans check for RULE_NOT_FOUND [[maybe_unused]] const auto ruleAttrType = ruleAttr.mType; @@ -603,24 +603,15 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { } MStatus PRTModifierAction::updateDynamicEnums() { - auto reverseLookupAttribute = [ruleAttributes = mRuleAttributes](const std::wstring& mayaFullAttrName) { - auto it = std::find_if(ruleAttributes.begin(), ruleAttributes.end(), - [&mayaFullAttrName](const auto& ra) { return (ra.mayaFullName == mayaFullAttrName); }); - if (it != ruleAttributes.end()) - return *it; - return RULE_NOT_FOUND; - }; - const AttributeMapUPtr defaultAttributeValues = getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, *inPrtMesh, mRandomSeed, *mGenerateAttrs); - for (auto& e : mEnums) { if (e.mValuesAttr.length() > 0) { const MString fullAttrName = e.mAttr.name(); - const RuleAttribute ruleAttr = reverseLookupAttribute(fullAttrName.asWChar()); + const RuleAttribute ruleAttr = reverseLookupAttribute(fullAttrName.asWChar(), mRuleAttributes); const std::wstring attrStyle = prtu::getStyle(ruleAttr.fqName).c_str(); std::wstring attrImport = prtu::getImport(ruleAttr.fqName).c_str(); From 7d6e582dd03172499bba1ee20f4046be6ca784ca Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 21 Jan 2022 10:13:37 +0100 Subject: [PATCH 253/668] Cleanup: run clang-format --- src/serlio/modifiers/PRTModifierAction.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 2b1986a0..9b4a4778 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -609,7 +609,7 @@ MStatus PRTModifierAction::updateDynamicEnums() { for (auto& e : mEnums) { if (e.mValuesAttr.length() > 0) { - + const MString fullAttrName = e.mAttr.name(); const RuleAttribute ruleAttr = reverseLookupAttribute(fullAttrName.asWChar(), mRuleAttributes); @@ -623,17 +623,17 @@ MStatus PRTModifierAction::updateDynamicEnums() { const wchar_t* valuesAttr = (MString(prefix.c_str()) + e.mValuesAttr).asWChar(); prt::Attributable::PrimitiveType type = defaultAttributeValues->getType(valuesAttr); - switch (type) { + switch (type) { case prt::Attributable::PT_STRING_ARRAY: { size_t arr_length = 0; const wchar_t* const* stringArray = defaultAttributeValues->getStringArray(valuesAttr, &arr_length); - + for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { std::wstring currString = stringArray[enumIndex]; - //remove newlines from strings, because they break the maya UI + // remove newlines from strings, because they break the maya UI currString.erase(std::remove(currString.begin(), currString.end(), '\n'), currString.end()); - + const MString mCurrString = currString.c_str(); e.mAttr.addField(mCurrString, enumIndex); } @@ -674,7 +674,7 @@ MStatus PRTModifierAction::updateDynamicEnums() { const MString mCurrString = std::to_wstring(currFloat).c_str(); e.mAttr.addField(mCurrString, 0); - + break; } case prt::Attributable::PT_BOOL: { From bd358d8471d16f1061657a258594428f9676bb38 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 21 Jan 2022 11:43:19 +0100 Subject: [PATCH 254/668] Cleanup: use constructor instead of type casting and add MCheck to addField --- src/serlio/modifiers/PRTModifierAction.cpp | 23 +++++++++++----------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 9b4a4778..64697900 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -634,8 +634,8 @@ MStatus PRTModifierAction::updateDynamicEnums() { // remove newlines from strings, because they break the maya UI currString.erase(std::remove(currString.begin(), currString.end(), '\n'), currString.end()); - const MString mCurrString = currString.c_str(); - e.mAttr.addField(mCurrString, enumIndex); + const MString mCurrString(currString.c_str()); + MCHECK(e.mAttr.addField(mCurrString, enumIndex)); } break; } @@ -646,8 +646,8 @@ MStatus PRTModifierAction::updateDynamicEnums() { for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { const double currDouble = doubleArray[enumIndex]; - const MString mCurrString = std::to_wstring(currDouble).c_str(); - e.mAttr.addField(mCurrString, enumIndex); + const MString mCurrString(std::to_wstring(currDouble).c_str()); + MCHECK(e.mAttr.addField(mCurrString, enumIndex)); } break; } @@ -658,30 +658,29 @@ MStatus PRTModifierAction::updateDynamicEnums() { for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { const bool currBool = boolArray[enumIndex]; - const MString mCurrString = std::to_wstring(currBool).c_str(); - e.mAttr.addField(mCurrString, enumIndex); + const MString mCurrString(std::to_wstring(currBool).c_str()); + MCHECK(e.mAttr.addField(mCurrString, enumIndex)); } break; } case prt::Attributable::PT_STRING: { const MString mCurrString = defaultAttributeValues->getString(valuesAttr); - e.mAttr.addField(mCurrString, 0); + MCHECK(e.mAttr.addField(mCurrString, 0)); break; } case prt::Attributable::PT_FLOAT: { const bool currFloat = defaultAttributeValues->getFloat(valuesAttr); - const MString mCurrString = std::to_wstring(currFloat).c_str(); - e.mAttr.addField(mCurrString, 0); - + const MString mCurrString(std::to_wstring(currFloat).c_str()); + MCHECK(e.mAttr.addField(mCurrString, 0)); break; } case prt::Attributable::PT_BOOL: { const bool currBool = defaultAttributeValues->getBool(valuesAttr); - const MString mCurrString = std::to_wstring(currBool).c_str(); - e.mAttr.addField(mCurrString, 0); + const MString mCurrString(std::to_wstring(currBool).c_str()); + MCHECK(e.mAttr.addField(mCurrString, 0)); break; } } From ead30b95a5670476bb60b543d1efef5b16344ef9 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 21 Jan 2022 14:45:12 +0100 Subject: [PATCH 255/668] Only update dynamic enums once when loading the rpk. Currently the maya API does not intuitively allow for changing/removing enum options for the dropdown menu --- src/serlio/modifiers/PRTModifierAction.cpp | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 64697900..c8bbaac1 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -596,17 +596,12 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { } }; - updateDynamicEnums(); iterateThroughAttributesAndApply(node, mRuleAttributes, updateUIFromAttributes); return MStatus::kSuccess; } MStatus PRTModifierAction::updateDynamicEnums() { - const AttributeMapUPtr defaultAttributeValues = - getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, *inPrtMesh, - mRandomSeed, *mGenerateAttrs); - for (auto& e : mEnums) { if (e.mValuesAttr.length() > 0) { @@ -621,12 +616,12 @@ MStatus PRTModifierAction::updateDynamicEnums() { const std::wstring prefix = attrStyle + prtu::STYLE_DELIMITER + attrImport; const wchar_t* valuesAttr = (MString(prefix.c_str()) + e.mValuesAttr).asWChar(); - prt::Attributable::PrimitiveType type = defaultAttributeValues->getType(valuesAttr); + prt::Attributable::PrimitiveType type = mGenerateAttrs->getType(valuesAttr); switch (type) { case prt::Attributable::PT_STRING_ARRAY: { size_t arr_length = 0; - const wchar_t* const* stringArray = defaultAttributeValues->getStringArray(valuesAttr, &arr_length); + const wchar_t* const* stringArray = mGenerateAttrs->getStringArray(valuesAttr, &arr_length); for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { std::wstring currString = stringArray[enumIndex]; @@ -641,7 +636,7 @@ MStatus PRTModifierAction::updateDynamicEnums() { } case prt::Attributable::PT_FLOAT_ARRAY: { size_t arr_length = 0; - const double* doubleArray = defaultAttributeValues->getFloatArray(valuesAttr, &arr_length); + const double* doubleArray = mGenerateAttrs->getFloatArray(valuesAttr, &arr_length); for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { const double currDouble = doubleArray[enumIndex]; @@ -653,7 +648,7 @@ MStatus PRTModifierAction::updateDynamicEnums() { } case prt::Attributable::PT_BOOL_ARRAY: { size_t arr_length = 0; - const bool* boolArray = defaultAttributeValues->getBoolArray(valuesAttr, &arr_length); + const bool* boolArray = mGenerateAttrs->getBoolArray(valuesAttr, &arr_length); for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { const bool currBool = boolArray[enumIndex]; @@ -664,20 +659,20 @@ MStatus PRTModifierAction::updateDynamicEnums() { break; } case prt::Attributable::PT_STRING: { - const MString mCurrString = defaultAttributeValues->getString(valuesAttr); + const MString mCurrString = mGenerateAttrs->getString(valuesAttr); MCHECK(e.mAttr.addField(mCurrString, 0)); break; } case prt::Attributable::PT_FLOAT: { - const bool currFloat = defaultAttributeValues->getFloat(valuesAttr); + const bool currFloat = mGenerateAttrs->getFloat(valuesAttr); const MString mCurrString(std::to_wstring(currFloat).c_str()); MCHECK(e.mAttr.addField(mCurrString, 0)); break; } case prt::Attributable::PT_BOOL: { - const bool currBool = defaultAttributeValues->getBool(valuesAttr); + const bool currBool = mGenerateAttrs->getBool(valuesAttr); const MString mCurrString(std::to_wstring(currBool).c_str()); MCHECK(e.mAttr.addField(mCurrString, 0)); @@ -750,6 +745,7 @@ MStatus PRTModifierAction::updateRuleFiles(const MObject& node, const MString& r sortRuleAttributes(mRuleAttributes); createNodeAttributes(node, info.get()); + updateDynamicEnums(); } return MS::kSuccess; From e7e74b838be491ab93f71fbc1e295518d31de725 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 21 Jan 2022 16:11:43 +0100 Subject: [PATCH 256/668] Switch from wcsncpy_s to wcsncpy Otherwise linux and some windows builds fail --- src/serlio/modifiers/MayaCallbacks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 37ef54b1..98d53c55 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -461,7 +461,7 @@ void MayaCallbacks::addAsset(const wchar_t* uri, const wchar_t* fileName, const return; } - wcsncpy_s(result, resultSize, pathStr.c_str(), resultSize); + wcsncpy(result, pathStr.c_str(), resultSize); result[resultSize - 1] = 0x0; resultSize = pathStr.length() + 1; } From ebb24bb296a3ec319d33345d90c3f2fb3660f96e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 21 Jan 2022 16:12:02 +0100 Subject: [PATCH 257/668] Add AssetCache dependency to tests --- src/test/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index b1e1e4d8..1f856e58 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -5,6 +5,7 @@ add_executable(${TEST_TARGET} ../serlio/PRTContext.cpp ../serlio/utils/Utilities.cpp ../serlio/utils/ResolveMapCache.cpp + ../serlio/utils/AssetCache.cpp ../serlio/modifiers/RuleAttributes.cpp) set_target_properties(${TEST_TARGET} PROPERTIES CXX_STANDARD 14) From d126a7d3d1951f59e298d1cb6f9b5344b35f1fcd Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 24 Jan 2022 11:04:31 +0100 Subject: [PATCH 258/668] remove stduuid library dependency --- src/serlio/utils/AssetCache.cpp | 5 +- src/serlio/utils/stduuid/gsl/gsl | 29 - src/serlio/utils/stduuid/gsl/gsl_algorithm | 63 - src/serlio/utils/stduuid/gsl/gsl_assert | 145 -- src/serlio/utils/stduuid/gsl/gsl_byte | 181 -- src/serlio/utils/stduuid/gsl/gsl_util | 158 -- src/serlio/utils/stduuid/gsl/multi_span | 2242 -------------------- src/serlio/utils/stduuid/gsl/pointers | 193 -- src/serlio/utils/stduuid/gsl/span | 766 ------- src/serlio/utils/stduuid/gsl/string_span | 730 ------- src/serlio/utils/stduuid/uuid.h | 873 -------- 11 files changed, 1 insertion(+), 5384 deletions(-) delete mode 100644 src/serlio/utils/stduuid/gsl/gsl delete mode 100644 src/serlio/utils/stduuid/gsl/gsl_algorithm delete mode 100644 src/serlio/utils/stduuid/gsl/gsl_assert delete mode 100644 src/serlio/utils/stduuid/gsl/gsl_byte delete mode 100644 src/serlio/utils/stduuid/gsl/gsl_util delete mode 100644 src/serlio/utils/stduuid/gsl/multi_span delete mode 100644 src/serlio/utils/stduuid/gsl/pointers delete mode 100644 src/serlio/utils/stduuid/gsl/span delete mode 100644 src/serlio/utils/stduuid/gsl/string_span delete mode 100644 src/serlio/utils/stduuid/uuid.h diff --git a/src/serlio/utils/AssetCache.cpp b/src/serlio/utils/AssetCache.cpp index 8a74ea08..ceb9dfef 100644 --- a/src/serlio/utils/AssetCache.cpp +++ b/src/serlio/utils/AssetCache.cpp @@ -20,7 +20,6 @@ #include "AssetCache.h" #include "utils/LogHandler.h" -#include "utils/stduuid/uuid.h" #include #include @@ -89,9 +88,7 @@ std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileNam } std::filesystem::path AssetCache::getCachedPath(const wchar_t* fileName) const { - // we use an UUID to have unique entries (filenames might clash across different RPKs) - const uuids::uuid uuid = uuids::uuid_system_generator{}(); - std::wstring cachedAssetName = uuids::to_wstring(uuid); + std::wstring cachedAssetName = L""; // we append the filename constructed by the encoder from the URI assert(fileName != nullptr); diff --git a/src/serlio/utils/stduuid/gsl/gsl b/src/serlio/utils/stduuid/gsl/gsl deleted file mode 100644 index 55862ebd..00000000 --- a/src/serlio/utils/stduuid/gsl/gsl +++ /dev/null @@ -1,29 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) 2015 Microsoft Corporation. All rights reserved. -// -// This code is licensed under the MIT License (MIT). -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef GSL_GSL_H -#define GSL_GSL_H - -#include // copy -#include // Ensures/Expects -#include // byte -#include // finally()/narrow()/narrow_cast()... -#include // multi_span, strided_span... -#include // owner, not_null -#include // span -#include // zstring, string_span, zstring_builder... - -#endif // GSL_GSL_H diff --git a/src/serlio/utils/stduuid/gsl/gsl_algorithm b/src/serlio/utils/stduuid/gsl/gsl_algorithm deleted file mode 100644 index 710792fb..00000000 --- a/src/serlio/utils/stduuid/gsl/gsl_algorithm +++ /dev/null @@ -1,63 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) 2015 Microsoft Corporation. All rights reserved. -// -// This code is licensed under the MIT License (MIT). -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef GSL_ALGORITHM_H -#define GSL_ALGORITHM_H - -#include // for Expects -#include // for dynamic_extent, span - -#include // for copy_n -#include // for ptrdiff_t -#include // for is_assignable - -#ifdef _MSC_VER -#pragma warning(push) - -// turn off some warnings that are noisy about our Expects statements -#pragma warning(disable : 4127) // conditional expression is constant -#pragma warning(disable : 4996) // unsafe use of std::copy_n - -// blanket turn off warnings from CppCoreCheck for now -// so people aren't annoyed by them when running the tool. -// more targeted suppressions will be added in a future update to the GSL -#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) -#endif // _MSC_VER - -namespace gsl -{ - -template -void copy(span src, span dest) -{ - static_assert(std::is_assignable::value, - "Elements of source span can not be assigned to elements of destination span"); - static_assert(SrcExtent == dynamic_extent || DestExtent == dynamic_extent || - (SrcExtent <= DestExtent), - "Source range is longer than target range"); - - Expects(dest.size() >= src.size()); - std::copy_n(src.data(), src.size(), dest.data()); -} - -} // namespace gsl - -#ifdef _MSC_VER -#pragma warning(pop) -#endif // _MSC_VER - -#endif // GSL_ALGORITHM_H diff --git a/src/serlio/utils/stduuid/gsl/gsl_assert b/src/serlio/utils/stduuid/gsl/gsl_assert deleted file mode 100644 index 131fa8b1..00000000 --- a/src/serlio/utils/stduuid/gsl/gsl_assert +++ /dev/null @@ -1,145 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) 2015 Microsoft Corporation. All rights reserved. -// -// This code is licensed under the MIT License (MIT). -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef GSL_CONTRACTS_H -#define GSL_CONTRACTS_H - -#include -#include // for logic_error - -// -// Temporary until MSVC STL supports no-exceptions mode. -// Currently terminate is a no-op in this mode, so we add termination behavior back -// -#if defined(_MSC_VER) && defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS -#define GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND -#endif - -// -// There are three configuration options for this GSL implementation's behavior -// when pre/post conditions on the GSL types are violated: -// -// 1. GSL_TERMINATE_ON_CONTRACT_VIOLATION: std::terminate will be called (default) -// 2. GSL_THROW_ON_CONTRACT_VIOLATION: a gsl::fail_fast exception will be thrown -// 3. GSL_UNENFORCED_ON_CONTRACT_VIOLATION: nothing happens -// -#if !(defined(GSL_THROW_ON_CONTRACT_VIOLATION) || defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) || \ - defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION)) -#define GSL_TERMINATE_ON_CONTRACT_VIOLATION -#endif - -#define GSL_STRINGIFY_DETAIL(x) #x -#define GSL_STRINGIFY(x) GSL_STRINGIFY_DETAIL(x) - -#if defined(__clang__) || defined(__GNUC__) -#define GSL_LIKELY(x) __builtin_expect(!!(x), 1) -#define GSL_UNLIKELY(x) __builtin_expect(!!(x), 0) -#else -#define GSL_LIKELY(x) (!!(x)) -#define GSL_UNLIKELY(x) (!!(x)) -#endif - -// -// GSL_ASSUME(cond) -// -// Tell the optimizer that the predicate cond must hold. It is unspecified -// whether or not cond is actually evaluated. -// -#ifdef _MSC_VER -#define GSL_ASSUME(cond) __assume(cond) -#elif defined(__GNUC__) -#define GSL_ASSUME(cond) ((cond) ? static_cast(0) : __builtin_unreachable()) -#else -#define GSL_ASSUME(cond) static_cast((cond) ? 0 : 0) -#endif - -// -// GSL.assert: assertions -// - -namespace gsl -{ -struct fail_fast : public std::logic_error -{ - explicit fail_fast(char const* const message) : std::logic_error(message) {} -}; - -namespace details -{ -#if defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND) - - typedef void (__cdecl *terminate_handler)(); - - inline gsl::details::terminate_handler& get_terminate_handler() noexcept - { - static terminate_handler handler = &abort; - return handler; - } - -#endif - - [[noreturn]] inline void terminate() noexcept - { -#if defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND) - (*gsl::details::get_terminate_handler())(); -#else - std::terminate(); -#endif - } - -#if defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) - - template - [[noreturn]] void throw_exception(Exception&&) - { - gsl::details::terminate(); - } - -#else - - template - [[noreturn]] void throw_exception(Exception&& exception) - { - throw std::forward(exception); - } - -#endif - -} // namespace details -} // namespace gsl - -#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) - -#define GSL_CONTRACT_CHECK(type, cond) \ - (GSL_LIKELY(cond) ? static_cast(0) \ - : gsl::details::throw_exception(gsl::fail_fast( \ - "GSL: " type " failure at " __FILE__ ": " GSL_STRINGIFY(__LINE__)))) - -#elif defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) - -#define GSL_CONTRACT_CHECK(type, cond) \ - (GSL_LIKELY(cond) ? static_cast(0) : gsl::details::terminate()) - -#elif defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION) - -#define GSL_CONTRACT_CHECK(type, cond) GSL_ASSUME(cond) - -#endif - -#define Expects(cond) GSL_CONTRACT_CHECK("Precondition", cond) -#define Ensures(cond) GSL_CONTRACT_CHECK("Postcondition", cond) - -#endif // GSL_CONTRACTS_H diff --git a/src/serlio/utils/stduuid/gsl/gsl_byte b/src/serlio/utils/stduuid/gsl/gsl_byte deleted file mode 100644 index e8611733..00000000 --- a/src/serlio/utils/stduuid/gsl/gsl_byte +++ /dev/null @@ -1,181 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) 2015 Microsoft Corporation. All rights reserved. -// -// This code is licensed under the MIT License (MIT). -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef GSL_BYTE_H -#define GSL_BYTE_H - -#include - -#ifdef _MSC_VER - -#pragma warning(push) - -// don't warn about function style casts in byte related operators -#pragma warning(disable : 26493) - -#ifndef GSL_USE_STD_BYTE -// this tests if we are under MSVC and the standard lib has std::byte and it is enabled -#if defined(_HAS_STD_BYTE) && _HAS_STD_BYTE - -#define GSL_USE_STD_BYTE 1 - -#else // defined(_HAS_STD_BYTE) && _HAS_STD_BYTE - -#define GSL_USE_STD_BYTE 0 - -#endif // defined(_HAS_STD_BYTE) && _HAS_STD_BYTE -#endif // GSL_USE_STD_BYTE - -#else // _MSC_VER - -#ifndef GSL_USE_STD_BYTE -// this tests if we are under GCC or Clang with enough -std:c++1z power to get us std::byte -#if defined(__cplusplus) && (__cplusplus >= 201703L) - -#define GSL_USE_STD_BYTE 1 -#include - -#else // defined(__cplusplus) && (__cplusplus >= 201703L) - -#define GSL_USE_STD_BYTE 0 - -#endif //defined(__cplusplus) && (__cplusplus >= 201703L) -#endif // GSL_USE_STD_BYTE - -#endif // _MSC_VER - -// Use __may_alias__ attribute on gcc and clang -#if defined __clang__ || (__GNUC__ > 5) -#define byte_may_alias __attribute__((__may_alias__)) -#else // defined __clang__ || defined __GNUC__ -#define byte_may_alias -#endif // defined __clang__ || defined __GNUC__ - -namespace gsl -{ -#if GSL_USE_STD_BYTE - - -using std::byte; -using std::to_integer; - -#else // GSL_USE_STD_BYTE - -// This is a simple definition for now that allows -// use of byte within span<> to be standards-compliant -enum class byte_may_alias byte : unsigned char -{ -}; - -template ::value>> -constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept -{ - return b = byte(static_cast(b) << shift); -} - -template ::value>> -constexpr byte operator<<(byte b, IntegerType shift) noexcept -{ - return byte(static_cast(b) << shift); -} - -template ::value>> -constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept -{ - return b = byte(static_cast(b) >> shift); -} - -template ::value>> -constexpr byte operator>>(byte b, IntegerType shift) noexcept -{ - return byte(static_cast(b) >> shift); -} - -constexpr byte& operator|=(byte& l, byte r) noexcept -{ - return l = byte(static_cast(l) | static_cast(r)); -} - -constexpr byte operator|(byte l, byte r) noexcept -{ - return byte(static_cast(l) | static_cast(r)); -} - -constexpr byte& operator&=(byte& l, byte r) noexcept -{ - return l = byte(static_cast(l) & static_cast(r)); -} - -constexpr byte operator&(byte l, byte r) noexcept -{ - return byte(static_cast(l) & static_cast(r)); -} - -constexpr byte& operator^=(byte& l, byte r) noexcept -{ - return l = byte(static_cast(l) ^ static_cast(r)); -} - -constexpr byte operator^(byte l, byte r) noexcept -{ - return byte(static_cast(l) ^ static_cast(r)); -} - -constexpr byte operator~(byte b) noexcept { return byte(~static_cast(b)); } - -template ::value>> -constexpr IntegerType to_integer(byte b) noexcept -{ - return static_cast(b); -} - -#endif // GSL_USE_STD_BYTE - -template -constexpr byte to_byte_impl(T t) noexcept -{ - static_assert( - E, "gsl::to_byte(t) must be provided an unsigned char, otherwise data loss may occur. " - "If you are calling to_byte with an integer contant use: gsl::to_byte() version."); - return static_cast(t); -} -template <> -constexpr byte to_byte_impl(unsigned char t) noexcept -{ - return byte(t); -} - -template -constexpr byte to_byte(T t) noexcept -{ - return to_byte_impl::value, T>(t); -} - -template -constexpr byte to_byte() noexcept -{ - static_assert(I >= 0 && I <= 255, - "gsl::byte only has 8 bits of storage, values must be in range 0-255"); - return static_cast(I); -} - -} // namespace gsl - -#ifdef _MSC_VER -#pragma warning(pop) -#endif // _MSC_VER - -#endif // GSL_BYTE_H diff --git a/src/serlio/utils/stduuid/gsl/gsl_util b/src/serlio/utils/stduuid/gsl/gsl_util deleted file mode 100644 index 93d60217..00000000 --- a/src/serlio/utils/stduuid/gsl/gsl_util +++ /dev/null @@ -1,158 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) 2015 Microsoft Corporation. All rights reserved. -// -// This code is licensed under the MIT License (MIT). -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef GSL_UTIL_H -#define GSL_UTIL_H - -#include // for Expects - -#include -#include // for ptrdiff_t, size_t -#include // for exception -#include // for initializer_list -#include // for is_signed, integral_constant -#include // for forward - -#if defined(_MSC_VER) - -#pragma warning(push) -#pragma warning(disable : 4127) // conditional expression is constant - -#if _MSC_VER < 1910 -#pragma push_macro("constexpr") -#define constexpr /*constexpr*/ -#endif // _MSC_VER < 1910 -#endif // _MSC_VER - -namespace gsl -{ -// -// GSL.util: utilities -// - -// index type for all container indexes/subscripts/sizes -using index = std::ptrdiff_t; - -// final_action allows you to ensure something gets run at the end of a scope -template -class final_action -{ -public: - explicit final_action(F f) noexcept : f_(std::move(f)) {} - - final_action(final_action&& other) noexcept : f_(std::move(other.f_)), invoke_(other.invoke_) - { - other.invoke_ = false; - } - - final_action(const final_action&) = delete; - final_action& operator=(const final_action&) = delete; - final_action& operator=(final_action&&) = delete; - - ~final_action() noexcept - { - if (invoke_) f_(); - } - -private: - F f_; - bool invoke_ {true}; -}; - -// finally() - convenience function to generate a final_action -template - -final_action finally(const F& f) noexcept -{ - return final_action(f); -} - -template -final_action finally(F&& f) noexcept -{ - return final_action(std::forward(f)); -} - -// narrow_cast(): a searchable way to do narrowing casts of values -template -constexpr T narrow_cast(U&& u) noexcept -{ - return static_cast(std::forward(u)); -} - -struct narrowing_error : public std::exception -{ -}; - -namespace details -{ - template - struct is_same_signedness - : public std::integral_constant::value == std::is_signed::value> - { - }; -} - -// narrow() : a checked version of narrow_cast() that throws if the cast changed the value -template -T narrow(U u) -{ - T t = narrow_cast(u); - if (static_cast(t) != u) gsl::details::throw_exception(narrowing_error()); - if (!details::is_same_signedness::value && ((t < T{}) != (u < U{}))) - gsl::details::throw_exception(narrowing_error()); - return t; -} - -// -// at() - Bounds-checked way of accessing builtin arrays, std::array, std::vector -// -template -constexpr T& at(T (&arr)[N], const index i) -{ - Expects(i >= 0 && i < narrow_cast(N)); - return arr[static_cast(i)]; -} - -template -constexpr auto at(Cont& cont, const index i) -> decltype(cont[cont.size()]) -{ - Expects(i >= 0 && i < narrow_cast(cont.size())); - using size_type = decltype(cont.size()); - return cont[static_cast(i)]; -} - -template -constexpr T at(const std::initializer_list cont, const index i) -{ - Expects(i >= 0 && i < narrow_cast(cont.size())); - return *(cont.begin() + i); -} - -} // namespace gsl - -#if defined(_MSC_VER) -#if _MSC_VER < 1910 -#undef constexpr -#pragma pop_macro("constexpr") - -#endif // _MSC_VER < 1910 - -#pragma warning(pop) - -#endif // _MSC_VER - -#endif // GSL_UTIL_H diff --git a/src/serlio/utils/stduuid/gsl/multi_span b/src/serlio/utils/stduuid/gsl/multi_span deleted file mode 100644 index 9c0c27b3..00000000 --- a/src/serlio/utils/stduuid/gsl/multi_span +++ /dev/null @@ -1,2242 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) 2015 Microsoft Corporation. All rights reserved. -// -// This code is licensed under the MIT License (MIT). -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef GSL_MULTI_SPAN_H -#define GSL_MULTI_SPAN_H - -#include // for Expects -#include // for byte -#include // for narrow_cast - -#include // for transform, lexicographical_compare -#include // for array -#include -#include // for ptrdiff_t, size_t, nullptr_t -#include // for PTRDIFF_MAX -#include // for divides, multiplies, minus, negate, plus -#include // for initializer_list -#include // for iterator, random_access_iterator_tag -#include // for numeric_limits -#include -#include -#include -#include // for basic_string -#include // for enable_if_t, remove_cv_t, is_same, is_co... -#include - -#ifdef _MSC_VER - -// turn off some warnings that are noisy about our Expects statements -#pragma warning(push) -#pragma warning(disable : 4127) // conditional expression is constant -#pragma warning(disable : 4702) // unreachable code - -#if _MSC_VER < 1910 -#pragma push_macro("constexpr") -#define constexpr /*constexpr*/ - -#endif // _MSC_VER < 1910 -#endif // _MSC_VER - -// GCC 7 does not like the signed unsigned missmatch (size_t ptrdiff_t) -// While there is a conversion from signed to unsigned, it happens at -// compiletime, so the compiler wouldn't have to warn indiscriminently, but -// could check if the source value actually doesn't fit into the target type -// and only warn in those cases. -#if __GNUC__ > 6 -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wsign-conversion" -#endif - -#ifdef GSL_THROW_ON_CONTRACT_VIOLATION -#define GSL_NOEXCEPT /*noexcept*/ -#else -#define GSL_NOEXCEPT noexcept -#endif // GSL_THROW_ON_CONTRACT_VIOLATION - -namespace gsl -{ - -/* -** begin definitions of index and bounds -*/ -namespace details -{ - template - struct SizeTypeTraits - { - static const SizeType max_value = std::numeric_limits::max(); - }; - - template - class are_integral : public std::integral_constant - { - }; - - template - class are_integral - : public std::integral_constant::value && are_integral::value> - { - }; -} - -template -class multi_span_index final -{ - static_assert(Rank > 0, "Rank must be greater than 0!"); - - template - friend class multi_span_index; - -public: - static const std::size_t rank = Rank; - using value_type = std::ptrdiff_t; - using size_type = value_type; - using reference = std::add_lvalue_reference_t; - using const_reference = std::add_lvalue_reference_t>; - - constexpr multi_span_index() GSL_NOEXCEPT {} - - constexpr multi_span_index(const value_type (&values)[Rank]) GSL_NOEXCEPT - { - std::copy(values, values + Rank, elems); - } - - template ::value>> - constexpr multi_span_index(Ts... ds) GSL_NOEXCEPT : elems{narrow_cast(ds)...} - { - } - - constexpr multi_span_index(const multi_span_index& other) GSL_NOEXCEPT = default; - - constexpr multi_span_index& operator=(const multi_span_index& rhs) GSL_NOEXCEPT = default; - - // Preconditions: component_idx < rank - constexpr reference operator[](std::size_t component_idx) - { - Expects(component_idx < Rank); // Component index must be less than rank - return elems[component_idx]; - } - - // Preconditions: component_idx < rank - constexpr const_reference operator[](std::size_t component_idx) const GSL_NOEXCEPT - { - Expects(component_idx < Rank); // Component index must be less than rank - return elems[component_idx]; - } - - constexpr bool operator==(const multi_span_index& rhs) const GSL_NOEXCEPT - { - return std::equal(elems, elems + rank, rhs.elems); - } - - constexpr bool operator!=(const multi_span_index& rhs) const GSL_NOEXCEPT { return !(*this == rhs); } - - constexpr multi_span_index operator+() const GSL_NOEXCEPT { return *this; } - - constexpr multi_span_index operator-() const GSL_NOEXCEPT - { - multi_span_index ret = *this; - std::transform(ret, ret + rank, ret, std::negate{}); - return ret; - } - - constexpr multi_span_index operator+(const multi_span_index& rhs) const GSL_NOEXCEPT - { - multi_span_index ret = *this; - ret += rhs; - return ret; - } - - constexpr multi_span_index operator-(const multi_span_index& rhs) const GSL_NOEXCEPT - { - multi_span_index ret = *this; - ret -= rhs; - return ret; - } - - constexpr multi_span_index& operator+=(const multi_span_index& rhs) GSL_NOEXCEPT - { - std::transform(elems, elems + rank, rhs.elems, elems, std::plus{}); - return *this; - } - - constexpr multi_span_index& operator-=(const multi_span_index& rhs) GSL_NOEXCEPT - { - std::transform(elems, elems + rank, rhs.elems, elems, std::minus{}); - return *this; - } - - constexpr multi_span_index operator*(value_type v) const GSL_NOEXCEPT - { - multi_span_index ret = *this; - ret *= v; - return ret; - } - - constexpr multi_span_index operator/(value_type v) const GSL_NOEXCEPT - { - multi_span_index ret = *this; - ret /= v; - return ret; - } - - friend constexpr multi_span_index operator*(value_type v, const multi_span_index& rhs) GSL_NOEXCEPT - { - return rhs * v; - } - - constexpr multi_span_index& operator*=(value_type v) GSL_NOEXCEPT - { - std::transform(elems, elems + rank, elems, - [v](value_type x) { return std::multiplies{}(x, v); }); - return *this; - } - - constexpr multi_span_index& operator/=(value_type v) GSL_NOEXCEPT - { - std::transform(elems, elems + rank, elems, - [v](value_type x) { return std::divides{}(x, v); }); - return *this; - } - -private: - value_type elems[Rank] = {}; -}; - -#if !defined(_MSC_VER) || _MSC_VER >= 1910 - -struct static_bounds_dynamic_range_t -{ - template ::value>> - constexpr operator T() const GSL_NOEXCEPT - { - return narrow_cast(-1); - } -}; - -constexpr bool operator==(static_bounds_dynamic_range_t, static_bounds_dynamic_range_t) GSL_NOEXCEPT -{ - return true; -} - -constexpr bool operator!=(static_bounds_dynamic_range_t, static_bounds_dynamic_range_t) GSL_NOEXCEPT -{ - return false; -} - -template ::value>> -constexpr bool operator==(static_bounds_dynamic_range_t, T other) GSL_NOEXCEPT -{ - return narrow_cast(-1) == other; -} - -template ::value>> -constexpr bool operator==(T left, static_bounds_dynamic_range_t right) GSL_NOEXCEPT -{ - return right == left; -} - -template ::value>> -constexpr bool operator!=(static_bounds_dynamic_range_t, T other) GSL_NOEXCEPT -{ - return narrow_cast(-1) != other; -} - -template ::value>> -constexpr bool operator!=(T left, static_bounds_dynamic_range_t right) GSL_NOEXCEPT -{ - return right != left; -} - -constexpr static_bounds_dynamic_range_t dynamic_range{}; -#else -const std::ptrdiff_t dynamic_range = -1; -#endif - -struct generalized_mapping_tag -{ -}; -struct contiguous_mapping_tag : generalized_mapping_tag -{ -}; - -namespace details -{ - - template - struct LessThan - { - static const bool value = Left < Right; - }; - - template - struct BoundsRanges - { - using size_type = std::ptrdiff_t; - static const size_type Depth = 0; - static const size_type DynamicNum = 0; - static const size_type CurrentRange = 1; - static const size_type TotalSize = 1; - - // TODO : following signature is for work around VS bug - template - BoundsRanges(const OtherRange&, bool /* firstLevel */) - { - } - - BoundsRanges(const std::ptrdiff_t* const) {} - BoundsRanges() = default; - - template - void serialize(T&) const - { - } - - template - size_type linearize(const T&) const - { - return 0; - } - - template - size_type contains(const T&) const - { - return -1; - } - - size_type elementNum(std::size_t) const GSL_NOEXCEPT { return 0; } - - size_type totalSize() const GSL_NOEXCEPT { return TotalSize; } - - bool operator==(const BoundsRanges&) const GSL_NOEXCEPT { return true; } - }; - - template - struct BoundsRanges : BoundsRanges - { - using Base = BoundsRanges; - using size_type = std::ptrdiff_t; - static const std::size_t Depth = Base::Depth + 1; - static const std::size_t DynamicNum = Base::DynamicNum + 1; - static const size_type CurrentRange = dynamic_range; - static const size_type TotalSize = dynamic_range; - - private: - size_type m_bound; - - public: - BoundsRanges(const std::ptrdiff_t* const arr) - : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) - { - Expects(0 <= *arr); - } - - BoundsRanges() : m_bound(0) {} - - template - BoundsRanges(const BoundsRanges& other, - bool /* firstLevel */ = true) - : Base(static_cast&>(other), false) - , m_bound(other.totalSize()) - { - } - - template - void serialize(T& arr) const - { - arr[Dim] = elementNum(); - this->Base::template serialize(arr); - } - - template - size_type linearize(const T& arr) const - { - const size_type index = this->Base::totalSize() * arr[Dim]; - Expects(index < m_bound); - return index + this->Base::template linearize(arr); - } - - template - size_type contains(const T& arr) const - { - const ptrdiff_t last = this->Base::template contains(arr); - if (last == -1) return -1; - const ptrdiff_t cur = this->Base::totalSize() * arr[Dim]; - return cur < m_bound ? cur + last : -1; - } - - size_type totalSize() const GSL_NOEXCEPT { return m_bound; } - - size_type elementNum() const GSL_NOEXCEPT { return totalSize() / this->Base::totalSize(); } - - size_type elementNum(std::size_t dim) const GSL_NOEXCEPT - { - if (dim > 0) - return this->Base::elementNum(dim - 1); - else - return elementNum(); - } - - bool operator==(const BoundsRanges& rhs) const GSL_NOEXCEPT - { - return m_bound == rhs.m_bound && - static_cast(*this) == static_cast(rhs); - } - }; - - template - struct BoundsRanges : BoundsRanges - { - using Base = BoundsRanges; - using size_type = std::ptrdiff_t; - static const std::size_t Depth = Base::Depth + 1; - static const std::size_t DynamicNum = Base::DynamicNum; - static const size_type CurrentRange = CurRange; - static const size_type TotalSize = - Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize; - - BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) {} - BoundsRanges() = default; - - template - BoundsRanges(const BoundsRanges& other, - bool firstLevel = true) - : Base(static_cast&>(other), false) - { - (void) firstLevel; - } - - template - void serialize(T& arr) const - { - arr[Dim] = elementNum(); - this->Base::template serialize(arr); - } - - template - size_type linearize(const T& arr) const - { - Expects(arr[Dim] >= 0 && arr[Dim] < CurrentRange); // Index is out of range - return this->Base::totalSize() * arr[Dim] + - this->Base::template linearize(arr); - } - - template - size_type contains(const T& arr) const - { - if (arr[Dim] >= CurrentRange) return -1; - const size_type last = this->Base::template contains(arr); - if (last == -1) return -1; - return this->Base::totalSize() * arr[Dim] + last; - } - - size_type totalSize() const GSL_NOEXCEPT { return CurrentRange * this->Base::totalSize(); } - - size_type elementNum() const GSL_NOEXCEPT { return CurrentRange; } - - size_type elementNum(std::size_t dim) const GSL_NOEXCEPT - { - if (dim > 0) - return this->Base::elementNum(dim - 1); - else - return elementNum(); - } - - bool operator==(const BoundsRanges& rhs) const GSL_NOEXCEPT - { - return static_cast(*this) == static_cast(rhs); - } - }; - - template - struct BoundsRangeConvertible - : public std::integral_constant= TargetType::TotalSize || - TargetType::TotalSize == dynamic_range || - SourceType::TotalSize == dynamic_range || - TargetType::TotalSize == 0)> - { - }; - - template - struct TypeListIndexer - { - const TypeChain& obj_; - TypeListIndexer(const TypeChain& obj) : obj_(obj) {} - - template - const TypeChain& getObj(std::true_type) - { - return obj_; - } - - template - auto getObj(std::false_type) - -> decltype(TypeListIndexer(static_cast(obj_)).template get()) - { - return TypeListIndexer(static_cast(obj_)).template get(); - } - - template - auto get() -> decltype(getObj(std::integral_constant())) - { - return getObj(std::integral_constant()); - } - }; - - template - TypeListIndexer createTypeListIndexer(const TypeChain& obj) - { - return TypeListIndexer(obj); - } - - template 1), - typename Ret = std::enable_if_t>> - constexpr Ret shift_left(const multi_span_index& other) GSL_NOEXCEPT - { - Ret ret{}; - for (std::size_t i = 0; i < Rank - 1; ++i) { - ret[i] = other[i + 1]; - } - return ret; - } -} - -template -class bounds_iterator; - -template -class static_bounds -{ -public: - static_bounds(const details::BoundsRanges&) {} -}; - -template -class static_bounds -{ - using MyRanges = details::BoundsRanges; - - MyRanges m_ranges; - constexpr static_bounds(const MyRanges& range) : m_ranges(range) {} - - template - friend class static_bounds; - -public: - static const std::size_t rank = MyRanges::Depth; - static const std::size_t dynamic_rank = MyRanges::DynamicNum; - static const std::ptrdiff_t static_size = MyRanges::TotalSize; - - using size_type = std::ptrdiff_t; - using index_type = multi_span_index; - using const_index_type = std::add_const_t; - using iterator = bounds_iterator; - using const_iterator = bounds_iterator; - using difference_type = std::ptrdiff_t; - using sliced_type = static_bounds; - using mapping_type = contiguous_mapping_tag; - - constexpr static_bounds(const static_bounds&) = default; - - template - struct BoundsRangeConvertible2; - - template > - static auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret; - - template - static auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type; - - template - struct BoundsRangeConvertible2 - : decltype(helpBoundsRangeConvertible( - SourceType(), TargetType(), - std::integral_constant())) - { - }; - - template - struct BoundsRangeConvertible2 : std::true_type - { - }; - - template - struct BoundsRangeConvertible - : decltype(helpBoundsRangeConvertible( - SourceType(), TargetType(), - std::integral_constant::value || - TargetType::CurrentRange == dynamic_range || - SourceType::CurrentRange == dynamic_range)>())) - { - }; - - template - struct BoundsRangeConvertible : std::true_type - { - }; - - template , - details::BoundsRanges>::value>> - constexpr static_bounds(const static_bounds& other) : m_ranges(other.m_ranges) - { - Expects((MyRanges::DynamicNum == 0 && details::BoundsRanges::DynamicNum == 0) || - MyRanges::DynamicNum > 0 || other.m_ranges.totalSize() >= m_ranges.totalSize()); - } - - constexpr static_bounds(std::initializer_list il) - : m_ranges(il.begin()) - { - // Size of the initializer list must match the rank of the array - Expects((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || - MyRanges::DynamicNum == il.size()); - // Size of the range must be less than the max element of the size type - Expects(m_ranges.totalSize() <= PTRDIFF_MAX); - } - - constexpr static_bounds() = default; - - constexpr sliced_type slice() const GSL_NOEXCEPT - { - return sliced_type{static_cast&>(m_ranges)}; - } - - constexpr size_type stride() const GSL_NOEXCEPT { return rank > 1 ? slice().size() : 1; } - - constexpr size_type size() const GSL_NOEXCEPT { return m_ranges.totalSize(); } - - constexpr size_type total_size() const GSL_NOEXCEPT { return m_ranges.totalSize(); } - - constexpr size_type linearize(const index_type& idx) const { return m_ranges.linearize(idx); } - - constexpr bool contains(const index_type& idx) const GSL_NOEXCEPT - { - return m_ranges.contains(idx) != -1; - } - - constexpr size_type operator[](std::size_t idx) const GSL_NOEXCEPT - { - return m_ranges.elementNum(idx); - } - - template - constexpr size_type extent() const GSL_NOEXCEPT - { - static_assert(Dim < rank, - "dimension should be less than rank (dimension count starts from 0)"); - return details::createTypeListIndexer(m_ranges).template get().elementNum(); - } - - template - constexpr size_type extent(IntType dim) const GSL_NOEXCEPT - { - static_assert(std::is_integral::value, - "Dimension parameter must be supplied as an integral type."); - auto real_dim = narrow_cast(dim); - Expects(real_dim < rank); - - return m_ranges.elementNum(real_dim); - } - - constexpr index_type index_bounds() const GSL_NOEXCEPT - { - size_type extents[rank] = {}; - m_ranges.serialize(extents); - return {extents}; - } - - template - constexpr bool operator==(const static_bounds& rhs) const GSL_NOEXCEPT - { - return this->size() == rhs.size(); - } - - template - constexpr bool operator!=(const static_bounds& rhs) const GSL_NOEXCEPT - { - return !(*this == rhs); - } - - constexpr const_iterator begin() const GSL_NOEXCEPT - { - return const_iterator(*this, index_type{}); - } - - constexpr const_iterator end() const GSL_NOEXCEPT - { - return const_iterator(*this, this->index_bounds()); - } -}; - -template -class strided_bounds -{ - template - friend class strided_bounds; - -public: - static const std::size_t rank = Rank; - using value_type = std::ptrdiff_t; - using reference = std::add_lvalue_reference_t; - using const_reference = std::add_const_t; - using size_type = value_type; - using difference_type = value_type; - using index_type = multi_span_index; - using const_index_type = std::add_const_t; - using iterator = bounds_iterator; - using const_iterator = bounds_iterator; - static const value_type dynamic_rank = rank; - static const value_type static_size = dynamic_range; - using sliced_type = std::conditional_t, void>; - using mapping_type = generalized_mapping_tag; - - constexpr strided_bounds(const strided_bounds&) GSL_NOEXCEPT = default; - - constexpr strided_bounds& operator=(const strided_bounds&) GSL_NOEXCEPT = default; - - constexpr strided_bounds(const value_type (&values)[rank], index_type strides) - : m_extents(values), m_strides(std::move(strides)) - { - } - - constexpr strided_bounds(const index_type& extents, const index_type& strides) GSL_NOEXCEPT - : m_extents(extents), - m_strides(strides) - { - } - - constexpr index_type strides() const GSL_NOEXCEPT { return m_strides; } - - constexpr size_type total_size() const GSL_NOEXCEPT - { - size_type ret = 0; - for (std::size_t i = 0; i < rank; ++i) { - ret += (m_extents[i] - 1) * m_strides[i]; - } - return ret + 1; - } - - constexpr size_type size() const GSL_NOEXCEPT - { - size_type ret = 1; - for (std::size_t i = 0; i < rank; ++i) { - ret *= m_extents[i]; - } - return ret; - } - - constexpr bool contains(const index_type& idx) const GSL_NOEXCEPT - { - for (std::size_t i = 0; i < rank; ++i) { - if (idx[i] < 0 || idx[i] >= m_extents[i]) return false; - } - return true; - } - - constexpr size_type linearize(const index_type& idx) const GSL_NOEXCEPT - { - size_type ret = 0; - for (std::size_t i = 0; i < rank; i++) { - Expects(idx[i] < m_extents[i]); // index is out of bounds of the array - ret += idx[i] * m_strides[i]; - } - return ret; - } - - constexpr size_type stride() const GSL_NOEXCEPT { return m_strides[0]; } - - template 1), typename Ret = std::enable_if_t> - constexpr sliced_type slice() const - { - return {details::shift_left(m_extents), details::shift_left(m_strides)}; - } - - template - constexpr size_type extent() const GSL_NOEXCEPT - { - static_assert(Dim < Rank, - "dimension should be less than rank (dimension count starts from 0)"); - return m_extents[Dim]; - } - - constexpr index_type index_bounds() const GSL_NOEXCEPT { return m_extents; } - constexpr const_iterator begin() const GSL_NOEXCEPT - { - return const_iterator{*this, index_type{}}; - } - - constexpr const_iterator end() const GSL_NOEXCEPT - { - return const_iterator{*this, index_bounds()}; - } - -private: - index_type m_extents; - index_type m_strides; -}; - -template -struct is_bounds : std::integral_constant -{ -}; -template -struct is_bounds> : std::integral_constant -{ -}; -template -struct is_bounds> : std::integral_constant -{ -}; - -template -class bounds_iterator -{ -public: - static const std::size_t rank = IndexType::rank; - using iterator_category = std::random_access_iterator_tag; - using value_type = IndexType; - using difference_type = std::ptrdiff_t; - using pointer = value_type*; - using reference = value_type&; - using index_type = value_type; - using index_size_type = typename IndexType::value_type; - template - explicit bounds_iterator(const Bounds& bnd, value_type curr) GSL_NOEXCEPT - : boundary_(bnd.index_bounds()), - curr_(std::move(curr)) - { - static_assert(is_bounds::value, "Bounds type must be provided"); - } - - constexpr reference operator*() const GSL_NOEXCEPT { return curr_; } - - constexpr pointer operator->() const GSL_NOEXCEPT { return &curr_; } - - constexpr bounds_iterator& operator++() GSL_NOEXCEPT - { - for (std::size_t i = rank; i-- > 0;) { - if (curr_[i] < boundary_[i] - 1) { - curr_[i]++; - return *this; - } - curr_[i] = 0; - } - // If we're here we've wrapped over - set to past-the-end. - curr_ = boundary_; - return *this; - } - - constexpr bounds_iterator operator++(int) GSL_NOEXCEPT - { - auto ret = *this; - ++(*this); - return ret; - } - - constexpr bounds_iterator& operator--() GSL_NOEXCEPT - { - if (!less(curr_, boundary_)) { - // if at the past-the-end, set to last element - for (std::size_t i = 0; i < rank; ++i) { - curr_[i] = boundary_[i] - 1; - } - return *this; - } - for (std::size_t i = rank; i-- > 0;) { - if (curr_[i] >= 1) { - curr_[i]--; - return *this; - } - curr_[i] = boundary_[i] - 1; - } - // If we're here the preconditions were violated - // "pre: there exists s such that r == ++s" - Expects(false); - return *this; - } - - constexpr bounds_iterator operator--(int) GSL_NOEXCEPT - { - auto ret = *this; - --(*this); - return ret; - } - - constexpr bounds_iterator operator+(difference_type n) const GSL_NOEXCEPT - { - bounds_iterator ret{*this}; - return ret += n; - } - - constexpr bounds_iterator& operator+=(difference_type n) GSL_NOEXCEPT - { - auto linear_idx = linearize(curr_) + n; - std::remove_const_t stride = 0; - stride[rank - 1] = 1; - for (std::size_t i = rank - 1; i-- > 0;) { - stride[i] = stride[i + 1] * boundary_[i + 1]; - } - for (std::size_t i = 0; i < rank; ++i) { - curr_[i] = linear_idx / stride[i]; - linear_idx = linear_idx % stride[i]; - } - // index is out of bounds of the array - Expects(!less(curr_, index_type{}) && !less(boundary_, curr_)); - return *this; - } - - constexpr bounds_iterator operator-(difference_type n) const GSL_NOEXCEPT - { - bounds_iterator ret{*this}; - return ret -= n; - } - - constexpr bounds_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; } - - constexpr difference_type operator-(const bounds_iterator& rhs) const GSL_NOEXCEPT - { - return linearize(curr_) - linearize(rhs.curr_); - } - - constexpr value_type operator[](difference_type n) const GSL_NOEXCEPT { return *(*this + n); } - - constexpr bool operator==(const bounds_iterator& rhs) const GSL_NOEXCEPT - { - return curr_ == rhs.curr_; - } - - constexpr bool operator!=(const bounds_iterator& rhs) const GSL_NOEXCEPT - { - return !(*this == rhs); - } - - constexpr bool operator<(const bounds_iterator& rhs) const GSL_NOEXCEPT - { - return less(curr_, rhs.curr_); - } - - constexpr bool operator<=(const bounds_iterator& rhs) const GSL_NOEXCEPT - { - return !(rhs < *this); - } - - constexpr bool operator>(const bounds_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; } - - constexpr bool operator>=(const bounds_iterator& rhs) const GSL_NOEXCEPT - { - return !(rhs > *this); - } - - void swap(bounds_iterator& rhs) GSL_NOEXCEPT - { - std::swap(boundary_, rhs.boundary_); - std::swap(curr_, rhs.curr_); - } - -private: - constexpr bool less(index_type& one, index_type& other) const GSL_NOEXCEPT - { - for (std::size_t i = 0; i < rank; ++i) { - if (one[i] < other[i]) return true; - } - return false; - } - - constexpr index_size_type linearize(const value_type& idx) const GSL_NOEXCEPT - { - // TODO: Smarter impl. - // Check if past-the-end - index_size_type multiplier = 1; - index_size_type res = 0; - if (!less(idx, boundary_)) { - res = 1; - for (std::size_t i = rank; i-- > 0;) { - res += (idx[i] - 1) * multiplier; - multiplier *= boundary_[i]; - } - } - else - { - for (std::size_t i = rank; i-- > 0;) { - res += idx[i] * multiplier; - multiplier *= boundary_[i]; - } - } - return res; - } - - value_type boundary_; - std::remove_const_t curr_; -}; - -template -bounds_iterator operator+(typename bounds_iterator::difference_type n, - const bounds_iterator& rhs) GSL_NOEXCEPT -{ - return rhs + n; -} - -namespace details -{ - template - constexpr std::enable_if_t< - std::is_same::value, - typename Bounds::index_type> - make_stride(const Bounds& bnd) GSL_NOEXCEPT - { - return bnd.strides(); - } - - // Make a stride vector from bounds, assuming contiguous memory. - template - constexpr std::enable_if_t< - std::is_same::value, - typename Bounds::index_type> - make_stride(const Bounds& bnd) GSL_NOEXCEPT - { - auto extents = bnd.index_bounds(); - typename Bounds::size_type stride[Bounds::rank] = {}; - - stride[Bounds::rank - 1] = 1; - for (std::size_t i = 1; i < Bounds::rank; ++i) { - stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i]; - } - return {stride}; - } - - template - void verifyBoundsReshape(const BoundsSrc& src, const BoundsDest& dest) - { - static_assert(is_bounds::value && is_bounds::value, - "The src type and dest type must be bounds"); - static_assert(std::is_same::value, - "The source type must be a contiguous bounds"); - static_assert(BoundsDest::static_size == dynamic_range || - BoundsSrc::static_size == dynamic_range || - BoundsDest::static_size == BoundsSrc::static_size, - "The source bounds must have same size as dest bounds"); - Expects(src.size() == dest.size()); - } - -} // namespace details - -template -class contiguous_span_iterator; -template -class general_span_iterator; - -template -struct dim_t -{ - static const std::ptrdiff_t value = DimSize; -}; -template <> -struct dim_t -{ - static const std::ptrdiff_t value = dynamic_range; - const std::ptrdiff_t dvalue; - constexpr dim_t(std::ptrdiff_t size) GSL_NOEXCEPT : dvalue(size) {} -}; - -template = 0)>> -constexpr dim_t dim() GSL_NOEXCEPT -{ - return dim_t(); -} - -template > -constexpr dim_t dim(std::ptrdiff_t n) GSL_NOEXCEPT -{ - return dim_t<>(n); -} - -template -class multi_span; -template -class strided_span; - -namespace details -{ - template - struct SpanTypeTraits - { - using value_type = T; - using size_type = std::size_t; - }; - - template - struct SpanTypeTraits::type> - { - using value_type = typename Traits::span_traits::value_type; - using size_type = typename Traits::span_traits::size_type; - }; - - template - struct SpanArrayTraits - { - using type = multi_span; - using value_type = T; - using bounds_type = static_bounds; - using pointer = T*; - using reference = T&; - }; - template - struct SpanArrayTraits : SpanArrayTraits - { - }; - - template - BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size - { - Expects(totalSize >= 0 && totalSize <= PTRDIFF_MAX); - return BoundsType{totalSize}; - } - template - BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size - { - Expects(BoundsType::static_size <= totalSize); - return {}; - } - template - BoundsType newBoundsHelper(std::ptrdiff_t totalSize) - { - static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1"); - return newBoundsHelperImpl( - totalSize, std::integral_constant()); - } - - struct Sep - { - }; - - template - T static_as_multi_span_helper(Sep, Args... args) - { - return T{narrow_cast(args)...}; - } - template - std::enable_if_t< - !std::is_same>::value && !std::is_same::value, T> - static_as_multi_span_helper(Arg, Args... args) - { - return static_as_multi_span_helper(args...); - } - template - T static_as_multi_span_helper(dim_t val, Args... args) - { - return static_as_multi_span_helper(args..., val.dvalue); - } - - template - struct static_as_multi_span_static_bounds_helper - { - using type = static_bounds<(Dimensions::value)...>; - }; - - template - struct is_multi_span_oracle : std::false_type - { - }; - - template - struct is_multi_span_oracle> - : std::true_type - { - }; - - template - struct is_multi_span_oracle> : std::true_type - { - }; - - template - struct is_multi_span : is_multi_span_oracle> - { - }; -} - -template -class multi_span -{ - // TODO do we still need this? - template - friend class multi_span; - -public: - using bounds_type = static_bounds; - static const std::size_t Rank = bounds_type::rank; - using size_type = typename bounds_type::size_type; - using index_type = typename bounds_type::index_type; - using value_type = ValueType; - using const_value_type = std::add_const_t; - using pointer = std::add_pointer_t; - using reference = std::add_lvalue_reference_t; - using iterator = contiguous_span_iterator; - using const_span = multi_span; - using const_iterator = contiguous_span_iterator; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - using sliced_type = - std::conditional_t>; - -private: - pointer data_; - bounds_type bounds_; - - friend iterator; - friend const_iterator; - -public: - // default constructor - same as constructing from nullptr_t - constexpr multi_span() GSL_NOEXCEPT : multi_span(nullptr, bounds_type{}) - { - static_assert(bounds_type::dynamic_rank != 0 || - (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), - "Default construction of multi_span only possible " - "for dynamic or fixed, zero-length spans."); - } - - // construct from nullptr - get an empty multi_span - constexpr multi_span(std::nullptr_t) GSL_NOEXCEPT : multi_span(nullptr, bounds_type{}) - { - static_assert(bounds_type::dynamic_rank != 0 || - (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), - "nullptr_t construction of multi_span only possible " - "for dynamic or fixed, zero-length spans."); - } - - // construct from nullptr with size of 0 (helps with template function calls) - template ::value>> - constexpr multi_span(std::nullptr_t, IntType size) GSL_NOEXCEPT - : multi_span(nullptr, bounds_type{}) - { - static_assert(bounds_type::dynamic_rank != 0 || - (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), - "nullptr_t construction of multi_span only possible " - "for dynamic or fixed, zero-length spans."); - Expects(size == 0); - } - - // construct from a single element - constexpr multi_span(reference data) GSL_NOEXCEPT : multi_span(&data, bounds_type{1}) - { - static_assert(bounds_type::dynamic_rank > 0 || bounds_type::static_size == 0 || - bounds_type::static_size == 1, - "Construction from a single element only possible " - "for dynamic or fixed spans of length 0 or 1."); - } - - // prevent constructing from temporaries for single-elements - constexpr multi_span(value_type&&) = delete; - - // construct from pointer + length - constexpr multi_span(pointer ptr, size_type size) GSL_NOEXCEPT - : multi_span(ptr, bounds_type{size}) - { - } - - // construct from pointer + length - multidimensional - constexpr multi_span(pointer data, bounds_type bounds) GSL_NOEXCEPT : data_(data), - bounds_(std::move(bounds)) - { - Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0); - } - - // construct from begin,end pointer pair - template ::value && - details::LessThan::value>> - constexpr multi_span(pointer begin, Ptr end) - : multi_span(begin, - details::newBoundsHelper(static_cast(end) - begin)) - { - Expects(begin != nullptr && end != nullptr && begin <= static_cast(end)); - } - - // construct from n-dimensions static array - template > - constexpr multi_span(T (&arr)[N]) - : multi_span(reinterpret_cast(arr), bounds_type{typename Helper::bounds_type{}}) - { - static_assert(std::is_convertible::value, - "Cannot convert from source type to target multi_span type."); - static_assert(std::is_convertible::value, - "Cannot construct a multi_span from an array with fewer elements."); - } - - // construct from n-dimensions dynamic array (e.g. new int[m][4]) - // (precedence will be lower than the 1-dimension pointer) - template > - constexpr multi_span(T* const& data, size_type size) - : multi_span(reinterpret_cast(data), typename Helper::bounds_type{size}) - { - static_assert(std::is_convertible::value, - "Cannot convert from source type to target multi_span type."); - } - - // construct from std::array - template - constexpr multi_span(std::array& arr) - : multi_span(arr.data(), bounds_type{static_bounds{}}) - { - static_assert( - std::is_convertible(*)[]>::value, - "Cannot convert from source type to target multi_span type."); - static_assert(std::is_convertible, bounds_type>::value, - "You cannot construct a multi_span from a std::array of smaller size."); - } - - // construct from const std::array - template - constexpr multi_span(const std::array& arr) - : multi_span(arr.data(), bounds_type{static_bounds{}}) - { - static_assert(std::is_convertible(*)[]>::value, - "Cannot convert from source type to target multi_span type."); - static_assert(std::is_convertible, bounds_type>::value, - "You cannot construct a multi_span from a std::array of smaller size."); - } - - // prevent constructing from temporary std::array - template - constexpr multi_span(std::array&& arr) = delete; - - // construct from containers - // future: could use contiguous_iterator_traits to identify only contiguous containers - // type-requirements: container must have .size(), operator[] which are value_type compatible - template ::value && - std::is_convertible::value && - std::is_same().size(), - *std::declval().data())>, - DataType>::value>> - constexpr multi_span(Cont& cont) - : multi_span(static_cast(cont.data()), - details::newBoundsHelper(narrow_cast(cont.size()))) - { - } - - // prevent constructing from temporary containers - template ::value && - std::is_convertible::value && - std::is_same().size(), - *std::declval().data())>, - DataType>::value>> - explicit constexpr multi_span(Cont&& cont) = delete; - - // construct from a convertible multi_span - template , - typename = std::enable_if_t::value && - std::is_convertible::value>> - constexpr multi_span(multi_span other) GSL_NOEXCEPT - : data_(other.data_), - bounds_(other.bounds_) - { - } - - // trivial copy and move - constexpr multi_span(const multi_span&) = default; - constexpr multi_span(multi_span&&) = default; - - // trivial assignment - constexpr multi_span& operator=(const multi_span&) = default; - constexpr multi_span& operator=(multi_span&&) = default; - - // first() - extract the first Count elements into a new multi_span - template - constexpr multi_span first() const GSL_NOEXCEPT - { - static_assert(Count >= 0, "Count must be >= 0."); - static_assert(bounds_type::static_size == dynamic_range || - Count <= bounds_type::static_size, - "Count is out of bounds."); - - Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); - return {this->data(), Count}; - } - - // first() - extract the first count elements into a new multi_span - constexpr multi_span first(size_type count) const GSL_NOEXCEPT - { - Expects(count >= 0 && count <= this->size()); - return {this->data(), count}; - } - - // last() - extract the last Count elements into a new multi_span - template - constexpr multi_span last() const GSL_NOEXCEPT - { - static_assert(Count >= 0, "Count must be >= 0."); - static_assert(bounds_type::static_size == dynamic_range || - Count <= bounds_type::static_size, - "Count is out of bounds."); - - Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); - return {this->data() + this->size() - Count, Count}; - } - - // last() - extract the last count elements into a new multi_span - constexpr multi_span last(size_type count) const GSL_NOEXCEPT - { - Expects(count >= 0 && count <= this->size()); - return {this->data() + this->size() - count, count}; - } - - // subspan() - create a subview of Count elements starting at Offset - template - constexpr multi_span subspan() const GSL_NOEXCEPT - { - static_assert(Count >= 0, "Count must be >= 0."); - static_assert(Offset >= 0, "Offset must be >= 0."); - static_assert(bounds_type::static_size == dynamic_range || - ((Offset <= bounds_type::static_size) && - Count <= bounds_type::static_size - Offset), - "You must describe a sub-range within bounds of the multi_span."); - - Expects(bounds_type::static_size != dynamic_range || - (Offset <= this->size() && Count <= this->size() - Offset)); - return {this->data() + Offset, Count}; - } - - // subspan() - create a subview of count elements starting at offset - // supplying dynamic_range for count will consume all available elements from offset - constexpr multi_span - subspan(size_type offset, size_type count = dynamic_range) const GSL_NOEXCEPT - { - Expects((offset >= 0 && offset <= this->size()) && - (count == dynamic_range || (count <= this->size() - offset))); - return {this->data() + offset, count == dynamic_range ? this->length() - offset : count}; - } - - // section - creates a non-contiguous, strided multi_span from a contiguous one - constexpr strided_span section(index_type origin, - index_type extents) const GSL_NOEXCEPT - { - size_type size = this->bounds().total_size() - this->bounds().linearize(origin); - return {&this->operator[](origin), size, - strided_bounds{extents, details::make_stride(bounds())}}; - } - - // length of the multi_span in elements - constexpr size_type size() const GSL_NOEXCEPT { return bounds_.size(); } - - // length of the multi_span in elements - constexpr size_type length() const GSL_NOEXCEPT { return this->size(); } - - // length of the multi_span in bytes - constexpr size_type size_bytes() const GSL_NOEXCEPT - { - return narrow_cast(sizeof(value_type)) * this->size(); - } - - // length of the multi_span in bytes - constexpr size_type length_bytes() const GSL_NOEXCEPT { return this->size_bytes(); } - - constexpr bool empty() const GSL_NOEXCEPT { return this->size() == 0; } - - static constexpr std::size_t rank() { return Rank; } - - template - constexpr size_type extent() const GSL_NOEXCEPT - { - static_assert(Dim < Rank, - "Dimension should be less than rank (dimension count starts from 0)."); - return bounds_.template extent(); - } - - template - constexpr size_type extent(IntType dim) const GSL_NOEXCEPT - { - return bounds_.extent(dim); - } - - constexpr bounds_type bounds() const GSL_NOEXCEPT { return bounds_; } - - constexpr pointer data() const GSL_NOEXCEPT { return data_; } - - template - constexpr reference operator()(FirstIndex idx) - { - return this->operator[](narrow_cast(idx)); - } - - template - constexpr reference operator()(FirstIndex firstIndex, OtherIndices... indices) - { - index_type idx = {narrow_cast(firstIndex), - narrow_cast(indices)...}; - return this->operator[](idx); - } - - constexpr reference operator[](const index_type& idx) const GSL_NOEXCEPT - { - return data_[bounds_.linearize(idx)]; - } - - template 1), typename Ret = std::enable_if_t> - constexpr Ret operator[](size_type idx) const GSL_NOEXCEPT - { - Expects(idx >= 0 && idx < bounds_.size()); // index is out of bounds of the array - const size_type ridx = idx * bounds_.stride(); - - // index is out of bounds of the underlying data - Expects(ridx < bounds_.total_size()); - return Ret{data_ + ridx, bounds_.slice()}; - } - - constexpr iterator begin() const GSL_NOEXCEPT { return iterator{this, true}; } - - constexpr iterator end() const GSL_NOEXCEPT { return iterator{this, false}; } - - constexpr const_iterator cbegin() const GSL_NOEXCEPT - { - return const_iterator{reinterpret_cast(this), true}; - } - - constexpr const_iterator cend() const GSL_NOEXCEPT - { - return const_iterator{reinterpret_cast(this), false}; - } - - constexpr reverse_iterator rbegin() const GSL_NOEXCEPT { return reverse_iterator{end()}; } - - constexpr reverse_iterator rend() const GSL_NOEXCEPT { return reverse_iterator{begin()}; } - - constexpr const_reverse_iterator crbegin() const GSL_NOEXCEPT - { - return const_reverse_iterator{cend()}; - } - - constexpr const_reverse_iterator crend() const GSL_NOEXCEPT - { - return const_reverse_iterator{cbegin()}; - } - - template , std::remove_cv_t>::value>> - constexpr bool - operator==(const multi_span& other) const GSL_NOEXCEPT - { - return bounds_.size() == other.bounds_.size() && - (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); - } - - template , std::remove_cv_t>::value>> - constexpr bool - operator!=(const multi_span& other) const GSL_NOEXCEPT - { - return !(*this == other); - } - - template , std::remove_cv_t>::value>> - constexpr bool - operator<(const multi_span& other) const GSL_NOEXCEPT - { - return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); - } - - template , std::remove_cv_t>::value>> - constexpr bool - operator<=(const multi_span& other) const GSL_NOEXCEPT - { - return !(other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool - operator>(const multi_span& other) const GSL_NOEXCEPT - { - return (other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool - operator>=(const multi_span& other) const GSL_NOEXCEPT - { - return !(*this < other); - } -}; - -// -// Free functions for manipulating spans -// - -// reshape a multi_span into a different dimensionality -// DimCount and Enabled here are workarounds for a bug in MSVC 2015 -template 0), typename = std::enable_if_t> -constexpr auto as_multi_span(SpanType s, Dimensions2... dims) - -> multi_span -{ - static_assert(details::is_multi_span::value, - "Variadic as_multi_span() is for reshaping existing spans."); - using BoundsType = - typename multi_span::bounds_type; - auto tobounds = details::static_as_multi_span_helper(dims..., details::Sep{}); - details::verifyBoundsReshape(s.bounds(), tobounds); - return {s.data(), tobounds}; -} - -// convert a multi_span to a multi_span -template -multi_span as_bytes(multi_span s) GSL_NOEXCEPT -{ - static_assert(std::is_trivial>::value, - "The value_type of multi_span must be a trivial type."); - return {reinterpret_cast(s.data()), s.size_bytes()}; -} - -// convert a multi_span to a multi_span (a writeable byte multi_span) -// this is not currently a portable function that can be relied upon to work -// on all implementations. It should be considered an experimental extension -// to the standard GSL interface. -template -multi_span as_writeable_bytes(multi_span s) GSL_NOEXCEPT -{ - static_assert(std::is_trivial>::value, - "The value_type of multi_span must be a trivial type."); - return {reinterpret_cast(s.data()), s.size_bytes()}; -} - -// convert a multi_span to a multi_span -// this is not currently a portable function that can be relied upon to work -// on all implementations. It should be considered an experimental extension -// to the standard GSL interface. -template -constexpr auto -as_multi_span(multi_span s) GSL_NOEXCEPT -> multi_span< - const U, static_cast( - multi_span::bounds_type::static_size != dynamic_range - ? (static_cast( - multi_span::bounds_type::static_size) / - sizeof(U)) - : dynamic_range)> -{ - using ConstByteSpan = multi_span; - static_assert( - std::is_trivial>::value && - (ConstByteSpan::bounds_type::static_size == dynamic_range || - ConstByteSpan::bounds_type::static_size % narrow_cast(sizeof(U)) == 0), - "Target type must be a trivial type and its size must match the byte array size"); - - Expects((s.size_bytes() % narrow_cast(sizeof(U))) == 0 && - (s.size_bytes() / narrow_cast(sizeof(U))) < PTRDIFF_MAX); - return {reinterpret_cast(s.data()), - s.size_bytes() / narrow_cast(sizeof(U))}; -} - -// convert a multi_span to a multi_span -// this is not currently a portable function that can be relied upon to work -// on all implementations. It should be considered an experimental extension -// to the standard GSL interface. -template -constexpr auto as_multi_span(multi_span s) GSL_NOEXCEPT - -> multi_span( - multi_span::bounds_type::static_size != dynamic_range - ? static_cast( - multi_span::bounds_type::static_size) / - sizeof(U) - : dynamic_range)> -{ - using ByteSpan = multi_span; - static_assert( - std::is_trivial>::value && - (ByteSpan::bounds_type::static_size == dynamic_range || - ByteSpan::bounds_type::static_size % sizeof(U) == 0), - "Target type must be a trivial type and its size must match the byte array size"); - - Expects((s.size_bytes() % sizeof(U)) == 0); - return {reinterpret_cast(s.data()), - s.size_bytes() / narrow_cast(sizeof(U))}; -} - -template -constexpr auto as_multi_span(T* const& ptr, dim_t... args) - -> multi_span, Dimensions...> -{ - return {reinterpret_cast*>(ptr), - details::static_as_multi_span_helper>(args..., - details::Sep{})}; -} - -template -constexpr auto as_multi_span(T* arr, std::ptrdiff_t len) -> - typename details::SpanArrayTraits::type -{ - return {reinterpret_cast*>(arr), len}; -} - -template -constexpr auto as_multi_span(T (&arr)[N]) -> typename details::SpanArrayTraits::type -{ - return {arr}; -} - -template -constexpr multi_span as_multi_span(const std::array& arr) -{ - return {arr}; -} - -template -constexpr multi_span as_multi_span(const std::array&&) = delete; - -template -constexpr multi_span as_multi_span(std::array& arr) -{ - return {arr}; -} - -template -constexpr multi_span as_multi_span(T* begin, T* end) -{ - return {begin, end}; -} - -template -constexpr auto as_multi_span(Cont& arr) -> std::enable_if_t< - !details::is_multi_span>::value, - multi_span, dynamic_range>> -{ - Expects(arr.size() < PTRDIFF_MAX); - return {arr.data(), narrow_cast(arr.size())}; -} - -template -constexpr auto as_multi_span(Cont&& arr) -> std::enable_if_t< - !details::is_multi_span>::value, - multi_span, dynamic_range>> = delete; - -// from basic_string which doesn't have nonconst .data() member like other contiguous containers -template -constexpr auto as_multi_span(std::basic_string& str) - -> multi_span -{ - Expects(str.size() < PTRDIFF_MAX); - return {&str[0], narrow_cast(str.size())}; -} - -// strided_span is an extension that is not strictly part of the GSL at this time. -// It is kept here while the multidimensional interface is still being defined. -template -class strided_span -{ -public: - using bounds_type = strided_bounds; - using size_type = typename bounds_type::size_type; - using index_type = typename bounds_type::index_type; - using value_type = ValueType; - using const_value_type = std::add_const_t; - using pointer = std::add_pointer_t; - using reference = std::add_lvalue_reference_t; - using iterator = general_span_iterator; - using const_strided_span = strided_span; - using const_iterator = general_span_iterator; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - using sliced_type = - std::conditional_t>; - -private: - pointer data_; - bounds_type bounds_; - - friend iterator; - friend const_iterator; - template - friend class strided_span; - -public: - // from raw data - constexpr strided_span(pointer ptr, size_type size, bounds_type bounds) - : data_(ptr), bounds_(std::move(bounds)) - { - Expects((bounds_.size() > 0 && ptr != nullptr) || bounds_.size() == 0); - // Bounds cross data boundaries - Expects(this->bounds().total_size() <= size); - (void) size; - } - - // from static array of size N - template - constexpr strided_span(value_type (&values)[N], bounds_type bounds) - : strided_span(values, N, std::move(bounds)) - { - } - - // from array view - template ::value, - typename = std::enable_if_t> - constexpr strided_span(multi_span av, bounds_type bounds) - : strided_span(av.data(), av.bounds().total_size(), std::move(bounds)) - { - } - - // convertible - template ::value>> - constexpr strided_span(const strided_span& other) - : data_(other.data_), bounds_(other.bounds_) - { - } - - // convert from bytes - template - constexpr strided_span< - typename std::enable_if::value, OtherValueType>::type, - Rank> - as_strided_span() const - { - static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && - (sizeof(OtherValueType) % sizeof(value_type) == 0), - "OtherValueType should have a size to contain a multiple of ValueTypes"); - auto d = narrow_cast(sizeof(OtherValueType) / sizeof(value_type)); - - size_type size = this->bounds().total_size() / d; - return {const_cast(reinterpret_cast(this->data())), - size, - bounds_type{resize_extent(this->bounds().index_bounds(), d), - resize_stride(this->bounds().strides(), d)}}; - } - - constexpr strided_span section(index_type origin, index_type extents) const - { - size_type size = this->bounds().total_size() - this->bounds().linearize(origin); - return {&this->operator[](origin), size, - bounds_type{extents, details::make_stride(bounds())}}; - } - - constexpr reference operator[](const index_type& idx) const - { - return data_[bounds_.linearize(idx)]; - } - - template 1), typename Ret = std::enable_if_t> - constexpr Ret operator[](size_type idx) const - { - Expects(idx < bounds_.size()); // index is out of bounds of the array - const size_type ridx = idx * bounds_.stride(); - - // index is out of bounds of the underlying data - Expects(ridx < bounds_.total_size()); - return {data_ + ridx, bounds_.slice().total_size(), bounds_.slice()}; - } - - constexpr bounds_type bounds() const GSL_NOEXCEPT { return bounds_; } - - template - constexpr size_type extent() const GSL_NOEXCEPT - { - static_assert(Dim < Rank, - "dimension should be less than Rank (dimension count starts from 0)"); - return bounds_.template extent(); - } - - constexpr size_type size() const GSL_NOEXCEPT { return bounds_.size(); } - - constexpr pointer data() const GSL_NOEXCEPT { return data_; } - - constexpr explicit operator bool() const GSL_NOEXCEPT { return data_ != nullptr; } - - constexpr iterator begin() const { return iterator{this, true}; } - - constexpr iterator end() const { return iterator{this, false}; } - - constexpr const_iterator cbegin() const - { - return const_iterator{reinterpret_cast(this), true}; - } - - constexpr const_iterator cend() const - { - return const_iterator{reinterpret_cast(this), false}; - } - - constexpr reverse_iterator rbegin() const { return reverse_iterator{end()}; } - - constexpr reverse_iterator rend() const { return reverse_iterator{begin()}; } - - constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator{cend()}; } - - constexpr const_reverse_iterator crend() const { return const_reverse_iterator{cbegin()}; } - - template , std::remove_cv_t>::value>> - constexpr bool - operator==(const strided_span& other) const GSL_NOEXCEPT - { - return bounds_.size() == other.bounds_.size() && - (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); - } - - template , std::remove_cv_t>::value>> - constexpr bool - operator!=(const strided_span& other) const GSL_NOEXCEPT - { - return !(*this == other); - } - - template , std::remove_cv_t>::value>> - constexpr bool - operator<(const strided_span& other) const GSL_NOEXCEPT - { - return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); - } - - template , std::remove_cv_t>::value>> - constexpr bool - operator<=(const strided_span& other) const GSL_NOEXCEPT - { - return !(other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool - operator>(const strided_span& other) const GSL_NOEXCEPT - { - return (other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool - operator>=(const strided_span& other) const GSL_NOEXCEPT - { - return !(*this < other); - } - -private: - static index_type resize_extent(const index_type& extent, std::ptrdiff_t d) - { - // The last dimension of the array needs to contain a multiple of new type elements - Expects(extent[Rank - 1] >= d && (extent[Rank - 1] % d == 0)); - - index_type ret = extent; - ret[Rank - 1] /= d; - - return ret; - } - - template > - static index_type resize_stride(const index_type& strides, std::ptrdiff_t, void* = nullptr) - { - // Only strided arrays with regular strides can be resized - Expects(strides[Rank - 1] == 1); - - return strides; - } - - template 1), typename = std::enable_if_t> - static index_type resize_stride(const index_type& strides, std::ptrdiff_t d) - { - // Only strided arrays with regular strides can be resized - Expects(strides[Rank - 1] == 1); - // The strides must have contiguous chunks of - // memory that can contain a multiple of new type elements - Expects(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0)); - - for (std::size_t i = Rank - 1; i > 0; --i) { - // Only strided arrays with regular strides can be resized - Expects((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0)); - } - - index_type ret = strides / d; - ret[Rank - 1] = 1; - - return ret; - } -}; - -template -class contiguous_span_iterator -{ -public: - using iterator_category = std::random_access_iterator_tag; - using value_type = typename Span::value_type; - using difference_type = std::ptrdiff_t; - using pointer = value_type*; - using reference = value_type&; - -private: - template - friend class multi_span; - - pointer data_; - const Span* m_validator; - void validateThis() const - { - // iterator is out of range of the array - Expects(data_ >= m_validator->data_ && data_ < m_validator->data_ + m_validator->size()); - } - contiguous_span_iterator(const Span* container, bool isbegin) - : data_(isbegin ? container->data_ : container->data_ + container->size()) - , m_validator(container) - { - } - -public: - reference operator*() const GSL_NOEXCEPT - { - validateThis(); - return *data_; - } - pointer operator->() const GSL_NOEXCEPT - { - validateThis(); - return data_; - } - contiguous_span_iterator& operator++() GSL_NOEXCEPT - { - ++data_; - return *this; - } - contiguous_span_iterator operator++(int) GSL_NOEXCEPT - { - auto ret = *this; - ++(*this); - return ret; - } - contiguous_span_iterator& operator--() GSL_NOEXCEPT - { - --data_; - return *this; - } - contiguous_span_iterator operator--(int) GSL_NOEXCEPT - { - auto ret = *this; - --(*this); - return ret; - } - contiguous_span_iterator operator+(difference_type n) const GSL_NOEXCEPT - { - contiguous_span_iterator ret{*this}; - return ret += n; - } - contiguous_span_iterator& operator+=(difference_type n) GSL_NOEXCEPT - { - data_ += n; - return *this; - } - contiguous_span_iterator operator-(difference_type n) const GSL_NOEXCEPT - { - contiguous_span_iterator ret{*this}; - return ret -= n; - } - contiguous_span_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; } - difference_type operator-(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT - { - Expects(m_validator == rhs.m_validator); - return data_ - rhs.data_; - } - reference operator[](difference_type n) const GSL_NOEXCEPT { return *(*this + n); } - bool operator==(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT - { - Expects(m_validator == rhs.m_validator); - return data_ == rhs.data_; - } - bool operator!=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT - { - return !(*this == rhs); - } - bool operator<(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT - { - Expects(m_validator == rhs.m_validator); - return data_ < rhs.data_; - } - bool operator<=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT - { - return !(rhs < *this); - } - bool operator>(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; } - bool operator>=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT - { - return !(rhs > *this); - } - void swap(contiguous_span_iterator& rhs) GSL_NOEXCEPT - { - std::swap(data_, rhs.data_); - std::swap(m_validator, rhs.m_validator); - } -}; - -template -contiguous_span_iterator operator+(typename contiguous_span_iterator::difference_type n, - const contiguous_span_iterator& rhs) GSL_NOEXCEPT -{ - return rhs + n; -} - -template -class general_span_iterator -{ -public: - using iterator_category = std::random_access_iterator_tag; - using value_type = typename Span::value_type; - using difference_type = std::ptrdiff_t; - using pointer = value_type*; - using reference = value_type&; - -private: - template - friend class strided_span; - - const Span* m_container; - typename Span::bounds_type::iterator m_itr; - general_span_iterator(const Span* container, bool isbegin) - : m_container(container) - , m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) - { - } - -public: - reference operator*() GSL_NOEXCEPT { return (*m_container)[*m_itr]; } - pointer operator->() GSL_NOEXCEPT { return &(*m_container)[*m_itr]; } - general_span_iterator& operator++() GSL_NOEXCEPT - { - ++m_itr; - return *this; - } - general_span_iterator operator++(int) GSL_NOEXCEPT - { - auto ret = *this; - ++(*this); - return ret; - } - general_span_iterator& operator--() GSL_NOEXCEPT - { - --m_itr; - return *this; - } - general_span_iterator operator--(int) GSL_NOEXCEPT - { - auto ret = *this; - --(*this); - return ret; - } - general_span_iterator operator+(difference_type n) const GSL_NOEXCEPT - { - general_span_iterator ret{*this}; - return ret += n; - } - general_span_iterator& operator+=(difference_type n) GSL_NOEXCEPT - { - m_itr += n; - return *this; - } - general_span_iterator operator-(difference_type n) const GSL_NOEXCEPT - { - general_span_iterator ret{*this}; - return ret -= n; - } - general_span_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; } - difference_type operator-(const general_span_iterator& rhs) const GSL_NOEXCEPT - { - Expects(m_container == rhs.m_container); - return m_itr - rhs.m_itr; - } - value_type operator[](difference_type n) const GSL_NOEXCEPT { return (*m_container)[m_itr[n]]; } - - bool operator==(const general_span_iterator& rhs) const GSL_NOEXCEPT - { - Expects(m_container == rhs.m_container); - return m_itr == rhs.m_itr; - } - bool operator!=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(*this == rhs); } - bool operator<(const general_span_iterator& rhs) const GSL_NOEXCEPT - { - Expects(m_container == rhs.m_container); - return m_itr < rhs.m_itr; - } - bool operator<=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(rhs < *this); } - bool operator>(const general_span_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; } - bool operator>=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(rhs > *this); } - void swap(general_span_iterator& rhs) GSL_NOEXCEPT - { - std::swap(m_itr, rhs.m_itr); - std::swap(m_container, rhs.m_container); - } -}; - -template -general_span_iterator operator+(typename general_span_iterator::difference_type n, - const general_span_iterator& rhs) GSL_NOEXCEPT -{ - return rhs + n; -} - -} // namespace gsl - -#undef GSL_NOEXCEPT - -#ifdef _MSC_VER -#if _MSC_VER < 1910 - -#undef constexpr -#pragma pop_macro("constexpr") -#endif // _MSC_VER < 1910 - -#pragma warning(pop) - -#endif // _MSC_VER - -#if __GNUC__ > 6 -#pragma GCC diagnostic pop -#endif // __GNUC__ > 6 - -#endif // GSL_MULTI_SPAN_H diff --git a/src/serlio/utils/stduuid/gsl/pointers b/src/serlio/utils/stduuid/gsl/pointers deleted file mode 100644 index 69499d6f..00000000 --- a/src/serlio/utils/stduuid/gsl/pointers +++ /dev/null @@ -1,193 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) 2015 Microsoft Corporation. All rights reserved. -// -// This code is licensed under the MIT License (MIT). -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef GSL_POINTERS_H -#define GSL_POINTERS_H - -#include // for Ensures, Expects - -#include // for forward -#include // for ptrdiff_t, nullptr_t, ostream, size_t -#include // for shared_ptr, unique_ptr -#include // for hash -#include // for enable_if_t, is_convertible, is_assignable - -#if defined(_MSC_VER) && _MSC_VER < 1910 -#pragma push_macro("constexpr") -#define constexpr /*constexpr*/ - -#endif // defined(_MSC_VER) && _MSC_VER < 1910 - -namespace gsl -{ - -// -// GSL.owner: ownership pointers -// -using std::unique_ptr; -using std::shared_ptr; - -// -// owner -// -// owner is designed as a bridge for code that must deal directly with owning pointers for some reason -// -// T must be a pointer type -// - disallow construction from any type other than pointer type -// -template ::value>> -using owner = T; - -// -// not_null -// -// Restricts a pointer or smart pointer to only hold non-null values. -// -// Has zero size overhead over T. -// -// If T is a pointer (i.e. T == U*) then -// - allow construction from U* -// - disallow construction from nullptr_t -// - disallow default construction -// - ensure construction from null U* fails -// - allow implicit conversion to U* -// -template -class not_null -{ -public: - static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); - - template ::value>> - constexpr explicit not_null(U&& u) : ptr_(std::forward(u)) - { - Expects(ptr_ != nullptr); - } - - template ::value>> - constexpr explicit not_null(T u) : ptr_(u) - { - Expects(ptr_ != nullptr); - } - - template ::value>> - constexpr not_null(const not_null& other) : not_null(other.get()) - { - } - - not_null(not_null&& other) = default; - not_null(const not_null& other) = default; - not_null& operator=(const not_null& other) = default; - - constexpr T get() const - { - Ensures(ptr_ != nullptr); - return ptr_; - } - - constexpr operator T() const { return get(); } - constexpr T operator->() const { return get(); } - constexpr decltype(auto) operator*() const { return *get(); } - - // prevents compilation when someone attempts to assign a null pointer constant - not_null(std::nullptr_t) = delete; - not_null& operator=(std::nullptr_t) = delete; - - // unwanted operators...pointers only point to single objects! - not_null& operator++() = delete; - not_null& operator--() = delete; - not_null operator++(int) = delete; - not_null operator--(int) = delete; - not_null& operator+=(std::ptrdiff_t) = delete; - not_null& operator-=(std::ptrdiff_t) = delete; - void operator[](std::ptrdiff_t) const = delete; - -private: - T ptr_; -}; - -template -std::ostream& operator<<(std::ostream& os, const not_null& val) -{ - os << val.get(); - return os; -} - -template -auto operator==(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() == rhs.get()) -{ - return lhs.get() == rhs.get(); -} - -template -auto operator!=(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() != rhs.get()) -{ - return lhs.get() != rhs.get(); -} - -template -auto operator<(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() < rhs.get()) -{ - return lhs.get() < rhs.get(); -} - -template -auto operator<=(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() <= rhs.get()) -{ - return lhs.get() <= rhs.get(); -} - -template -auto operator>(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() > rhs.get()) -{ - return lhs.get() > rhs.get(); -} - -template -auto operator>=(const not_null& lhs, const not_null& rhs) -> decltype(lhs.get() >= rhs.get()) -{ - return lhs.get() >= rhs.get(); -} - -// more unwanted operators -template -std::ptrdiff_t operator-(const not_null&, const not_null&) = delete; -template -not_null operator-(const not_null&, std::ptrdiff_t) = delete; -template -not_null operator+(const not_null&, std::ptrdiff_t) = delete; -template -not_null operator+(std::ptrdiff_t, const not_null&) = delete; - -} // namespace gsl - -namespace std -{ -template -struct hash> -{ - std::size_t operator()(const gsl::not_null& value) const { return hash{}(value); } -}; - -} // namespace std - -#if defined(_MSC_VER) && _MSC_VER < 1910 -#undef constexpr -#pragma pop_macro("constexpr") - -#endif // defined(_MSC_VER) && _MSC_VER < 1910 - -#endif // GSL_POINTERS_H diff --git a/src/serlio/utils/stduuid/gsl/span b/src/serlio/utils/stduuid/gsl/span deleted file mode 100644 index dcb78a61..00000000 --- a/src/serlio/utils/stduuid/gsl/span +++ /dev/null @@ -1,766 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) 2015 Microsoft Corporation. All rights reserved. -// -// This code is licensed under the MIT License (MIT). -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef GSL_SPAN_H -#define GSL_SPAN_H - -#include // for Expects -#include // for byte -#include // for narrow_cast, narrow - -#include // for lexicographical_compare -#include // for array -#include // for ptrdiff_t, size_t, nullptr_t -#include // for reverse_iterator, distance, random_access_... -#include -#include -#include // for enable_if_t, declval, is_convertible, inte... -#include - -#ifdef _MSC_VER -#pragma warning(push) - -// turn off some warnings that are noisy about our Expects statements -#pragma warning(disable : 4127) // conditional expression is constant -#pragma warning(disable : 4702) // unreachable code - -// blanket turn off warnings from CppCoreCheck for now -// so people aren't annoyed by them when running the tool. -// more targeted suppressions will be added in a future update to the GSL -#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) - -#if _MSC_VER < 1910 -#pragma push_macro("constexpr") -#define constexpr /*constexpr*/ -#define GSL_USE_STATIC_CONSTEXPR_WORKAROUND - -#endif // _MSC_VER < 1910 -#else // _MSC_VER - -// See if we have enough C++17 power to use a static constexpr data member -// without needing an out-of-line definition -#if !(defined(__cplusplus) && (__cplusplus >= 201703L)) -#define GSL_USE_STATIC_CONSTEXPR_WORKAROUND -#endif // !(defined(__cplusplus) && (__cplusplus >= 201703L)) - -#endif // _MSC_VER - -// GCC 7 does not like the signed unsigned missmatch (size_t ptrdiff_t) -// While there is a conversion from signed to unsigned, it happens at -// compiletime, so the compiler wouldn't have to warn indiscriminently, but -// could check if the source value actually doesn't fit into the target type -// and only warn in those cases. -#if __GNUC__ > 6 -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wsign-conversion" -#endif - -namespace gsl -{ - -// [views.constants], constants -constexpr const std::ptrdiff_t dynamic_extent = -1; - -template -class span; - -// implementation details -namespace details -{ - template - struct is_span_oracle : std::false_type - { - }; - - template - struct is_span_oracle> : std::true_type - { - }; - - template - struct is_span : public is_span_oracle> - { - }; - - template - struct is_std_array_oracle : std::false_type - { - }; - - template - struct is_std_array_oracle> : std::true_type - { - }; - - template - struct is_std_array : public is_std_array_oracle> - { - }; - - template - struct is_allowed_extent_conversion - : public std::integral_constant - { - }; - - template - struct is_allowed_element_type_conversion - : public std::integral_constant::value> - { - }; - - template - class span_iterator - { - using element_type_ = typename Span::element_type; - - public: - -#ifdef _MSC_VER - // Tell Microsoft standard library that span_iterators are checked. - using _Unchecked_type = typename Span::pointer; -#endif - - using iterator_category = std::random_access_iterator_tag; - using value_type = std::remove_cv_t; - using difference_type = typename Span::index_type; - - using reference = std::conditional_t&; - using pointer = std::add_pointer_t; - - span_iterator() = default; - - constexpr span_iterator(const Span* span, typename Span::index_type idx) noexcept - : span_(span), index_(idx) - {} - - friend span_iterator; - template* = nullptr> - constexpr span_iterator(const span_iterator& other) noexcept - : span_iterator(other.span_, other.index_) - { - } - - constexpr reference operator*() const - { - Expects(index_ != span_->size()); - return *(span_->data() + index_); - } - - constexpr pointer operator->() const - { - Expects(index_ != span_->size()); - return span_->data() + index_; - } - - constexpr span_iterator& operator++() - { - Expects(0 <= index_ && index_ != span_->size()); - ++index_; - return *this; - } - - constexpr span_iterator operator++(int) - { - auto ret = *this; - ++(*this); - return ret; - } - - constexpr span_iterator& operator--() - { - Expects(index_ != 0 && index_ <= span_->size()); - --index_; - return *this; - } - - constexpr span_iterator operator--(int) - { - auto ret = *this; - --(*this); - return ret; - } - - constexpr span_iterator operator+(difference_type n) const - { - auto ret = *this; - return ret += n; - } - - friend constexpr span_iterator operator+(difference_type n, span_iterator const& rhs) - { - return rhs + n; - } - - constexpr span_iterator& operator+=(difference_type n) - { - Expects((index_ + n) >= 0 && (index_ + n) <= span_->size()); - index_ += n; - return *this; - } - - constexpr span_iterator operator-(difference_type n) const - { - auto ret = *this; - return ret -= n; - } - - constexpr span_iterator& operator-=(difference_type n) { return *this += -n; } - - constexpr difference_type operator-(span_iterator rhs) const - { - Expects(span_ == rhs.span_); - return index_ - rhs.index_; - } - - constexpr reference operator[](difference_type n) const - { - return *(*this + n); - } - - constexpr friend bool operator==(span_iterator lhs, - span_iterator rhs) noexcept - { - return lhs.span_ == rhs.span_ && lhs.index_ == rhs.index_; - } - - constexpr friend bool operator!=(span_iterator lhs, - span_iterator rhs) noexcept - { - return !(lhs == rhs); - } - - constexpr friend bool operator<(span_iterator lhs, - span_iterator rhs) noexcept - { - return lhs.index_ < rhs.index_; - } - - constexpr friend bool operator<=(span_iterator lhs, - span_iterator rhs) noexcept - { - return !(rhs < lhs); - } - - constexpr friend bool operator>(span_iterator lhs, - span_iterator rhs) noexcept - { - return rhs < lhs; - } - - constexpr friend bool operator>=(span_iterator lhs, - span_iterator rhs) noexcept - { - return !(rhs > lhs); - } - -#ifdef _MSC_VER - // MSVC++ iterator debugging support; allows STL algorithms in 15.8+ - // to unwrap span_iterator to a pointer type after a range check in STL - // algorithm calls - friend constexpr void _Verify_range(span_iterator lhs, - span_iterator rhs) noexcept - { // test that [lhs, rhs) forms a valid range inside an STL algorithm - Expects(lhs.span_ == rhs.span_ // range spans have to match - && lhs.index_ <= rhs.index_); // range must not be transposed - } - - constexpr void _Verify_offset(const difference_type n) const noexcept - { // test that the iterator *this + n is a valid range in an STL - // algorithm call - Expects((index_ + n) >= 0 && (index_ + n) <= span_->size()); - } - - constexpr pointer _Unwrapped() const noexcept - { // after seeking *this to a high water mark, or using one of the - // _Verify_xxx functions above, unwrap this span_iterator to a raw - // pointer - return span_->data() + index_; - } - - // Tell the STL that span_iterator should not be unwrapped if it can't - // validate in advance, even in release / optimized builds: -#if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND) - static constexpr const bool _Unwrap_when_unverified = false; -#else - static constexpr bool _Unwrap_when_unverified = false; -#endif - constexpr void _Seek_to(const pointer p) noexcept - { // adjust the position of *this to previously verified location p - // after _Unwrapped - index_ = p - span_->data(); - } -#endif - - protected: - const Span* span_ = nullptr; - std::ptrdiff_t index_ = 0; - }; - - template - class extent_type - { - public: - using index_type = std::ptrdiff_t; - - static_assert(Ext >= 0, "A fixed-size span must be >= 0 in size."); - - constexpr extent_type() noexcept {} - - template - constexpr extent_type(extent_type ext) - { - static_assert(Other == Ext || Other == dynamic_extent, - "Mismatch between fixed-size extent and size of initializing data."); - Expects(ext.size() == Ext); - } - - constexpr extent_type(index_type size) { Expects(size == Ext); } - - constexpr index_type size() const noexcept { return Ext; } - }; - - template <> - class extent_type - { - public: - using index_type = std::ptrdiff_t; - - template - explicit constexpr extent_type(extent_type ext) : size_(ext.size()) - { - } - - explicit constexpr extent_type(index_type size) : size_(size) { Expects(size >= 0); } - - constexpr index_type size() const noexcept { return size_; } - - private: - index_type size_; - }; - - template - struct calculate_subspan_type - { - using type = span; - }; -} // namespace details - -// [span], class template span -template -class span -{ -public: - // constants and types - using element_type = ElementType; - using value_type = std::remove_cv_t; - using index_type = std::ptrdiff_t; - using pointer = element_type*; - using reference = element_type&; - - using iterator = details::span_iterator, false>; - using const_iterator = details::span_iterator, true>; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - - using size_type = index_type; - -#if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND) - static constexpr const index_type extent { Extent }; -#else - static constexpr index_type extent { Extent }; -#endif - - // [span.cons], span constructors, copy, assignment, and destructor - template " SFINAE, - // since "std::enable_if_t" is ill-formed when Extent is greater than 0. - class = std::enable_if_t<(Dependent || Extent <= 0)>> - constexpr span() noexcept : storage_(nullptr, details::extent_type<0>()) - { - } - - constexpr span(pointer ptr, index_type count) : storage_(ptr, count) {} - - constexpr span(pointer firstElem, pointer lastElem) - : storage_(firstElem, std::distance(firstElem, lastElem)) - { - } - - template - constexpr span(element_type (&arr)[N]) noexcept - : storage_(KnownNotNull{&arr[0]}, details::extent_type()) - { - } - - template > - constexpr span(std::array& arr) noexcept - : storage_(&arr[0], details::extent_type()) - { - } - - template - constexpr span(const std::array, N>& arr) noexcept - : storage_(&arr[0], details::extent_type()) - { - } - - // NB: the SFINAE here uses .data() as a incomplete/imperfect proxy for the requirement - // on Container to be a contiguous sequence container. - template ::value && !details::is_std_array::value && - std::is_convertible::value && - std::is_convertible().data())>::value>> - constexpr span(Container& cont) : span(cont.data(), narrow(cont.size())) - { - } - - template ::value && !details::is_span::value && - std::is_convertible::value && - std::is_convertible().data())>::value>> - constexpr span(const Container& cont) : span(cont.data(), narrow(cont.size())) - { - } - - constexpr span(const span& other) noexcept = default; - - template < - class OtherElementType, std::ptrdiff_t OtherExtent, - class = std::enable_if_t< - details::is_allowed_extent_conversion::value && - details::is_allowed_element_type_conversion::value>> - constexpr span(const span& other) - : storage_(other.data(), details::extent_type(other.size())) - { - } - - ~span() noexcept = default; - constexpr span& operator=(const span& other) noexcept = default; - - // [span.sub], span subviews - template - constexpr span first() const - { - Expects(Count >= 0 && Count <= size()); - return {data(), Count}; - } - - template - constexpr span last() const - { - Expects(Count >= 0 && size() - Count >= 0); - return {data() + (size() - Count), Count}; - } - - template - constexpr auto subspan() const -> typename details::calculate_subspan_type::type - { - Expects((Offset >= 0 && size() - Offset >= 0) && - (Count == dynamic_extent || (Count >= 0 && Offset + Count <= size()))); - - return {data() + Offset, Count == dynamic_extent ? size() - Offset : Count}; - } - - constexpr span first(index_type count) const - { - Expects(count >= 0 && count <= size()); - return {data(), count}; - } - - constexpr span last(index_type count) const - { - return make_subspan(size() - count, dynamic_extent, subspan_selector{}); - } - - constexpr span subspan(index_type offset, - index_type count = dynamic_extent) const - { - return make_subspan(offset, count, subspan_selector{}); - } - - - // [span.obs], span observers - constexpr index_type size() const noexcept { return storage_.size(); } - constexpr index_type size_bytes() const noexcept - { - return size() * narrow_cast(sizeof(element_type)); - } - constexpr bool empty() const noexcept { return size() == 0; } - - // [span.elem], span element access - constexpr reference operator[](index_type idx) const - { - Expects(idx >= 0 && idx < storage_.size()); - return data()[idx]; - } - - constexpr reference at(index_type idx) const { return this->operator[](idx); } - constexpr reference operator()(index_type idx) const { return this->operator[](idx); } - constexpr pointer data() const noexcept { return storage_.data(); } - - // [span.iter], span iterator support - constexpr iterator begin() const noexcept { return {this, 0}; } - constexpr iterator end() const noexcept { return {this, size()}; } - - constexpr const_iterator cbegin() const noexcept { return {this, 0}; } - constexpr const_iterator cend() const noexcept { return {this, size()}; } - - constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } - constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } - - constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{cend()}; } - constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator{cbegin()}; } - -#ifdef _MSC_VER - // Tell MSVC how to unwrap spans in range-based-for - constexpr pointer _Unchecked_begin() const noexcept { return data(); } - constexpr pointer _Unchecked_end() const noexcept { return data() + size(); } -#endif // _MSC_VER - -private: - - // Needed to remove unnecessary null check in subspans - struct KnownNotNull - { - pointer p; - }; - - // this implementation detail class lets us take advantage of the - // empty base class optimization to pay for only storage of a single - // pointer in the case of fixed-size spans - template - class storage_type : public ExtentType - { - public: - // KnownNotNull parameter is needed to remove unnecessary null check - // in subspans and constructors from arrays - template - constexpr storage_type(KnownNotNull data, OtherExtentType ext) : ExtentType(ext), data_(data.p) - { - Expects(ExtentType::size() >= 0); - } - - - template - constexpr storage_type(pointer data, OtherExtentType ext) : ExtentType(ext), data_(data) - { - Expects(ExtentType::size() >= 0); - Expects(data || ExtentType::size() == 0); - } - - constexpr pointer data() const noexcept { return data_; } - - private: - pointer data_; - }; - - storage_type> storage_; - - // The rest is needed to remove unnecessary null check - // in subspans and constructors from arrays - constexpr span(KnownNotNull ptr, index_type count) : storage_(ptr, count) {} - - template - class subspan_selector {}; - - template - span make_subspan(index_type offset, - index_type count, - subspan_selector) const - { - span tmp(*this); - return tmp.subspan(offset, count); - } - - span make_subspan(index_type offset, - index_type count, - subspan_selector) const - { - Expects(offset >= 0 && size() - offset >= 0); - if (count == dynamic_extent) - { - return { KnownNotNull{ data() + offset }, size() - offset }; - } - - Expects(count >= 0 && size() - offset >= count); - return { KnownNotNull{ data() + offset }, count }; - } -}; - -#if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND) -template -constexpr const typename span::index_type span::extent; -#endif - - -// [span.comparison], span comparison operators -template -constexpr bool operator==(span l, - span r) -{ - return std::equal(l.begin(), l.end(), r.begin(), r.end()); -} - -template -constexpr bool operator!=(span l, - span r) -{ - return !(l == r); -} - -template -constexpr bool operator<(span l, - span r) -{ - return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); -} - -template -constexpr bool operator<=(span l, - span r) -{ - return !(l > r); -} - -template -constexpr bool operator>(span l, - span r) -{ - return r < l; -} - -template -constexpr bool operator>=(span l, - span r) -{ - return !(l < r); -} - -namespace details -{ - // if we only supported compilers with good constexpr support then - // this pair of classes could collapse down to a constexpr function - - // we should use a narrow_cast<> to go to std::size_t, but older compilers may not see it as - // constexpr - // and so will fail compilation of the template - template - struct calculate_byte_size - : std::integral_constant(sizeof(ElementType) * - static_cast(Extent))> - { - }; - - template - struct calculate_byte_size - : std::integral_constant - { - }; -} - -// [span.objectrep], views of object representation -template -span::value> -as_bytes(span s) noexcept -{ - return {reinterpret_cast(s.data()), s.size_bytes()}; -} - -template ::value>> -span::value> -as_writeable_bytes(span s) noexcept -{ - return {reinterpret_cast(s.data()), s.size_bytes()}; -} - -// -// make_span() - Utility functions for creating spans -// -template -constexpr span make_span(ElementType* ptr, typename span::index_type count) -{ - return span(ptr, count); -} - -template -constexpr span make_span(ElementType* firstElem, ElementType* lastElem) -{ - return span(firstElem, lastElem); -} - -template -constexpr span make_span(ElementType (&arr)[N]) noexcept -{ - return span(arr); -} - -template -constexpr span make_span(Container& cont) -{ - return span(cont); -} - -template -constexpr span make_span(const Container& cont) -{ - return span(cont); -} - -template -constexpr span make_span(Ptr& cont, std::ptrdiff_t count) -{ - return span(cont, count); -} - -template -constexpr span make_span(Ptr& cont) -{ - return span(cont); -} - -// Specialization of gsl::at for span -template -constexpr ElementType& at(span s, index i) -{ - // No bounds checking here because it is done in span::operator[] called below - return s[i]; -} - -} // namespace gsl - -#ifdef _MSC_VER -#if _MSC_VER < 1910 -#undef constexpr -#pragma pop_macro("constexpr") - -#endif // _MSC_VER < 1910 - -#pragma warning(pop) -#endif // _MSC_VER - -#if __GNUC__ > 6 -#pragma GCC diagnostic pop -#endif // __GNUC__ > 6 - -#endif // GSL_SPAN_H diff --git a/src/serlio/utils/stduuid/gsl/string_span b/src/serlio/utils/stduuid/gsl/string_span deleted file mode 100644 index c08f2467..00000000 --- a/src/serlio/utils/stduuid/gsl/string_span +++ /dev/null @@ -1,730 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) 2015 Microsoft Corporation. All rights reserved. -// -// This code is licensed under the MIT License (MIT). -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef GSL_STRING_SPAN_H -#define GSL_STRING_SPAN_H - -#include // for Ensures, Expects -#include // for narrow_cast -#include // for operator!=, operator==, dynamic_extent - -#include // for equal, lexicographical_compare -#include // for array -#include // for ptrdiff_t, size_t, nullptr_t -#include // for PTRDIFF_MAX -#include -#include // for basic_string, allocator, char_traits -#include // for declval, is_convertible, enable_if_t, add_... - -#ifdef _MSC_VER -#pragma warning(push) - -// blanket turn off warnings from CppCoreCheck for now -// so people aren't annoyed by them when running the tool. -// more targeted suppressions will be added in a future update to the GSL -#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) - -#if _MSC_VER < 1910 -#pragma push_macro("constexpr") -#define constexpr /*constexpr*/ - -#endif // _MSC_VER < 1910 -#endif // _MSC_VER - -// In order to test the library, we need it to throw exceptions that we can catch -#ifdef GSL_THROW_ON_CONTRACT_VIOLATION -#define GSL_NOEXCEPT /*noexcept*/ -#else -#define GSL_NOEXCEPT noexcept -#endif // GSL_THROW_ON_CONTRACT_VIOLATION - -namespace gsl -{ -// -// czstring and wzstring -// -// These are "tag" typedefs for C-style strings (i.e. null-terminated character arrays) -// that allow static analysis to help find bugs. -// -// There are no additional features/semantics that we can find a way to add inside the -// type system for these types that will not either incur significant runtime costs or -// (sometimes needlessly) break existing programs when introduced. -// - -template -using basic_zstring = CharT*; - -template -using czstring = basic_zstring; - -template -using cwzstring = basic_zstring; - -template -using cu16zstring = basic_zstring; - -template -using cu32zstring = basic_zstring; - -template -using zstring = basic_zstring; - -template -using wzstring = basic_zstring; - -template -using u16zstring = basic_zstring; - -template -using u32zstring = basic_zstring; - -namespace details -{ - template - std::ptrdiff_t string_length(const CharT* str, std::ptrdiff_t n) - { - if (str == nullptr || n <= 0) return 0; - - const span str_span{str, n}; - - std::ptrdiff_t len = 0; - while (len < n && str_span[len]) len++; - - return len; - } -} - -// -// ensure_sentinel() -// -// Provides a way to obtain an span from a contiguous sequence -// that ends with a (non-inclusive) sentinel value. -// -// Will fail-fast if sentinel cannot be found before max elements are examined. -// -template -span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX) -{ - auto cur = seq; - while ((cur - seq) < max && *cur != Sentinel) ++cur; - Ensures(*cur == Sentinel); - return {seq, cur - seq}; -} - -// -// ensure_z - creates a span for a zero terminated strings. -// Will fail fast if a null-terminator cannot be found before -// the limit of size_type. -// -template -span ensure_z(CharT* const& sz, std::ptrdiff_t max = PTRDIFF_MAX) -{ - return ensure_sentinel(sz, max); -} - -template -span ensure_z(CharT (&sz)[N]) -{ - return ensure_z(&sz[0], static_cast(N)); -} - -template -span::type, dynamic_extent> -ensure_z(Cont& cont) -{ - return ensure_z(cont.data(), static_cast(cont.size())); -} - -template -class basic_string_span; - -namespace details -{ - template - struct is_basic_string_span_oracle : std::false_type - { - }; - - template - struct is_basic_string_span_oracle> : std::true_type - { - }; - - template - struct is_basic_string_span : is_basic_string_span_oracle> - { - }; -} - -// -// string_span and relatives -// -template -class basic_string_span -{ -public: - using element_type = CharT; - using pointer = std::add_pointer_t; - using reference = std::add_lvalue_reference_t; - using const_reference = std::add_lvalue_reference_t>; - using impl_type = span; - - using index_type = typename impl_type::index_type; - using iterator = typename impl_type::iterator; - using const_iterator = typename impl_type::const_iterator; - using reverse_iterator = typename impl_type::reverse_iterator; - using const_reverse_iterator = typename impl_type::const_reverse_iterator; - - // default (empty) - constexpr basic_string_span() GSL_NOEXCEPT = default; - - // copy - constexpr basic_string_span(const basic_string_span& other) GSL_NOEXCEPT = default; - - // assign - constexpr basic_string_span& operator=(const basic_string_span& other) GSL_NOEXCEPT = default; - - constexpr basic_string_span(pointer ptr, index_type length) : span_(ptr, length) {} - constexpr basic_string_span(pointer firstElem, pointer lastElem) : span_(firstElem, lastElem) {} - - // From static arrays - if 0-terminated, remove 0 from the view - // All other containers allow 0s within the length, so we do not remove them - template - constexpr basic_string_span(element_type (&arr)[N]) : span_(remove_z(arr)) - { - } - - template > - constexpr basic_string_span(std::array& arr) GSL_NOEXCEPT : span_(arr) - { - } - - template > - constexpr basic_string_span(const std::array& arr) GSL_NOEXCEPT - : span_(arr) - { - } - - // Container signature should work for basic_string after C++17 version exists - template - constexpr basic_string_span(std::basic_string& str) - : span_(&str[0], narrow_cast(str.length())) - { - } - - template - constexpr basic_string_span(const std::basic_string& str) - : span_(&str[0], str.length()) - { - } - - // from containers. Containers must have a pointer type and data() function signatures - template ::value && - std::is_convertible::value && - std::is_convertible().data())>::value>> - constexpr basic_string_span(Container& cont) : span_(cont) - { - } - - template ::value && - std::is_convertible::value && - std::is_convertible().data())>::value>> - constexpr basic_string_span(const Container& cont) : span_(cont) - { - } - - // from string_span - template < - class OtherValueType, std::ptrdiff_t OtherExtent, - class = std::enable_if_t::impl_type, impl_type>::value>> - constexpr basic_string_span(basic_string_span other) - : span_(other.data(), other.length()) - { - } - - template - constexpr basic_string_span first() const - { - return {span_.template first()}; - } - - constexpr basic_string_span first(index_type count) const - { - return {span_.first(count)}; - } - - template - constexpr basic_string_span last() const - { - return {span_.template last()}; - } - - constexpr basic_string_span last(index_type count) const - { - return {span_.last(count)}; - } - - template - constexpr basic_string_span subspan() const - { - return {span_.template subspan()}; - } - - constexpr basic_string_span - subspan(index_type offset, index_type count = dynamic_extent) const - { - return {span_.subspan(offset, count)}; - } - - constexpr reference operator[](index_type idx) const { return span_[idx]; } - constexpr reference operator()(index_type idx) const { return span_[idx]; } - - constexpr pointer data() const { return span_.data(); } - - constexpr index_type length() const GSL_NOEXCEPT { return span_.size(); } - constexpr index_type size() const GSL_NOEXCEPT { return span_.size(); } - constexpr index_type size_bytes() const GSL_NOEXCEPT { return span_.size_bytes(); } - constexpr index_type length_bytes() const GSL_NOEXCEPT { return span_.length_bytes(); } - constexpr bool empty() const GSL_NOEXCEPT { return size() == 0; } - - constexpr iterator begin() const GSL_NOEXCEPT { return span_.begin(); } - constexpr iterator end() const GSL_NOEXCEPT { return span_.end(); } - - constexpr const_iterator cbegin() const GSL_NOEXCEPT { return span_.cbegin(); } - constexpr const_iterator cend() const GSL_NOEXCEPT { return span_.cend(); } - - constexpr reverse_iterator rbegin() const GSL_NOEXCEPT { return span_.rbegin(); } - constexpr reverse_iterator rend() const GSL_NOEXCEPT { return span_.rend(); } - - constexpr const_reverse_iterator crbegin() const GSL_NOEXCEPT { return span_.crbegin(); } - constexpr const_reverse_iterator crend() const GSL_NOEXCEPT { return span_.crend(); } - -private: - static impl_type remove_z(pointer const& sz, std::ptrdiff_t max) - { - return {sz, details::string_length(sz, max)}; - } - - template - static impl_type remove_z(element_type (&sz)[N]) - { - return remove_z(&sz[0], narrow_cast(N)); - } - - impl_type span_; -}; - -template -using string_span = basic_string_span; - -template -using cstring_span = basic_string_span; - -template -using wstring_span = basic_string_span; - -template -using cwstring_span = basic_string_span; - -template -using u16string_span = basic_string_span; - -template -using cu16string_span = basic_string_span; - -template -using u32string_span = basic_string_span; - -template -using cu32string_span = basic_string_span; - -// -// to_string() allow (explicit) conversions from string_span to string -// - -template -std::basic_string::type> -to_string(basic_string_span view) -{ - return {view.data(), static_cast(view.length())}; -} - -template , - typename Allocator = std::allocator, typename gCharT, std::ptrdiff_t Extent> -std::basic_string to_basic_string(basic_string_span view) -{ - return {view.data(), static_cast(view.length())}; -} - -template -basic_string_span::value> -as_bytes(basic_string_span s) noexcept -{ - return { reinterpret_cast(s.data()), s.size_bytes() }; -} - -template ::value>> -basic_string_span::value> -as_writeable_bytes(basic_string_span s) noexcept -{ - return {reinterpret_cast(s.data()), s.size_bytes()}; -} - -// zero-terminated string span, used to convert -// zero-terminated spans to legacy strings -template -class basic_zstring_span -{ -public: - using value_type = CharT; - using const_value_type = std::add_const_t; - - using pointer = std::add_pointer_t; - using const_pointer = std::add_pointer_t; - - using zstring_type = basic_zstring; - using const_zstring_type = basic_zstring; - - using impl_type = span; - using string_span_type = basic_string_span; - - constexpr basic_zstring_span(impl_type s) GSL_NOEXCEPT : span_(s) - { - // expects a zero-terminated span - Expects(s[s.size() - 1] == '\0'); - } - - // copy - constexpr basic_zstring_span(const basic_zstring_span& other) = default; - - // move - constexpr basic_zstring_span(basic_zstring_span&& other) = default; - - // assign - constexpr basic_zstring_span& operator=(const basic_zstring_span& other) = default; - - // move assign - constexpr basic_zstring_span& operator=(basic_zstring_span&& other) = default; - - constexpr bool empty() const GSL_NOEXCEPT { return span_.size() == 0; } - - constexpr string_span_type as_string_span() const GSL_NOEXCEPT - { - auto sz = span_.size(); - return { span_.data(), sz > 1 ? sz - 1 : 0 }; - } - constexpr string_span_type ensure_z() const GSL_NOEXCEPT { return gsl::ensure_z(span_); } - - constexpr const_zstring_type assume_z() const GSL_NOEXCEPT { return span_.data(); } - -private: - impl_type span_; -}; - -template -using zstring_span = basic_zstring_span; - -template -using wzstring_span = basic_zstring_span; - -template -using u16zstring_span = basic_zstring_span; - -template -using u32zstring_span = basic_zstring_span; - -template -using czstring_span = basic_zstring_span; - -template -using cwzstring_span = basic_zstring_span; - -template -using cu16zstring_span = basic_zstring_span; - -template -using cu32zstring_span = basic_zstring_span; - -// operator == -template ::value || - std::is_convertible>>::value>> -bool operator==(const gsl::basic_string_span& one, const T& other) GSL_NOEXCEPT -{ - const gsl::basic_string_span> tmp(other); - return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end()); -} - -template ::value && - std::is_convertible>>::value>> -bool operator==(const T& one, const gsl::basic_string_span& other) GSL_NOEXCEPT -{ - gsl::basic_string_span> tmp(one); - return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end()); -} - -// operator != -template , Extent>>::value>> -bool operator!=(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT -{ - return !(one == other); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename = std::enable_if_t< - std::is_convertible, Extent>>::value && - !gsl::details::is_basic_string_span::value>> -bool operator!=(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT -{ - return !(one == other); -} - -// operator< -template , Extent>>::value>> -bool operator<(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT -{ - const gsl::basic_string_span, Extent> tmp(other); - return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename = std::enable_if_t< - std::is_convertible, Extent>>::value && - !gsl::details::is_basic_string_span::value>> -bool operator<(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT -{ - gsl::basic_string_span, Extent> tmp(one); - return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); -} - -#ifndef _MSC_VER - -// VS treats temp and const containers as convertible to basic_string_span, -// so the cases below are already covered by the previous operators - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator<(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT -{ - gsl::basic_string_span, Extent> tmp(other); - return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator<(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT -{ - gsl::basic_string_span, Extent> tmp(one); - return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); -} -#endif - -// operator <= -template , Extent>>::value>> -bool operator<=(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT -{ - return !(other < one); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename = std::enable_if_t< - std::is_convertible, Extent>>::value && - !gsl::details::is_basic_string_span::value>> -bool operator<=(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT -{ - return !(other < one); -} - -#ifndef _MSC_VER - -// VS treats temp and const containers as convertible to basic_string_span, -// so the cases below are already covered by the previous operators - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator<=(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT -{ - return !(other < one); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator<=(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT -{ - return !(other < one); -} -#endif - -// operator> -template , Extent>>::value>> -bool operator>(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT -{ - return other < one; -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename = std::enable_if_t< - std::is_convertible, Extent>>::value && - !gsl::details::is_basic_string_span::value>> -bool operator>(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT -{ - return other < one; -} - -#ifndef _MSC_VER - -// VS treats temp and const containers as convertible to basic_string_span, -// so the cases below are already covered by the previous operators - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator>(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT -{ - return other < one; -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator>(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT -{ - return other < one; -} -#endif - -// operator >= -template , Extent>>::value>> -bool operator>=(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT -{ - return !(one < other); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename = std::enable_if_t< - std::is_convertible, Extent>>::value && - !gsl::details::is_basic_string_span::value>> -bool operator>=(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT -{ - return !(one < other); -} - -#ifndef _MSC_VER - -// VS treats temp and const containers as convertible to basic_string_span, -// so the cases below are already covered by the previous operators - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator>=(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT -{ - return !(one < other); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator>=(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT -{ - return !(one < other); -} -#endif -} // namespace gsl - -#undef GSL_NOEXCEPT - -#ifdef _MSC_VER -#pragma warning(pop) - -#if _MSC_VER < 1910 -#undef constexpr -#pragma pop_macro("constexpr") - -#endif // _MSC_VER < 1910 -#endif // _MSC_VER - -#endif // GSL_STRING_SPAN_H diff --git a/src/serlio/utils/stduuid/uuid.h b/src/serlio/utils/stduuid/uuid.h deleted file mode 100644 index 89565c81..00000000 --- a/src/serlio/utils/stduuid/uuid.h +++ /dev/null @@ -1,873 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#elif defined(__linux__) || defined(__unix__) -#include -#elif defined(__APPLE__) -#include -#endif - -namespace uuids -{ - namespace detail - { - template - constexpr inline unsigned char hex2char(TChar const ch) - { - if (ch >= static_cast('0') && ch <= static_cast('9')) - return ch - static_cast('0'); - if (ch >= static_cast('a') && ch <= static_cast('f')) - return 10 + ch - static_cast('a'); - if (ch >= static_cast('A') && ch <= static_cast('F')) - return 10 + ch - static_cast('A'); - return 0; - } - - template - constexpr inline bool is_hex(TChar const ch) - { - return - (ch >= static_cast('0') && ch <= static_cast('9')) || - (ch >= static_cast('a') && ch <= static_cast('f')) || - (ch >= static_cast('A') && ch <= static_cast('F')); - } - - template - constexpr inline unsigned char hexpair2char(TChar const a, TChar const b) - { - return (hex2char(a) << 4) | hex2char(b); - } - - class sha1 - { - public: - using digest32_t = uint32_t[5]; - using digest8_t = uint8_t[20]; - - static constexpr unsigned int block_bytes = 64; - - inline static uint32_t left_rotate(uint32_t value, size_t const count) - { - return (value << count) ^ (value >> (32 - count)); - } - - sha1() { reset(); } - - void reset() - { - m_digest[0] = 0x67452301; - m_digest[1] = 0xEFCDAB89; - m_digest[2] = 0x98BADCFE; - m_digest[3] = 0x10325476; - m_digest[4] = 0xC3D2E1F0; - m_blockByteIndex = 0; - m_byteCount = 0; - } - - void process_byte(uint8_t octet) - { - this->m_block[this->m_blockByteIndex++] = octet; - ++this->m_byteCount; - if (m_blockByteIndex == block_bytes) - { - this->m_blockByteIndex = 0; - process_block(); - } - } - - void process_block(void const * const start, void const * const end) - { - const uint8_t* begin = static_cast(start); - const uint8_t* finish = static_cast(end); - while (begin != finish) - { - process_byte(*begin); - begin++; - } - } - - void process_bytes(void const * const data, size_t const len) - { - const uint8_t* block = static_cast(data); - process_block(block, block + len); - } - - uint32_t const * get_digest(digest32_t digest) - { - size_t const bitCount = this->m_byteCount * 8; - process_byte(0x80); - if (this->m_blockByteIndex > 56) { - while (m_blockByteIndex != 0) { - process_byte(0); - } - while (m_blockByteIndex < 56) { - process_byte(0); - } - } - else { - while (m_blockByteIndex < 56) { - process_byte(0); - } - } - process_byte(0); - process_byte(0); - process_byte(0); - process_byte(0); - process_byte(static_cast((bitCount >> 24) & 0xFF)); - process_byte(static_cast((bitCount >> 16) & 0xFF)); - process_byte(static_cast((bitCount >> 8) & 0xFF)); - process_byte(static_cast((bitCount) & 0xFF)); - - memcpy(digest, m_digest, 5 * sizeof(uint32_t)); - return digest; - } - - uint8_t const * get_digest_bytes(digest8_t digest) - { - digest32_t d32; - get_digest(d32); - size_t di = 0; - digest[di++] = ((d32[0] >> 24) & 0xFF); - digest[di++] = ((d32[0] >> 16) & 0xFF); - digest[di++] = ((d32[0] >> 8) & 0xFF); - digest[di++] = ((d32[0]) & 0xFF); - - digest[di++] = ((d32[1] >> 24) & 0xFF); - digest[di++] = ((d32[1] >> 16) & 0xFF); - digest[di++] = ((d32[1] >> 8) & 0xFF); - digest[di++] = ((d32[1]) & 0xFF); - - digest[di++] = ((d32[2] >> 24) & 0xFF); - digest[di++] = ((d32[2] >> 16) & 0xFF); - digest[di++] = ((d32[2] >> 8) & 0xFF); - digest[di++] = ((d32[2]) & 0xFF); - - digest[di++] = ((d32[3] >> 24) & 0xFF); - digest[di++] = ((d32[3] >> 16) & 0xFF); - digest[di++] = ((d32[3] >> 8) & 0xFF); - digest[di++] = ((d32[3]) & 0xFF); - - digest[di++] = ((d32[4] >> 24) & 0xFF); - digest[di++] = ((d32[4] >> 16) & 0xFF); - digest[di++] = ((d32[4] >> 8) & 0xFF); - digest[di++] = ((d32[4]) & 0xFF); - - return digest; - } - - private: - void process_block() - { - uint32_t w[80]; - for (size_t i = 0; i < 16; i++) { - w[i] = (m_block[i * 4 + 0] << 24); - w[i] |= (m_block[i * 4 + 1] << 16); - w[i] |= (m_block[i * 4 + 2] << 8); - w[i] |= (m_block[i * 4 + 3]); - } - for (size_t i = 16; i < 80; i++) { - w[i] = left_rotate((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1); - } - - uint32_t a = m_digest[0]; - uint32_t b = m_digest[1]; - uint32_t c = m_digest[2]; - uint32_t d = m_digest[3]; - uint32_t e = m_digest[4]; - - for (std::size_t i = 0; i < 80; ++i) - { - uint32_t f = 0; - uint32_t k = 0; - - if (i < 20) { - f = (b & c) | (~b & d); - k = 0x5A827999; - } - else if (i < 40) { - f = b ^ c ^ d; - k = 0x6ED9EBA1; - } - else if (i < 60) { - f = (b & c) | (b & d) | (c & d); - k = 0x8F1BBCDC; - } - else { - f = b ^ c ^ d; - k = 0xCA62C1D6; - } - uint32_t temp = left_rotate(a, 5) + f + e + k + w[i]; - e = d; - d = c; - c = left_rotate(b, 30); - b = a; - a = temp; - } - - m_digest[0] += a; - m_digest[1] += b; - m_digest[2] += c; - m_digest[3] += d; - m_digest[4] += e; - } - - private: - digest32_t m_digest; - uint8_t m_block[64]; - size_t m_blockByteIndex; - size_t m_byteCount; - }; - } - - // UUID format https://tools.ietf.org/html/rfc4122 - // Field NDR Data Type Octet # Note - // -------------------------------------------------------------------------------------------------------------------------- - // time_low unsigned long 0 - 3 The low field of the timestamp. - // time_mid unsigned short 4 - 5 The middle field of the timestamp. - // time_hi_and_version unsigned short 6 - 7 The high field of the timestamp multiplexed with the version number. - // clock_seq_hi_and_reserved unsigned small 8 The high field of the clock sequence multiplexed with the variant. - // clock_seq_low unsigned small 9 The low field of the clock sequence. - // node character 10 - 15 The spatially unique node identifier. - // -------------------------------------------------------------------------------------------------------------------------- - // 0 1 2 3 - // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | time_low | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | time_mid | time_hi_and_version | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // |clk_seq_hi_res | clk_seq_low | node (0-1) | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - // | node (2-5) | - // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - // indicated by a bit pattern in octet 8, marked with N in xxxxxxxx-xxxx-xxxx-Nxxx-xxxxxxxxxxxx - enum class uuid_variant - { - // NCS backward compatibility (with the obsolete Apollo Network Computing System 1.5 UUID format) - // N bit pattern: 0xxx - // > the first 6 octets of the UUID are a 48-bit timestamp (the number of 4 microsecond units of time since 1 Jan 1980 UTC); - // > the next 2 octets are reserved; - // > the next octet is the "address family"; - // > the final 7 octets are a 56-bit host ID in the form specified by the address family - ncs, - - // RFC 4122/DCE 1.1 - // N bit pattern: 10xx - // > big-endian byte order - rfc, - - // Microsoft Corporation backward compatibility - // N bit pattern: 110x - // > little endian byte order - // > formely used in the Component Object Model (COM) library - microsoft, - - // reserved for possible future definition - // N bit pattern: 111x - reserved - }; - - struct uuid_error : public std::runtime_error - { - explicit uuid_error(std::string_view message) - : std::runtime_error(message.data()) - { - } - - explicit uuid_error(char const * message) - : std::runtime_error(message) - { - } - }; - - // indicated by a bit pattern in octet 6, marked with M in xxxxxxxx-xxxx-Mxxx-xxxx-xxxxxxxxxxxx - enum class uuid_version - { - none = 0, // only possible for nil or invalid uuids - time_based = 1, // The time-based version specified in RFC 4122 - dce_security = 2, // DCE Security version, with embedded POSIX UIDs. - name_based_md5 = 3, // The name-based version specified in RFS 4122 with MD5 hashing - random_number_based = 4, // The randomly or pseudo-randomly generated version specified in RFS 4122 - name_based_sha1 = 5 // The name-based version specified in RFS 4122 with SHA1 hashing - }; - - struct uuid - { - struct uuid_const_iterator - { - using self_type = uuid_const_iterator; - using value_type = uint8_t; - using reference = uint8_t const &; - using pointer = uint8_t const *; - using iterator_category = std::random_access_iterator_tag; - using difference_type = ptrdiff_t; - - protected: - pointer ptr = nullptr; - size_t index = 0; - - bool compatible(self_type const & other) const noexcept - { - return ptr == other.ptr; - } - - public: - constexpr explicit uuid_const_iterator(pointer ptr, size_t const index) : - ptr(ptr), index(index) - { - } - - uuid_const_iterator(uuid_const_iterator const & o) = default; - uuid_const_iterator& operator=(uuid_const_iterator const & o) = default; - ~uuid_const_iterator() = default; - - self_type & operator++ () - { - if (index >= 16) - throw std::out_of_range("Iterator cannot be incremented past the end of the data."); - ++index; - return *this; - } - - self_type operator++ (int) - { - self_type tmp = *this; - ++*this; - return tmp; - } - - bool operator== (self_type const & other) const - { - assert(compatible(other)); - return index == other.index; - } - - bool operator!= (self_type const & other) const - { - return !(*this == other); - } - - reference operator* () const - { - if (ptr == nullptr) - throw std::bad_function_call(); - return *(ptr + index); - } - - reference operator-> () const - { - if (ptr == nullptr) - throw std::bad_function_call(); - return *(ptr + index); - } - - uuid_const_iterator() = default; - - self_type & operator--() - { - if (index <= 0) - throw std::out_of_range("Iterator cannot be decremented past the beginning of the data."); - --index; - return *this; - } - - self_type operator--(int) - { - self_type tmp = *this; - --*this; - return tmp; - } - - self_type operator+(difference_type offset) const - { - self_type tmp = *this; - return tmp += offset; - } - - self_type operator-(difference_type offset) const - { - self_type tmp = *this; - return tmp -= offset; - } - - difference_type operator-(self_type const & other) const - { - assert(compatible(other)); - return (index - other.index); - } - - bool operator<(self_type const & other) const - { - assert(compatible(other)); - return index < other.index; - } - - bool operator>(self_type const & other) const - { - return other < *this; - } - - bool operator<=(self_type const & other) const - { - return !(other < *this); - } - - bool operator>=(self_type const & other) const - { - return !(*this < other); - } - - self_type & operator+=(difference_type const offset) - { - if (static_cast(index) + offset < 0 || - static_cast(index) + offset > 16) - throw std::out_of_range("Iterator cannot be incremented outside data bounds."); - - index += offset; - return *this; - } - - self_type & operator-=(difference_type const offset) - { - return *this += -offset; - } - - value_type const & operator[](difference_type const offset) const - { - return (*(*this + offset)); - } - }; - - using value_type = uint8_t; - - public: - constexpr uuid() noexcept : data({}) {}; - - explicit uuid(gsl::span bytes) - { - std::copy(std::cbegin(bytes), std::cend(bytes), std::begin(data)); - } - - template - explicit uuid(ForwardIterator first, ForwardIterator last) - { - if (std::distance(first, last) == 16) - std::copy(first, last, std::begin(data)); - } - - constexpr uuid_variant variant() const noexcept - { - if ((data[8] & 0x80) == 0x00) - return uuid_variant::ncs; - else if ((data[8] & 0xC0) == 0x80) - return uuid_variant::rfc; - else if ((data[8] & 0xE0) == 0xC0) - return uuid_variant::microsoft; - else - return uuid_variant::reserved; - } - - constexpr uuid_version version() const noexcept - { - if ((data[6] & 0xF0) == 0x10) - return uuid_version::time_based; - else if ((data[6] & 0xF0) == 0x20) - return uuid_version::dce_security; - else if ((data[6] & 0xF0) == 0x30) - return uuid_version::name_based_md5; - else if ((data[6] & 0xF0) == 0x40) - return uuid_version::random_number_based; - else if ((data[6] & 0xF0) == 0x50) - return uuid_version::name_based_sha1; - else - return uuid_version::none; - } - - constexpr std::size_t size() const noexcept { return 16; } - - constexpr bool is_nil() const noexcept - { - for (size_t i = 0; i < data.size(); ++i) if (data[i] != 0) return false; - return true; - } - - void swap(uuid & other) noexcept - { - data.swap(other.data); - } - - constexpr uuid_const_iterator begin() const noexcept { return uuid_const_iterator(&data[0], 0); } - constexpr uuid_const_iterator end() const noexcept { return uuid_const_iterator(&data[0], 16); } - - inline gsl::span as_bytes() const - { - return gsl::span(reinterpret_cast(data.data()), 16); - } - - template - static uuid from_string(TChar const * const str, size_t const size) - { - TChar digit = 0; - bool firstDigit = true; - int hasBraces = 0; - size_t index = 0; - std::array data{ { 0 } }; - - if (str == nullptr || size == 0) - throw uuid_error{ "Wrong uuid format" }; - - if (str[0] == static_cast('{')) - hasBraces = 1; - if (hasBraces && str[size - 1] != static_cast('}')) - throw uuid_error{ "Wrong uuid format" }; - - for (size_t i = hasBraces; i < size - hasBraces; ++i) - { - if (str[i] == static_cast('-')) continue; - - if (index >= 16 || !detail::is_hex(str[i])) - { - throw uuid_error{ "Wrong uuid format" }; - } - - if (firstDigit) - { - digit = str[i]; - firstDigit = false; - } - else - { - data[index++] = detail::hexpair2char(digit, str[i]); - firstDigit = true; - } - } - - if (index < 16) - { - throw uuid_error{ "Wrong uuid format" }; - } - - return uuid{ std::cbegin(data), std::cend(data) }; - } - - static uuid from_string(std::string_view str) - { - return from_string(str.data(), str.size()); - } - - static uuid from_string(std::wstring_view str) - { - return from_string(str.data(), str.size()); - } - - private: - std::array data{ { 0 } }; - - friend bool operator==(uuid const & lhs, uuid const & rhs) noexcept; - friend bool operator<(uuid const & lhs, uuid const & rhs) noexcept; - - template - friend std::basic_ostream & operator<<(std::basic_ostream &s, uuid const & id); - }; - - inline bool operator== (uuid const& lhs, uuid const& rhs) noexcept - { - return lhs.data == rhs.data; - } - - inline bool operator!= (uuid const& lhs, uuid const& rhs) noexcept - { - return !(lhs == rhs); - } - - inline bool operator< (uuid const& lhs, uuid const& rhs) noexcept - { - return lhs.data < rhs.data; - } - - template - std::basic_ostream & operator<<(std::basic_ostream &s, uuid const & id) - { - return s << std::hex << std::setfill(static_cast('0')) - << std::setw(2) << (int)id.data[0] - << std::setw(2) << (int)id.data[1] - << std::setw(2) << (int)id.data[2] - << std::setw(2) << (int)id.data[3] - << '-' - << std::setw(2) << (int)id.data[4] - << std::setw(2) << (int)id.data[5] - << '-' - << std::setw(2) << (int)id.data[6] - << std::setw(2) << (int)id.data[7] - << '-' - << std::setw(2) << (int)id.data[8] - << std::setw(2) << (int)id.data[9] - << '-' - << std::setw(2) << (int)id.data[10] - << std::setw(2) << (int)id.data[11] - << std::setw(2) << (int)id.data[12] - << std::setw(2) << (int)id.data[13] - << std::setw(2) << (int)id.data[14] - << std::setw(2) << (int)id.data[15]; - } - - inline std::string to_string(uuid const & id) - { - std::stringstream sstr; - sstr << id; - return sstr.str(); - } - - inline std::wstring to_wstring(uuid const & id) - { - std::wstringstream sstr; - sstr << id; - return sstr.str(); - } - - inline void swap(uuids::uuid & lhs, uuids::uuid & rhs) - { - lhs.swap(rhs); - } - - class uuid_system_generator - { - public: - using result_type = uuid; - - uuid operator()() - { -#ifdef _WIN32 - - GUID newId; - ::CoCreateGuid(&newId); - - std::array bytes = - { { - (unsigned char)((newId.Data1 >> 24) & 0xFF), - (unsigned char)((newId.Data1 >> 16) & 0xFF), - (unsigned char)((newId.Data1 >> 8) & 0xFF), - (unsigned char)((newId.Data1) & 0xFF), - - (unsigned char)((newId.Data2 >> 8) & 0xFF), - (unsigned char)((newId.Data2) & 0xFF), - - (unsigned char)((newId.Data3 >> 8) & 0xFF), - (unsigned char)((newId.Data3) & 0xFF), - - newId.Data4[0], - newId.Data4[1], - newId.Data4[2], - newId.Data4[3], - newId.Data4[4], - newId.Data4[5], - newId.Data4[6], - newId.Data4[7] - } }; - - return uuid{ std::begin(bytes), std::end(bytes) }; - -#elif defined(__linux__) || defined(__unix__) - - uuid_t id; - uuid_generate(id); - - std::array bytes = - { { - id[0], - id[1], - id[2], - id[3], - id[4], - id[5], - id[6], - id[7], - id[8], - id[9], - id[10], - id[11], - id[12], - id[13], - id[14], - id[15] - } }; - - return uuid{ std::begin(bytes), std::end(bytes) }; - -#elif defined(__APPLE__) - auto newId = CFUUIDCreate(NULL); - auto bytes = CFUUIDGetUUIDBytes(newId); - CFRelease(newId); - - std::array arrbytes = - { { - bytes.byte0, - bytes.byte1, - bytes.byte2, - bytes.byte3, - bytes.byte4, - bytes.byte5, - bytes.byte6, - bytes.byte7, - bytes.byte8, - bytes.byte9, - bytes.byte10, - bytes.byte11, - bytes.byte12, - bytes.byte13, - bytes.byte14, - bytes.byte15 - } }; - return uuid{ std::begin(arrbytes), std::end(arrbytes) }; -#elif - return uuid{}; -#endif - } - }; - - template - class basic_uuid_random_generator - { - public: - using result_type = uuid; - - basic_uuid_random_generator() - :generator(new UniformRandomNumberGenerator) - { - std::random_device rd; - generator->seed(rd()); - } - - explicit basic_uuid_random_generator(UniformRandomNumberGenerator& gen) : - generator(&gen, [](auto) {}) {} - explicit basic_uuid_random_generator(UniformRandomNumberGenerator* gen) : - generator(gen, [](auto) {}) {} - - uuid operator()() - { - uint8_t bytes[16]; - for (int i = 0; i < 16; i += 4) - *reinterpret_cast(bytes + i) = distribution(*generator); - - // variant must be 10xxxxxx - bytes[8] &= 0xBF; - bytes[8] |= 0x80; - - // version must be 0100xxxx - bytes[6] &= 0x4F; - bytes[6] |= 0x40; - - return uuid{std::begin(bytes), std::end(bytes)}; - } - - private: - std::uniform_int_distribution distribution; - std::shared_ptr generator; - }; - - using uuid_random_generator = basic_uuid_random_generator; - - class uuid_name_generator - { - public: - using result_type = uuid; - - explicit uuid_name_generator(uuid const& namespace_uuid) noexcept - : nsuuid(namespace_uuid) - {} - - uuid operator()(std::string_view name) - { - reset(); - process_characters(name.data(), name.size()); - return make_uuid(); - } - - uuid operator()(std::wstring_view name) - { - reset(); - process_characters(name.data(), name.size()); - return make_uuid(); - } - - private: - void reset() - { - hasher.reset(); - uint8_t bytes[16]; - std::copy(std::begin(nsuuid), std::end(nsuuid), bytes); - hasher.process_bytes(bytes, 16); - } - - template ::value>> - void process_characters(char_type const * const characters, size_t const count) - { - for (size_t i = 0; i < count; i++) - { - uint32_t c = characters[i]; - hasher.process_byte(static_cast((c >> 0) & 0xFF)); - hasher.process_byte(static_cast((c >> 8) & 0xFF)); - hasher.process_byte(static_cast((c >> 16) & 0xFF)); - hasher.process_byte(static_cast((c >> 24) & 0xFF)); - } - } - - void process_characters(const char * const characters, size_t const count) - { - hasher.process_bytes(characters, count); - } - - uuid make_uuid() - { - detail::sha1::digest8_t digest; - hasher.get_digest_bytes(digest); - - // variant must be 0b10xxxxxx - digest[8] &= 0xBF; - digest[8] |= 0x80; - - // version must be 0b0101xxxx - digest[6] &= 0x5F; - digest[6] |= 0x50; - - return uuid{ digest, digest + 16 }; - } - - private: - uuid nsuuid; - detail::sha1 hasher; - }; -} - -namespace std -{ - template <> - struct hash - { - using argument_type = uuids::uuid; - using result_type = std::size_t; - - result_type operator()(argument_type const &uuid) const - { - std::hash hasher; - return static_cast(hasher(uuids::to_string(uuid))); - } - }; -} From a747bdc7fe98614457f49e3cfe4123f51340f443 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 24 Jan 2022 12:08:06 +0100 Subject: [PATCH 259/668] Use asset hash as uuid --- src/serlio/utils/AssetCache.cpp | 19 +++++++++++++------ src/serlio/utils/AssetCache.h | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/serlio/utils/AssetCache.cpp b/src/serlio/utils/AssetCache.cpp index ceb9dfef..0077ead3 100644 --- a/src/serlio/utils/AssetCache.cpp +++ b/src/serlio/utils/AssetCache.cpp @@ -64,7 +64,7 @@ std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileNam return assetPath; } - const std::filesystem::path newAssetPath = getCachedPath(fileName); + const std::filesystem::path newAssetPath = getCachedPath(fileName, hash); if (newAssetPath.empty()) { LOG_ERR << "Invalid URI, cannot cache the asset: " << uri; return {}; @@ -87,12 +87,19 @@ std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileNam return newAssetPath; } -std::filesystem::path AssetCache::getCachedPath(const wchar_t* fileName) const { - std::wstring cachedAssetName = L""; - - // we append the filename constructed by the encoder from the URI +std::filesystem::path AssetCache::getCachedPath(const wchar_t* fileName, const size_t hash) const { + // we start with the filename constructed by the encoder from the URI assert(fileName != nullptr); - cachedAssetName.append(L"_").append(fileName); + + std::filesystem::path assetFile(fileName); + std::filesystem::path cachedAssetName = assetFile.stem(); + std::wstring hashString = std::to_wstring(hash); + + // we then append the hash constructed from the texturecontent + cachedAssetName += L"_" + hashString; + + std::filesystem::path extension = assetFile.extension(); + cachedAssetName += extension; const std::filesystem::path cachedAssetPath = mCacheRootPath / cachedAssetName; return cachedAssetPath; diff --git a/src/serlio/utils/AssetCache.h b/src/serlio/utils/AssetCache.h index affbd290..22e023f6 100644 --- a/src/serlio/utils/AssetCache.h +++ b/src/serlio/utils/AssetCache.h @@ -30,7 +30,7 @@ class AssetCache { std::filesystem::path put(const wchar_t* uri, const wchar_t* fileName, const uint8_t* buffer, size_t size); private: - std::filesystem::path getCachedPath(const wchar_t* fileName) const; + std::filesystem::path getCachedPath(const wchar_t* fileName, const size_t hash) const; std::unordered_map> mCache; const std::filesystem::path mCacheRootPath; From 6a512f5a765d63d5e9eb67e6fd5c82f7b94933ba Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 24 Jan 2022 14:13:31 +0100 Subject: [PATCH 260/668] Add mel commands for querying the current workspace --- src/serlio/utils/MELScriptBuilder.cpp | 4 ++++ src/serlio/utils/MELScriptBuilder.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/serlio/utils/MELScriptBuilder.cpp b/src/serlio/utils/MELScriptBuilder.cpp index cae0b60e..359dcdf1 100644 --- a/src/serlio/utils/MELScriptBuilder.cpp +++ b/src/serlio/utils/MELScriptBuilder.cpp @@ -151,6 +151,10 @@ void MELScriptBuilder::addCmdLine(const std::wstring& line) { commandStream << line << L"\n"; } +void MELScriptBuilder::getWorkspaceDir() { + commandStream << L"workspace -q -rd;\n"; +} + MStatus MELScriptBuilder::executeSync(std::wstring& output) { MStatus status; MString result = diff --git a/src/serlio/utils/MELScriptBuilder.h b/src/serlio/utils/MELScriptBuilder.h index 8083662b..d1aa4bd3 100644 --- a/src/serlio/utils/MELScriptBuilder.h +++ b/src/serlio/utils/MELScriptBuilder.h @@ -82,6 +82,8 @@ class MELScriptBuilder { void setUndoState(const MELVariable& undoName); void setUndoState(bool undoState); + void getWorkspaceDir(); + void python(const std::wstring& pythonCmd); void addCmdLine(const std::wstring& line); From c9494d574c748e48de797e0144354f764c9dab55 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 24 Jan 2022 14:13:56 +0100 Subject: [PATCH 261/668] Add utility function for getting the current workspace directory --- src/serlio/utils/Utilities.cpp | 11 +++++++++++ src/serlio/utils/Utilities.h | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/src/serlio/utils/Utilities.cpp b/src/serlio/utils/Utilities.cpp index 4c3b7284..f82b38c3 100644 --- a/src/serlio/utils/Utilities.cpp +++ b/src/serlio/utils/Utilities.cpp @@ -19,6 +19,7 @@ #include "utils/Utilities.h" #include "utils/LogHandler.h" +#include "utils/MELScriptBuilder.h" #include "prt/API.h" #include "prt/StringUtils.h" @@ -71,6 +72,16 @@ std::filesystem::path getPluginRoot() { return rootPath; } +std::filesystem::path getWorkspaceRoot(MStatus& status) { + MELScriptBuilder scriptBuilder; + scriptBuilder.getWorkspaceDir(); + + std::wstring output; + status = scriptBuilder.executeSync(output); + + return output; +} + int fromHex(wchar_t c) { // clang-format off switch (c) { diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index 15fd62f1..72744ec8 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -40,6 +40,8 @@ #include #include +#include + // PRT version >= VERSION_MAJOR.VERSION_MINOR #define PRT_VERSION_GTE(VERSION_MAJOR, VERSION_MINOR) \ ((PRT_VERSION_MAJOR >= (VERSION_MAJOR)) && \ @@ -82,6 +84,8 @@ namespace prtu { std::filesystem::path getPluginRoot(); +std::filesystem::path getWorkspaceRoot(MStatus &status); + template std::vector toPtrVec(const std::vector>& sv) { std::vector pv(sv.size()); From 473cad09af7edc4b6249140703b591b83aba91b6 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 24 Jan 2022 14:17:01 +0100 Subject: [PATCH 262/668] Write serlio assets into current workspace asset folder instead of the tmp folder --- src/serlio/PRTContext.cpp | 30 +----------------------------- src/serlio/utils/AssetCache.cpp | 26 +++++++++++++++++++++----- src/serlio/utils/AssetCache.h | 3 --- 3 files changed, 22 insertions(+), 37 deletions(-) diff --git a/src/serlio/PRTContext.cpp b/src/serlio/PRTContext.cpp index 91ebd4ac..a89c7500 100644 --- a/src/serlio/PRTContext.cpp +++ b/src/serlio/PRTContext.cpp @@ -36,32 +36,6 @@ bool verifyMayaEncoder() { const auto mayaEncOpts = prtu::createValidatedOptions(ENC_ID_MAYA); return static_cast(mayaEncOpts); } - -std::filesystem::path getAssetCacheDir() { - return std::filesystem::temp_directory_path() / SRL_TMP_FOLDER; -} - -std::filesystem::path createAndGetAssetCacheDir() { - std::filesystem::path assetCacheDir = getAssetCacheDir(); - try { - std::filesystem::create_directories(assetCacheDir); - } - catch (std::exception& e) { - LOG_ERR << "Error while creating the asset cache directory at " << assetCacheDir << ": " << e.what(); - } - - return assetCacheDir; -} - -void cleanAssetCache() { - std::filesystem::path assetCacheDir = getAssetCacheDir(); - try { - std::filesystem::remove_all(assetCacheDir); - } - catch (std::exception& e) { - LOG_ERR << "Error while cleaning the asset cache at " << assetCacheDir << ": " << e.what(); - } -} } // namespace PRTContext& PRTContext::get() { @@ -70,7 +44,7 @@ PRTContext& PRTContext::get() { } PRTContext::PRTContext(const std::vector& addExtDirs) - : mPluginRootPath(prtu::getPluginRoot()), mAssetCache(createAndGetAssetCacheDir()) { + : mPluginRootPath(prtu::getPluginRoot()) { if (ENABLE_LOG_CONSOLE) { theLogHandler = std::make_unique(); prt::addLogHandler(theLogHandler.get()); @@ -120,8 +94,6 @@ PRTContext::~PRTContext() { theCache.reset(); thePRT.reset(); - cleanAssetCache(); - if (ENABLE_LOG_CONSOLE && (theLogHandler != nullptr)) { prt::removeLogHandler(theLogHandler.get()); } diff --git a/src/serlio/utils/AssetCache.cpp b/src/serlio/utils/AssetCache.cpp index 0077ead3..ca1b27dd 100644 --- a/src/serlio/utils/AssetCache.cpp +++ b/src/serlio/utils/AssetCache.cpp @@ -20,6 +20,7 @@ #include "AssetCache.h" #include "utils/LogHandler.h" +#include "utils/MELScriptBuilder.h" #include #include @@ -27,6 +28,10 @@ #include #include +#include +#include + + namespace { bool writeCacheEntry(const std::filesystem::path& assetPath, const uint8_t* buffer, size_t size) noexcept { @@ -47,8 +52,6 @@ void removeCacheEntry(const std::filesystem::path& expiredAssetPath) { } // namespace -AssetCache::AssetCache(const std::filesystem::path& cacheRootPath) : mCacheRootPath(cacheRootPath) {} - std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileName, const uint8_t* buffer, size_t size) { assert(uri != nullptr); @@ -88,9 +91,22 @@ std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileNam } std::filesystem::path AssetCache::getCachedPath(const wchar_t* fileName, const size_t hash) const { - // we start with the filename constructed by the encoder from the URI - assert(fileName != nullptr); + // we start with the root folder in the current workspace + MStatus status; + std::filesystem::path workspaceDir = prtu::getWorkspaceRoot(status); + MCHECK(status); + std::filesystem::path assetsDir = workspaceDir.make_preferred() / "assets" / "serlio_assets"; + + //create dir if it does not exist + try { + std::filesystem::create_directories(assetsDir); + } + catch (std::exception& e) { + LOG_ERR << "Error while creating the asset cache directory at " << assetsDir << ": " << e.what(); + } + // we then get the filename constructed by the encoder from the URI + assert(fileName != nullptr); std::filesystem::path assetFile(fileName); std::filesystem::path cachedAssetName = assetFile.stem(); std::wstring hashString = std::to_wstring(hash); @@ -101,6 +117,6 @@ std::filesystem::path AssetCache::getCachedPath(const wchar_t* fileName, const s std::filesystem::path extension = assetFile.extension(); cachedAssetName += extension; - const std::filesystem::path cachedAssetPath = mCacheRootPath / cachedAssetName; + const std::filesystem::path cachedAssetPath = assetsDir / cachedAssetName; return cachedAssetPath; } diff --git a/src/serlio/utils/AssetCache.h b/src/serlio/utils/AssetCache.h index 22e023f6..d76fd950 100644 --- a/src/serlio/utils/AssetCache.h +++ b/src/serlio/utils/AssetCache.h @@ -25,13 +25,10 @@ class AssetCache { public: - explicit AssetCache(const std::filesystem::path& cacheRootPath); - std::filesystem::path put(const wchar_t* uri, const wchar_t* fileName, const uint8_t* buffer, size_t size); private: std::filesystem::path getCachedPath(const wchar_t* fileName, const size_t hash) const; std::unordered_map> mCache; - const std::filesystem::path mCacheRootPath; }; From edd6eca147bd9ec46c6b24132cb6d55735f66704 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 24 Jan 2022 14:25:08 +0100 Subject: [PATCH 263/668] Only write the asset file if it does not yet exist --- src/serlio/utils/AssetCache.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/serlio/utils/AssetCache.cpp b/src/serlio/utils/AssetCache.cpp index ca1b27dd..ad55c064 100644 --- a/src/serlio/utils/AssetCache.cpp +++ b/src/serlio/utils/AssetCache.cpp @@ -35,6 +35,8 @@ namespace { bool writeCacheEntry(const std::filesystem::path& assetPath, const uint8_t* buffer, size_t size) noexcept { + if (std::filesystem::exists(assetPath)) + return true; std::ofstream stream(assetPath, std::ofstream::binary | std::ofstream::trunc); if (!stream) return false; From 5c1448bc01a542a11751cded94b6151495357672 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 24 Jan 2022 14:35:47 +0100 Subject: [PATCH 264/668] Use wcsncpy_s on windows and wcsncpy on linux This is done in order to prevent warnings when building on windows. --- src/serlio/modifiers/MayaCallbacks.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 98d53c55..8bf35dd7 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -460,8 +460,11 @@ void MayaCallbacks::addAsset(const wchar_t* uri, const wchar_t* fileName, const resultSize = pathStr.size() + 1; // ask for space for null-terminator return; } - +#ifdef _WIN32 + wcsncpy_s(result, resultSize, pathStr.c_str(), resultSize); +#else wcsncpy(result, pathStr.c_str(), resultSize); +#endif result[resultSize - 1] = 0x0; resultSize = pathStr.length() + 1; } From 57f604f57a27c35ff50f7ccada0c0eacf366bfb9 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 24 Jan 2022 15:15:52 +0100 Subject: [PATCH 265/668] Cleanup: remove unused constexpr --- src/serlio/PRTContext.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/serlio/PRTContext.cpp b/src/serlio/PRTContext.cpp index a89c7500..203255bf 100644 --- a/src/serlio/PRTContext.cpp +++ b/src/serlio/PRTContext.cpp @@ -25,7 +25,6 @@ namespace { constexpr bool DBG = false; -constexpr const wchar_t* SRL_TMP_FOLDER = L"serlio/asset_cache"; constexpr const wchar_t* PRT_EXT_SUBDIR = L"ext"; constexpr prt::LogLevel PRT_LOG_LEVEL = prt::LOG_INFO; constexpr bool ENABLE_LOG_CONSOLE = true; From 97fa9cdef68277fa93ac460129c9aa79b4e3b68e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 24 Jan 2022 15:22:10 +0100 Subject: [PATCH 266/668] Cleanup: make class out of PRTContext --- src/serlio/PRTContext.cpp | 36 ++++++++++++---------- src/serlio/PRTContext.h | 17 +++++----- src/serlio/modifiers/PRTModifierAction.cpp | 12 ++++---- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/serlio/PRTContext.cpp b/src/serlio/PRTContext.cpp index 203255bf..63bdedc7 100644 --- a/src/serlio/PRTContext.cpp +++ b/src/serlio/PRTContext.cpp @@ -45,15 +45,15 @@ PRTContext& PRTContext::get() { PRTContext::PRTContext(const std::vector& addExtDirs) : mPluginRootPath(prtu::getPluginRoot()) { if (ENABLE_LOG_CONSOLE) { - theLogHandler = std::make_unique(); - prt::addLogHandler(theLogHandler.get()); + mLogHandler = std::make_unique(); + prt::addLogHandler(mLogHandler.get()); } if (ENABLE_LOG_FILE) { const std::wstring logPath = (mPluginRootPath / L"serlio.log").wstring(); - theFileLogHandler = + mFileLogHandler = prt::FileLogHandler::create(prt::LogHandler::ALL, prt::LogHandler::ALL_COUNT, logPath.c_str()); - prt::addLogHandler(theFileLogHandler); + prt::addLogHandler(mFileLogHandler); } // Not the best place, but here we are sure the console logger is running and we are before PRT init info @@ -69,7 +69,7 @@ PRTContext::PRTContext(const std::vector& addExtDirs) prt::Status status = prt::STATUS_UNSPECIFIED_ERROR; const auto extensionPathPtrs = prtu::toPtrVec(extensionPaths); - thePRT.reset(prt::init(extensionPathPtrs.data(), extensionPathPtrs.size(), PRT_LOG_LEVEL, &status)); + mPRTHandle.reset(prt::init(extensionPathPtrs.data(), extensionPathPtrs.size(), PRT_LOG_LEVEL, &status)); // early sanity check for maya encoder if (!verifyMayaEncoder()) { @@ -77,29 +77,33 @@ PRTContext::PRTContext(const std::vector& addExtDirs) status = prt::STATUS_ENCODER_NOT_FOUND; } - if (!thePRT || status != prt::STATUS_OK) { + if (!mPRTHandle || status != prt::STATUS_OK) { LOG_FTL << "Could not initialize PRT: " << prt::getStatusDescription(status); - thePRT.reset(); + mPRTHandle.reset(); } else { - theCache.reset(prt::CacheObject::create(prt::CacheObject::CACHE_TYPE_DEFAULT)); + mPRTCache.reset(prt::CacheObject::create(prt::CacheObject::CACHE_TYPE_DEFAULT)); mResolveMapCache = std::make_unique(); } } +bool PRTContext::isAlive() const { + return static_cast(mPRTHandle); +} + PRTContext::~PRTContext() { // the cache needs to be destructed before PRT, so reset them explicitely in the right order here - theCache.reset(); - thePRT.reset(); + mPRTCache.reset(); + mPRTHandle.reset(); - if (ENABLE_LOG_CONSOLE && (theLogHandler != nullptr)) { - prt::removeLogHandler(theLogHandler.get()); + if (ENABLE_LOG_CONSOLE && (mLogHandler != nullptr)) { + prt::removeLogHandler(mLogHandler.get()); } - if (ENABLE_LOG_FILE && (theFileLogHandler != nullptr)) { - prt::removeLogHandler(theFileLogHandler); - theFileLogHandler->destroy(); - theFileLogHandler = nullptr; + if (ENABLE_LOG_FILE && (mFileLogHandler != nullptr)) { + prt::removeLogHandler(mFileLogHandler); + mFileLogHandler->destroy(); + mFileLogHandler = nullptr; } } diff --git a/src/serlio/PRTContext.h b/src/serlio/PRTContext.h index 95c53a6f..92cc0992 100644 --- a/src/serlio/PRTContext.h +++ b/src/serlio/PRTContext.h @@ -29,10 +29,11 @@ #include #include -struct PRTContext; +class PRTContext; using PRTContextUPtr = std::unique_ptr; -struct SRL_TEST_EXPORTS_API PRTContext final { +class SRL_TEST_EXPORTS_API PRTContext final { +public: static PRTContext& get(); explicit PRTContext(const std::vector& addExtDirs = {}); @@ -42,15 +43,13 @@ struct SRL_TEST_EXPORTS_API PRTContext final { PRTContext& operator=(PRTContext&&) = delete; ~PRTContext(); - bool isAlive() const { - return static_cast(thePRT); - } + bool isAlive() const; const std::filesystem::path mPluginRootPath; // the path where serlio dso resides AssetCache mAssetCache; - ObjectUPtr thePRT; - CacheObjectUPtr theCache; - logging::LogHandlerUPtr theLogHandler; - prt::FileLogHandler* theFileLogHandler = nullptr; + ObjectUPtr mPRTHandle; + CacheObjectUPtr mPRTCache; + logging::LogHandlerUPtr mLogHandler; + prt::FileLogHandler* mFileLogHandler = nullptr; ResolveMapCacheUPtr mResolveMapCache; }; diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 065add67..5a0481d7 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -430,7 +430,7 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { const auto updateUserSetAttribute = [this](const MFnDependencyNode& fnNode, const MFnAttribute& fnAttribute, const RuleAttribute& ruleAttribute, const PrtAttributeType attrType) { const AttributeMapUPtr defaultAttributeValues = - getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, + getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().mPRTCache, *inPrtMesh, mRandomSeed, *mGenerateAttrs); if (getAndResetForceDefault(fnNode, fnAttribute)) { @@ -509,7 +509,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { const auto updateUIFromAttributes = [this](const MFnDependencyNode& fnNode, const MFnAttribute& fnAttribute, const RuleAttribute& ruleAttribute, const PrtAttributeType attrType) { const AttributeMapUPtr defaultAttributeValues = - getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, + getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().mPRTCache, *inPrtMesh, mRandomSeed, *mGenerateAttrs); MPlug plug(fnNode.object(), fnAttribute.object()); const std::wstring fqAttrName = ruleAttribute.fqName; @@ -622,7 +622,7 @@ MStatus PRTModifierAction::updateRuleFiles(const MObject& node, const MString& r mRuleFile.clear(); mStartRule.clear(); mRuleAttributes.clear(); - PRTContext::get().theCache.get()->flushAll(); + PRTContext::get().mPRTCache.get()->flushAll(); ResolveMapSPtr resolveMap = getResolveMap(); if (!resolveMap) { @@ -643,14 +643,14 @@ MStatus PRTModifierAction::updateRuleFiles(const MObject& node, const MString& r return MS::kFailure; } - RuleFileInfoUPtr info(prt::createRuleFileInfo(ruleFileURI, PRTContext::get().theCache.get(), &infoStatus)); + RuleFileInfoUPtr info(prt::createRuleFileInfo(ruleFileURI, PRTContext::get().mPRTCache.get(), &infoStatus)); if (!info || infoStatus != prt::STATUS_OK) { LOG_ERR << "could not get rule file info from rule file " << mRuleFile; return MS::kFailure; } mStartRule = prtu::detectStartRule(info); - mGenerateAttrs = getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().theCache, + mGenerateAttrs = getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().mPRTCache, *inPrtMesh, mRandomSeed, *EMPTY_ATTRIBUTES); if (DBG) LOG_DBG << "default attrs: " << prtu::objectToXML(mGenerateAttrs); @@ -691,7 +691,7 @@ MStatus PRTModifierAction::doIt() { InitialShapeNOPtrVector shapes = {shape.get()}; const prt::Status generateStatus = prt::generate(shapes.data(), shapes.size(), nullptr, encIDs.data(), encIDs.size(), encOpts.data(), - outputHandler.get(), PRTContext::get().theCache.get(), nullptr); + outputHandler.get(), PRTContext::get().mPRTCache.get(), nullptr); if (generateStatus != prt::STATUS_OK) LOG_ERR << "prt generate failed: " << prt::getStatusDescription(generateStatus); From 094cf3447b76919dc3f8dd100b98782053d59da0 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 24 Jan 2022 15:38:55 +0100 Subject: [PATCH 267/668] Make sure user deleted assets are regenerated --- src/serlio/utils/AssetCache.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/serlio/utils/AssetCache.cpp b/src/serlio/utils/AssetCache.cpp index ad55c064..00f0535f 100644 --- a/src/serlio/utils/AssetCache.cpp +++ b/src/serlio/utils/AssetCache.cpp @@ -31,7 +31,6 @@ #include #include - namespace { bool writeCacheEntry(const std::filesystem::path& assetPath, const uint8_t* buffer, size_t size) noexcept { @@ -63,16 +62,23 @@ std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileNam const auto it = std::find_if(mCache.begin(), mCache.end(), [&uri](const auto& p) { return (std::wcscmp(p.first.c_str(), uri) == 0); }); + const std::filesystem::path newAssetPath = getCachedPath(fileName, hash); + + bool fileWasDeletedByUser = false; // reuse cached asset if uri and hash match if ((it != mCache.end()) && (it->second.second == hash)) { - const std::filesystem::path& assetPath = it->second.first; - return assetPath; + if (!std::filesystem::exists(newAssetPath)) { + fileWasDeletedByUser = true; + } + else { + const std::filesystem::path& assetPath = it->second.first; + return assetPath; + } } - const std::filesystem::path newAssetPath = getCachedPath(fileName, hash); if (newAssetPath.empty()) { LOG_ERR << "Invalid URI, cannot cache the asset: " << uri; - return {}; + return {}; } if (!writeCacheEntry(newAssetPath, buffer, size)) { @@ -80,9 +86,12 @@ std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileNam return {}; } - if (it == mCache.end()) { + if (it == mCache.end() || fileWasDeletedByUser) { mCache.emplace(uri, std::make_pair(newAssetPath, hash)); } + else if (fileWasDeletedByUser) { + it->second = std::make_pair(newAssetPath, hash); + } else { // handle hash mismatch removeCacheEntry(it->second.first); From 710e58779f883e738b8e88e198cc3146f7784564 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 24 Jan 2022 15:39:08 +0100 Subject: [PATCH 268/668] Cleanup: run clang-format --- src/serlio/utils/AssetCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/utils/AssetCache.cpp b/src/serlio/utils/AssetCache.cpp index 00f0535f..af1240d1 100644 --- a/src/serlio/utils/AssetCache.cpp +++ b/src/serlio/utils/AssetCache.cpp @@ -108,7 +108,7 @@ std::filesystem::path AssetCache::getCachedPath(const wchar_t* fileName, const s MCHECK(status); std::filesystem::path assetsDir = workspaceDir.make_preferred() / "assets" / "serlio_assets"; - //create dir if it does not exist + // create dir if it does not exist try { std::filesystem::create_directories(assetsDir); } From 564ab0bfe86adf3199ae8bbfeb2ecfdf4f0af4e0 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 24 Jan 2022 15:42:54 +0100 Subject: [PATCH 269/668] Cleanup: use constexpr for static folder names --- src/serlio/utils/AssetCache.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/serlio/utils/AssetCache.cpp b/src/serlio/utils/AssetCache.cpp index af1240d1..a437f6bb 100644 --- a/src/serlio/utils/AssetCache.cpp +++ b/src/serlio/utils/AssetCache.cpp @@ -32,6 +32,8 @@ #include namespace { +constexpr const wchar_t* MAYA_ASSET_FOLDER = L"assets"; +constexpr const wchar_t* SERLIO_ASSET_FOLDER = L"serlio_assets"; bool writeCacheEntry(const std::filesystem::path& assetPath, const uint8_t* buffer, size_t size) noexcept { if (std::filesystem::exists(assetPath)) @@ -106,7 +108,7 @@ std::filesystem::path AssetCache::getCachedPath(const wchar_t* fileName, const s MStatus status; std::filesystem::path workspaceDir = prtu::getWorkspaceRoot(status); MCHECK(status); - std::filesystem::path assetsDir = workspaceDir.make_preferred() / "assets" / "serlio_assets"; + std::filesystem::path assetsDir = workspaceDir.make_preferred() / MAYA_ASSET_FOLDER / SERLIO_ASSET_FOLDER; // create dir if it does not exist try { From 992bb2b86f288e6b98c3c07d17328eb3e3649378 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 24 Jan 2022 15:49:52 +0100 Subject: [PATCH 270/668] Cleanup: remove unused function --- src/serlio/utils/Utilities.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index 72744ec8..a657406e 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -101,7 +101,6 @@ std::vector toPtrVec(const std::vector>& sv) { } time_t getFileModificationTime(const std::wstring& p); -std::filesystem::path getProcessTempDir(const std::wstring& prefix); int fromHex(wchar_t c); wchar_t toHex(int i); From 860c1251c3b4006abde07042f974dcdb8546e608 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 24 Jan 2022 15:51:34 +0100 Subject: [PATCH 271/668] Cleanup: remove unused includes --- src/serlio/utils/AssetCache.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/serlio/utils/AssetCache.cpp b/src/serlio/utils/AssetCache.cpp index a437f6bb..12df1b78 100644 --- a/src/serlio/utils/AssetCache.cpp +++ b/src/serlio/utils/AssetCache.cpp @@ -28,9 +28,6 @@ #include #include -#include -#include - namespace { constexpr const wchar_t* MAYA_ASSET_FOLDER = L"assets"; constexpr const wchar_t* SERLIO_ASSET_FOLDER = L"serlio_assets"; From 1e0d6995009dd7d021d8f8a441d5f81e92142f63 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 24 Jan 2022 15:52:16 +0100 Subject: [PATCH 272/668] Cleanup: move getWorkspaceRoot to MayaUtilities --- src/serlio/utils/AssetCache.cpp | 2 +- src/serlio/utils/MayaUtilities.cpp | 10 ++++++++++ src/serlio/utils/MayaUtilities.h | 1 + src/serlio/utils/Utilities.cpp | 11 ----------- src/serlio/utils/Utilities.h | 4 ---- 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/serlio/utils/AssetCache.cpp b/src/serlio/utils/AssetCache.cpp index 12df1b78..248ae480 100644 --- a/src/serlio/utils/AssetCache.cpp +++ b/src/serlio/utils/AssetCache.cpp @@ -103,7 +103,7 @@ std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileNam std::filesystem::path AssetCache::getCachedPath(const wchar_t* fileName, const size_t hash) const { // we start with the root folder in the current workspace MStatus status; - std::filesystem::path workspaceDir = prtu::getWorkspaceRoot(status); + std::filesystem::path workspaceDir = mu::getWorkspaceRoot(status); MCHECK(status); std::filesystem::path assetsDir = workspaceDir.make_preferred() / MAYA_ASSET_FOLDER / SERLIO_ASSET_FOLDER; diff --git a/src/serlio/utils/MayaUtilities.cpp b/src/serlio/utils/MayaUtilities.cpp index 295fd2db..082f205b 100644 --- a/src/serlio/utils/MayaUtilities.cpp +++ b/src/serlio/utils/MayaUtilities.cpp @@ -1,4 +1,5 @@ #include "utils/MayaUtilities.h" +#include "utils/MELScriptBuilder.h" #include @@ -29,4 +30,13 @@ void statusCheck(const MStatus& status, const char* file, int line) { } } +std::filesystem::path getWorkspaceRoot(MStatus& status) { + MELScriptBuilder scriptBuilder; + scriptBuilder.getWorkspaceDir(); + + std::wstring output; + status = scriptBuilder.executeSync(output); + + return output; +} } // namespace mu \ No newline at end of file diff --git a/src/serlio/utils/MayaUtilities.h b/src/serlio/utils/MayaUtilities.h index 4cbc0121..5a1f6ed7 100644 --- a/src/serlio/utils/MayaUtilities.h +++ b/src/serlio/utils/MayaUtilities.h @@ -47,4 +47,5 @@ class NamedType { T value_; }; +std::filesystem::path getWorkspaceRoot(MStatus& status); } // namespace mu \ No newline at end of file diff --git a/src/serlio/utils/Utilities.cpp b/src/serlio/utils/Utilities.cpp index f82b38c3..4c3b7284 100644 --- a/src/serlio/utils/Utilities.cpp +++ b/src/serlio/utils/Utilities.cpp @@ -19,7 +19,6 @@ #include "utils/Utilities.h" #include "utils/LogHandler.h" -#include "utils/MELScriptBuilder.h" #include "prt/API.h" #include "prt/StringUtils.h" @@ -72,16 +71,6 @@ std::filesystem::path getPluginRoot() { return rootPath; } -std::filesystem::path getWorkspaceRoot(MStatus& status) { - MELScriptBuilder scriptBuilder; - scriptBuilder.getWorkspaceDir(); - - std::wstring output; - status = scriptBuilder.executeSync(output); - - return output; -} - int fromHex(wchar_t c) { // clang-format off switch (c) { diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index a657406e..a039c5c5 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -40,8 +40,6 @@ #include #include -#include - // PRT version >= VERSION_MAJOR.VERSION_MINOR #define PRT_VERSION_GTE(VERSION_MAJOR, VERSION_MINOR) \ ((PRT_VERSION_MAJOR >= (VERSION_MAJOR)) && \ @@ -84,8 +82,6 @@ namespace prtu { std::filesystem::path getPluginRoot(); -std::filesystem::path getWorkspaceRoot(MStatus &status); - template std::vector toPtrVec(const std::vector>& sv) { std::vector pv(sv.size()); From 55a202450cac05360484ccbab07207e2a48981cd Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 24 Jan 2022 16:13:55 +0100 Subject: [PATCH 273/668] Cleanup: fix imports --- src/serlio/utils/AssetCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/utils/AssetCache.cpp b/src/serlio/utils/AssetCache.cpp index 248ae480..fd6d331a 100644 --- a/src/serlio/utils/AssetCache.cpp +++ b/src/serlio/utils/AssetCache.cpp @@ -20,7 +20,7 @@ #include "AssetCache.h" #include "utils/LogHandler.h" -#include "utils/MELScriptBuilder.h" +#include "utils/MayaUtilities.h" #include #include From 08f6350246741e98f3d0792d7930d254d2c30304 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 26 Jan 2022 11:58:44 +0100 Subject: [PATCH 274/668] Fix getImport utility function --- src/serlio/utils/Utilities.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index ac54a0d1..701d2d2b 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -198,10 +198,10 @@ SRL_TEST_EXPORTS_API inline std::wstring removeImport(const std::wstring& fqRule SRL_TEST_EXPORTS_API inline std::wstring getImport(const std::wstring& fqRuleName) { const std::wstring ruleWithoutStyle = removeStyle(fqRuleName); - const auto sepPos = ruleWithoutStyle.find(IMPORT_DELIMITER); + const auto sepPos = ruleWithoutStyle.rfind(IMPORT_DELIMITER); if (sepPos == std::wstring::npos || sepPos == 0) return {}; - return fqRuleName.substr(0, sepPos); + return ruleWithoutStyle.substr(0, sepPos); } } // namespace prtu From e5c40770e6469e09a57c6c4a1a54ec998dc05f98 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 26 Jan 2022 12:01:28 +0100 Subject: [PATCH 275/668] Cleanup: remove redundant empty string initialization --- src/serlio/modifiers/PRTModifierAction.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 7d89eb51..5007b08b 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -55,7 +55,7 @@ class PRTModifierEnum { private: bool mRestricted = true; - MString mValuesAttr = ""; + MString mValuesAttr; }; // class PRTModifierEnum class PRTModifierAction : public polyModifierFty { From cd13ed306f0bc24fbccd2293dfb08365534ade80 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 26 Jan 2022 12:03:49 +0100 Subject: [PATCH 276/668] Cleanup: remove unnecessary empty rule definition --- src/serlio/modifiers/PRTModifierAction.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index c8bbaac1..5bed2707 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -167,14 +167,12 @@ std::list getNodeAttributesCorrespondingToCGA(const MFnDependencyNode& return rawAttrs; } -const RuleAttribute RULE_NOT_FOUND{}; - const RuleAttribute reverseLookupAttribute(const std::wstring& mayaFullAttrName, const RuleAttributes& ruleAttributes) { auto it = std::find_if(ruleAttributes.begin(), ruleAttributes.end(), [&mayaFullAttrName](const auto& ra) { return (ra.mayaFullName == mayaFullAttrName); }); if (it != ruleAttributes.end()) return *it; - return RULE_NOT_FOUND; + return {}; } template From ade0c23acde4cd8da5b4b332984d44f3f1c924c3 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 26 Jan 2022 12:06:38 +0100 Subject: [PATCH 277/668] Cleanup: use continue to avoid big if indentation in loop --- src/serlio/modifiers/PRTModifierAction.cpp | 114 ++++++++++----------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 5bed2707..ded9456f 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -601,81 +601,81 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { MStatus PRTModifierAction::updateDynamicEnums() { for (auto& e : mEnums) { - if (e.mValuesAttr.length() > 0) { + if (e.mValuesAttr.length() == 0) + continue; - const MString fullAttrName = e.mAttr.name(); - const RuleAttribute ruleAttr = reverseLookupAttribute(fullAttrName.asWChar(), mRuleAttributes); + const MString fullAttrName = e.mAttr.name(); + const RuleAttribute ruleAttr = reverseLookupAttribute(fullAttrName.asWChar(), mRuleAttributes); - const std::wstring attrStyle = prtu::getStyle(ruleAttr.fqName).c_str(); - std::wstring attrImport = prtu::getImport(ruleAttr.fqName).c_str(); - if (!attrImport.empty()) - attrImport += prtu::IMPORT_DELIMITER; + const std::wstring attrStyle = prtu::getStyle(ruleAttr.fqName).c_str(); + std::wstring attrImport = prtu::getImport(ruleAttr.fqName).c_str(); + if (!attrImport.empty()) + attrImport += prtu::IMPORT_DELIMITER; - const std::wstring prefix = attrStyle + prtu::STYLE_DELIMITER + attrImport; + const std::wstring prefix = attrStyle + prtu::STYLE_DELIMITER + attrImport; - const wchar_t* valuesAttr = (MString(prefix.c_str()) + e.mValuesAttr).asWChar(); - prt::Attributable::PrimitiveType type = mGenerateAttrs->getType(valuesAttr); + const wchar_t* valuesAttr = (MString(prefix.c_str()) + e.mValuesAttr).asWChar(); + prt::Attributable::PrimitiveType type = mGenerateAttrs->getType(valuesAttr); - switch (type) { - case prt::Attributable::PT_STRING_ARRAY: { - size_t arr_length = 0; - const wchar_t* const* stringArray = mGenerateAttrs->getStringArray(valuesAttr, &arr_length); + switch (type) { + case prt::Attributable::PT_STRING_ARRAY: { + size_t arr_length = 0; + const wchar_t* const* stringArray = mGenerateAttrs->getStringArray(valuesAttr, &arr_length); - for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { - std::wstring currString = stringArray[enumIndex]; + for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { + std::wstring currString = stringArray[enumIndex]; - // remove newlines from strings, because they break the maya UI - currString.erase(std::remove(currString.begin(), currString.end(), '\n'), currString.end()); + // remove newlines from strings, because they break the maya UI + currString.erase(std::remove(currString.begin(), currString.end(), '\n'), currString.end()); - const MString mCurrString(currString.c_str()); - MCHECK(e.mAttr.addField(mCurrString, enumIndex)); - } - break; + const MString mCurrString(currString.c_str()); + MCHECK(e.mAttr.addField(mCurrString, enumIndex)); } - case prt::Attributable::PT_FLOAT_ARRAY: { - size_t arr_length = 0; - const double* doubleArray = mGenerateAttrs->getFloatArray(valuesAttr, &arr_length); + break; + } + case prt::Attributable::PT_FLOAT_ARRAY: { + size_t arr_length = 0; + const double* doubleArray = mGenerateAttrs->getFloatArray(valuesAttr, &arr_length); - for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { - const double currDouble = doubleArray[enumIndex]; + for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { + const double currDouble = doubleArray[enumIndex]; - const MString mCurrString(std::to_wstring(currDouble).c_str()); - MCHECK(e.mAttr.addField(mCurrString, enumIndex)); - } - break; + const MString mCurrString(std::to_wstring(currDouble).c_str()); + MCHECK(e.mAttr.addField(mCurrString, enumIndex)); } - case prt::Attributable::PT_BOOL_ARRAY: { - size_t arr_length = 0; - const bool* boolArray = mGenerateAttrs->getBoolArray(valuesAttr, &arr_length); + break; + } + case prt::Attributable::PT_BOOL_ARRAY: { + size_t arr_length = 0; + const bool* boolArray = mGenerateAttrs->getBoolArray(valuesAttr, &arr_length); - for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { - const bool currBool = boolArray[enumIndex]; + for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { + const bool currBool = boolArray[enumIndex]; - const MString mCurrString(std::to_wstring(currBool).c_str()); - MCHECK(e.mAttr.addField(mCurrString, enumIndex)); - } - break; + const MString mCurrString(std::to_wstring(currBool).c_str()); + MCHECK(e.mAttr.addField(mCurrString, enumIndex)); } - case prt::Attributable::PT_STRING: { - const MString mCurrString = mGenerateAttrs->getString(valuesAttr); + break; + } + case prt::Attributable::PT_STRING: { + const MString mCurrString = mGenerateAttrs->getString(valuesAttr); - MCHECK(e.mAttr.addField(mCurrString, 0)); - break; - } - case prt::Attributable::PT_FLOAT: { - const bool currFloat = mGenerateAttrs->getFloat(valuesAttr); + MCHECK(e.mAttr.addField(mCurrString, 0)); + break; + } + case prt::Attributable::PT_FLOAT: { + const bool currFloat = mGenerateAttrs->getFloat(valuesAttr); - const MString mCurrString(std::to_wstring(currFloat).c_str()); - MCHECK(e.mAttr.addField(mCurrString, 0)); - break; - } - case prt::Attributable::PT_BOOL: { - const bool currBool = mGenerateAttrs->getBool(valuesAttr); + const MString mCurrString(std::to_wstring(currFloat).c_str()); + MCHECK(e.mAttr.addField(mCurrString, 0)); + break; + } + case prt::Attributable::PT_BOOL: { + const bool currBool = mGenerateAttrs->getBool(valuesAttr); - const MString mCurrString(std::to_wstring(currBool).c_str()); - MCHECK(e.mAttr.addField(mCurrString, 0)); - break; - } + const MString mCurrString(std::to_wstring(currBool).c_str()); + MCHECK(e.mAttr.addField(mCurrString, 0)); + break; } } } From 2b90da9a723ea9d152278dbf3d99e32466244842 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 28 Jan 2022 10:11:33 +0100 Subject: [PATCH 278/668] Cleanup: replace use std::map and std::set for accessing/sorting rule attributes --- src/serlio/modifiers/PRTModifierAction.cpp | 37 +++++++------- src/serlio/modifiers/PRTModifierAction.h | 7 ++- src/serlio/modifiers/RuleAttributes.cpp | 56 +++++++++++----------- src/serlio/modifiers/RuleAttributes.h | 14 ++++-- 4 files changed, 59 insertions(+), 55 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index ded9456f..37ecf090 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -167,16 +167,8 @@ std::list getNodeAttributesCorrespondingToCGA(const MFnDependencyNode& return rawAttrs; } -const RuleAttribute reverseLookupAttribute(const std::wstring& mayaFullAttrName, const RuleAttributes& ruleAttributes) { - auto it = std::find_if(ruleAttributes.begin(), ruleAttributes.end(), - [&mayaFullAttrName](const auto& ra) { return (ra.mayaFullName == mayaFullAttrName); }); - if (it != ruleAttributes.end()) - return *it; - return {}; -} - template -MStatus iterateThroughAttributesAndApply(const MObject& node, const RuleAttributes& mRuleAttributes, T attrFunction) { +MStatus iterateThroughAttributesAndApply(const MObject& node, const RuleAttributeMap& ruleAttributes, T attrFunction) { MStatus stat; const MFnDependencyNode fNode(node, &stat); MCHECK(stat); @@ -187,7 +179,8 @@ MStatus iterateThroughAttributesAndApply(const MObject& node, const RuleAttribut MFnAttribute fnAttr(attrObj); const MString fullAttrName = fnAttr.name(); - const RuleAttribute ruleAttr = reverseLookupAttribute(fullAttrName.asWChar(), mRuleAttributes); + RuleAttribute ruleAttr = ruleAttributes.at(fullAttrName.asWChar()); + assert(!ruleAttr.fqName.empty()); // poor mans check for RULE_NOT_FOUND [[maybe_unused]] const auto ruleAttrType = ruleAttr.mType; @@ -605,7 +598,7 @@ MStatus PRTModifierAction::updateDynamicEnums() { continue; const MString fullAttrName = e.mAttr.name(); - const RuleAttribute ruleAttr = reverseLookupAttribute(fullAttrName.asWChar(), mRuleAttributes); + const RuleAttribute ruleAttr = mRuleAttributes[fullAttrName.asWChar()]; const std::wstring attrStyle = prtu::getStyle(ruleAttr.fqName).c_str(); std::wstring attrImport = prtu::getImport(ruleAttr.fqName).c_str(); @@ -739,10 +732,12 @@ MStatus PRTModifierAction::updateRuleFiles(const MObject& node, const MString& r if (node != MObject::kNullObj) { // derive necessary data from PRT rule info to populate node with dynamic rule attributes - mRuleAttributes = getRuleAttributes(mRuleFile, info.get()); - sortRuleAttributes(mRuleAttributes); - - createNodeAttributes(node, info.get()); + RuleAttributeSet ruleAttributes = getRuleAttributes(mRuleFile, info.get()); + for (const RuleAttribute& ruleAttr : ruleAttributes) { + mRuleAttributes[ruleAttr.mayaFullName] = ruleAttr; + } + + createNodeAttributes(ruleAttributes, node, info.get()); updateDynamicEnums(); } @@ -781,12 +776,12 @@ MStatus PRTModifierAction::doIt() { return status; } -MStatus PRTModifierAction::createNodeAttributes(const MObject& nodeObj, const prt::RuleFileInfo* info) { +MStatus PRTModifierAction::createNodeAttributes(const RuleAttributeSet& ruleAttributes, const MObject& nodeObj, const prt::RuleFileInfo* info) { MStatus stat; MFnDependencyNode node(nodeObj, &stat); MCHECK(stat); - for (const RuleAttribute& p : mRuleAttributes) { + for (auto const& p : ruleAttributes) { const std::wstring fqName = p.fqName; // only use attributes of current style @@ -952,10 +947,10 @@ MStatus PRTModifierAction::createNodeAttributes(const MObject& nodeObj, const pr void PRTModifierAction::removeUnusedAttribs(MFnDependencyNode& node) { auto isInUse = [this](const MString& attrName) { - auto it = std::find_if(mRuleAttributes.begin(), mRuleAttributes.end(), [&attrName](const auto& ra) { - return (ra.mayaFullName == attrName.asWChar() || - ra.mayaFullName + ATTRIBUTE_USER_SET_SUFFIX == attrName.asWChar() || - ra.mayaFullName + ATTRIBUTE_FORCE_DEFAULT_SUFFIX == attrName.asWChar()); + auto it = std::find_if(mRuleAttributes.begin(), mRuleAttributes.end(), [&attrName](const auto& attrPair) { + return (attrPair.second.mayaFullName == attrName.asWChar() || + attrPair.second.mayaFullName + ATTRIBUTE_USER_SET_SUFFIX == attrName.asWChar() || + attrPair.second.mayaFullName + ATTRIBUTE_FORCE_DEFAULT_SUFFIX == attrName.asWChar()); }); return (it != mRuleAttributes.end()); }; diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 5007b08b..1e4db4ed 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -42,6 +42,8 @@ class PRTModifierAction; +using RuleAttributeMap = std::map; + class PRTModifierEnum { friend class PRTModifierAction; @@ -95,7 +97,7 @@ class PRTModifierAction : public polyModifierFty { std::wstring mStartRule; const std::wstring mRuleStyle = L"Default"; // Serlio atm only supports the "Default" style int32_t mRandomSeed = 0; - RuleAttributes mRuleAttributes; // TODO: could be cached together with ResolveMap + RuleAttributeMap mRuleAttributes; // TODO: could be cached together with ResolveMap ResolveMapSPtr getResolveMap(); @@ -105,7 +107,8 @@ class PRTModifierAction : public polyModifierFty { std::list mEnums; MStatus updateDynamicEnums(); // std::map mBriefName2prtAttr; - MStatus createNodeAttributes(const MObject& node, const prt::RuleFileInfo* info); + MStatus createNodeAttributes(const RuleAttributeSet& ruleAttributes, const MObject& node, + const prt::RuleFileInfo* info); void removeUnusedAttribs(MFnDependencyNode& node); static MStatus addParameter(MFnDependencyNode& node, MObject& attr, MFnAttribute& tAttr); diff --git a/src/serlio/modifiers/RuleAttributes.cpp b/src/serlio/modifiers/RuleAttributes.cpp index 4733f2d7..130c16c9 100644 --- a/src/serlio/modifiers/RuleAttributes.cpp +++ b/src/serlio/modifiers/RuleAttributes.cpp @@ -83,8 +83,27 @@ std::map getImportOrderMap(const prt::RuleFileInfo* ruleFileIn return importOrderMap; } -RuleAttributes getRuleAttributes(const std::wstring& ruleFile, const prt::RuleFileInfo* ruleFileInfo) { - RuleAttributes ra; + +void setGlobalGroupOrder(RuleAttributeVec& ruleAttributes) { + AttributeGroupOrder globalGroupOrder; + for (const auto& attribute : ruleAttributes) { + for (auto it = std::rbegin(attribute.groups); it != std::rend(attribute.groups); ++it) { + std::vector g(it, std::rend(attribute.groups)); + std::reverse(g.begin(), g.end()); + auto ggoIt = globalGroupOrder.emplace(std::make_pair(attribute.ruleFile, g), ORDER_NONE).first; + ggoIt->second = std::min(attribute.groupOrder, ggoIt->second); + } + } + + for (auto& attribute : ruleAttributes) { + const auto it = globalGroupOrder.find(std::make_pair(attribute.ruleFile, attribute.groups)); + attribute.globalGroupOrder = (it != globalGroupOrder.end()) ? it->second : ORDER_NONE; + } +} + + +RuleAttributeSet getRuleAttributes(const std::wstring& ruleFile, const prt::RuleFileInfo* ruleFileInfo) { + RuleAttributeVec ra; std::wstring mainCgaRuleName = std::filesystem::path(ruleFile).stem().wstring(); @@ -155,23 +174,13 @@ RuleAttributes getRuleAttributes(const std::wstring& ruleFile, const prt::RuleFi LOG_DBG << p; } - return ra; -} + setGlobalGroupOrder(ra); + RuleAttributeSet sortedRuleAttributes(ra.begin(), ra.end()); -AttributeGroupOrder getGlobalGroupOrder(const RuleAttributes& ruleAttributes) { - AttributeGroupOrder globalGroupOrder; - for (const auto& attribute : ruleAttributes) { - for (auto it = std::rbegin(attribute.groups); it != std::rend(attribute.groups); ++it) { - std::vector g(it, std::rend(attribute.groups)); - std::reverse(g.begin(), g.end()); - auto ggoIt = globalGroupOrder.emplace(std::make_pair(attribute.ruleFile,g), ORDER_NONE).first; - ggoIt->second = std::min(attribute.groupOrder, ggoIt->second); - } - } - return globalGroupOrder; + return sortedRuleAttributes; } -void sortRuleAttributes(RuleAttributes& ra) { +bool RuleAttributeCmp::operator()(const RuleAttribute& lhs, const RuleAttribute& rhs) const { auto lowerCaseOrdering = [](std::wstring a, std::wstring b) { std::transform(a.begin(), a.end(), a.begin(), ::tolower); std::transform(b.begin(), b.end(), b.begin(), ::tolower); @@ -217,15 +226,6 @@ void sortRuleAttributes(RuleAttributes& ra) { return a.groups[i]; }; - const AttributeGroupOrder globalGroupOrder = getGlobalGroupOrder(ra); - if (DBG) - LOG_DBG << "globalGroupOrder:\n" << globalGroupOrder; - - auto getGroupOrder = [&globalGroupOrder](const RuleAttribute& ap) { - const auto it = globalGroupOrder.find(std::make_pair(ap.ruleFile,ap.groups)); - return (it != globalGroupOrder.end()) ? it->second : ORDER_NONE; - }; - auto compareGroups = [&](const RuleAttribute& a, const RuleAttribute& b) { if (isChildOf(a, b)) return false; // child a should be sorted after parent b @@ -233,8 +233,8 @@ void sortRuleAttributes(RuleAttributes& ra) { if (isChildOf(b, a)) return true; // child b should be sorted after parent a - const auto globalOrderA = getGroupOrder(a); - const auto globalOrderB = getGroupOrder(b); + const auto globalOrderA = a.globalGroupOrder; + const auto globalOrderB = b.globalGroupOrder; if (globalOrderA != globalOrderB) return (globalOrderA < globalOrderB); @@ -262,7 +262,7 @@ void sortRuleAttributes(RuleAttributes& ra) { return compareAttributeOrder(a, b); }; - std::sort(ra.begin(), ra.end(), attributeOrder); + return attributeOrder(lhs, rhs); } std::wostream& operator<<(std::wostream& ostr, const RuleAttribute& ap) { diff --git a/src/serlio/modifiers/RuleAttributes.h b/src/serlio/modifiers/RuleAttributes.h index b41f6878..3c7a7dbf 100644 --- a/src/serlio/modifiers/RuleAttributes.h +++ b/src/serlio/modifiers/RuleAttributes.h @@ -27,6 +27,7 @@ #include #include #include +#include namespace prt { class RuleFileInfo; @@ -59,18 +60,23 @@ struct RuleAttribute { AttributeGroup groups; // groups can be nested int order = ORDER_NONE; int groupOrder = ORDER_NONE; + int globalGroupOrder = ORDER_NONE; std::wstring ruleFile; int ruleOrder = ORDER_NONE; bool memberOfStartRuleFile = false; }; -using RuleAttributes = std::vector; +struct RuleAttributeCmp { + bool operator()(const RuleAttribute& lhs, const RuleAttribute& rhs) const; +}; + +using RuleAttributeVec = std::vector; +using RuleAttributeSet = std::set; -SRL_TEST_EXPORTS_API RuleAttributes getRuleAttributes(const std::wstring& ruleFile, +SRL_TEST_EXPORTS_API RuleAttributeSet getRuleAttributes(const std::wstring& ruleFile, const prt::RuleFileInfo* ruleFileInfo); -AttributeGroupOrder getGlobalGroupOrder(const RuleAttributes& ruleAttributes); -void sortRuleAttributes(RuleAttributes& ra); +void setGlobalGroupOrder(RuleAttributeVec& ruleAttributes); std::wostream& operator<<(std::wostream& ostr, const RuleAttribute& ap); std::ostream& operator<<(std::ostream& ostr, const RuleAttribute& ap); std::wostream& operator<<(std::wostream& wostr, const AttributeGroupOrder& ago); \ No newline at end of file From bed295f999cfde3565bced469e5e001f48a9fa91 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 28 Jan 2022 10:35:48 +0100 Subject: [PATCH 279/668] Cleanup: use find to access element in map --- src/serlio/modifiers/PRTModifierAction.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 37ecf090..1499596f 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -179,9 +179,10 @@ MStatus iterateThroughAttributesAndApply(const MObject& node, const RuleAttribut MFnAttribute fnAttr(attrObj); const MString fullAttrName = fnAttr.name(); - RuleAttribute ruleAttr = ruleAttributes.at(fullAttrName.asWChar()); + auto ruleAttrIt = ruleAttributes.find(fullAttrName.asWChar()); + assert(ruleAttrIt != ruleAttributes.end()); // Rule not found + RuleAttribute ruleAttr = (ruleAttrIt != ruleAttributes.end()) ? ruleAttrIt->second : RuleAttribute(); - assert(!ruleAttr.fqName.empty()); // poor mans check for RULE_NOT_FOUND [[maybe_unused]] const auto ruleAttrType = ruleAttr.mType; @@ -598,7 +599,9 @@ MStatus PRTModifierAction::updateDynamicEnums() { continue; const MString fullAttrName = e.mAttr.name(); - const RuleAttribute ruleAttr = mRuleAttributes[fullAttrName.asWChar()]; + const auto ruleAttrIt = mRuleAttributes.find(fullAttrName.asWChar()); + assert(ruleAttrIt != ruleAttributes.end()); // Rule not found + RuleAttribute ruleAttr = (ruleAttrIt != mRuleAttributes.end()) ? ruleAttrIt->second : RuleAttribute(); const std::wstring attrStyle = prtu::getStyle(ruleAttr.fqName).c_str(); std::wstring attrImport = prtu::getImport(ruleAttr.fqName).c_str(); From 111ada241faf0124afd32c199af78276435de44e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 28 Jan 2022 11:06:23 +0100 Subject: [PATCH 280/668] Cleanup: add helper function for removing suffixes from string --- src/serlio/modifiers/PRTModifierAction.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 1499596f..f84c04ce 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -131,6 +131,15 @@ bool getAndResetForceDefault(const MFnDependencyNode& node, const MFnAttribute& return false; } +std::wstring removeSuffix(std::wstring const& fullString) { + for (const std::wstring suffix : {ATTRIBUTE_USER_SET_SUFFIX, ATTRIBUTE_FORCE_DEFAULT_SUFFIX}) { + if ((fullString.length() >= suffix.length()) && + (fullString.compare(fullString.length() - suffix.length(), suffix.length(), suffix) == 0)) + return fullString.substr(0, fullString.length() - suffix.length()); + } + return fullString; +} + enum class PrtAttributeType { BOOL, FLOAT, COLOR, STRING, ENUM }; std::list getNodeAttributesCorrespondingToCGA(const MFnDependencyNode& node) { @@ -950,11 +959,8 @@ MStatus PRTModifierAction::createNodeAttributes(const RuleAttributeSet& ruleAttr void PRTModifierAction::removeUnusedAttribs(MFnDependencyNode& node) { auto isInUse = [this](const MString& attrName) { - auto it = std::find_if(mRuleAttributes.begin(), mRuleAttributes.end(), [&attrName](const auto& attrPair) { - return (attrPair.second.mayaFullName == attrName.asWChar() || - attrPair.second.mayaFullName + ATTRIBUTE_USER_SET_SUFFIX == attrName.asWChar() || - attrPair.second.mayaFullName + ATTRIBUTE_FORCE_DEFAULT_SUFFIX == attrName.asWChar()); - }); + std::wstring attrNameWithoutSuffix = removeSuffix(attrName.asWChar()); + auto it = mRuleAttributes.find(attrNameWithoutSuffix); return (it != mRuleAttributes.end()); }; From d30c4012b027e667d67b6b7a04c209f0be7695bb Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 28 Jan 2022 11:11:45 +0100 Subject: [PATCH 281/668] Cleanup: use const where possible --- src/serlio/modifiers/PRTModifierAction.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index f84c04ce..0fc2a339 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -302,14 +302,14 @@ short getDefaultEnumIdx(const prt::Annotation* annot, const PRTEnumDefaultValue& switch (annot->getArgument(arg)->getType()) { case prt::AAT_BOOL: { - bool val = annot->getArgument(arg)->getBool(); + const bool val = annot->getArgument(arg)->getBool(); if (val == std::get(defaultValue)) return idx; idx++; break; } case prt::AAT_FLOAT: { - double val = annot->getArgument(arg)->getFloat(); + const double val = annot->getArgument(arg)->getFloat(); if (val == std::get(defaultValue)) return idx; idx++; @@ -620,7 +620,7 @@ MStatus PRTModifierAction::updateDynamicEnums() { const std::wstring prefix = attrStyle + prtu::STYLE_DELIMITER + attrImport; const wchar_t* valuesAttr = (MString(prefix.c_str()) + e.mValuesAttr).asWChar(); - prt::Attributable::PrimitiveType type = mGenerateAttrs->getType(valuesAttr); + const prt::Attributable::PrimitiveType type = mGenerateAttrs->getType(valuesAttr); switch (type) { case prt::Attributable::PT_STRING_ARRAY: { @@ -959,7 +959,7 @@ MStatus PRTModifierAction::createNodeAttributes(const RuleAttributeSet& ruleAttr void PRTModifierAction::removeUnusedAttribs(MFnDependencyNode& node) { auto isInUse = [this](const MString& attrName) { - std::wstring attrNameWithoutSuffix = removeSuffix(attrName.asWChar()); + const std::wstring attrNameWithoutSuffix = removeSuffix(attrName.asWChar()); auto it = mRuleAttributes.find(attrNameWithoutSuffix); return (it != mRuleAttributes.end()); }; @@ -1015,12 +1015,12 @@ MStatus PRTModifierEnum::fill(const prt::Annotation* annot) { switch (annot->getArgument(arg)->getType()) { case prt::AAT_BOOL: { - bool val = annot->getArgument(arg)->getBool(); + const bool val = annot->getArgument(arg)->getBool(); MCHECK(mAttr.addField(MString(std::to_wstring(val).c_str()), enumIndex++)); break; } case prt::AAT_FLOAT: { - double val = annot->getArgument(arg)->getFloat(); + const double val = annot->getArgument(arg)->getFloat(); MCHECK(mAttr.addField(MString(std::to_wstring(val).c_str()), enumIndex++)); break; } From 42e65546e5f04eb35f2617d0a4fbb5e8f6eedec0 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 28 Jan 2022 13:35:06 +0100 Subject: [PATCH 282/668] Make sure windows formatted newlines are removed aswell Switched to regex_remove for readability and ease of use --- src/serlio/modifiers/PRTModifierAction.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 0fc2a339..0889a4b0 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -37,6 +37,7 @@ #include #include +#include namespace { @@ -628,12 +629,12 @@ MStatus PRTModifierAction::updateDynamicEnums() { const wchar_t* const* stringArray = mGenerateAttrs->getStringArray(valuesAttr, &arr_length); for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { - std::wstring currString = stringArray[enumIndex]; + const std::wstring currString = stringArray[enumIndex]; // remove newlines from strings, because they break the maya UI - currString.erase(std::remove(currString.begin(), currString.end(), '\n'), currString.end()); + const std::wstring cleanedString = std::regex_replace(currString, std::wregex(L"\r|\n"), L""); - const MString mCurrString(currString.c_str()); + const MString mCurrString(cleanedString.c_str()); MCHECK(e.mAttr.addField(mCurrString, enumIndex)); } break; From e0756e7116f74efa136168f2062cd7fda6a55375 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 28 Jan 2022 14:29:30 +0100 Subject: [PATCH 283/668] Cleanup: return {} instead of L"" --- src/codec/encoder/MayaEncoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index bbe3191a..80e54de1 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -118,7 +118,7 @@ std::basic_string callAPI(FUNC f, OBJ& obj, ARGS&&... args) { std::wstring getTexturePath(const prtx::TexturePtr& texture, IMayaCallbacks* callbacks, prt::Cache* cache) { if (!texture || !texture->isValid()) - return L""; + return {}; const prtx::URIPtr& uri = texture->getURI(); const std::wstring& uriStr = uri->wstring(); From a883c81bbac6c122f1d33c1e80f8decbba483a1c Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 28 Jan 2022 14:31:42 +0100 Subject: [PATCH 284/668] Cleanup: use empty instead of checking size --- src/codec/encoder/MayaEncoder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index 80e54de1..6d38d473 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -313,7 +313,7 @@ void convertMaterialToAttributeMap(prtx::PRTUtils::AttributeMapBuilderPtr& aBuil case prtx::Material::PT_TEXTURE: { const auto& t = prtxAttr.getTexture(key); const std::wstring p = getTexturePath(t, cb, cache); - if (p.length() > 0) { + if (!p.empty()) { aBuilder->setString(key.c_str(), p.c_str()); } break; @@ -331,7 +331,7 @@ void convertMaterialToAttributeMap(prtx::PRTUtils::AttributeMapBuilderPtr& aBuil texPaths.push_back(texPath); } - if (texPaths.size() > 0) { + if (!texPaths.empty()) { std::vector pTexPaths = toPtrVec(texPaths); aBuilder->setStringArray(key.c_str(), pTexPaths.data(), pTexPaths.size()); } From f0b85378391481d9f5b66f2cf2205c451ef17a87 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 28 Jan 2022 14:34:18 +0100 Subject: [PATCH 285/668] Cleanup: make variable const --- src/codec/encoder/MayaEncoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index 6d38d473..c1c716c0 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -332,7 +332,7 @@ void convertMaterialToAttributeMap(prtx::PRTUtils::AttributeMapBuilderPtr& aBuil } if (!texPaths.empty()) { - std::vector pTexPaths = toPtrVec(texPaths); + const std::vector pTexPaths = toPtrVec(texPaths); aBuilder->setStringArray(key.c_str(), pTexPaths.data(), pTexPaths.size()); } From 37702f4e34fc252c8e47ec6501e89e43c32bd47d Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 28 Jan 2022 14:44:21 +0100 Subject: [PATCH 286/668] Cleanup: check compiler version instead of os --- src/serlio/modifiers/MayaCallbacks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 8bf35dd7..1a1e2e59 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -460,7 +460,7 @@ void MayaCallbacks::addAsset(const wchar_t* uri, const wchar_t* fileName, const resultSize = pathStr.size() + 1; // ask for space for null-terminator return; } -#ifdef _WIN32 +#if _MSC_VER >= 1400 wcsncpy_s(result, resultSize, pathStr.c_str(), resultSize); #else wcsncpy(result, pathStr.c_str(), resultSize); From 49deeb037fa148185c24035f6b1c9ac035cfc03e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 28 Jan 2022 15:09:51 +0100 Subject: [PATCH 287/668] Cleanup: add helper function to copy string into wchar pointer location --- src/serlio/modifiers/MayaCallbacks.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 1a1e2e59..a7d844fc 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -380,6 +380,17 @@ void updateMayaMesh(double const* const* uvs, size_t const* uvsSizes, uint32_t c outputMesh.setMetadata(newMetadata); } +void copyStringToWCharPtr(const std::wstring input, wchar_t* result, size_t& resultSize) { + if (resultSize <= input.size()) // also check for null-terminator + resultSize = input.size() + 1; // ask for space for null-terminator +#if _MSC_VER >= 1400 + wcsncpy_s(result, resultSize, input.c_str(), resultSize); +#else + wcsncpy(result, pathStr.c_str(), resultSize); +#endif + result[resultSize - 1] = 0x0; + resultSize = input.length() + 1; +} } // namespace void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, const double* nrm, size_t nrmSize, @@ -456,17 +467,7 @@ void MayaCallbacks::addAsset(const wchar_t* uri, const wchar_t* fileName, const const std::wstring pathStr = assetPath.generic_wstring(); - if (resultSize <= pathStr.size()) { // also check for null-terminator - resultSize = pathStr.size() + 1; // ask for space for null-terminator - return; - } -#if _MSC_VER >= 1400 - wcsncpy_s(result, resultSize, pathStr.c_str(), resultSize); -#else - wcsncpy(result, pathStr.c_str(), resultSize); -#endif - result[resultSize - 1] = 0x0; - resultSize = pathStr.length() + 1; + copyStringToWCharPtr(pathStr, result, resultSize); } // PRT version >= 2.3 From ca04db0a6107098bff7715357581b673c5f9e265 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 28 Jan 2022 15:10:17 +0100 Subject: [PATCH 288/668] Cleanup: run clang-format --- src/serlio/modifiers/MayaCallbacks.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index a7d844fc..70a3c561 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -22,11 +22,11 @@ #include "materials/MaterialInfo.h" +#include "PRTContext.h" +#include "utils/AssetCache.h" #include "utils/LogHandler.h" #include "utils/MayaUtilities.h" #include "utils/Utilities.h" -#include "utils/AssetCache.h" -#include "PRTContext.h" #include "prt/StringUtils.h" @@ -141,8 +141,9 @@ void assignTextureCoordinates(MFnMesh& fnMesh, double const* const* uvs, size_t } } -void assignVertexNormals(MFnMesh& mFnMesh, const MIntArray& mayaFaceCounts, MIntArray& mayaVertexIndices, const double* nrm, - size_t nrmSize, const uint32_t* normalIndices, MAYBE_UNUSED size_t normalIndicesSize) { +void assignVertexNormals(MFnMesh& mFnMesh, const MIntArray& mayaFaceCounts, MIntArray& mayaVertexIndices, + const double* nrm, size_t nrmSize, const uint32_t* normalIndices, + MAYBE_UNUSED size_t normalIndicesSize) { if (nrmSize == 0) return; @@ -358,7 +359,8 @@ void updateMayaMesh(double const* const* uvs, size_t const* uvsSizes, uint32_t c size_t const* uvCountsSizes, uint32_t const* const* uvIndices, size_t const* uvIndicesSizes, size_t uvSetsCount, const double* nrm, size_t nrmSize, const uint32_t* normalIndices, size_t normalIndicesSize, const MFloatPointArray& mayaVertices, const MIntArray& mayaFaceCounts, - MIntArray& mayaVertexIndices, const MObject& outMeshObj, const adsk::Data::Associations& newMetadata) { + MIntArray& mayaVertexIndices, const MObject& outMeshObj, + const adsk::Data::Associations& newMetadata) { MStatus stat; MFnMeshData dataCreator; From a55e0f1f5ed4aabec5a2336745d45945173fc255 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 28 Jan 2022 15:17:58 +0100 Subject: [PATCH 289/668] Cleanup: remove redundant fileWasDeletedByUser variable --- src/serlio/utils/AssetCache.cpp | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/serlio/utils/AssetCache.cpp b/src/serlio/utils/AssetCache.cpp index fd6d331a..5466505e 100644 --- a/src/serlio/utils/AssetCache.cpp +++ b/src/serlio/utils/AssetCache.cpp @@ -45,11 +45,6 @@ bool writeCacheEntry(const std::filesystem::path& assetPath, const uint8_t* buff return true; } -void removeCacheEntry(const std::filesystem::path& expiredAssetPath) { - if (std::error_code removeError; !std::filesystem::remove(expiredAssetPath, removeError)) - LOG_WRN << "Failed to delete expired asset cache entry " << expiredAssetPath << ": " << removeError.message(); -} - } // namespace std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileName, const uint8_t* buffer, size_t size) { @@ -63,13 +58,9 @@ std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileNam const std::filesystem::path newAssetPath = getCachedPath(fileName, hash); - bool fileWasDeletedByUser = false; // reuse cached asset if uri and hash match if ((it != mCache.end()) && (it->second.second == hash)) { - if (!std::filesystem::exists(newAssetPath)) { - fileWasDeletedByUser = true; - } - else { + if (std::filesystem::exists(newAssetPath)) { const std::filesystem::path& assetPath = it->second.first; return assetPath; } @@ -85,15 +76,10 @@ std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileNam return {}; } - if (it == mCache.end() || fileWasDeletedByUser) { + if (it == mCache.end()) { mCache.emplace(uri, std::make_pair(newAssetPath, hash)); } - else if (fileWasDeletedByUser) { - it->second = std::make_pair(newAssetPath, hash); - } else { - // handle hash mismatch - removeCacheEntry(it->second.first); it->second = std::make_pair(newAssetPath, hash); } From dbdf64645dcf8e58677b10c2f214b4c0d8d3e09e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 28 Jan 2022 15:48:01 +0100 Subject: [PATCH 290/668] Cleanup: only call make_pair once. --- src/serlio/utils/AssetCache.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/serlio/utils/AssetCache.cpp b/src/serlio/utils/AssetCache.cpp index 5466505e..789feb95 100644 --- a/src/serlio/utils/AssetCache.cpp +++ b/src/serlio/utils/AssetCache.cpp @@ -76,11 +76,12 @@ std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileNam return {}; } + const std::pair newVal = std::make_pair(newAssetPath, hash); if (it == mCache.end()) { - mCache.emplace(uri, std::make_pair(newAssetPath, hash)); + mCache.emplace(uri, newVal); } else { - it->second = std::make_pair(newAssetPath, hash); + it->second = newVal; } return newAssetPath; From 219b80ae69d6040d8338e3a2ca11980000cdb31f Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 28 Jan 2022 16:52:30 +0100 Subject: [PATCH 291/668] Cleanup: only keep string as value in AssetCache the hash is already embedded in the assetpath --- src/serlio/utils/AssetCache.cpp | 14 +++++++------- src/serlio/utils/AssetCache.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/serlio/utils/AssetCache.cpp b/src/serlio/utils/AssetCache.cpp index 789feb95..902defe6 100644 --- a/src/serlio/utils/AssetCache.cpp +++ b/src/serlio/utils/AssetCache.cpp @@ -56,16 +56,17 @@ std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileNam const auto it = std::find_if(mCache.begin(), mCache.end(), [&uri](const auto& p) { return (std::wcscmp(p.first.c_str(), uri) == 0); }); - const std::filesystem::path newAssetPath = getCachedPath(fileName, hash); // reuse cached asset if uri and hash match - if ((it != mCache.end()) && (it->second.second == hash)) { - if (std::filesystem::exists(newAssetPath)) { - const std::filesystem::path& assetPath = it->second.first; + if (it != mCache.end()) { + const std::filesystem::path& assetPath = it->second; + if (std::filesystem::exists(assetPath)) { return assetPath; } } + const std::filesystem::path newAssetPath = getCachedPath(fileName, hash); + if (newAssetPath.empty()) { LOG_ERR << "Invalid URI, cannot cache the asset: " << uri; return {}; @@ -76,12 +77,11 @@ std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileNam return {}; } - const std::pair newVal = std::make_pair(newAssetPath, hash); if (it == mCache.end()) { - mCache.emplace(uri, newVal); + mCache.emplace(key, newAssetPath); } else { - it->second = newVal; + it->second = newAssetPath; } return newAssetPath; diff --git a/src/serlio/utils/AssetCache.h b/src/serlio/utils/AssetCache.h index d76fd950..1ecd27c5 100644 --- a/src/serlio/utils/AssetCache.h +++ b/src/serlio/utils/AssetCache.h @@ -30,5 +30,5 @@ class AssetCache { private: std::filesystem::path getCachedPath(const wchar_t* fileName, const size_t hash) const; - std::unordered_map> mCache; + std::unordered_map mCache; }; From b22a938605579fc5cd84b7d4fe011da422c96633 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 28 Jan 2022 17:30:55 +0100 Subject: [PATCH 292/668] Fix declaration of 'PRTContext' warnings in compiler --- src/serlio/materials/StingrayMaterialNode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/materials/StingrayMaterialNode.h b/src/serlio/materials/StingrayMaterialNode.h index 3266ff6a..cf6d6b9e 100644 --- a/src/serlio/materials/StingrayMaterialNode.h +++ b/src/serlio/materials/StingrayMaterialNode.h @@ -26,7 +26,7 @@ #include class MaterialColor; -struct PRTContext; +class PRTContext; class MaterialInfo; class MaterialTrafo; class MELScriptBuilder; From a1a97c7611f58102d2a5ccc5873f9317be4385d6 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 28 Jan 2022 17:46:13 +0100 Subject: [PATCH 293/668] Add hash to AssetCache key --- src/serlio/utils/AssetCache.cpp | 6 +++--- src/serlio/utils/AssetCache.h | 4 +++- src/serlio/utils/Utilities.h | 15 +++++++++++++++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/serlio/utils/AssetCache.cpp b/src/serlio/utils/AssetCache.cpp index 902defe6..6ed6b26f 100644 --- a/src/serlio/utils/AssetCache.cpp +++ b/src/serlio/utils/AssetCache.cpp @@ -49,13 +49,13 @@ bool writeCacheEntry(const std::filesystem::path& assetPath, const uint8_t* buff std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileName, const uint8_t* buffer, size_t size) { assert(uri != nullptr); + std::wstring stringUri(uri); const std::string_view bufferView(reinterpret_cast(buffer), size); const size_t hash = std::hash{}(bufferView); + const auto key = std::make_pair(stringUri, hash); - const auto it = std::find_if(mCache.begin(), mCache.end(), - [&uri](const auto& p) { return (std::wcscmp(p.first.c_str(), uri) == 0); }); - + const auto it = mCache.find(key); // reuse cached asset if uri and hash match if (it != mCache.end()) { diff --git a/src/serlio/utils/AssetCache.h b/src/serlio/utils/AssetCache.h index 1ecd27c5..f4be17fb 100644 --- a/src/serlio/utils/AssetCache.h +++ b/src/serlio/utils/AssetCache.h @@ -19,6 +19,8 @@ #pragma once +#include "utils/Utilities.h" + #include #include #include @@ -30,5 +32,5 @@ class AssetCache { private: std::filesystem::path getCachedPath(const wchar_t* fileName, const size_t hash) const; - std::unordered_map mCache; + std::unordered_map, std::filesystem::path, prtu::pair_hash> mCache; }; diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index a039c5c5..ac283616 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -96,6 +96,21 @@ std::vector toPtrVec(const std::vector>& sv) { return pv; } +template +void hash_combine(SizeT& seed, SizeT value) { + seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2); +} + +struct pair_hash { + template + std::size_t operator()(const std::pair& v) const { + std::size_t seed = 0; + hash_combine(seed, std::hash{}(v.first)); + hash_combine(seed, std::hash{}(v.second)); + return seed; + } +}; + time_t getFileModificationTime(const std::wstring& p); int fromHex(wchar_t c); From 8e0037bace17ead507e5f21cc23c778457664289 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 28 Jan 2022 17:51:54 +0100 Subject: [PATCH 294/668] Cleanup: add inline keyword --- src/serlio/utils/MArrayWrapper.h | 3 +-- src/serlio/utils/Utilities.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/serlio/utils/MArrayWrapper.h b/src/serlio/utils/MArrayWrapper.h index ccfcfc96..f5872cf7 100644 --- a/src/serlio/utils/MArrayWrapper.h +++ b/src/serlio/utils/MArrayWrapper.h @@ -168,9 +168,8 @@ template struct isIterable().begin(), std::declval().end(), bool())>> : public bool_constant {}; -// add inline specifier to isIterableV once we switch to C++17 template -constexpr bool isIterableV = isIterable::value; +inline constexpr bool isIterableV = isIterable::value; } // namespace detail diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index ac283616..0d41fd95 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -97,7 +97,7 @@ std::vector toPtrVec(const std::vector>& sv) { } template -void hash_combine(SizeT& seed, SizeT value) { +inline void hash_combine(SizeT& seed, SizeT value) { seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2); } From def270211ee7acfe3eb106954d3a0d91c48ae569 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 28 Jan 2022 18:00:18 +0100 Subject: [PATCH 295/668] Cleanup: add warning if we can't get texture from MemoryOutputCallbacks --- src/codec/encoder/MayaEncoder.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index c1c716c0..9b87909a 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -159,6 +159,9 @@ std::wstring getTexturePath(const prtx::TexturePtr& texture, IMayaCallbacks* cal else srl_log_warn("Received invalid asset path while trying to write asset with URI: %1%") % uriStr; } + else { + srl_log_warn("Failed to get texture at %1%, texture will be missing") % uriStr; + } } catch (std::exception& e) { srl_log_warn("Failed to encode or write texture at %1% to the local filesystem: %2%") % uriStr % e.what(); From 27f340526e5cc163dc12ee66ee76aa53e7da76dc Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 31 Jan 2022 13:17:07 +0100 Subject: [PATCH 296/668] Cleanup: fix typo --- src/serlio/modifiers/MayaCallbacks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 70a3c561..d43085ac 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -388,7 +388,7 @@ void copyStringToWCharPtr(const std::wstring input, wchar_t* result, size_t& res #if _MSC_VER >= 1400 wcsncpy_s(result, resultSize, input.c_str(), resultSize); #else - wcsncpy(result, pathStr.c_str(), resultSize); + wcsncpy(result, input.c_str(), resultSize); #endif result[resultSize - 1] = 0x0; resultSize = input.length() + 1; From b910a498bd11e85b3343089fa49c602f62e4e028 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 31 Jan 2022 13:19:46 +0100 Subject: [PATCH 297/668] Add early return back before copying string Otherwise we might try to write in a non-allocated memory location --- src/serlio/modifiers/MayaCallbacks.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index d43085ac..1e136012 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -383,8 +383,6 @@ void updateMayaMesh(double const* const* uvs, size_t const* uvsSizes, uint32_t c } void copyStringToWCharPtr(const std::wstring input, wchar_t* result, size_t& resultSize) { - if (resultSize <= input.size()) // also check for null-terminator - resultSize = input.size() + 1; // ask for space for null-terminator #if _MSC_VER >= 1400 wcsncpy_s(result, resultSize, input.c_str(), resultSize); #else @@ -469,6 +467,11 @@ void MayaCallbacks::addAsset(const wchar_t* uri, const wchar_t* fileName, const const std::wstring pathStr = assetPath.generic_wstring(); + if (resultSize <= pathStr.size()) { // also check for null-terminator + resultSize = pathStr.size() + 1; // ask for space for null-terminator + return; + } + copyStringToWCharPtr(pathStr, result, resultSize); } From ffdd2e4562393e1f9098ff675dda7fb8a4fe766b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 31 Jan 2022 13:22:32 +0100 Subject: [PATCH 298/668] Cleanup: add link to original boost implementation of hash_combine --- src/serlio/utils/Utilities.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index 0d41fd95..0d99d998 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -96,6 +96,7 @@ std::vector toPtrVec(const std::vector>& sv) { return pv; } +//hash_combine function from boost library: https://www.boost.org/doc/libs/1_73_0/boost/container_hash/hash.hpp template inline void hash_combine(SizeT& seed, SizeT value) { seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2); From 9d32f5569505c5f658db0936666212ae247e9d5b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 31 Jan 2022 13:30:32 +0100 Subject: [PATCH 299/668] Cleanup: move make_preferred into getWorkspaceRoot --- src/serlio/utils/MayaUtilities.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/utils/MayaUtilities.cpp b/src/serlio/utils/MayaUtilities.cpp index 082f205b..16ae235f 100644 --- a/src/serlio/utils/MayaUtilities.cpp +++ b/src/serlio/utils/MayaUtilities.cpp @@ -37,6 +37,6 @@ std::filesystem::path getWorkspaceRoot(MStatus& status) { std::wstring output; status = scriptBuilder.executeSync(output); - return output; + return std::filesystem::path(output).make_preferred(); } } // namespace mu \ No newline at end of file From ac1559edc3122155e0c2260544302728fca06b5b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 31 Jan 2022 13:33:55 +0100 Subject: [PATCH 300/668] Cleanup: move getWorkspaceRoot function out of asset cache This should allow the tests to work properly again. --- src/serlio/modifiers/MayaCallbacks.cpp | 8 ++++++-- src/serlio/utils/AssetCache.cpp | 13 +++++-------- src/serlio/utils/AssetCache.h | 6 ++++-- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 1e136012..a6acccc6 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -458,8 +458,12 @@ void MayaCallbacks::addAsset(const wchar_t* uri, const wchar_t* fileName, const resultSize = 0; return; } - - const std::filesystem::path& assetPath = PRTContext::get().mAssetCache.put(uri, fileName, buffer, size); + + MStatus status; + const std::filesystem::path workspaceRoot = mu::getWorkspaceRoot(status); + MCHECK(status); + const std::filesystem::path& assetPath = + PRTContext::get().mAssetCache.put(uri, fileName, workspaceRoot, buffer, size); if (assetPath.empty()) { resultSize = 0; return; diff --git a/src/serlio/utils/AssetCache.cpp b/src/serlio/utils/AssetCache.cpp index 6ed6b26f..e7f96597 100644 --- a/src/serlio/utils/AssetCache.cpp +++ b/src/serlio/utils/AssetCache.cpp @@ -20,7 +20,6 @@ #include "AssetCache.h" #include "utils/LogHandler.h" -#include "utils/MayaUtilities.h" #include #include @@ -47,7 +46,7 @@ bool writeCacheEntry(const std::filesystem::path& assetPath, const uint8_t* buff } // namespace -std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileName, const uint8_t* buffer, size_t size) { +std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileName, const std::filesystem::path workspaceRoot, const uint8_t* buffer, size_t size) { assert(uri != nullptr); std::wstring stringUri(uri); @@ -65,7 +64,7 @@ std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileNam } } - const std::filesystem::path newAssetPath = getCachedPath(fileName, hash); + const std::filesystem::path newAssetPath = getCachedPath(fileName, workspaceRoot, hash); if (newAssetPath.empty()) { LOG_ERR << "Invalid URI, cannot cache the asset: " << uri; @@ -87,12 +86,10 @@ std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileNam return newAssetPath; } -std::filesystem::path AssetCache::getCachedPath(const wchar_t* fileName, const size_t hash) const { +std::filesystem::path AssetCache::getCachedPath(const wchar_t* fileName, const std::filesystem::path workspaceRoot, + const size_t hash) const { // we start with the root folder in the current workspace - MStatus status; - std::filesystem::path workspaceDir = mu::getWorkspaceRoot(status); - MCHECK(status); - std::filesystem::path assetsDir = workspaceDir.make_preferred() / MAYA_ASSET_FOLDER / SERLIO_ASSET_FOLDER; + std::filesystem::path assetsDir = workspaceRoot / MAYA_ASSET_FOLDER / SERLIO_ASSET_FOLDER; // create dir if it does not exist try { diff --git a/src/serlio/utils/AssetCache.h b/src/serlio/utils/AssetCache.h index f4be17fb..a4fc83a7 100644 --- a/src/serlio/utils/AssetCache.h +++ b/src/serlio/utils/AssetCache.h @@ -27,10 +27,12 @@ class AssetCache { public: - std::filesystem::path put(const wchar_t* uri, const wchar_t* fileName, const uint8_t* buffer, size_t size); + std::filesystem::path put(const wchar_t* uri, const wchar_t* fileName, const std::filesystem::path workspaceRoot, + const uint8_t* buffer, size_t size); private: - std::filesystem::path getCachedPath(const wchar_t* fileName, const size_t hash) const; + std::filesystem::path getCachedPath(const wchar_t* fileName, const std::filesystem::path workspaceRoot, + const size_t hash) const; std::unordered_map, std::filesystem::path, prtu::pair_hash> mCache; }; From 60331259df66112f8a64974b1fc19796cbf02f80 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 31 Jan 2022 13:39:40 +0100 Subject: [PATCH 301/668] Avoid copying nullptr wchar_t* into wstring --- src/serlio/modifiers/PRTModifierAction.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 0889a4b0..9563f208 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -629,6 +629,8 @@ MStatus PRTModifierAction::updateDynamicEnums() { const wchar_t* const* stringArray = mGenerateAttrs->getStringArray(valuesAttr, &arr_length); for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { + if (stringArray[enumIndex] == nullptr) + continue; const std::wstring currString = stringArray[enumIndex]; // remove newlines from strings, because they break the maya UI From a3beb7b681b244235fbc199fdb235ca14d3517fd Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 31 Jan 2022 14:28:55 +0100 Subject: [PATCH 302/668] Cleanup: don't use auto for simple const reference during foreach --- src/serlio/modifiers/PRTModifierAction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 9563f208..97e63c28 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -796,7 +796,7 @@ MStatus PRTModifierAction::createNodeAttributes(const RuleAttributeSet& ruleAttr MFnDependencyNode node(nodeObj, &stat); MCHECK(stat); - for (auto const& p : ruleAttributes) { + for (const RuleAttribute& p : ruleAttributes) { const std::wstring fqName = p.fqName; // only use attributes of current style From d4564d4ca531ff703b2316327eb7d8f5ea7e7d9d Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 31 Jan 2022 15:01:28 +0100 Subject: [PATCH 303/668] Cleanup: avoid using regex for removing newlines from string Now only the substring to the next "\r" or "\n" is used. While not ideal this behaviour is in line with how CE handles newlines in dynamic enums. If there are no newlines, find_last_of will return string::npos and return the entire string. --- src/serlio/modifiers/PRTModifierAction.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 97e63c28..fa53fd15 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -37,7 +37,6 @@ #include #include -#include namespace { @@ -634,9 +633,8 @@ MStatus PRTModifierAction::updateDynamicEnums() { const std::wstring currString = stringArray[enumIndex]; // remove newlines from strings, because they break the maya UI - const std::wstring cleanedString = std::regex_replace(currString, std::wregex(L"\r|\n"), L""); - - const MString mCurrString(cleanedString.c_str()); + const size_t cutoffIndex = currString.find_last_of(L"\r\n"); + const MString mCurrString(currString.substr(0, cutoffIndex).c_str()); MCHECK(e.mAttr.addField(mCurrString, enumIndex)); } break; From 965a32e262106855da64037495dcce767a8457f4 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 31 Jan 2022 15:23:52 +0100 Subject: [PATCH 304/668] Cleanup: make ruleAttr const --- src/serlio/modifiers/PRTModifierAction.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index fa53fd15..3c956131 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -190,7 +190,7 @@ MStatus iterateThroughAttributesAndApply(const MObject& node, const RuleAttribut const MString fullAttrName = fnAttr.name(); auto ruleAttrIt = ruleAttributes.find(fullAttrName.asWChar()); assert(ruleAttrIt != ruleAttributes.end()); // Rule not found - RuleAttribute ruleAttr = (ruleAttrIt != ruleAttributes.end()) ? ruleAttrIt->second : RuleAttribute(); + const RuleAttribute ruleAttr = (ruleAttrIt != ruleAttributes.end()) ? ruleAttrIt->second : RuleAttribute(); [[maybe_unused]] const auto ruleAttrType = ruleAttr.mType; @@ -610,7 +610,7 @@ MStatus PRTModifierAction::updateDynamicEnums() { const MString fullAttrName = e.mAttr.name(); const auto ruleAttrIt = mRuleAttributes.find(fullAttrName.asWChar()); assert(ruleAttrIt != ruleAttributes.end()); // Rule not found - RuleAttribute ruleAttr = (ruleAttrIt != mRuleAttributes.end()) ? ruleAttrIt->second : RuleAttribute(); + const RuleAttribute ruleAttr = (ruleAttrIt != mRuleAttributes.end()) ? ruleAttrIt->second : RuleAttribute(); const std::wstring attrStyle = prtu::getStyle(ruleAttr.fqName).c_str(); std::wstring attrImport = prtu::getImport(ruleAttr.fqName).c_str(); From b1122db0bd39c9175c3cb30bc63c93f40be7e551 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 31 Jan 2022 15:48:41 +0100 Subject: [PATCH 305/668] Copy string value into wstring instead of using a wchar_t pointer to the MString --- src/serlio/modifiers/PRTModifierAction.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 3c956131..b4379a64 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -619,13 +619,13 @@ MStatus PRTModifierAction::updateDynamicEnums() { const std::wstring prefix = attrStyle + prtu::STYLE_DELIMITER + attrImport; - const wchar_t* valuesAttr = (MString(prefix.c_str()) + e.mValuesAttr).asWChar(); - const prt::Attributable::PrimitiveType type = mGenerateAttrs->getType(valuesAttr); + const std::wstring valuesAttr = (MString(prefix.c_str()) + e.mValuesAttr).asWChar(); + const prt::Attributable::PrimitiveType type = mGenerateAttrs->getType(valuesAttr.c_str()); switch (type) { case prt::Attributable::PT_STRING_ARRAY: { size_t arr_length = 0; - const wchar_t* const* stringArray = mGenerateAttrs->getStringArray(valuesAttr, &arr_length); + const wchar_t* const* stringArray = mGenerateAttrs->getStringArray(valuesAttr.c_str(), &arr_length); for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { if (stringArray[enumIndex] == nullptr) @@ -641,7 +641,7 @@ MStatus PRTModifierAction::updateDynamicEnums() { } case prt::Attributable::PT_FLOAT_ARRAY: { size_t arr_length = 0; - const double* doubleArray = mGenerateAttrs->getFloatArray(valuesAttr, &arr_length); + const double* doubleArray = mGenerateAttrs->getFloatArray(valuesAttr.c_str(), &arr_length); for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { const double currDouble = doubleArray[enumIndex]; @@ -653,7 +653,7 @@ MStatus PRTModifierAction::updateDynamicEnums() { } case prt::Attributable::PT_BOOL_ARRAY: { size_t arr_length = 0; - const bool* boolArray = mGenerateAttrs->getBoolArray(valuesAttr, &arr_length); + const bool* boolArray = mGenerateAttrs->getBoolArray(valuesAttr.c_str(), &arr_length); for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { const bool currBool = boolArray[enumIndex]; @@ -664,20 +664,20 @@ MStatus PRTModifierAction::updateDynamicEnums() { break; } case prt::Attributable::PT_STRING: { - const MString mCurrString = mGenerateAttrs->getString(valuesAttr); + const MString mCurrString = mGenerateAttrs->getString(valuesAttr.c_str()); MCHECK(e.mAttr.addField(mCurrString, 0)); break; } case prt::Attributable::PT_FLOAT: { - const bool currFloat = mGenerateAttrs->getFloat(valuesAttr); + const bool currFloat = mGenerateAttrs->getFloat(valuesAttr.c_str()); const MString mCurrString(std::to_wstring(currFloat).c_str()); MCHECK(e.mAttr.addField(mCurrString, 0)); break; } case prt::Attributable::PT_BOOL: { - const bool currBool = mGenerateAttrs->getBool(valuesAttr); + const bool currBool = mGenerateAttrs->getBool(valuesAttr.c_str()); const MString mCurrString(std::to_wstring(currBool).c_str()); MCHECK(e.mAttr.addField(mCurrString, 0)); From f0497b601b967adcb3b0946b7070f5e209a407c9 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 31 Jan 2022 18:36:19 +0100 Subject: [PATCH 306/668] Update << function for RuleAttributes --- src/serlio/modifiers/RuleAttributes.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serlio/modifiers/RuleAttributes.cpp b/src/serlio/modifiers/RuleAttributes.cpp index 130c16c9..d2d504ee 100644 --- a/src/serlio/modifiers/RuleAttributes.cpp +++ b/src/serlio/modifiers/RuleAttributes.cpp @@ -268,8 +268,8 @@ bool RuleAttributeCmp::operator()(const RuleAttribute& lhs, const RuleAttribute& std::wostream& operator<<(std::wostream& ostr, const RuleAttribute& ap) { auto orderVal = [](int order) { return (order == ORDER_NONE) ? L"none" : std::to_wstring(order); }; ostr << L"RuleAttribute '" << ap.fqName << L"':" << L" order = " << orderVal(ap.order) << L", groupOrder = " - << orderVal(ap.groupOrder) << L", ruleFile = '" << ap.ruleFile << L"'" << L", groups = [ " - << join(ap.groups, L" ") << L" ]\n"; + << orderVal(ap.groupOrder) << L", globalGroupOrder = " << orderVal(ap.globalGroupOrder) << L", ruleFile = '" + << ap.ruleFile << L"'" << L", groups = [ " << join(ap.groups, L" ") << L" ]\n"; return ostr; } From 5eae1ef71a9e365a487ec25b4df8888d1b84b656 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 31 Jan 2022 18:36:37 +0100 Subject: [PATCH 307/668] Cleanup: run clang-format --- src/serlio/modifiers/RuleAttributes.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/serlio/modifiers/RuleAttributes.cpp b/src/serlio/modifiers/RuleAttributes.cpp index d2d504ee..e1bca3ea 100644 --- a/src/serlio/modifiers/RuleAttributes.cpp +++ b/src/serlio/modifiers/RuleAttributes.cpp @@ -26,10 +26,10 @@ #include #include +#include #include #include #include -#include namespace { @@ -58,7 +58,7 @@ std::wstring getNiceName(const std::wstring& fqAttrName) { } // namespace -std::map getImportOrderMap(const prt::RuleFileInfo* ruleFileInfo) { +std::map getImportOrderMap(const prt::RuleFileInfo* ruleFileInfo) { std::map importOrderMap; int importOrder = 0; for (size_t i = 0; i < ruleFileInfo->getNumAnnotations(); i++) { @@ -69,7 +69,7 @@ std::map getImportOrderMap(const prt::RuleFileInfo* ruleFileIn const prt::AnnotationArgument* anArg = an->getArgument(argIdx); if (anArg->getType() == prt::AAT_STR) { const wchar_t* anKey = anArg->getKey(); - if(std::wcscmp(anKey, ANNOT_IMPORTS_KEY) == 0) { + if (std::wcscmp(anKey, ANNOT_IMPORTS_KEY) == 0) { const wchar_t* importRuleCharPtr = anArg->getStr(); if (importRuleCharPtr != nullptr) { std::wstring importRule = importRuleCharPtr; @@ -83,7 +83,6 @@ std::map getImportOrderMap(const prt::RuleFileInfo* ruleFileIn return importOrderMap; } - void setGlobalGroupOrder(RuleAttributeVec& ruleAttributes) { AttributeGroupOrder globalGroupOrder; for (const auto& attribute : ruleAttributes) { @@ -101,13 +100,12 @@ void setGlobalGroupOrder(RuleAttributeVec& ruleAttributes) { } } - RuleAttributeSet getRuleAttributes(const std::wstring& ruleFile, const prt::RuleFileInfo* ruleFileInfo) { RuleAttributeVec ra; std::wstring mainCgaRuleName = std::filesystem::path(ruleFile).stem().wstring(); - const std::map importOrderMap = getImportOrderMap(ruleFileInfo); + const std::map importOrderMap = getImportOrderMap(ruleFileInfo); for (size_t i = 0; i < ruleFileInfo->getNumAttributes(); i++) { const prt::RuleFileInfo::Entry* attr = ruleFileInfo->getAttribute(i); @@ -137,8 +135,8 @@ RuleAttributeSet getRuleAttributes(const std::wstring& ruleFile, const prt::Rule } const auto importOrder = importOrderMap.find(p.ruleFile); - p.ruleOrder = (importOrder != importOrderMap.end()) ? importOrder->second : ORDER_NONE; - + p.ruleOrder = (importOrder != importOrderMap.end()) ? importOrder->second : ORDER_NONE; + bool hidden = false; for (size_t a = 0; a < attr->getNumAnnotations(); a++) { const prt::Annotation* an = attr->getAnnotation(a); @@ -194,9 +192,9 @@ bool RuleAttributeCmp::operator()(const RuleAttribute& lhs, const RuleAttribute& if (b.memberOfStartRuleFile && !a.memberOfStartRuleFile) return false; - if(a.ruleOrder != b.ruleOrder) + if (a.ruleOrder != b.ruleOrder) return a.ruleOrder < b.ruleOrder; - + return lowerCaseOrdering(a.ruleFile, b.ruleFile); }; From bf0e99a4a03caa7658f32b9c1ab072be99b99bb5 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 31 Jan 2022 18:37:34 +0100 Subject: [PATCH 308/668] Update tests to work with new datastructures used for rule attributes --- src/test/tests.cpp | 95 ++++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 46 deletions(-) diff --git a/src/test/tests.cpp b/src/test/tests.cpp index 98f214da..2b1413e3 100644 --- a/src/test/tests.cpp +++ b/src/test/tests.cpp @@ -128,7 +128,7 @@ TEST_CASE("default attribute values") { std::wstring ruleFile = L"bin/r1.cgb"; RuleFileInfoUPtr ruleInfo(prt::createRuleFileInfo(resolveMap->getString(ruleFile.c_str()))); - RuleAttributes ruleAttrs = getRuleAttributes(ruleFile, ruleInfo.get()); + RuleAttributeSet ruleAttrs = getRuleAttributes(ruleFile, ruleInfo.get()); for (const auto& ap : ruleAttrs) { LOG_DBG << ap.fqName; @@ -145,7 +145,7 @@ const AttributeGroup AG_BK = {L"b", L"k"}; const AttributeGroup AG_BKP = {L"b", L"k", L"p"}; RuleAttribute getAttr(std::wstring fqName, AttributeGroup ag, int o, int go, std::wstring rf,int ro, bool sr) { - return RuleAttribute{fqName, L"", L"", L"", prt::AAT_UNKNOWN, ag, o, go, rf,ro, sr}; + return RuleAttribute{fqName, L"", L"", L"", prt::AAT_UNKNOWN, ag, o, go, go, rf,ro, sr}; } TEST_CASE("global group order") { @@ -155,14 +155,14 @@ TEST_CASE("global group order") { const RuleAttribute D = getAttr(L"style$D", AG_A, ORDER_NONE, 20, L"foo", ORDER_NONE, true); const RuleAttribute E = getAttr(L"style$E", AG_AK, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); - const RuleAttributes inp = {A, B, C, D, E}; - const AttributeGroupOrder ago = getGlobalGroupOrder(inp); - CHECK(ago.size() == 5); - CHECK(ago.at(std::make_pair( L"foo",AG_BKP)) == 10); - CHECK(ago.at(std::make_pair( L"foo",AG_BK)) == 10); - CHECK(ago.at(std::make_pair( L"foo",AG_B)) == 10); - CHECK(ago.at(std::make_pair( L"foo",AG_AK)) == ORDER_NONE); - CHECK(ago.at(std::make_pair( L"foo",AG_A)) == 20); + RuleAttributeVec inp = {A, B, C, D, E}; + setGlobalGroupOrder(inp); + CHECK(inp.size() == 5); + CHECK(inp[0].globalGroupOrder == 10); + CHECK(inp[1].globalGroupOrder == 10); + CHECK(inp[2].globalGroupOrder == 10); + CHECK(inp[3].globalGroupOrder == 20); + CHECK(inp[4].globalGroupOrder == ORDER_NONE); } TEST_CASE("rule attribute sorting") { @@ -171,50 +171,50 @@ TEST_CASE("rule attribute sorting") { const RuleAttribute A = getAttr(L"style$A", AG_NONE, ORDER_NONE, ORDER_NONE, L"bar", ORDER_NONE, true); const RuleAttribute B = getAttr(L"style$B", AG_NONE, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, false); - RuleAttributes inp = {B, A}; - const RuleAttributes exp = {A, B}; - sortRuleAttributes(inp); - CHECK(inp == exp); + const RuleAttributeSet inp = {B, A}; + const RuleAttributeVec inpAsVec(inp.begin(), inp.end()); + const RuleAttributeVec exp = {A, B}; + CHECK(inpAsVec == exp); } SECTION("rule file 2") { const RuleAttribute A = getAttr(L"style$A", AG_NONE, ORDER_NONE, ORDER_NONE, L"bar", ORDER_NONE, false); const RuleAttribute B = getAttr(L"style$B", AG_NONE, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); - RuleAttributes inp = {B, A}; - const RuleAttributes exp = {B, A}; - sortRuleAttributes(inp); - CHECK(inp == exp); + const RuleAttributeSet inp = {B, A}; + const RuleAttributeVec inpAsVec(inp.begin(), inp.end()); + const RuleAttributeVec exp = {B, A}; + CHECK(inpAsVec == exp); } SECTION("group order") { const RuleAttribute A = getAttr(L"style$A", {L"foo"}, ORDER_NONE, 0, L"foo", ORDER_NONE, true); const RuleAttribute B = getAttr(L"style$B", {L"foo"}, ORDER_NONE, 1, L"foo", ORDER_NONE, true); - RuleAttributes inp = {B, A}; - const RuleAttributes exp = {A, B}; - sortRuleAttributes(inp); - CHECK(inp == exp); + const RuleAttributeSet inp = {B, A}; + const RuleAttributeVec inpAsVec(inp.begin(), inp.end()); + const RuleAttributeVec exp = {A, B}; + CHECK(inpAsVec == exp); } SECTION("nested groups") { const RuleAttribute A = getAttr(L"style$A", {L"foo", L"bar"}, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); const RuleAttribute B = getAttr(L"style$B", {L"foo"}, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); - RuleAttributes inp = {A, B}; - const RuleAttributes exp = {B, A}; - sortRuleAttributes(inp); - CHECK(inp == exp); + const RuleAttributeSet inp = {A, B}; + const RuleAttributeVec inpAsVec(inp.begin(), inp.end()); + const RuleAttributeVec exp = {B, A}; + CHECK(inpAsVec == exp); } SECTION("nested groups disjunct") { const RuleAttribute A = getAttr(L"style$A", {L"foo1", L"bar"}, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); const RuleAttribute B = getAttr(L"style$B", {L"foo"}, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); - RuleAttributes inp = {A, B}; - const RuleAttributes exp = {B, A}; - sortRuleAttributes(inp); - CHECK(inp == exp); + const RuleAttributeSet inp = {A, B}; + const RuleAttributeVec inpAsVec(inp.begin(), inp.end()); + const RuleAttributeVec exp = {B, A}; + CHECK(inpAsVec == exp); } SECTION("nested groups on same level") { @@ -222,10 +222,10 @@ TEST_CASE("rule attribute sorting") { const RuleAttribute B = getAttr(L"style$B", {L"foo"}, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); const RuleAttribute C = getAttr(L"style$C", {L"foo", L"baz"}, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); - RuleAttributes inp = {C, A, B}; - const RuleAttributes exp = {B, A, C}; - sortRuleAttributes(inp); - CHECK(inp == exp); + const RuleAttributeSet inp = {C, A, B}; + const RuleAttributeVec inpAsVec(inp.begin(), inp.end()); + const RuleAttributeVec exp = {B, A, C}; + CHECK(inpAsVec == exp); } SECTION("nested groups with group order") { @@ -233,10 +233,13 @@ TEST_CASE("rule attribute sorting") { const RuleAttribute B = getAttr(L"style$B", {L"foo"}, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); const RuleAttribute C = getAttr(L"style$C", {L"foo", L"baz"}, ORDER_NONE, 0, L"foo", ORDER_NONE, true); - RuleAttributes inp = {C, A, B}; - const RuleAttributes exp = {B, C, A}; - sortRuleAttributes(inp); - CHECK(inp == exp); + const RuleAttributeSet inp = {C, A, B}; + const RuleAttributeVec inpAsVec(inp.begin(), inp.end()); + const RuleAttributeVec exp = {B, C, A}; + for (const auto& ref: inpAsVec){ + std::wcout << ref; + } + CHECK(inpAsVec == exp); } SECTION("all properties") { @@ -246,10 +249,10 @@ TEST_CASE("rule attribute sorting") { const RuleAttribute D = getAttr(L"style$D", {L"First", L"Second"}, 1, 2, L"foo", ORDER_NONE, true); const RuleAttribute E = getAttr(L"style$E", {L"First", L"Second", L"Third"}, ORDER_NONE, 1, L"foo", ORDER_NONE, true); - RuleAttributes inp = {B, A, C, D, E}; - const RuleAttributes exp = {A, B, C, D, E}; - sortRuleAttributes(inp); - CHECK(inp == exp); + const RuleAttributeSet inp = {B, A, C, D, E}; + const RuleAttributeVec inpAsVec(inp.begin(), inp.end()); + const RuleAttributeVec exp = {A, B, C, D, E}; + CHECK(inpAsVec == exp); } SECTION("review example") { @@ -261,10 +264,10 @@ TEST_CASE("rule attribute sorting") { const RuleAttribute D = getAttr(L"style$D", AG_A, ORDER_NONE, 20, L"foo", ORDER_NONE, true); const RuleAttribute E = getAttr(L"style$E", AG_AK, ORDER_NONE, ORDER_NONE, L"foo", ORDER_NONE, true); - RuleAttributes inp = {A, B, C, D, E}; - const RuleAttributes exp = {A, B, C, D, E}; - sortRuleAttributes(inp); - CHECK(inp == exp); + const RuleAttributeSet inp = {A, B, C, D, E}; + const RuleAttributeVec inpAsVec(inp.begin(), inp.end()); + const RuleAttributeVec exp = {A, B, C, D, E}; + CHECK(inpAsVec == exp); } } From d2a7d1034fec2be1077bb118008c8b114201a20f Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 1 Feb 2022 10:06:00 +0100 Subject: [PATCH 309/668] Cleanup: remove unnecessary MString construction --- src/serlio/modifiers/PRTModifierAction.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index b4379a64..d34a3a3a 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -617,9 +617,9 @@ MStatus PRTModifierAction::updateDynamicEnums() { if (!attrImport.empty()) attrImport += prtu::IMPORT_DELIMITER; - const std::wstring prefix = attrStyle + prtu::STYLE_DELIMITER + attrImport; + std::wstring valuesAttr = attrStyle + prtu::STYLE_DELIMITER + attrImport; + valuesAttr.append(e.mValuesAttr.asWChar()); - const std::wstring valuesAttr = (MString(prefix.c_str()) + e.mValuesAttr).asWChar(); const prt::Attributable::PrimitiveType type = mGenerateAttrs->getType(valuesAttr.c_str()); switch (type) { From 10250697d718e932c63b4f77bb5ac60afe6c9385 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 1 Feb 2022 10:08:14 +0100 Subject: [PATCH 310/668] Cleanup: use string_view to avoid unnecessary string copies In case we have to use a substring, we can't just pass the string_view to the MString constructor, since it expects a null-terminated string. --- src/serlio/modifiers/PRTModifierAction.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index d34a3a3a..f2fdcc3c 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -630,11 +630,20 @@ MStatus PRTModifierAction::updateDynamicEnums() { for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { if (stringArray[enumIndex] == nullptr) continue; - const std::wstring currString = stringArray[enumIndex]; + const std::wstring_view currStringView = stringArray[enumIndex]; // remove newlines from strings, because they break the maya UI - const size_t cutoffIndex = currString.find_last_of(L"\r\n"); - const MString mCurrString(currString.substr(0, cutoffIndex).c_str()); + const size_t cutoffIndex = currStringView.find_last_of(L"\r\n"); + + MString mCurrString; + if (cutoffIndex == std::wstring_view::npos) { + mCurrString.setWChar(stringArray[enumIndex]); + } + else { + const std::wstring currStringWithoutNewlines(currStringView.substr(0, cutoffIndex)); + mCurrString.setWChar(currStringWithoutNewlines.c_str()); + } + MCHECK(e.mAttr.addField(mCurrString, enumIndex)); } break; From 38ad3e8a71ce75d120dcea39bb3a37a7f2eeb528 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 1 Feb 2022 10:46:42 +0100 Subject: [PATCH 311/668] Cleanup: construnct MString directly from string_view --- src/serlio/modifiers/PRTModifierAction.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index f2fdcc3c..905992ea 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -630,20 +630,13 @@ MStatus PRTModifierAction::updateDynamicEnums() { for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { if (stringArray[enumIndex] == nullptr) continue; - const std::wstring_view currStringView = stringArray[enumIndex]; + std::wstring_view currStringView = stringArray[enumIndex]; // remove newlines from strings, because they break the maya UI const size_t cutoffIndex = currStringView.find_last_of(L"\r\n"); + currStringView = currStringView.substr(0, cutoffIndex); - MString mCurrString; - if (cutoffIndex == std::wstring_view::npos) { - mCurrString.setWChar(stringArray[enumIndex]); - } - else { - const std::wstring currStringWithoutNewlines(currStringView.substr(0, cutoffIndex)); - mCurrString.setWChar(currStringWithoutNewlines.c_str()); - } - + const MString mCurrString(currStringView.data(), currStringView.length()); MCHECK(e.mAttr.addField(mCurrString, enumIndex)); } break; From c40dc4e80bd2386b82e8cd2398dad0430b3f204c Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 1 Feb 2022 10:47:32 +0100 Subject: [PATCH 312/668] Cleanup: clang-format --- src/serlio/modifiers/PRTModifierAction.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 905992ea..a4701dad 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -135,7 +135,7 @@ std::wstring removeSuffix(std::wstring const& fullString) { for (const std::wstring suffix : {ATTRIBUTE_USER_SET_SUFFIX, ATTRIBUTE_FORCE_DEFAULT_SUFFIX}) { if ((fullString.length() >= suffix.length()) && (fullString.compare(fullString.length() - suffix.length(), suffix.length(), suffix) == 0)) - return fullString.substr(0, fullString.length() - suffix.length()); + return fullString.substr(0, fullString.length() - suffix.length()); } return fullString; } @@ -192,7 +192,6 @@ MStatus iterateThroughAttributesAndApply(const MObject& node, const RuleAttribut assert(ruleAttrIt != ruleAttributes.end()); // Rule not found const RuleAttribute ruleAttr = (ruleAttrIt != ruleAttributes.end()) ? ruleAttrIt->second : RuleAttribute(); - [[maybe_unused]] const auto ruleAttrType = ruleAttr.mType; if (attrObj.hasFn(MFn::kNumericAttribute)) { @@ -749,9 +748,9 @@ MStatus PRTModifierAction::updateRuleFiles(const MObject& node, const MString& r // derive necessary data from PRT rule info to populate node with dynamic rule attributes RuleAttributeSet ruleAttributes = getRuleAttributes(mRuleFile, info.get()); for (const RuleAttribute& ruleAttr : ruleAttributes) { - mRuleAttributes[ruleAttr.mayaFullName] = ruleAttr; + mRuleAttributes[ruleAttr.mayaFullName] = ruleAttr; } - + createNodeAttributes(ruleAttributes, node, info.get()); updateDynamicEnums(); } @@ -791,7 +790,8 @@ MStatus PRTModifierAction::doIt() { return status; } -MStatus PRTModifierAction::createNodeAttributes(const RuleAttributeSet& ruleAttributes, const MObject& nodeObj, const prt::RuleFileInfo* info) { +MStatus PRTModifierAction::createNodeAttributes(const RuleAttributeSet& ruleAttributes, const MObject& nodeObj, + const prt::RuleFileInfo* info) { MStatus stat; MFnDependencyNode node(nodeObj, &stat); MCHECK(stat); From 38ac4fc440c151ad58e9d23861ff7f9c91c94718 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 1 Feb 2022 11:25:40 +0100 Subject: [PATCH 313/668] Cleanup: return empty path if scriptbuilder fails --- src/serlio/utils/MayaUtilities.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/serlio/utils/MayaUtilities.cpp b/src/serlio/utils/MayaUtilities.cpp index 16ae235f..1a245756 100644 --- a/src/serlio/utils/MayaUtilities.cpp +++ b/src/serlio/utils/MayaUtilities.cpp @@ -37,6 +37,11 @@ std::filesystem::path getWorkspaceRoot(MStatus& status) { std::wstring output; status = scriptBuilder.executeSync(output); - return std::filesystem::path(output).make_preferred(); + if (status == MS::kSuccess) { + return std::filesystem::path(output).make_preferred(); + } + else { + return std::filesystem::path(); + } } } // namespace mu \ No newline at end of file From 3b2c64b88e33701e52b0da7a56940da8a9a72fdb Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 1 Feb 2022 11:27:08 +0100 Subject: [PATCH 314/668] Cleanup: move all references to workspace and assets folder from AssetCache to MayaCallbacks --- src/serlio/modifiers/MayaCallbacks.cpp | 32 ++++++++++++++++++++++---- src/serlio/utils/AssetCache.cpp | 24 +++++-------------- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index a6acccc6..29e9778d 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -45,6 +45,8 @@ namespace { constexpr bool DBG = false; +constexpr const wchar_t* MAYA_ASSET_FOLDER = L"assets"; +constexpr const wchar_t* SERLIO_ASSET_FOLDER = L"serlio_assets"; void checkStringLength(const wchar_t* string, const size_t& maxStringLength) { if (wcslen(string) >= maxStringLength) { @@ -391,6 +393,25 @@ void copyStringToWCharPtr(const std::wstring input, wchar_t* result, size_t& res result[resultSize - 1] = 0x0; resultSize = input.length() + 1; } + +std::filesystem::path getAssetDir() { + MStatus status; + const std::filesystem::path workspaceRoot = mu::getWorkspaceRoot(status); + + if (status != MS::kSuccess) + return std::filesystem::path(); + + std::filesystem::path assetDir = workspaceRoot / MAYA_ASSET_FOLDER / SERLIO_ASSET_FOLDER; + // create dir if it does not exist + try { + std::filesystem::create_directories(assetDir); + } + catch (std::exception& e) { + LOG_ERR << "Error while creating the asset cache directory at " << assetDir << ": " << e.what(); + return std::filesystem::path(); + } + return assetDir; +} } // namespace void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, const double* nrm, size_t nrmSize, @@ -458,12 +479,13 @@ void MayaCallbacks::addAsset(const wchar_t* uri, const wchar_t* fileName, const resultSize = 0; return; } - - MStatus status; - const std::filesystem::path workspaceRoot = mu::getWorkspaceRoot(status); - MCHECK(status); + + std::filesystem::path assetDir = getAssetDir(); + const std::filesystem::path& assetPath = - PRTContext::get().mAssetCache.put(uri, fileName, workspaceRoot, buffer, size); + (!assetDir.empty()) ? PRTContext::get().mAssetCache.put(uri, fileName, assetDir, buffer, size) + : std::filesystem::path(); + if (assetPath.empty()) { resultSize = 0; return; diff --git a/src/serlio/utils/AssetCache.cpp b/src/serlio/utils/AssetCache.cpp index e7f96597..0a68708b 100644 --- a/src/serlio/utils/AssetCache.cpp +++ b/src/serlio/utils/AssetCache.cpp @@ -28,8 +28,6 @@ #include namespace { -constexpr const wchar_t* MAYA_ASSET_FOLDER = L"assets"; -constexpr const wchar_t* SERLIO_ASSET_FOLDER = L"serlio_assets"; bool writeCacheEntry(const std::filesystem::path& assetPath, const uint8_t* buffer, size_t size) noexcept { if (std::filesystem::exists(assetPath)) @@ -46,7 +44,8 @@ bool writeCacheEntry(const std::filesystem::path& assetPath, const uint8_t* buff } // namespace -std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileName, const std::filesystem::path workspaceRoot, const uint8_t* buffer, size_t size) { +std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileName, + const std::filesystem::path cacheRootDir, const uint8_t* buffer, size_t size) { assert(uri != nullptr); std::wstring stringUri(uri); @@ -64,7 +63,7 @@ std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileNam } } - const std::filesystem::path newAssetPath = getCachedPath(fileName, workspaceRoot, hash); + const std::filesystem::path newAssetPath = getCachedPath(fileName, cacheRootDir, hash); if (newAssetPath.empty()) { LOG_ERR << "Invalid URI, cannot cache the asset: " << uri; @@ -86,20 +85,9 @@ std::filesystem::path AssetCache::put(const wchar_t* uri, const wchar_t* fileNam return newAssetPath; } -std::filesystem::path AssetCache::getCachedPath(const wchar_t* fileName, const std::filesystem::path workspaceRoot, +std::filesystem::path AssetCache::getCachedPath(const wchar_t* fileName, const std::filesystem::path cacheRootDir, const size_t hash) const { - // we start with the root folder in the current workspace - std::filesystem::path assetsDir = workspaceRoot / MAYA_ASSET_FOLDER / SERLIO_ASSET_FOLDER; - - // create dir if it does not exist - try { - std::filesystem::create_directories(assetsDir); - } - catch (std::exception& e) { - LOG_ERR << "Error while creating the asset cache directory at " << assetsDir << ": " << e.what(); - } - - // we then get the filename constructed by the encoder from the URI + // we get the filename constructed by the encoder from the URI assert(fileName != nullptr); std::filesystem::path assetFile(fileName); std::filesystem::path cachedAssetName = assetFile.stem(); @@ -111,6 +99,6 @@ std::filesystem::path AssetCache::getCachedPath(const wchar_t* fileName, const s std::filesystem::path extension = assetFile.extension(); cachedAssetName += extension; - const std::filesystem::path cachedAssetPath = assetsDir / cachedAssetName; + const std::filesystem::path cachedAssetPath = cacheRootDir / cachedAssetName; return cachedAssetPath; } From b704c99c5552811ad86c2f64df9947c7ef8eae8b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 1 Feb 2022 14:11:04 +0100 Subject: [PATCH 315/668] Cleanup: fix typo: use find_first_of --- src/serlio/modifiers/PRTModifierAction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index a4701dad..8816a562 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -632,7 +632,7 @@ MStatus PRTModifierAction::updateDynamicEnums() { std::wstring_view currStringView = stringArray[enumIndex]; // remove newlines from strings, because they break the maya UI - const size_t cutoffIndex = currStringView.find_last_of(L"\r\n"); + const size_t cutoffIndex = currStringView.find_first_of(L"\r\n"); currStringView = currStringView.substr(0, cutoffIndex); const MString mCurrString(currStringView.data(), currStringView.length()); From 94a4beede4452ee07b541ba973fae753d5e4c222 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 1 Feb 2022 15:07:15 +0100 Subject: [PATCH 316/668] Cleanup: return empty initializer list instead of empty path constructor --- src/serlio/modifiers/MayaCallbacks.cpp | 4 ++-- src/serlio/utils/MayaUtilities.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 29e9778d..24719ad1 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -399,7 +399,7 @@ std::filesystem::path getAssetDir() { const std::filesystem::path workspaceRoot = mu::getWorkspaceRoot(status); if (status != MS::kSuccess) - return std::filesystem::path(); + return {}; std::filesystem::path assetDir = workspaceRoot / MAYA_ASSET_FOLDER / SERLIO_ASSET_FOLDER; // create dir if it does not exist @@ -408,7 +408,7 @@ std::filesystem::path getAssetDir() { } catch (std::exception& e) { LOG_ERR << "Error while creating the asset cache directory at " << assetDir << ": " << e.what(); - return std::filesystem::path(); + return {}; } return assetDir; } diff --git a/src/serlio/utils/MayaUtilities.cpp b/src/serlio/utils/MayaUtilities.cpp index 1a245756..cbecdcc5 100644 --- a/src/serlio/utils/MayaUtilities.cpp +++ b/src/serlio/utils/MayaUtilities.cpp @@ -41,7 +41,7 @@ std::filesystem::path getWorkspaceRoot(MStatus& status) { return std::filesystem::path(output).make_preferred(); } else { - return std::filesystem::path(); + return {}; } } } // namespace mu \ No newline at end of file From 1c40d4803e088f1968da88c974e76c7469402855 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 2 Feb 2022 13:37:34 +0100 Subject: [PATCH 317/668] Add Help menu --- src/serlio/scripts/serlioCreateUI.mel | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index b0c4409b..638a0a67 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -140,6 +140,16 @@ global proc createMaterialNode(string $materialType) { select -r $select; } +global proc createHelpMenuItems(){ + string $serlioWebsite = "https://esri.github.io/cityengine/serlio"; + string $cgaReference = "https://doc.arcgis.com/en/cityengine/latest/cga/cityengine-cga-introduction.htm"; + string $rulePackageManual = "https://doc.arcgis.com/en/cityengine/latest/help/help-rule-package.htm"; + + menuItem -label "Serlio Website" -c ("launch -web \"" + $serlioWebsite + "\""); + menuItem -label "CityEngine CGA Reference" -c ("launch -web \"" + $cgaReference + "\""); + menuItem -label "CityEngine Rule Package Manual" -c ("launch -web \"" + $rulePackageManual + "\""); +} + global proc createPrtMenu() { global string $gMainWindow; global string $gPrtMenu = "prtMenu"; @@ -154,6 +164,9 @@ global proc createPrtMenu() { menuItem -label "Remove CityEngine Rule Package" -c "removePrtNode" -annotation "Remove a CGA rule package from a geometry"; menuItem -label "Create Materials" -c "createMaterialNode(\"stingray\")" -annotation "Create Materials"; menuItem -label "Create Arnold Materials" -c "createMaterialNode(\"arnold\")" -annotation "Create Arnold Materials"; + menuItem -subMenu true -label "Help"; + createHelpMenuItems(); + setParent -menu ..; setParent -m ..; } } From 0d99e33247bdcd18b3668b024d23c96d201f550e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 2 Feb 2022 13:50:47 +0100 Subject: [PATCH 318/668] Add About page --- src/serlio/scripts/serlioCreateUI.mel | 56 +++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index 638a0a67..b85fba2e 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -150,6 +150,61 @@ global proc createHelpMenuItems(){ menuItem -label "CityEngine Rule Package Manual" -c ("launch -web \"" + $rulePackageManual + "\""); } +global proc createAboutDialog(){ + string $pluginPath = `pluginInfo -query -path serlio`; + string $iconPath = $pluginPath + "/../../doc/img/serlio_32.png"; + string $serlioVersion = `pluginInfo -query -version serlio`; + string $serlioVendor = `pluginInfo -query -vendor serlio`; + string $legalText = "Serlio is free for personal, educational, and non-commercial use. " + + "Commercial use requires at least one commercial license of the latest CityEngine version installed in the organization. " + + "Redistribution or web service offerings are not allowed unless expressly permitted."; + + string $window = `window -title "Serlio: About" + -resizeToFitChildren true + -iconName "Serlio" + -widthHeight 500 200`; + + string $form = `formLayout -numberOfDivisions 100`; + + string $serlioImage = `image -h 32 -image $iconPath`; + string $versionText = `text -label ("Version: " + $serlioVersion)`; + string $vendorText = `text -label ("Vendor: " + $serlioVendor)`; + string $legalField = `scrollField -h 85 -wordWrap true + -text $legalText + -editable false`; + + string $okButton = `button -label "OK" -command ("deleteUI -window " + $window)`; + + formLayout -edit + -attachForm $serlioImage "top" 5 + -attachForm $serlioImage "left" 5 + -attachNone $serlioImage "bottom" + -attachNone $serlioImage "right" + + -attachControl $versionText "top" 5 $serlioImage + -attachForm $versionText "left" 5 + -attachNone $versionText "bottom" + -attachNone $versionText "right" + + -attachControl $vendorText "top" 5 $versionText + -attachForm $vendorText "left" 5 + -attachNone $vendorText "bottom" + -attachNone $vendorText "right" + + -attachControl $legalField "top" 5 $vendorText + -attachForm $legalField "left" 5 + -attachControl $legalField "bottom" 5 $okButton + -attachForm $legalField "right" 5 + + -attachNone $okButton "top" + -attachForm $okButton "left" 5 + -attachForm $okButton "bottom" 5 + -attachForm $okButton "right" 5 + $form; + + showWindow $window; +} + global proc createPrtMenu() { global string $gMainWindow; global string $gPrtMenu = "prtMenu"; @@ -167,6 +222,7 @@ global proc createPrtMenu() { menuItem -subMenu true -label "Help"; createHelpMenuItems(); setParent -menu ..; + menuItem -label "About" -c "createAboutDialog()" -annotation "Show about dialog"; setParent -m ..; } } From c4a7f53581af685bc887bc3b69de64be951e238f Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Wed, 2 Feb 2022 14:49:55 +0100 Subject: [PATCH 319/668] On Linux, ensure that the serlio plugin is actually called "serlio", not "libserlio" --- src/serlio/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/serlio/CMakeLists.txt b/src/serlio/CMakeLists.txt index 69f413ad..437c6561 100644 --- a/src/serlio/CMakeLists.txt +++ b/src/serlio/CMakeLists.txt @@ -65,6 +65,9 @@ set_target_properties(${SERLIO_TARGET} PROPERTIES if (WIN32) # On Windows, Maya requires plugins to have the extension "mll" set_target_properties(${SERLIO_TARGET} PROPERTIES SUFFIX ".mll") +else() + # On linux, we need to remove the "lib" prefix otherwise the plugin will be named "libserlio" + set_target_properties(${SERLIO_TARGET} PROPERTIES PREFIX "") endif () From 7ce227556d6741abc4da4fa3000bb2b128e38f4b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 2 Feb 2022 16:05:43 +0100 Subject: [PATCH 320/668] Add missing legal text --- src/serlio/scripts/serlioCreateUI.mel | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index b85fba2e..46b2b118 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -157,7 +157,8 @@ global proc createAboutDialog(){ string $serlioVendor = `pluginInfo -query -vendor serlio`; string $legalText = "Serlio is free for personal, educational, and non-commercial use. " + "Commercial use requires at least one commercial license of the latest CityEngine version installed in the organization. " + - "Redistribution or web service offerings are not allowed unless expressly permitted."; + "Redistribution or web service offerings are not allowed unless expressly permitted.\n" + + "Serlio is under the same license as the included CityEngine SDK."; string $window = `window -title "Serlio: About" -resizeToFitChildren true From 58ec47540e5938defb45833898fee00c46bcdf5e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 2 Feb 2022 16:06:13 +0100 Subject: [PATCH 321/668] Add divider for between about/help and other serlio commands --- src/serlio/scripts/serlioCreateUI.mel | 1 + 1 file changed, 1 insertion(+) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index 46b2b118..03f59a56 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -220,6 +220,7 @@ global proc createPrtMenu() { menuItem -label "Remove CityEngine Rule Package" -c "removePrtNode" -annotation "Remove a CGA rule package from a geometry"; menuItem -label "Create Materials" -c "createMaterialNode(\"stingray\")" -annotation "Create Materials"; menuItem -label "Create Arnold Materials" -c "createMaterialNode(\"arnold\")" -annotation "Create Arnold Materials"; + menuItem -divider true; menuItem -subMenu true -label "Help"; createHelpMenuItems(); setParent -menu ..; From cb10e0ecb5505068394bb1c1169829d44232adef Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 2 Feb 2022 16:09:43 +0100 Subject: [PATCH 322/668] Add help icon --- src/serlio/scripts/serlioCreateUI.mel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index 03f59a56..54a8b6eb 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -221,7 +221,7 @@ global proc createPrtMenu() { menuItem -label "Create Materials" -c "createMaterialNode(\"stingray\")" -annotation "Create Materials"; menuItem -label "Create Arnold Materials" -c "createMaterialNode(\"arnold\")" -annotation "Create Arnold Materials"; menuItem -divider true; - menuItem -subMenu true -label "Help"; + menuItem -subMenu true -label "Help" -i "help.png"; createHelpMenuItems(); setParent -menu ..; menuItem -label "About" -c "createAboutDialog()" -annotation "Show about dialog"; From 65cabb03cab3bb82d308b32c085b36ecb0bbd39d Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 2 Feb 2022 16:49:09 +0100 Subject: [PATCH 323/668] Cleanup: rename "About" to "About Serlio" --- src/serlio/scripts/serlioCreateUI.mel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index 54a8b6eb..accec4e8 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -224,7 +224,7 @@ global proc createPrtMenu() { menuItem -subMenu true -label "Help" -i "help.png"; createHelpMenuItems(); setParent -menu ..; - menuItem -label "About" -c "createAboutDialog()" -annotation "Show about dialog"; + menuItem -label "About Serlio" -c "createAboutDialog()" -annotation "Show about dialog"; setParent -m ..; } } From e7971a82f9d75249aa577fbebc18106bba61ca35 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 2 Feb 2022 16:50:33 +0100 Subject: [PATCH 324/668] Replace icon with xpm file in icon folder.. instead of using the png in the docs. The path is also fixed and fix the linux bug, where the icon does not appear. --- src/serlio/scripts/serlioCreateUI.mel | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index accec4e8..5f465a4c 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -152,7 +152,7 @@ global proc createHelpMenuItems(){ global proc createAboutDialog(){ string $pluginPath = `pluginInfo -query -path serlio`; - string $iconPath = $pluginPath + "/../../doc/img/serlio_32.png"; + string $iconPath = $pluginPath + "../../icons/serlio.xpm"; string $serlioVersion = `pluginInfo -query -version serlio`; string $serlioVendor = `pluginInfo -query -vendor serlio`; string $legalText = "Serlio is free for personal, educational, and non-commercial use. " + @@ -167,7 +167,7 @@ global proc createAboutDialog(){ string $form = `formLayout -numberOfDivisions 100`; - string $serlioImage = `image -h 32 -image $iconPath`; + string $serlioImage = `iconTextStaticLabel -h 32 -w 28 -st "iconOnly" -i1 $iconPath`; string $versionText = `text -label ("Version: " + $serlioVersion)`; string $vendorText = `text -label ("Vendor: " + $serlioVendor)`; string $legalField = `scrollField -h 85 -wordWrap true From 4009ca374ed98d619c624759c87ad1385c09a0cc Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 2 Feb 2022 16:51:34 +0100 Subject: [PATCH 325/668] Cleanup: Use showHelp instead of launch -web This is more in line with how an about page should be handled. Also, this might remove the warning on linux, when opening the browser. --- src/serlio/scripts/serlioCreateUI.mel | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index 5f465a4c..affdff0d 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -145,9 +145,9 @@ global proc createHelpMenuItems(){ string $cgaReference = "https://doc.arcgis.com/en/cityengine/latest/cga/cityengine-cga-introduction.htm"; string $rulePackageManual = "https://doc.arcgis.com/en/cityengine/latest/help/help-rule-package.htm"; - menuItem -label "Serlio Website" -c ("launch -web \"" + $serlioWebsite + "\""); - menuItem -label "CityEngine CGA Reference" -c ("launch -web \"" + $cgaReference + "\""); - menuItem -label "CityEngine Rule Package Manual" -c ("launch -web \"" + $rulePackageManual + "\""); + menuItem -label "Serlio Website" -c ("showHelp -absolute \"" + $serlioWebsite + "\""); + menuItem -label "CityEngine CGA Reference" -c ("showHelp -absolute \"" + $cgaReference + "\""); + menuItem -label "CityEngine Rule Package Manual" -c ("showHelp -absolute \"" + $rulePackageManual + "\""); } global proc createAboutDialog(){ From b8d664696520b41c895f8e2d31d7363e06a0bd44 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 2 Feb 2022 16:54:18 +0100 Subject: [PATCH 326/668] Enlargen the the About page to fit of the legal text --- src/serlio/scripts/serlioCreateUI.mel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index affdff0d..a19829dc 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -163,7 +163,7 @@ global proc createAboutDialog(){ string $window = `window -title "Serlio: About" -resizeToFitChildren true -iconName "Serlio" - -widthHeight 500 200`; + -widthHeight 500 210`; string $form = `formLayout -numberOfDivisions 100`; From 5d15f72a5a936137ab439fa4985d02630359adfe Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 2 Feb 2022 17:55:35 +0100 Subject: [PATCH 327/668] Cleanup: generalize SetSymbolButtonVisibility --- src/serlio/scripts/AEserlioTemplate.mel | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 312a589e..9a4149a8 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -52,8 +52,8 @@ proc int prtAttributeExists(string $attr){ return`attributeExists $attributeName $node`; } -proc prtSetSymbolButtonVisibility(int $visible, string $control){ - symbolButton -edit -visible $visible $control; +proc prtSetVisibility(int $visible, string $control){ + control -edit -visible $visible $control; } proc prtSetBackgroundColor(int $enabled, float $grayScaleColor, string $control){ @@ -136,8 +136,8 @@ global proc prtUpdateEditHighlights(string $attr){ int $userSet = `getAttr ($attr + "_user_set")`; string $control = prtControlName($attr); - prtSetSymbolButtonVisibility($userSet, $control + "_user_set"); - prtSetSymbolButtonVisibility($userSet, $control + "_force_default_reset"); + prtSetVisibility($userSet, $control + "_user_set"); + prtSetVisibility($userSet, $control + "_force_default_reset"); prtSetBackgroundColor($userSet, 0.32, $control + "_column_layout"); prtSetBackgroundColor(false, 0.4, $control); } From 876b662b0f493655b49f60bd0a502015bb5ba308 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 2 Feb 2022 17:56:27 +0100 Subject: [PATCH 328/668] Cleanup: replace disabled symbolbutton in attribute editor with iconTextStaticLabel --- src/serlio/scripts/AEserlioTemplate.mel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 9a4149a8..e4d72e65 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -160,7 +160,7 @@ global proc prtAttributeControl(string $attr, string $varname){ $label = `niceName $attr`; - symbolButton -visible false -hlc 0.2715 0.2715 0.2715 -i "pencilCursor.png" -w 25 -h 25 -enable false ($control + "_user_set"); + iconTextStaticLabel -st "iconOnly" -visible false -hlc 0.2715 0.2715 0.2715 -i "pencilCursor.png" -w 25 -h 25 -enable false ($control + "_user_set"); if (prtIsColorAttr($attr)){ colorSliderGrp-label $label $control; From 3343b396001f73a0c1301539d63cb0515b1d75c4 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 2 Feb 2022 17:57:48 +0100 Subject: [PATCH 329/668] Cleanup: use -image flag instead of -image1 flag since we only use fixed icon for serlioImage --- src/serlio/scripts/serlioCreateUI.mel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index a19829dc..cff88405 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -167,7 +167,7 @@ global proc createAboutDialog(){ string $form = `formLayout -numberOfDivisions 100`; - string $serlioImage = `iconTextStaticLabel -h 32 -w 28 -st "iconOnly" -i1 $iconPath`; + string $serlioImage = `iconTextStaticLabel -h 32 -w 28 -st "iconOnly" -i $iconPath`; string $versionText = `text -label ("Version: " + $serlioVersion)`; string $vendorText = `text -label ("Vendor: " + $serlioVendor)`; string $legalField = `scrollField -h 85 -wordWrap true From cf200a6cabb2beb205df2a91b81d3d28fb9e4d83 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Thu, 3 Feb 2022 10:29:20 +0100 Subject: [PATCH 330/668] Fix compiler warning regarding unused enum values. --- src/serlio/modifiers/PRTModifierAction.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index a4229870..9b3f8e6e 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -684,6 +684,8 @@ MStatus PRTModifierAction::updateDynamicEnums() { MCHECK(e.mAttr.addField(mCurrString, 0)); break; } + default: + break; } } return MStatus(); From 1436e9ae2820184368b022b86a3cad5df21119a3 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Thu, 3 Feb 2022 10:30:24 +0100 Subject: [PATCH 331/668] Removed unused return value --- src/serlio/modifiers/PRTModifierAction.cpp | 3 +-- src/serlio/modifiers/PRTModifierAction.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 9b3f8e6e..8c853f73 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -601,7 +601,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { return MStatus::kSuccess; } -MStatus PRTModifierAction::updateDynamicEnums() { +void PRTModifierAction::updateDynamicEnums() { for (auto& e : mEnums) { if (e.mValuesAttr.length() == 0) continue; @@ -688,7 +688,6 @@ MStatus PRTModifierAction::updateDynamicEnums() { break; } } - return MStatus(); } // Sets the mesh object for the action to operate on diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 1e4db4ed..cd642665 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -105,7 +105,7 @@ class PRTModifierAction : public polyModifierFty { AttributeMapUPtr mGenerateAttrs; std::list mEnums; - MStatus updateDynamicEnums(); + void updateDynamicEnums(); // std::map mBriefName2prtAttr; MStatus createNodeAttributes(const RuleAttributeSet& ruleAttributes, const MObject& node, const prt::RuleFileInfo* info); From 7d553241f001857194108f20dde219dbf4fd64bc Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Thu, 3 Feb 2022 10:31:13 +0100 Subject: [PATCH 332/668] Remove commented code --- src/serlio/modifiers/PRTModifierAction.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index cd642665..a3c865f4 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -106,7 +106,7 @@ class PRTModifierAction : public polyModifierFty { std::list mEnums; void updateDynamicEnums(); - // std::map mBriefName2prtAttr; + MStatus createNodeAttributes(const RuleAttributeSet& ruleAttributes, const MObject& node, const prt::RuleFileInfo* info); void removeUnusedAttribs(MFnDependencyNode& node); From 998794c21455335b31f05e757075074c906ca547 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Thu, 3 Feb 2022 10:34:46 +0100 Subject: [PATCH 333/668] Prefer free function over private static member. Additionally, templated function without body in header is not very useful. --- src/serlio/modifiers/PRTModifierAction.cpp | 54 +++++++++++----------- src/serlio/modifiers/PRTModifierAction.h | 2 - 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 8c853f73..952bb428 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -327,6 +327,34 @@ short getDefaultEnumIdx(const prt::Annotation* annot, const PRTEnumDefaultValue& } return 0; } + +template +T getPlugValueAndRemoveAttr(MFnDependencyNode& node, const MString& briefName, + const T& defaultValue) { + T plugValue = defaultValue; + + if (DBG) { + LOG_DBG << "node attrs:"; + mu::forAllAttributes(node, [&node](const MFnAttribute& a) { + MString val; + node.findPlug(a.object(), true).getValue(val); + LOG_DBG << a.name().asWChar() << " = " << val.asWChar(); + }); + } + + if (node.hasAttribute(briefName)) { + const MPlug plug = node.findPlug(briefName, true); + if (plug.isDynamic()) { + T d; + if (plug.getValue(d) == MS::kSuccess) + plugValue = d; + } + node.removeAttribute(node.attribute(briefName)); + } + + return plugValue; +} + } // namespace PRTModifierAction::PRTModifierAction() { @@ -1041,32 +1069,6 @@ MStatus PRTModifierEnum::fill(const prt::Annotation* annot) { return MS::kSuccess; } -template -T PRTModifierAction::getPlugValueAndRemoveAttr(MFnDependencyNode& node, const MString& briefName, - const T& defaultValue) { - T plugValue = defaultValue; - - if (DBG) { - LOG_DBG << "node attrs:"; - mu::forAllAttributes(node, [&node](const MFnAttribute& a) { - MString val; - node.findPlug(a.object(), true).getValue(val); - LOG_DBG << a.name().asWChar() << " = " << val.asWChar(); - }); - } - - if (node.hasAttribute(briefName)) { - const MPlug plug = node.findPlug(briefName, true); - if (plug.isDynamic()) { - T d; - if (plug.getValue(d) == MS::kSuccess) - plugValue = d; - } - node.removeAttribute(node.attribute(briefName)); - } - return plugValue; -} - MStatus PRTModifierAction::addParameter(MFnDependencyNode& node, MObject& attr, MFnAttribute& tAttr) { if (!(node.hasAttribute(tAttr.shortName()))) { MStatus stat; diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index a3c865f4..2755501f 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -124,6 +124,4 @@ class PRTModifierAction : public polyModifierFty { const RuleAttribute& name, short defaultValue, PRTModifierEnum& e); static MStatus addColorParameter(MFnDependencyNode& node, MObject& attr, const RuleAttribute& name, const MString& defaultValue); - template - static T getPlugValueAndRemoveAttr(MFnDependencyNode& node, const MString& briefName, const T& defaultValue); }; From 28bee0c753e6523b4510f4b1428ab27e139ac36a Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Thu, 3 Feb 2022 10:36:22 +0100 Subject: [PATCH 334/668] clang-format --- src/serlio/modifiers/PRTModifierAction.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 952bb428..0b2e3235 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -329,8 +329,7 @@ short getDefaultEnumIdx(const prt::Annotation* annot, const PRTEnumDefaultValue& } template -T getPlugValueAndRemoveAttr(MFnDependencyNode& node, const MString& briefName, - const T& defaultValue) { +T getPlugValueAndRemoveAttr(MFnDependencyNode& node, const MString& briefName, const T& defaultValue) { T plugValue = defaultValue; if (DBG) { From 9e3f942ef525b8f4eb4debff36837b6c61ffbadc Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Thu, 3 Feb 2022 12:13:59 +0100 Subject: [PATCH 335/668] Fix sign/unsigned compiler warning --- src/serlio/modifiers/PRTModifierAction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 0b2e3235..c4a32bdf 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -662,7 +662,7 @@ void PRTModifierAction::updateDynamicEnums() { const size_t cutoffIndex = currStringView.find_first_of(L"\r\n"); currStringView = currStringView.substr(0, cutoffIndex); - const MString mCurrString(currStringView.data(), currStringView.length()); + const MString mCurrString(currStringView.data(), static_cast(currStringView.length())); MCHECK(e.mAttr.addField(mCurrString, enumIndex)); } break; From 0ee03b152aaca79433fb4eace61e40898ff950a3 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 3 Feb 2022 14:02:50 +0100 Subject: [PATCH 336/668] Name fileNodes according to their actual filename --- src/serlio/materials/StingrayMaterialNode.cpp | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/serlio/materials/StingrayMaterialNode.cpp b/src/serlio/materials/StingrayMaterialNode.cpp index 538b9883..247003b6 100644 --- a/src/serlio/materials/StingrayMaterialNode.cpp +++ b/src/serlio/materials/StingrayMaterialNode.cpp @@ -57,10 +57,10 @@ const MELVariable MEL_VAR_MAP_FILE(L"mapFile"); const MELVariable MEL_VAR_MAP_NODE(L"mapNode"); const MELVariable MEL_VAR_SHADING_NODE_INDEX(L"shadingNodeIndex"); -void setTexture(MELScriptBuilder& sb, const std::wstring& target, const std::wstring& textureNodeBaseName, - const std::wstring& tex) { +void setTexture(MELScriptBuilder& sb, const std::wstring& target, const std::wstring& tex) { if (!tex.empty()) { - sb.setVar(MEL_VAR_MAP_NODE, MELStringLiteral(textureNodeBaseName)); + std::filesystem::path texPath(tex); + sb.setVar(MEL_VAR_MAP_NODE, MELStringLiteral(texPath.stem().wstring())); sb.setVar(MEL_VAR_MAP_FILE, MELStringLiteral(tex)); @@ -125,13 +125,13 @@ void appendToMaterialScriptBuilder(MELScriptBuilder& sb, const MaterialInfo& mat // ignored: bumpMap, specularMap, occlusionmap // TODO: avoid wide/narrow conversion of map strings - setTexture(sb, L"color_map", shaderBaseName, prtu::toUTF16FromOSNarrow(matInfo.colormap)); - setTexture(sb, L"dirt_map", shaderBaseName, prtu::toUTF16FromOSNarrow(matInfo.dirtmap)); - setTexture(sb, L"emissive_map", shaderBaseName, prtu::toUTF16FromOSNarrow(matInfo.emissiveMap)); - setTexture(sb, L"metallic_map", shaderBaseName, prtu::toUTF16FromOSNarrow(matInfo.metallicMap)); - setTexture(sb, L"normal_map", shaderBaseName, prtu::toUTF16FromOSNarrow(matInfo.normalMap)); - setTexture(sb, L"roughness_map", shaderBaseName, prtu::toUTF16FromOSNarrow(matInfo.roughnessMap)); - setTexture(sb, L"opacity_map", shaderBaseName, prtu::toUTF16FromOSNarrow(matInfo.opacityMap)); + setTexture(sb, L"color_map", prtu::toUTF16FromOSNarrow(matInfo.colormap)); + setTexture(sb, L"dirt_map", prtu::toUTF16FromOSNarrow(matInfo.dirtmap)); + setTexture(sb, L"emissive_map", prtu::toUTF16FromOSNarrow(matInfo.emissiveMap)); + setTexture(sb, L"metallic_map", prtu::toUTF16FromOSNarrow(matInfo.metallicMap)); + setTexture(sb, L"normal_map", prtu::toUTF16FromOSNarrow(matInfo.normalMap)); + setTexture(sb, L"roughness_map", prtu::toUTF16FromOSNarrow(matInfo.roughnessMap)); + setTexture(sb, L"opacity_map", prtu::toUTF16FromOSNarrow(matInfo.opacityMap)); } } // namespace From 53803e5510ae49a0a8ecbe22247ca28bb4629173 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 3 Feb 2022 15:45:48 +0100 Subject: [PATCH 337/668] Update stingray shader to support rgb-based opacity maps... alongside opacitymaps using alpha channels. --- src/serlio/materials/StingrayMaterialNode.cpp | 4 + src/serlio/shaders/serlioShaderStingray.sfx | 1015 ++++++++++++----- 2 files changed, 705 insertions(+), 314 deletions(-) diff --git a/src/serlio/materials/StingrayMaterialNode.cpp b/src/serlio/materials/StingrayMaterialNode.cpp index 247003b6..105964de 100644 --- a/src/serlio/materials/StingrayMaterialNode.cpp +++ b/src/serlio/materials/StingrayMaterialNode.cpp @@ -68,6 +68,10 @@ void setTexture(MELScriptBuilder& sb, const std::wstring& target, const std::wst sb.setAttr(MEL_VAR_MAP_NODE, L"fileTextureName", MEL_VAR_MAP_FILE); sb.connectAttr(MEL_VAR_MAP_NODE, L"outColor", MEL_VAR_SHADER_NODE, L"TEX_" + target); + + if (target.compare(L"opacity_map") == 0) + sb.connectAttr(MEL_VAR_MAP_NODE, L"fileHasAlpha", MEL_VAR_SHADER_NODE, L"opacity_map_uses_alpha_channel"); + sb.setAttr(MEL_VAR_SHADER_NODE, L"use_" + target, 1); } else { diff --git a/src/serlio/shaders/serlioShaderStingray.sfx b/src/serlio/shaders/serlioShaderStingray.sfx index 927c9c6d..42f8e9ce 100644 --- a/src/serlio/shaders/serlioShaderStingray.sfx +++ b/src/serlio/shaders/serlioShaderStingray.sfx @@ -1,14 +1,14 @@ -SFB_UNX = { /* +SFB_WIN = { /* Version=28 GroupVersion=-1.000000 Advanced=0 HelpID=0 ParentMaterial=0 -NumberOfNodes=74 +NumberOfNodes=81 #NT=20178 0 PC=3 - posx=1 v=2003 -438.892761 - posy=1 v=2003 -1197.870361 + posx=1 v=2003 -972.225708 + posy=1 v=2003 -571.203430 previewswatch=1 v=2002 1 group=-1 ISC=0 @@ -38,8 +38,8 @@ NumberOfNodes=74 #NT=20189 0 PC=3 name=1 v=5000 Normal_Map_Switch - posx=1 v=2003 1172.062012 - posy=1 v=2003 150.317429 + posx=1 v=2003 648.729248 + posy=1 v=2003 706.983521 group=-1 ISC=4 SVT=5022 2003 1 0 0 @@ -91,8 +91,8 @@ NumberOfNodes=74 #NT=20189 0 PC=3 name=1 v=5000 Color_Map_Switch - posx=1 v=2003 959.622864 - posy=1 v=2003 -1218.670410 + posx=1 v=2003 426.289581 + posy=1 v=2003 -592.003357 group=-1 ISC=4 SVT=5022 2003 1 0 0 @@ -108,8 +108,8 @@ NumberOfNodes=74 CPC=0 #NT=20194 0 PC=2 - posx=1 v=2003 854.919678 - posy=1 v=2003 321.746094 + posx=1 v=2003 331.585999 + posy=1 v=2003 878.411560 group=-1 ISC=0 OSC=1 @@ -119,8 +119,8 @@ NumberOfNodes=74 CPC=0 #NT=20195 0 PC=2 - posx=1 v=2003 850.475525 - posy=1 v=2003 202.539703 + posx=1 v=2003 327.141846 + posy=1 v=2003 759.205750 group=-1 ISC=1 SVT=5022 3002 1 0 1 @@ -131,10 +131,11 @@ NumberOfNodes=74 C=7 0 2 2 2 3 0 CPC=0 #NT=20176 0 - PC=3 + PC=4 posx=1 v=2003 1636.499512 posy=1 v=2003 871.666687 normalspace=2 e=0 v=5012 1 + blendmode=2 e=0 v=5012 1 group=-1 ISC=13 SVT=5022 3002 1 0 0 @@ -153,8 +154,8 @@ NumberOfNodes=74 OSC=0 #NT=20189 0 PC=2 - posx=1 v=2003 1106.586426 - posy=1 v=2003 -449.900818 + posx=1 v=2003 1113.251587 + posy=1 v=2003 100.516365 group=-1 ISC=4 SVT=5022 2003 1 0 0 @@ -171,8 +172,8 @@ NumberOfNodes=74 #NT=20177 0 PC=4 name=1 v=5000 color_map - posx=1 v=2003 543.512024 - posy=1 v=2003 -1111.884766 + posx=1 v=2003 10.178637 + posy=1 v=2003 -485.219147 uiorder=2 e=0 v=2002 2 group=-1 ISC=2 @@ -186,8 +187,8 @@ NumberOfNodes=74 #NT=20177 0 PC=5 name=1 v=5000 normal_map - posx=1 v=2003 573.777466 - posy=1 v=2003 154.460297 + posx=1 v=2003 50.444195 + posy=1 v=2003 711.126404 encoding=2 e=0 v=5012 2 uiorder=2 e=0 v=2002 3 group=-1 @@ -262,12 +263,11 @@ NumberOfNodes=74 C=15 0 3 8 7 8 0 CPC=0 #NT=20185 0 - PC=7 + PC=6 name=1 v=5000 use_color_map - posx=1 v=2003 542.361267 - posy=1 v=2003 -1214.742188 + posx=1 v=2003 9.027872 + posy=1 v=2003 -588.075134 type=2 e=0 v=5012 0 - defaultscalar=2 e=0 v=2003 1.000000 uitype=2 e=0 v=5012 1 uiorder=2 e=0 v=2002 11 group=-1 @@ -280,10 +280,10 @@ NumberOfNodes=74 #NT=20185 0 PC=7 name=1 v=5000 diffuse_color - posx=1 v=2003 542.261963 - posy=1 v=2003 -977.162415 + posx=1 v=2003 8.928568 + posy=1 v=2003 -350.497345 type=2 e=0 v=5012 2 - defaultvecthree=2 e=0 v=3002 0.988235,0.937255,0.886275 + defaultvecthree=2 e=0 v=3002 1.000000,1.000000,1.000000 defaultvector=2 e=0 v=3003 0.500000,0.500000,0.500000,0.000000 uiorder=2 e=0 v=2002 21 group=-1 @@ -296,8 +296,8 @@ NumberOfNodes=74 #NT=20185 0 PC=6 name=1 v=5000 use_opacity_map - posx=1 v=2003 574.821472 - posy=1 v=2003 -488.809418 + posx=1 v=2003 910.238281 + posy=1 v=2003 7.857183 type=2 e=0 v=5012 0 uitype=2 e=0 v=5012 1 uiorder=2 e=0 v=2002 12 @@ -311,8 +311,8 @@ NumberOfNodes=74 #NT=20185 0 PC=6 name=1 v=5000 opacity - posx=1 v=2003 577.500122 - posy=1 v=2003 -125.297607 + posx=1 v=2003 1102.915283 + posy=1 v=2003 388.869263 type=2 e=0 v=5012 0 defaultscalar=2 e=0 v=2003 1.000000 uiorder=2 e=0 v=2002 22 @@ -326,8 +326,8 @@ NumberOfNodes=74 #NT=20185 0 PC=6 name=1 v=5000 use_normal_map - posx=1 v=2003 573.491089 - posy=1 v=2003 44.126915 + posx=1 v=2003 50.157818 + posy=1 v=2003 600.793518 type=2 e=0 v=5012 0 uitype=2 e=0 v=5012 1 uiorder=2 e=0 v=2002 13 @@ -428,8 +428,8 @@ NumberOfNodes=74 CPC=0 #NT=20186 0 PC=2 - posx=1 v=2003 1215.337524 - posy=1 v=2003 -989.622620 + posx=1 v=2003 682.004456 + posy=1 v=2003 -362.957672 group=-1 ISC=2 SVT=5022 3002 1 0 0 @@ -441,8 +441,8 @@ NumberOfNodes=74 CPC=0 #NT=20190 0 PC=2 - posx=1 v=2003 780.059753 - posy=1 v=2003 -1055.316772 + posx=1 v=2003 246.726425 + posy=1 v=2003 -428.651733 group=-1 ISC=1 SVT=5022 3002 1 1 0 @@ -455,8 +455,8 @@ NumberOfNodes=74 #NT=20177 0 PC=4 name=1 v=5000 opacity_map - posx=1 v=2003 577.039429 - posy=1 v=2003 -377.170197 + posx=1 v=2003 -5.044054 + posy=1 v=2003 89.496941 uiorder=2 e=0 v=2002 2 group=-1 ISC=2 @@ -464,13 +464,19 @@ NumberOfNodes=74 SVT=5022 2003 2 0 0 OSC=1 SVT=5022 3003 3 - CC=1 - C=29 0 3 9 2 3 0 + CC=4 + C=29 0 3 76 0 1 0 + CPC=0 + C=29 0 3 77 0 1 0 + CPC=0 + C=29 0 3 78 0 1 0 + CPC=0 + C=29 0 3 79 2 3 0 CPC=0 #NT=20186 0 PC=2 - posx=1 v=2003 1279.500244 - posy=1 v=2003 -222.999985 + posx=1 v=2003 1328.665405 + posy=1 v=2003 198.666992 group=-1 ISC=2 SVT=5022 2003 1 0 0 @@ -482,8 +488,8 @@ NumberOfNodes=74 CPC=0 #NT=20196 0 PC=2 - posx=1 v=2003 853.745850 - posy=1 v=2003 -205.730118 + posx=1 v=2003 841.662842 + posy=1 v=2003 319.686646 group=-1 ISC=1 SVT=5022 2003 1 1 0 @@ -564,8 +570,8 @@ NumberOfNodes=74 #NT=20189 0 PC=3 name=1 v=5000 dirt_map_switch - posx=1 v=2003 941.047363 - posy=1 v=2003 -830.847717 + posx=1 v=2003 407.714081 + posy=1 v=2003 -204.182373 group=-1 ISC=4 SVT=5022 2003 1 0 0 @@ -582,8 +588,8 @@ NumberOfNodes=74 #NT=20177 0 PC=4 name=1 v=5000 dirt_map - posx=1 v=2003 544.877319 - posy=1 v=2003 -723.943176 + posx=1 v=2003 11.543928 + posy=1 v=2003 -97.277359 uiorder=2 e=0 v=2002 2 group=-1 ISC=2 @@ -595,12 +601,11 @@ NumberOfNodes=74 C=38 0 3 37 2 3 0 CPC=0 #NT=20185 0 - PC=7 + PC=6 name=1 v=5000 use_dirt_map - posx=1 v=2003 544.976562 - posy=1 v=2003 -833.050415 + posx=1 v=2003 11.643171 + posy=1 v=2003 -206.385086 type=2 e=0 v=5012 0 - defaultscalar=2 e=0 v=2003 1.000000 uitype=2 e=0 v=5012 1 uiorder=2 e=0 v=2002 11 group=-1 @@ -612,8 +617,8 @@ NumberOfNodes=74 CPC=0 #NT=20190 0 PC=3 - posx=1 v=2003 776.305542 - posy=1 v=2003 -638.982544 + posx=1 v=2003 242.972260 + posy=1 v=2003 -12.316059 collapsed=1 v=2001 1 group=-1 ISC=1 @@ -626,8 +631,8 @@ NumberOfNodes=74 CPC=0 #NT=20186 0 PC=2 - posx=1 v=2003 1409.227417 - posy=1 v=2003 -837.289124 + posx=1 v=2003 875.894226 + posy=1 v=2003 -210.623810 group=-1 ISC=2 SVT=5022 3002 1 0 0 @@ -639,8 +644,8 @@ NumberOfNodes=74 CPC=0 #NT=20186 0 PC=2 - posx=1 v=2003 129.678635 - posy=1 v=2003 -1105.106689 + posx=1 v=2003 -403.654968 + posy=1 v=2003 -478.441132 group=-1 ISC=2 SVT=5022 3001 1 0 1 @@ -653,8 +658,8 @@ NumberOfNodes=74 CPC=0 #NT=20187 0 PC=2 - posx=1 v=2003 292.178558 - posy=1 v=2003 -1199.392944 + posx=1 v=2003 -241.154785 + posy=1 v=2003 -572.726013 group=-1 ISC=2 SVT=5022 3001 1 0 1 @@ -668,8 +673,8 @@ NumberOfNodes=74 #NT=20185 0 PC=10 name=1 v=5000 colormap_trafo_suvw - posx=1 v=2003 -65.817390 - posy=1 v=2003 -1162.567627 + posx=1 v=2003 -599.150330 + posy=1 v=2003 -535.901367 type=2 e=0 v=5012 2 defaultvectwo=2 e=0 v=3001 1.000000,1.000000 defaultvecthree=2 e=0 v=3002 1.000000,1.000000,0.000000 @@ -686,8 +691,8 @@ NumberOfNodes=74 CPC=0 #NT=20186 0 PC=2 - posx=1 v=2003 97.178650 - posy=1 v=2003 -702.356812 + posx=1 v=2003 -436.154999 + posy=1 v=2003 -75.690872 group=-1 ISC=2 SVT=5022 3001 1 0 1 @@ -700,8 +705,8 @@ NumberOfNodes=74 CPC=0 #NT=20187 0 PC=2 - posx=1 v=2003 263.428589 - posy=1 v=2003 -792.893066 + posx=1 v=2003 -269.904785 + posy=1 v=2003 -166.227600 group=-1 ISC=2 SVT=5022 3001 1 0 1 @@ -715,8 +720,8 @@ NumberOfNodes=74 #NT=20185 0 PC=10 name=1 v=5000 dirtmap_trafo_suvw - posx=1 v=2003 -68.178497 - posy=1 v=2003 -818.428833 + posx=1 v=2003 -601.511414 + posy=1 v=2003 -191.763519 type=2 e=0 v=5012 2 defaultvectwo=2 e=0 v=3001 1.000000,1.000000 defaultvecthree=2 e=0 v=3002 1.000000,1.000000,0.000000 @@ -733,8 +738,8 @@ NumberOfNodes=74 CPC=0 #NT=20186 0 PC=3 - posx=1 v=2003 105.428665 - posy=1 v=2003 -362.106934 + posx=1 v=2003 -427.904938 + posy=1 v=2003 264.559814 previewswatch=1 v=2002 1 group=-1 ISC=2 @@ -748,8 +753,8 @@ NumberOfNodes=74 CPC=0 #NT=20187 0 PC=2 - posx=1 v=2003 262.928619 - posy=1 v=2003 -462.643188 + posx=1 v=2003 -270.404755 + posy=1 v=2003 164.023895 group=-1 ISC=2 SVT=5022 3001 1 0 1 @@ -763,8 +768,8 @@ NumberOfNodes=74 #NT=20185 0 PC=10 name=1 v=5000 opacitymap_trafo_suvw - posx=1 v=2003 -68.678497 - posy=1 v=2003 -486.928955 + posx=1 v=2003 -602.011414 + posy=1 v=2003 139.738235 type=2 e=0 v=5012 2 defaultvectwo=2 e=0 v=3001 1.000000,1.000000 defaultvecthree=2 e=0 v=3002 1.000000,1.000000,0.000000 @@ -781,8 +786,8 @@ NumberOfNodes=74 CPC=0 #NT=20186 0 PC=2 - posx=1 v=2003 125.928665 - posy=1 v=2003 166.143021 + posx=1 v=2003 -397.405060 + posy=1 v=2003 722.809204 group=-1 ISC=2 SVT=5022 3001 1 0 1 @@ -795,8 +800,8 @@ NumberOfNodes=74 CPC=0 #NT=20187 0 PC=2 - posx=1 v=2003 289.678619 - posy=1 v=2003 79.356758 + posx=1 v=2003 -233.654556 + posy=1 v=2003 636.023010 group=-1 ISC=2 SVT=5022 3001 1 0 1 @@ -810,8 +815,8 @@ NumberOfNodes=74 #NT=20185 0 PC=10 name=1 v=5000 normalmap_trafo_suvw - posx=1 v=2003 -41.928528 - posy=1 v=2003 55.070992 + posx=1 v=2003 -565.261475 + posy=1 v=2003 611.737488 type=2 e=0 v=5012 2 defaultvectwo=2 e=0 v=3001 1.000000,1.000000 defaultvecthree=2 e=0 v=3002 1.000000,1.000000,0.000000 @@ -970,8 +975,8 @@ NumberOfNodes=74 #NT=20185 0 PC=8 name=1 v=5000 colormap_trafo_tuv - posx=1 v=2003 -68.178497 - posy=1 v=2003 -1279.428711 + posx=1 v=2003 -601.511414 + posy=1 v=2003 -652.760681 type=2 e=0 v=5012 1 defaultvector=2 e=0 v=3003 1.000000,1.000000,0.000000,0.000000 minrange=2 e=0 v=2003 -100001.000000 @@ -987,8 +992,8 @@ NumberOfNodes=74 #NT=20185 0 PC=8 name=1 v=5000 dirtmap_trafo_tuv - posx=1 v=2003 -67.678497 - posy=1 v=2003 -922.928833 + posx=1 v=2003 -601.011414 + posy=1 v=2003 -296.263733 type=2 e=0 v=5012 1 defaultvector=2 e=0 v=3003 1.000000,0.000000,0.000000,0.000000 minrange=2 e=0 v=2003 -100001.000000 @@ -1004,8 +1009,8 @@ NumberOfNodes=74 #NT=20185 0 PC=8 name=1 v=5000 opacitymap_trafo_tuv - posx=1 v=2003 -64.428497 - posy=1 v=2003 -595.928955 + posx=1 v=2003 -597.761414 + posy=1 v=2003 30.737833 type=2 e=0 v=5012 1 defaultvector=2 e=0 v=3003 1.000000,1.000000,0.000000,0.000000 minrange=2 e=0 v=2003 -100001.000000 @@ -1021,8 +1026,8 @@ NumberOfNodes=74 #NT=20185 0 PC=8 name=1 v=5000 normalmap_trafo_tuv - posx=1 v=2003 -40.928528 - posy=1 v=2003 -63.679016 + posx=1 v=2003 -564.261475 + posy=1 v=2003 492.988098 type=2 e=0 v=5012 1 defaultvector=2 e=0 v=3003 1.000000,1.000000,0.000000,0.000000 minrange=2 e=0 v=2003 -100001.000000 @@ -1088,8 +1093,8 @@ NumberOfNodes=74 CPC=0 #NT=20179 0 PC=2 - posx=1 v=2003 -451.357483 - posy=1 v=2003 -924.207947 + posx=1 v=2003 -984.690552 + posy=1 v=2003 -297.542847 group=-1 ISC=0 OSC=1 @@ -1099,8 +1104,8 @@ NumberOfNodes=74 CPC=0 #NT=20180 0 PC=2 - posx=1 v=2003 -432.126617 - posy=1 v=2003 -47.511288 + posx=1 v=2003 -955.458862 + posy=1 v=2003 509.155853 group=-1 ISC=0 OSC=1 @@ -1110,8 +1115,8 @@ NumberOfNodes=74 CPC=0 #NT=20181 0 PC=2 - posx=1 v=2003 -450.226135 - posy=1 v=2003 -447.963623 + posx=1 v=2003 -983.559143 + posy=1 v=2003 178.703445 group=-1 ISC=0 OSC=1 @@ -1134,6 +1139,109 @@ NumberOfNodes=74 CPC=0 C=73 0 1 60 1 2 0 CPC=0 +#NT=20187 0 + PC=2 + posx=1 v=2003 605.417053 + posy=1 v=2003 260.416748 + group=-1 + ISC=2 + SVT=5022 2003 1 0 0 + SVT=5022 2003 2 0 0 + OSC=1 + SVT=5022 2003 3 + CC=1 + C=74 0 3 79 3 4 0 + CPC=0 +#NT=20187 0 + PC=2 + posx=1 v=2003 407.916718 + posy=1 v=2003 221.666885 + group=-1 + ISC=2 + SVT=5022 2003 1 0 0 + SVT=5022 2003 2 0 0 + OSC=1 + SVT=5022 2003 3 + CC=1 + C=75 0 3 74 0 1 0 + CPC=0 +#NT=20186 0 + PC=2 + posx=1 v=2003 230.416794 + posy=1 v=2003 156.667099 + group=-1 + ISC=2 + SVT=5022 2003 1 0 1 + SCS=r + SVT=5022 2003 2 1 0 + SDV=0.298900 + OSC=1 + SVT=5022 2003 3 + CC=1 + C=76 0 3 75 0 1 0 + CPC=0 +#NT=20186 0 + PC=2 + posx=1 v=2003 226.666870 + posy=1 v=2003 301.666748 + group=-1 + ISC=2 + SVT=5022 2003 1 0 1 + SCS=g + SVT=5022 2003 2 1 0 + SDV=0.587000 + OSC=1 + SVT=5022 2003 3 + CC=1 + C=77 0 3 75 1 2 0 + CPC=0 +#NT=20186 0 + PC=2 + posx=1 v=2003 227.916870 + posy=1 v=2003 459.167023 + group=-1 + ISC=2 + SVT=5022 2003 1 0 1 + SCS=b + SVT=5022 2003 2 1 0 + SDV=0.114000 + OSC=1 + SVT=5022 2003 3 + CC=1 + C=78 0 3 74 1 2 0 + CPC=0 +#NT=20189 0 + PC=2 + posx=1 v=2003 807.916992 + posy=1 v=2003 107.917221 + group=-1 + ISC=4 + SVT=5022 2003 1 0 0 + SVT=5022 2003 2 1 0 + SDV=1.000000 + SVT=5022 2003 3 0 1 + SCS=a + SVT=5022 2003 4 0 0 + OSC=1 + SVT=5022 2003 5 + CC=1 + C=79 0 5 9 2 3 0 + CPC=0 +#NT=20185 0 + PC=6 + name=1 v=5000 opacity_map_uses_alpha_channel + posx=1 v=2003 527.916992 + posy=1 v=2003 57.916878 + type=2 e=0 v=5012 0 + uitype=2 e=0 v=5012 1 + uiorder=2 e=0 v=2002 12 + group=-1 + ISC=0 + OSC=1 + SVT=5022 2003 1 + CC=1 + C=80 0 1 79 0 1 0 + CPC=0 */ } connections = [ @@ -1143,13 +1251,13 @@ connections = [ instance_id = "abbaabba-abba-abba-abba-abbaabbaabb9" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab44" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab42" } } { destination = { connector_id = "c5823c75-4ae5-4c71-b070-315fa4d03e8e" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab44" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab42" } source = { instance_id = "abbaabba-abba-abba-abba-abbaabbaab28" @@ -1191,46 +1299,46 @@ connections = [ instance_id = "abbaabba-abba-abba-abba-abbaabbaab11" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab46" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab44" } } { destination = { connector_id = "f72597c4-7487-419a-affb-df690e6582e1" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab46" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab44" } select = [ "xy" ] source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab66" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab64" } } { destination = { connector_id = "0806db0d-2c4a-43ca-99cc-f5a2f036a8e8" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab46" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab44" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab45" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab43" } } { destination = { connector_id = "c5823c75-4ae5-4c71-b070-315fa4d03e8e" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab45" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab43" } select = [ "xy" ] source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab47" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab45" } } { destination = { connector_id = "242d1648-a626-445b-9534-bccec094112f" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab45" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab43" } source = { instance_id = "abbaabba-abba-abba-abba-abbaabbaabb1" @@ -1257,91 +1365,91 @@ connections = [ { destination = { connector_id = "242d1648-a626-445b-9534-bccec094112f" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab44" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab42" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab40" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab38" } } { destination = { connector_id = "CED7BBF3-0B48-4335-B933-095A41CA0294" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab40" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab38" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab42" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab40" } } { destination = { connector_id = "4CBB4480-79E8-4CE7-AC0F-8B09BAF12390" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab40" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab38" } select = [ "rgb" ] source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab41" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab39" } } { destination = { connector_id = "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab41" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab39" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab49" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab47" } } { destination = { connector_id = "f72597c4-7487-419a-affb-df690e6582e1" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab49" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab47" } select = [ "xy" ] source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab67" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab65" } } { destination = { connector_id = "0806db0d-2c4a-43ca-99cc-f5a2f036a8e8" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab49" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab47" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab48" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab46" } } { destination = { connector_id = "c5823c75-4ae5-4c71-b070-315fa4d03e8e" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab48" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab46" } select = [ "xy" ] source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab50" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab48" } } { destination = { connector_id = "242d1648-a626-445b-9534-bccec094112f" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab48" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab46" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab73" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab71" } } { destination = { connector_id = "F2F74E58-402D-472B-87DD-331E00DB416C" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab40" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab38" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab43" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab41" } } { @@ -1376,6 +1484,60 @@ connections = [ connector_id = "4CBB4480-79E8-4CE7-AC0F-8B09BAF12390" instance_id = "abbaabba-abba-abba-abba-abbaabbaab10" } + source = { + instance_id = "abbaabba-abba-abba-abba-abbaabbaab80" + } + } + { + destination = { + connector_id = "CED7BBF3-0B48-4335-B933-095A41CA0294" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab80" + } + source = { + instance_id = "abbaabba-abba-abba-abba-abbaabbaab81" + } + } + { + destination = { + connector_id = "c5823c75-4ae5-4c71-b070-315fa4d03e8e" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab77" + } + select = [ + "r" + ] + source = { + instance_id = "abbaabba-abba-abba-abba-abbaabbaab30" + } + } + { + destination = { + connector_id = "c5823c75-4ae5-4c71-b070-315fa4d03e8e" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab78" + } + select = [ + "g" + ] + source = { + instance_id = "abbaabba-abba-abba-abba-abbaabbaab30" + } + } + { + destination = { + connector_id = "c5823c75-4ae5-4c71-b070-315fa4d03e8e" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab79" + } + select = [ + "b" + ] + source = { + instance_id = "abbaabba-abba-abba-abba-abbaabbaab30" + } + } + { + destination = { + connector_id = "4CBB4480-79E8-4CE7-AC0F-8B09BAF12390" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab80" + } select = [ "a" ] @@ -1389,51 +1551,96 @@ connections = [ instance_id = "abbaabba-abba-abba-abba-abbaabbaab30" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab52" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab50" } } { destination = { connector_id = "f72597c4-7487-419a-affb-df690e6582e1" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab52" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab50" } select = [ "xy" ] source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab68" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab66" } } { destination = { connector_id = "0806db0d-2c4a-43ca-99cc-f5a2f036a8e8" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab52" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab50" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab51" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab49" } } { destination = { connector_id = "c5823c75-4ae5-4c71-b070-315fa4d03e8e" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab51" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab49" } select = [ "xy" ] source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab53" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab51" } } { destination = { connector_id = "242d1648-a626-445b-9534-bccec094112f" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab51" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab49" + } + source = { + instance_id = "abbaabba-abba-abba-abba-abbaabbaab73" + } + } + { + destination = { + connector_id = "F2F74E58-402D-472B-87DD-331E00DB416C" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab80" } source = { instance_id = "abbaabba-abba-abba-abba-abbaabbaab75" } } + { + destination = { + connector_id = "f72597c4-7487-419a-affb-df690e6582e1" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab75" + } + source = { + instance_id = "abbaabba-abba-abba-abba-abbaabbaab76" + } + } + { + destination = { + connector_id = "f72597c4-7487-419a-affb-df690e6582e1" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab76" + } + source = { + instance_id = "abbaabba-abba-abba-abba-abbaabbaab77" + } + } + { + destination = { + connector_id = "0806db0d-2c4a-43ca-99cc-f5a2f036a8e8" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab76" + } + source = { + instance_id = "abbaabba-abba-abba-abba-abbaabbaab78" + } + } + { + destination = { + connector_id = "0806db0d-2c4a-43ca-99cc-f5a2f036a8e8" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab75" + } + source = { + instance_id = "abbaabba-abba-abba-abba-abbaabbaab79" + } + } { destination = { connector_id = "F2F74E58-402D-472B-87DD-331E00DB416C" @@ -1497,49 +1704,49 @@ connections = [ instance_id = "abbaabba-abba-abba-abba-abbaabbaab12" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab55" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab53" } } { destination = { connector_id = "f72597c4-7487-419a-affb-df690e6582e1" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab55" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab53" } select = [ "xy" ] source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab69" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab67" } } { destination = { connector_id = "0806db0d-2c4a-43ca-99cc-f5a2f036a8e8" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab55" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab53" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab54" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab52" } } { destination = { connector_id = "c5823c75-4ae5-4c71-b070-315fa4d03e8e" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab54" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab52" } select = [ "xy" ] source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab56" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab54" } } { destination = { connector_id = "242d1648-a626-445b-9534-bccec094112f" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab54" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab52" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab74" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab72" } } { @@ -1557,13 +1764,13 @@ connections = [ instance_id = "abbaabba-abba-abba-abba-abbaabbaabb9" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab35" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab33" } } { destination = { connector_id = "c5823c75-4ae5-4c71-b070-315fa4d03e8e" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab35" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab33" } source = { instance_id = "abbaabba-abba-abba-abba-abbaabbaabb4" @@ -1596,67 +1803,67 @@ connections = [ instance_id = "abbaabba-abba-abba-abba-abbaabbaab13" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab58" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab56" } } { destination = { connector_id = "f72597c4-7487-419a-affb-df690e6582e1" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab58" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab56" } select = [ "xy" ] source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab70" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab68" } } { destination = { connector_id = "0806db0d-2c4a-43ca-99cc-f5a2f036a8e8" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab58" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab56" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab57" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab55" } } { destination = { connector_id = "c5823c75-4ae5-4c71-b070-315fa4d03e8e" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab57" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab55" } select = [ "xy" ] source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab59" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab57" } } { destination = { connector_id = "242d1648-a626-445b-9534-bccec094112f" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab57" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab55" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab76" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab74" } } { destination = { connector_id = "242d1648-a626-445b-9534-bccec094112f" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab60" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab58" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab76" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab74" } } { destination = { connector_id = "242d1648-a626-445b-9534-bccec094112f" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab63" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab61" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab76" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab74" } } { @@ -1665,13 +1872,13 @@ connections = [ instance_id = "abbaabba-abba-abba-abba-abbaabbaabb4" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab36" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab34" } } { destination = { connector_id = "242d1648-a626-445b-9534-bccec094112f" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab35" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab33" } source = { instance_id = "abbaabba-abba-abba-abba-abbaabbaab23" @@ -1683,13 +1890,13 @@ connections = [ instance_id = "abbaabba-abba-abba-abba-abbaabbaabb9" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab38" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab36" } } { destination = { connector_id = "c5823c75-4ae5-4c71-b070-315fa4d03e8e" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab38" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab36" } source = { instance_id = "abbaabba-abba-abba-abba-abbaabbaabb2" @@ -1722,40 +1929,40 @@ connections = [ instance_id = "abbaabba-abba-abba-abba-abbaabbaab14" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab61" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab59" } } { destination = { connector_id = "f72597c4-7487-419a-affb-df690e6582e1" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab61" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab59" } select = [ "xy" ] source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab71" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab69" } } { destination = { connector_id = "0806db0d-2c4a-43ca-99cc-f5a2f036a8e8" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab61" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab59" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab60" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab58" } } { destination = { connector_id = "c5823c75-4ae5-4c71-b070-315fa4d03e8e" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab60" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab58" } select = [ "xy" ] source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab62" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab60" } } { @@ -1764,13 +1971,13 @@ connections = [ instance_id = "abbaabba-abba-abba-abba-abbaabbaabb2" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab37" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab35" } } { destination = { connector_id = "242d1648-a626-445b-9534-bccec094112f" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab38" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab36" } source = { instance_id = "abbaabba-abba-abba-abba-abbaabbaab25" @@ -1821,40 +2028,40 @@ connections = [ instance_id = "abbaabba-abba-abba-abba-abbaabbaab15" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab64" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab62" } } { destination = { connector_id = "f72597c4-7487-419a-affb-df690e6582e1" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab64" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab62" } select = [ "xy" ] source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab72" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab70" } } { destination = { connector_id = "0806db0d-2c4a-43ca-99cc-f5a2f036a8e8" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab64" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab62" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab63" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab61" } } { destination = { connector_id = "c5823c75-4ae5-4c71-b070-315fa4d03e8e" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab63" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab61" } select = [ "xy" ] source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab65" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab63" } } { @@ -1863,7 +2070,7 @@ connections = [ instance_id = "abbaabba-abba-abba-abba-abbaabbaabb5" } source = { - instance_id = "abbaabba-abba-abba-abba-abbaabbaab39" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab37" } } { @@ -1895,16 +2102,16 @@ constants = [ } { connector_id = "39BC7619-2768-480B-ACFD-63FA66EF6905" - id = "1bbaabba-abba-abba-abba-abbaabbaab40" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab40" + id = "1bbaabba-abba-abba-abba-abbaabbaab38" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab38" value = [ 1.000000 ] } { connector_id = "6ff26be7-68a1-4b89-b9dd-551d216086c2" - id = "0bbaabba-abba-abba-abba-abbaabbaab43" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab43" + id = "0bbaabba-abba-abba-abba-abbaabbaab41" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab41" value = [ 1.000000 1.000000 1.000000 ] @@ -1917,6 +2124,38 @@ constants = [ 1.000000 ] } + { + connector_id = "39BC7619-2768-480B-ACFD-63FA66EF6905" + id = "1bbaabba-abba-abba-abba-abbaabbaab80" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab80" + value = [ + 1.000000 + ] + } + { + connector_id = "242d1648-a626-445b-9534-bccec094112f" + id = "1bbaabba-abba-abba-abba-abbaabbaab77" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab77" + value = [ + 0.298900 + ] + } + { + connector_id = "242d1648-a626-445b-9534-bccec094112f" + id = "1bbaabba-abba-abba-abba-abbaabbaab78" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab78" + value = [ + 0.587000 + ] + } + { + connector_id = "242d1648-a626-445b-9534-bccec094112f" + id = "1bbaabba-abba-abba-abba-abbaabbaab79" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab79" + value = [ + 0.114000 + ] + } { connector_id = "c4d6bc08-c489-430f-a836-ed490e59c3f9" id = "0bbaabba-abba-abba-abba-abbaabbaab32" @@ -1943,8 +2182,8 @@ constants = [ } { connector_id = "c4d6bc08-c489-430f-a836-ed490e59c3f9" - id = "0bbaabba-abba-abba-abba-abbaabbaab36" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab36" + id = "0bbaabba-abba-abba-abba-abbaabbaab34" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab34" value = [ 1.000000 ] @@ -1959,8 +2198,8 @@ constants = [ } { connector_id = "c4d6bc08-c489-430f-a836-ed490e59c3f9" - id = "0bbaabba-abba-abba-abba-abbaabbaab37" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab37" + id = "0bbaabba-abba-abba-abba-abbaabbaab35" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab35" value = [ 1.000000 ] @@ -1975,8 +2214,8 @@ constants = [ } { connector_id = "6ff26be7-68a1-4b89-b9dd-551d216086c2" - id = "0bbaabba-abba-abba-abba-abbaabbaab39" - instance_id = "abbaabba-abba-abba-abba-abbaabbaab39" + id = "0bbaabba-abba-abba-abba-abbaabbaab37" + instance_id = "abbaabba-abba-abba-abba-abbaabbaab37" value = [ 1.000000 1.000000 1.000000 ] @@ -1992,6 +2231,7 @@ nodes = [ } id = "abbaabba-abba-abba-abba-abbaabbaabb9" options = [ + "dd7fcf97-0627-48ab-b29a-95b5685bb123" "2b136447-676e-4943-997b-04a28ae68497" ] position = [ @@ -2010,12 +2250,12 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab44" + id = "abbaabba-abba-abba-abba-abbaabbaab42" options = [ ] position = [ - 1409 - -837 + 875 + -210 ] samplers = { } @@ -2033,8 +2273,8 @@ nodes = [ options = [ ] position = [ - 1215 - -989 + 682 + -362 ] samplers = { } @@ -2053,8 +2293,8 @@ nodes = [ "9A84282B-F1A2-46D4-9FC4-5A76FC9B30DD" ] position = [ - 959 - -1218 + 426 + -592 ] samplers = { } @@ -2078,15 +2318,15 @@ nodes = [ ui_type = "checkbox" } type = "float" - value = 1.000000 + value = 0.000000 } } id = "abbaabba-abba-abba-abba-abbaabbaab17" options = [ ] position = [ - 542 - -1214 + 9 + -588 ] samplers = { } @@ -2107,8 +2347,8 @@ nodes = [ "5dd59b3d-1762-4a14-9930-7500230ef3db" ] position = [ - 543 - -1111 + 10 + -485 ] samplers = { texture_map = { @@ -2129,12 +2369,12 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab46" + id = "abbaabba-abba-abba-abba-abbaabbaab44" options = [ ] position = [ - 292 - -1199 + -241 + -572 ] samplers = { } @@ -2160,12 +2400,12 @@ nodes = [ value = [0.000000 0.000000] } } - id = "abbaabba-abba-abba-abba-abbaabbaab66" + id = "abbaabba-abba-abba-abba-abbaabbaab64" options = [ ] position = [ - -68 - -1279 + -601 + -652 ] samplers = { } @@ -2179,12 +2419,12 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab45" + id = "abbaabba-abba-abba-abba-abbaabbaab43" options = [ ] position = [ - 129 - -1105 + -403 + -478 ] samplers = { } @@ -2211,12 +2451,12 @@ nodes = [ value = [1.000000 1.000000 0.000000] } } - id = "abbaabba-abba-abba-abba-abbaabbaab47" + id = "abbaabba-abba-abba-abba-abbaabbaab45" options = [ ] position = [ - -65 - -1162 + -599 + -535 ] samplers = { } @@ -2234,8 +2474,8 @@ nodes = [ options = [ ] position = [ - -438 - -1197 + -972 + -571 ] samplers = { } @@ -2253,8 +2493,8 @@ nodes = [ options = [ ] position = [ - 780 - -1055 + 246 + -428 ] samplers = { } @@ -2278,15 +2518,15 @@ nodes = [ ui_type = "color" } type = "float3" - value = [0.988235 0.937255 0.886275] + value = [1.000000 1.000000 1.000000] } } id = "abbaabba-abba-abba-abba-abbaabbaab18" options = [ ] position = [ - 542 - -977 + 8 + -350 ] samplers = { } @@ -2300,13 +2540,13 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab40" + id = "abbaabba-abba-abba-abba-abbaabbaab38" options = [ "9A84282B-F1A2-46D4-9FC4-5A76FC9B30DD" ] position = [ - 941 - -830 + 407 + -204 ] samplers = { } @@ -2330,15 +2570,15 @@ nodes = [ ui_type = "checkbox" } type = "float" - value = 1.000000 + value = 0.000000 } } - id = "abbaabba-abba-abba-abba-abbaabbaab42" + id = "abbaabba-abba-abba-abba-abbaabbaab40" options = [ ] position = [ - 544 - -833 + 11 + -206 ] samplers = { } @@ -2352,15 +2592,15 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab41" + id = "abbaabba-abba-abba-abba-abbaabbaab39" options = [ "1e067464-12d8-4826-9b72-cfd5765003e3" "fb3f709b-a54a-4e93-ac9f-e9fc76fb8bcd" "5dd59b3d-1762-4a14-9930-7500230ef3db" ] position = [ - 544 - -723 + 11 + -97 ] samplers = { texture_map = { @@ -2381,12 +2621,12 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab49" + id = "abbaabba-abba-abba-abba-abbaabbaab47" options = [ ] position = [ - 263 - -792 + -269 + -166 ] samplers = { } @@ -2412,12 +2652,12 @@ nodes = [ value = [0.000000 0.000000] } } - id = "abbaabba-abba-abba-abba-abbaabbaab67" + id = "abbaabba-abba-abba-abba-abbaabbaab65" options = [ ] position = [ - -67 - -922 + -601 + -296 ] samplers = { } @@ -2431,12 +2671,12 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab48" + id = "abbaabba-abba-abba-abba-abbaabbaab46" options = [ ] position = [ - 97 - -702 + -436 + -75 ] samplers = { } @@ -2463,12 +2703,12 @@ nodes = [ value = [1.000000 1.000000 0.000000] } } - id = "abbaabba-abba-abba-abba-abbaabbaab50" + id = "abbaabba-abba-abba-abba-abbaabbaab48" options = [ ] position = [ - -68 - -818 + -601 + -191 ] samplers = { } @@ -2482,12 +2722,12 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab73" + id = "abbaabba-abba-abba-abba-abbaabbaab71" options = [ ] position = [ - -451 - -924 + -984 + -297 ] samplers = { } @@ -2501,12 +2741,12 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab43" + id = "abbaabba-abba-abba-abba-abbaabbaab41" options = [ ] position = [ - 776 - -638 + 242 + -12 ] samplers = { } @@ -2524,8 +2764,8 @@ nodes = [ options = [ ] position = [ - 1279 - -222 + 1328 + 198 ] samplers = { } @@ -2544,8 +2784,8 @@ nodes = [ "9A84282B-F1A2-46D4-9FC4-5A76FC9B30DD" ] position = [ - 1106 - -449 + 1113 + 100 ] samplers = { } @@ -2576,14 +2816,66 @@ nodes = [ options = [ ] position = [ - 574 - -488 + 910 + 7 ] samplers = { } title = "Use Opacity Map" type = "core/shader_nodes/material_variable" } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "abbaabba-abba-abba-abba-abbaabbaab80" + options = [ + "9A84282B-F1A2-46D4-9FC4-5A76FC9B30DD" + ] + position = [ + 807 + 107 + ] + samplers = { + } + title = "If Statement" + type = "core/shader_nodes/if" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Opacity Map Uses Alpha Channel" + name = "opacity_map_uses_alpha_channel" + ui = { + max = 1 + min = 0.000000 + step = 1 + order = 12 + ui_type = "checkbox" + } + type = "float" + value = 0.000000 + } + } + id = "abbaabba-abba-abba-abba-abbaabbaab81" + options = [ + ] + position = [ + 527 + 57 + ] + samplers = { + } + title = "Opacity Map Uses Alpha Channel" + type = "core/shader_nodes/material_variable" + } { content_size = [ 160 @@ -2598,8 +2890,8 @@ nodes = [ "5dd59b3d-1762-4a14-9930-7500230ef3db" ] position = [ - 577 - -377 + -5 + 89 ] samplers = { texture_map = { @@ -2620,12 +2912,12 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab52" + id = "abbaabba-abba-abba-abba-abbaabbaab50" options = [ ] position = [ - 262 - -462 + -270 + 164 ] samplers = { } @@ -2651,12 +2943,12 @@ nodes = [ value = [0.000000 0.000000] } } - id = "abbaabba-abba-abba-abba-abbaabbaab68" + id = "abbaabba-abba-abba-abba-abbaabbaab66" options = [ ] position = [ - -64 - -595 + -597 + 30 ] samplers = { } @@ -2670,12 +2962,12 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab51" + id = "abbaabba-abba-abba-abba-abbaabbaab49" options = [ ] position = [ - 105 - -362 + -427 + 264 ] samplers = { } @@ -2702,12 +2994,12 @@ nodes = [ value = [1.000000 1.000000 0.000000] } } - id = "abbaabba-abba-abba-abba-abbaabbaab53" + id = "abbaabba-abba-abba-abba-abbaabbaab51" options = [ ] position = [ - -68 - -486 + -602 + 139 ] samplers = { } @@ -2721,18 +3013,113 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab75" + id = "abbaabba-abba-abba-abba-abbaabbaab73" options = [ ] position = [ - -450 - -447 + -983 + 178 ] samplers = { } title = "Texcoord3" type = "core/shader_nodes/texture_coordinate3" } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "abbaabba-abba-abba-abba-abbaabbaab75" + options = [ + ] + position = [ + 605 + 260 + ] + samplers = { + } + title = "Add" + type = "core/shader_nodes/add" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "abbaabba-abba-abba-abba-abbaabbaab76" + options = [ + ] + position = [ + 407 + 221 + ] + samplers = { + } + title = "Add" + type = "core/shader_nodes/add" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "abbaabba-abba-abba-abba-abbaabbaab77" + options = [ + ] + position = [ + 230 + 156 + ] + samplers = { + } + title = "Multiply" + type = "core/shader_nodes/mul" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "abbaabba-abba-abba-abba-abbaabbaab78" + options = [ + ] + position = [ + 226 + 301 + ] + samplers = { + } + title = "Multiply" + type = "core/shader_nodes/mul" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "abbaabba-abba-abba-abba-abbaabbaab79" + options = [ + ] + position = [ + 227 + 459 + ] + samplers = { + } + title = "Multiply" + type = "core/shader_nodes/mul" + } { content_size = [ 160 @@ -2744,8 +3131,8 @@ nodes = [ options = [ ] position = [ - 853 - -205 + 841 + 319 ] samplers = { } @@ -2775,8 +3162,8 @@ nodes = [ options = [ ] position = [ - 577 - -125 + 1102 + 388 ] samplers = { } @@ -2795,8 +3182,8 @@ nodes = [ "9A84282B-F1A2-46D4-9FC4-5A76FC9B30DD" ] position = [ - 1172 - 150 + 648 + 706 ] samplers = { } @@ -2827,8 +3214,8 @@ nodes = [ options = [ ] position = [ - 573 - 44 + 50 + 600 ] samplers = { } @@ -2847,8 +3234,8 @@ nodes = [ "0a0fb5ad-145d-4229-abd4-5b36562607b3" ] position = [ - 850 - 202 + 327 + 759 ] samplers = { } @@ -2869,8 +3256,8 @@ nodes = [ "5dd59b3d-1762-4a14-9930-7500230ef3db" ] position = [ - 573 - 154 + 50 + 711 ] samplers = { texture_map = { @@ -2891,12 +3278,12 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab55" + id = "abbaabba-abba-abba-abba-abbaabbaab53" options = [ ] position = [ - 289 - 79 + -233 + 636 ] samplers = { } @@ -2922,12 +3309,12 @@ nodes = [ value = [0.000000 0.000000] } } - id = "abbaabba-abba-abba-abba-abbaabbaab69" + id = "abbaabba-abba-abba-abba-abbaabbaab67" options = [ ] position = [ - -40 - -63 + -564 + 492 ] samplers = { } @@ -2941,12 +3328,12 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab54" + id = "abbaabba-abba-abba-abba-abbaabbaab52" options = [ ] position = [ - 125 - 166 + -397 + 722 ] samplers = { } @@ -2973,12 +3360,12 @@ nodes = [ value = [1.000000 1.000000 0.000000] } } - id = "abbaabba-abba-abba-abba-abbaabbaab56" + id = "abbaabba-abba-abba-abba-abbaabbaab54" options = [ ] position = [ - -41 - 55 + -565 + 611 ] samplers = { } @@ -2992,12 +3379,12 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab74" + id = "abbaabba-abba-abba-abba-abbaabbaab72" options = [ ] position = [ - -432 - -47 + -955 + 509 ] samplers = { } @@ -3015,8 +3402,8 @@ nodes = [ options = [ ] position = [ - 854 - 321 + 331 + 878 ] samplers = { } @@ -3030,7 +3417,7 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab35" + id = "abbaabba-abba-abba-abba-abbaabbaab33" options = [ ] position = [ @@ -3130,7 +3517,7 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab58" + id = "abbaabba-abba-abba-abba-abbaabbaab56" options = [ ] position = [ @@ -3161,7 +3548,7 @@ nodes = [ value = [0.000000 0.000000] } } - id = "abbaabba-abba-abba-abba-abbaabbaab70" + id = "abbaabba-abba-abba-abba-abbaabbaab68" options = [ ] position = [ @@ -3180,7 +3567,7 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab57" + id = "abbaabba-abba-abba-abba-abbaabbaab55" options = [ ] position = [ @@ -3212,7 +3599,7 @@ nodes = [ value = [1.000000 1.000000 0.000000] } } - id = "abbaabba-abba-abba-abba-abbaabbaab59" + id = "abbaabba-abba-abba-abba-abbaabbaab57" options = [ ] position = [ @@ -3231,7 +3618,7 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab76" + id = "abbaabba-abba-abba-abba-abbaabbaab74" options = [ ] position = [ @@ -3250,7 +3637,7 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab36" + id = "abbaabba-abba-abba-abba-abbaabbaab34" options = [ ] position = [ @@ -3300,7 +3687,7 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab38" + id = "abbaabba-abba-abba-abba-abbaabbaab36" options = [ ] position = [ @@ -3400,7 +3787,7 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab61" + id = "abbaabba-abba-abba-abba-abbaabbaab59" options = [ ] position = [ @@ -3431,7 +3818,7 @@ nodes = [ value = [0.000000 0.000000] } } - id = "abbaabba-abba-abba-abba-abbaabbaab71" + id = "abbaabba-abba-abba-abba-abbaabbaab69" options = [ ] position = [ @@ -3450,7 +3837,7 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab60" + id = "abbaabba-abba-abba-abba-abbaabbaab58" options = [ ] position = [ @@ -3482,7 +3869,7 @@ nodes = [ value = [1.000000 1.000000 0.000000] } } - id = "abbaabba-abba-abba-abba-abbaabbaab62" + id = "abbaabba-abba-abba-abba-abbaabbaab60" options = [ ] position = [ @@ -3501,7 +3888,7 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab37" + id = "abbaabba-abba-abba-abba-abbaabbaab35" options = [ ] position = [ @@ -3651,7 +4038,7 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab64" + id = "abbaabba-abba-abba-abba-abbaabbaab62" options = [ ] position = [ @@ -3682,7 +4069,7 @@ nodes = [ value = [0.000000 0.000000] } } - id = "abbaabba-abba-abba-abba-abbaabbaab72" + id = "abbaabba-abba-abba-abba-abbaabbaab70" options = [ ] position = [ @@ -3701,7 +4088,7 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab63" + id = "abbaabba-abba-abba-abba-abbaabbaab61" options = [ ] position = [ @@ -3733,7 +4120,7 @@ nodes = [ value = [1.000000 1.000000 0.000000] } } - id = "abbaabba-abba-abba-abba-abbaabbaab65" + id = "abbaabba-abba-abba-abba-abbaabbaab63" options = [ ] position = [ @@ -3752,7 +4139,7 @@ nodes = [ ] export = { } - id = "abbaabba-abba-abba-abba-abbaabbaab39" + id = "abbaabba-abba-abba-abba-abbaabbaab37" options = [ ] position = [ From dd90f0e996fbedf39fe5e34128209dbf5cd97b42 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 7 Feb 2022 10:26:33 +0100 Subject: [PATCH 338/668] Cleanup: remove redundant public keyword --- src/serlio/modifiers/MayaCallbacks.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/serlio/modifiers/MayaCallbacks.h b/src/serlio/modifiers/MayaCallbacks.h index 10190575..473e67da 100644 --- a/src/serlio/modifiers/MayaCallbacks.h +++ b/src/serlio/modifiers/MayaCallbacks.h @@ -95,7 +95,6 @@ class MayaCallbacks : public IMayaCallbacks { #endif // PRT version >= 2.1 -public: // clang-format off void addMesh(const wchar_t* name, const double* vtx, size_t vtxSize, From 5d309565ecd05c288d7fe1dd65f377b043fe7e3b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 7 Feb 2022 10:41:39 +0100 Subject: [PATCH 339/668] Add a non-dynamic attribute to serlio nodes to store cgac warnings --- src/serlio/modifiers/PRTModifierNode.cpp | 12 ++++++++++++ src/serlio/modifiers/PRTModifierNode.h | 1 + 2 files changed, 13 insertions(+) diff --git a/src/serlio/modifiers/PRTModifierNode.cpp b/src/serlio/modifiers/PRTModifierNode.cpp index 98203f4f..d879a40a 100644 --- a/src/serlio/modifiers/PRTModifierNode.cpp +++ b/src/serlio/modifiers/PRTModifierNode.cpp @@ -38,11 +38,13 @@ namespace { const MString NAME_RULE_PKG = "Rule_Package"; const MString NAME_RANDOM_SEED = "Random_Seed"; +const MString CGAC_WARNINGS = "CGAC_Warnings"; } // namespace // Unique Node TypeId MTypeId PRTModifierNode::id(SerlioNodeIDs::SERLIO_PREFIX, SerlioNodeIDs::PRT_GEOMETRY_NODE); MObject PRTModifierNode::rulePkg; +MObject PRTModifierNode::cgacWarnings; MObject PRTModifierNode::currentRulePkg; MObject PRTModifierNode::mRandomSeed; @@ -208,6 +210,16 @@ MStatus PRTModifierNode::initialize() MCHECK(fAttr.setConnectable(false)); MCHECK(addAttribute(currentRulePkg)); + cgacWarnings = fAttr.create(CGAC_WARNINGS, "cgacErrors", MFnData::kString, + stringData.create(&stat2), &stat); + MCHECK(stat2); + MCHECK(stat); + MCHECK(fAttr.setCached(true)); + MCHECK(fAttr.setStorable(true)); + MCHECK(fAttr.setHidden(true)); + MCHECK(fAttr.setConnectable(false)); + MCHECK(addAttribute(cgacWarnings)); + // Set up a dependency between the input and the output. This will cause // the output to be marked dirty when the input changes. The output will // then be recomputed the next time the value of the output is requested. diff --git a/src/serlio/modifiers/PRTModifierNode.h b/src/serlio/modifiers/PRTModifierNode.h index 5b7eb70a..7450e4ad 100644 --- a/src/serlio/modifiers/PRTModifierNode.h +++ b/src/serlio/modifiers/PRTModifierNode.h @@ -38,6 +38,7 @@ class PRTModifierNode : public polyModifierNode { public: // non-dynamic node attributes static MObject rulePkg; + static MObject cgacWarnings; static MObject currentRulePkg; static MTypeId id; static MObject mRandomSeed; From 1ace0134d7deebe0aa508091f0ba5439e162cfed Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 7 Feb 2022 10:42:26 +0100 Subject: [PATCH 340/668] Add callback function to get cgac warnings --- src/serlio/modifiers/MayaCallbacks.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/serlio/modifiers/MayaCallbacks.h b/src/serlio/modifiers/MayaCallbacks.h index 473e67da..5e282bce 100644 --- a/src/serlio/modifiers/MayaCallbacks.h +++ b/src/serlio/modifiers/MayaCallbacks.h @@ -46,6 +46,12 @@ class MayaCallbacks : public IMayaCallbacks { prt::Status assetError(size_t /*isIndex*/, prt::CGAErrorLevel /*level*/, const wchar_t* /*key*/, const wchar_t* /*uri*/, const wchar_t* message) override { LOG_ERR << "ASSET ERROR: " << message; + if (message != nullptr && wcsstr(message, L"CGAC version")) { + if (!cgacWarnings.empty()) + cgacWarnings.append(L"\n"); + + cgacWarnings.append(message); + } return prt::STATUS_OK; } prt::Status cgaError(size_t /*isIndex*/, int32_t /*shapeID*/, prt::CGAErrorLevel /*level*/, int32_t /*methodId*/, @@ -95,6 +101,10 @@ class MayaCallbacks : public IMayaCallbacks { #endif // PRT version >= 2.1 + const std::wstring& getCGACWarnings() const { + return cgacWarnings; + } + // clang-format off void addMesh(const wchar_t* name, const double* vtx, size_t vtxSize, @@ -119,6 +129,7 @@ class MayaCallbacks : public IMayaCallbacks { private: + std::wstring cgacWarnings; MObject outMeshObj; MObject inMeshObj; From 5d491aab8a50a83853062b8fd41bc58f3db252b4 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 7 Feb 2022 10:51:41 +0100 Subject: [PATCH 341/668] Add warnings Icon to attribute editor.. next to the rpk selection field. It is only visible when there are valid warnings. --- src/serlio/scripts/AEserlioTemplate.mel | 30 ++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index e4d72e65..a1a97c1e 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -94,15 +94,43 @@ global proc prtShowFileDialog(string $attr, string $filters) { } } +global proc updateCGACWarning(string $node){ + string $attr = $node + ".CGAC_Warnings"; + + if (!prtAttributeExists($attr)) + return; + + string $cgacErrors = `getAttr ($attr)`; + int $hasErrors = (size($cgacErrors) > 0); + string $control = prtControlName($node) + "_CGAC_Warnings"; + + prtSetVisibility($hasErrors, $control); + iconTextStaticLabel -e -ann $cgacErrors $control; +} + global proc prtFileBrowse(string $attr, string $varname, string $filters){ $varname = prtRemoveNodeName($varname); $filters = prtRemoveNodeName($filters); setUITemplate -pst attributeEditorTemplate; - rowLayout -nc 4; + rowLayout + -nc 5 + -adj 2 + -cw 3 25 + -cw 4 25 + -cw 5 25; text -label `niceName($attr)`; string $control = prtControlName($attr); textField -fileName `getAttr $attr` $control; connectControl -fileName $control $attr; + + string $node = prtGetNodeName($attr); + + iconTextStaticLabel -st "iconOnly" -i "warningIcon.svg" -l "warning" -visible false ($node + "_CGAC_Warnings"); + updateCGACWarning($node); + + string $changeCommand = "updateCGACWarning " + $node; + scriptJob -rp -p $control -ac ($node + ".CGAC_Warnings") $changeCommand; + symbolButton -image "navButtonBrowse.xpm" -c ("prtShowFileDialog(\"" + $attr + "\",\"" + $filters + "\" )") ($control + "_browse"); symbolButton -ann "reload rule package" -image "refresh.png" -c ("prtReloadRPK(\"" + $attr + "\")") ($control + "_reload"); setParent ..; From 513d6d00cdb9707800bef6a69d3a63b6172516c0 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 7 Feb 2022 11:00:07 +0100 Subject: [PATCH 342/668] Update cgac warnings UI using the callbacks function --- src/serlio/modifiers/PRTModifierAction.cpp | 9 ++++++++- src/serlio/modifiers/PRTModifierAction.h | 3 ++- src/serlio/modifiers/PRTModifierNode.cpp | 5 ++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index a4229870..de30c0ef 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -28,6 +28,7 @@ #include "prt/StringUtils.h" +#include "maya/MDataHandle.h" #include "maya/MFloatPointArray.h" #include "maya/MFnCompoundAttribute.h" #include "maya/MFnMesh.h" @@ -506,7 +507,7 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { return MStatus::kSuccess; } -MStatus PRTModifierAction::updateUI(const MObject& node) { +MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacWarningData) { const auto updateUIFromAttributes = [this](const MFnDependencyNode& fnNode, const MFnAttribute& fnAttribute, const RuleAttribute& ruleAttribute, const PrtAttributeType attrType) { const AttributeMapUPtr defaultAttributeValues = @@ -596,6 +597,9 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { } }; + if (cgacWarningData.asString() != mCGACWarnings) + cgacWarningData.setString(mCGACWarnings); + iterateThroughAttributesAndApply(node, mRuleAttributes, updateUIFromAttributes); return MStatus::kSuccess; @@ -784,6 +788,9 @@ MStatus PRTModifierAction::doIt() { const prt::Status generateStatus = prt::generate(shapes.data(), shapes.size(), nullptr, encIDs.data(), encIDs.size(), encOpts.data(), outputHandler.get(), PRTContext::get().mPRTCache.get(), nullptr); + + mCGACWarnings = outputHandler->getCGACWarnings().c_str(); + if (generateStatus != prt::STATUS_OK) LOG_ERR << "prt generate failed: " << prt::getStatusDescription(generateStatus); diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 1e4db4ed..cf028a90 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -69,7 +69,7 @@ class PRTModifierAction : public polyModifierFty { MStatus updateRuleFiles(const MObject& node, const MString& rulePkg); MStatus fillAttributesFromNode(const MObject& node); MStatus updateUserSetAttributes(const MObject& node); - MStatus updateUI(const MObject& node); + MStatus updateUI(const MObject& node, MDataHandle& cgacWarningData); void setMesh(MObject& _inMesh, MObject& _outMesh); void setRandomSeed(int32_t randomSeed) { mRandomSeed = randomSeed; @@ -93,6 +93,7 @@ class PRTModifierAction : public polyModifierFty { // Set in updateRuleFiles(rulePkg) MString mRulePkg; + MString mCGACWarnings; std::wstring mRuleFile; std::wstring mStartRule; const std::wstring mRuleStyle = L"Default"; // Serlio atm only supports the "Default" style diff --git a/src/serlio/modifiers/PRTModifierNode.cpp b/src/serlio/modifiers/PRTModifierNode.cpp index d879a40a..cd426c64 100644 --- a/src/serlio/modifiers/PRTModifierNode.cpp +++ b/src/serlio/modifiers/PRTModifierNode.cpp @@ -96,6 +96,9 @@ MStatus PRTModifierNode::compute(const MPlug& plug, MDataBlock& data) { MDataHandle currentRulePkgData = data.inputValue(currentRulePkg, &status); MCheckStatus(status, "ERROR getting currentRulePkg"); + MDataHandle cgacWarningData = data.inputValue(cgacWarnings, &status); + MCheckStatus(status, "ERROR getting cgacWarnings"); + const bool ruleFileWasChanged = (rulePkgData.asString() != currentRulePkgData.asString()); // Copy the inMesh to the outMesh, so you can @@ -124,7 +127,7 @@ MStatus PRTModifierNode::compute(const MPlug& plug, MDataBlock& data) { // Now, perform the PRT status = fPRTModifierAction.doIt(); - fPRTModifierAction.updateUI(thisMObject()); + fPRTModifierAction.updateUI(thisMObject(), cgacWarningData); currentRulePkgData.setString(rulePkgData.asString()); From 935fd2bb1944a0333c1f4a0b3a522511803f60f4 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 7 Feb 2022 13:51:33 +0100 Subject: [PATCH 343/668] Show CGAC Errors in status line/console --- src/serlio/modifiers/PRTModifierAction.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index de30c0ef..a2ceb58c 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -35,6 +35,7 @@ #include "maya/MFnNumericAttribute.h" #include "maya/MFnStringData.h" #include "maya/MFnTypedAttribute.h" +#include "maya/MGlobal.h" #include #include @@ -328,6 +329,11 @@ short getDefaultEnumIdx(const prt::Annotation* annot, const PRTEnumDefaultValue& } return 0; } + +void displayCGACErrors(MString warningMessage){ + if (wcsstr(warningMessage.asWChar(), L"major number larger than current")) + MGlobal::displayError(warningMessage); +} } // namespace PRTModifierAction::PRTModifierAction() { @@ -597,8 +603,10 @@ MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacWarnin } }; - if (cgacWarningData.asString() != mCGACWarnings) + if (cgacWarningData.asString() != mCGACWarnings) { cgacWarningData.setString(mCGACWarnings); + displayCGACErrors(mCGACWarnings); + } iterateThroughAttributesAndApply(node, mRuleAttributes, updateUIFromAttributes); @@ -791,8 +799,13 @@ MStatus PRTModifierAction::doIt() { mCGACWarnings = outputHandler->getCGACWarnings().c_str(); - if (generateStatus != prt::STATUS_OK) - LOG_ERR << "prt generate failed: " << prt::getStatusDescription(generateStatus); + if (generateStatus != prt::STATUS_OK) { + std::string generateFailedMessage = "prt generate failed: "; + generateFailedMessage.append(prt::getStatusDescription(generateStatus)); + + LOG_ERR << generateFailedMessage; + MGlobal::displayError(generateFailedMessage.c_str()); + } return status; } From 2c9ae2e3d38ba4a0dfe7011650e5aa6cc4fd6c8e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 7 Feb 2022 15:11:43 +0100 Subject: [PATCH 344/668] Cleanup: use vector of tuples instead of string for storing errorlevel data --- src/serlio/modifiers/MayaCallbacks.h | 15 ++++++------ src/serlio/modifiers/PRTModifierAction.cpp | 28 +++++++++++++++------- src/serlio/modifiers/PRTModifierAction.h | 3 ++- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.h b/src/serlio/modifiers/MayaCallbacks.h index 5e282bce..4ec97cf7 100644 --- a/src/serlio/modifiers/MayaCallbacks.h +++ b/src/serlio/modifiers/MayaCallbacks.h @@ -33,6 +33,8 @@ #include #include +using CGACErrorList = std::vector>; + class MayaCallbacks : public IMayaCallbacks { public: MayaCallbacks(const MObject& inMesh, const MObject& outMesh, AttributeMapBuilderUPtr& amb) @@ -43,14 +45,11 @@ class MayaCallbacks : public IMayaCallbacks { LOG_ERR << "GENERATE ERROR: " << message; return prt::STATUS_OK; } - prt::Status assetError(size_t /*isIndex*/, prt::CGAErrorLevel /*level*/, const wchar_t* /*key*/, + prt::Status assetError(size_t /*isIndex*/, prt::CGAErrorLevel level, const wchar_t* /*key*/, const wchar_t* /*uri*/, const wchar_t* message) override { LOG_ERR << "ASSET ERROR: " << message; if (message != nullptr && wcsstr(message, L"CGAC version")) { - if (!cgacWarnings.empty()) - cgacWarnings.append(L"\n"); - - cgacWarnings.append(message); + cgacErrors.push_back(std::make_pair(level, message)); } return prt::STATUS_OK; } @@ -101,8 +100,8 @@ class MayaCallbacks : public IMayaCallbacks { #endif // PRT version >= 2.1 - const std::wstring& getCGACWarnings() const { - return cgacWarnings; + const CGACErrorList& getCGACErrors() const { + return cgacErrors; } // clang-format off @@ -129,7 +128,7 @@ class MayaCallbacks : public IMayaCallbacks { private: - std::wstring cgacWarnings; + CGACErrorList cgacErrors; MObject outMeshObj; MObject inMeshObj; diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index a2ceb58c..cadd2f99 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -18,7 +18,6 @@ */ #include "modifiers/PRTModifierAction.h" -#include "modifiers/MayaCallbacks.h" #include "modifiers/PRTModifierCommand.h" #include "modifiers/RuleAttributes.h" @@ -330,9 +329,20 @@ short getDefaultEnumIdx(const prt::Annotation* annot, const PRTEnumDefaultValue& return 0; } -void displayCGACErrors(MString warningMessage){ - if (wcsstr(warningMessage.asWChar(), L"major number larger than current")) - MGlobal::displayError(warningMessage); +bool cgacErrorListHasErrors(CGACErrorList errorList) { + for (const auto& error : errorList) { + if (error.first == prt::CGAErrorLevel::CGAERROR) + return true; + } + return false; +} + +MString cgacErrorListToString(CGACErrorList errorList) { + MString errorString; + for (const auto& error : errorList) { + errorString += error.second.c_str(); + } + return errorString; } } // namespace @@ -603,9 +613,11 @@ MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacWarnin } }; - if (cgacWarningData.asString() != mCGACWarnings) { - cgacWarningData.setString(mCGACWarnings); - displayCGACErrors(mCGACWarnings); + MString cgacErrorString = cgacErrorListToString(mCGACErrors); + if (cgacWarningData.asString() != cgacErrorString) { + cgacWarningData.setString(cgacErrorString); + if (cgacErrorListHasErrors(mCGACErrors)) + MGlobal::displayError(cgacErrorString); } iterateThroughAttributesAndApply(node, mRuleAttributes, updateUIFromAttributes); @@ -797,7 +809,7 @@ MStatus PRTModifierAction::doIt() { prt::generate(shapes.data(), shapes.size(), nullptr, encIDs.data(), encIDs.size(), encOpts.data(), outputHandler.get(), PRTContext::get().mPRTCache.get(), nullptr); - mCGACWarnings = outputHandler->getCGACWarnings().c_str(); + mCGACErrors = outputHandler->getCGACErrors(); if (generateStatus != prt::STATUS_OK) { std::string generateFailedMessage = "prt generate failed: "; diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index cf028a90..e7c6bc54 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -19,6 +19,7 @@ #pragma once +#include "modifiers/MayaCallbacks.h" #include "modifiers/PRTMesh.h" #include "modifiers/RuleAttributes.h" #include "modifiers/polyModifier/polyModifierFty.h" @@ -93,7 +94,7 @@ class PRTModifierAction : public polyModifierFty { // Set in updateRuleFiles(rulePkg) MString mRulePkg; - MString mCGACWarnings; + CGACErrorList mCGACErrors; std::wstring mRuleFile; std::wstring mStartRule; const std::wstring mRuleStyle = L"Default"; // Serlio atm only supports the "Default" style From 30b2abbc37dd03f669b00e92493609f95321ac1f Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 7 Feb 2022 15:15:48 +0100 Subject: [PATCH 345/668] Cleanup: rename CGACErrorList to CGACErrors This is also less confusing, since the datastructure used is a vector --- src/serlio/modifiers/MayaCallbacks.h | 6 +++--- src/serlio/modifiers/PRTModifierAction.cpp | 4 ++-- src/serlio/modifiers/PRTModifierAction.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.h b/src/serlio/modifiers/MayaCallbacks.h index 4ec97cf7..12e6ba08 100644 --- a/src/serlio/modifiers/MayaCallbacks.h +++ b/src/serlio/modifiers/MayaCallbacks.h @@ -33,7 +33,7 @@ #include #include -using CGACErrorList = std::vector>; +using CGACErrors = std::vector>; class MayaCallbacks : public IMayaCallbacks { public: @@ -100,7 +100,7 @@ class MayaCallbacks : public IMayaCallbacks { #endif // PRT version >= 2.1 - const CGACErrorList& getCGACErrors() const { + const CGACErrors& getCGACErrors() const { return cgacErrors; } @@ -128,7 +128,7 @@ class MayaCallbacks : public IMayaCallbacks { private: - CGACErrorList cgacErrors; + CGACErrors cgacErrors; MObject outMeshObj; MObject inMeshObj; diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index cadd2f99..aa54ebb6 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -329,7 +329,7 @@ short getDefaultEnumIdx(const prt::Annotation* annot, const PRTEnumDefaultValue& return 0; } -bool cgacErrorListHasErrors(CGACErrorList errorList) { +bool cgacErrorListHasErrors(CGACErrors errorList) { for (const auto& error : errorList) { if (error.first == prt::CGAErrorLevel::CGAERROR) return true; @@ -337,7 +337,7 @@ bool cgacErrorListHasErrors(CGACErrorList errorList) { return false; } -MString cgacErrorListToString(CGACErrorList errorList) { +MString cgacErrorListToString(CGACErrors errorList) { MString errorString; for (const auto& error : errorList) { errorString += error.second.c_str(); diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index e7c6bc54..22140257 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -94,7 +94,7 @@ class PRTModifierAction : public polyModifierFty { // Set in updateRuleFiles(rulePkg) MString mRulePkg; - CGACErrorList mCGACErrors; + CGACErrors mCGACErrors; std::wstring mRuleFile; std::wstring mStartRule; const std::wstring mRuleStyle = L"Default"; // Serlio atm only supports the "Default" style From 5d3fce138eca3b14e93ce1e0d802e95c314e713e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 7 Feb 2022 15:20:47 +0100 Subject: [PATCH 346/668] Cleanup: add newlines between errors MString has no .empty() function, therefore using length() > 0 --- src/serlio/modifiers/PRTModifierAction.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index aa54ebb6..08151bb6 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -340,6 +340,9 @@ bool cgacErrorListHasErrors(CGACErrors errorList) { MString cgacErrorListToString(CGACErrors errorList) { MString errorString; for (const auto& error : errorList) { + if (errorString.length() > 0) + errorString += "\n"; + errorString += error.second.c_str(); } return errorString; From 660cc392aab850d46a8443c7bfa588389675c6a5 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 7 Feb 2022 15:31:59 +0100 Subject: [PATCH 347/668] Cleanup: rename helper function --- src/serlio/modifiers/PRTModifierAction.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 9587d21d..3e6f658b 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -337,7 +337,7 @@ bool cgacErrorListHasErrors(CGACErrors errorList) { return false; } -MString cgacErrorListToString(CGACErrors errorList) { +MString cgacErrorsToString(CGACErrors errorList) { MString errorString; for (const auto& error : errorList) { if (errorString.length() > 0) @@ -642,7 +642,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacWarnin } }; - MString cgacErrorString = cgacErrorListToString(mCGACErrors); + MString cgacErrorString = cgacErrorsToString(mCGACErrors); if (cgacWarningData.asString() != cgacErrorString) { cgacWarningData.setString(cgacErrorString); if (cgacErrorListHasErrors(mCGACErrors)) From 58f4254efb65e12025277297ab6619b7f2155179 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 7 Feb 2022 15:39:23 +0100 Subject: [PATCH 348/668] Cleanup: remove hardcoded information from setTexture --- src/serlio/materials/StingrayMaterialNode.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/serlio/materials/StingrayMaterialNode.cpp b/src/serlio/materials/StingrayMaterialNode.cpp index 105964de..fda7a829 100644 --- a/src/serlio/materials/StingrayMaterialNode.cpp +++ b/src/serlio/materials/StingrayMaterialNode.cpp @@ -57,7 +57,8 @@ const MELVariable MEL_VAR_MAP_FILE(L"mapFile"); const MELVariable MEL_VAR_MAP_NODE(L"mapNode"); const MELVariable MEL_VAR_SHADING_NODE_INDEX(L"shadingNodeIndex"); -void setTexture(MELScriptBuilder& sb, const std::wstring& target, const std::wstring& tex) { +void setTexture(MELScriptBuilder& sb, const std::wstring& target, const std::wstring& tex, + const std::wstring& alphaTarget = {}) { if (!tex.empty()) { std::filesystem::path texPath(tex); sb.setVar(MEL_VAR_MAP_NODE, MELStringLiteral(texPath.stem().wstring())); @@ -69,8 +70,8 @@ void setTexture(MELScriptBuilder& sb, const std::wstring& target, const std::wst sb.connectAttr(MEL_VAR_MAP_NODE, L"outColor", MEL_VAR_SHADER_NODE, L"TEX_" + target); - if (target.compare(L"opacity_map") == 0) - sb.connectAttr(MEL_VAR_MAP_NODE, L"fileHasAlpha", MEL_VAR_SHADER_NODE, L"opacity_map_uses_alpha_channel"); + if (!alphaTarget.empty()) + sb.connectAttr(MEL_VAR_MAP_NODE, L"fileHasAlpha", MEL_VAR_SHADER_NODE, alphaTarget); sb.setAttr(MEL_VAR_SHADER_NODE, L"use_" + target, 1); } @@ -135,7 +136,7 @@ void appendToMaterialScriptBuilder(MELScriptBuilder& sb, const MaterialInfo& mat setTexture(sb, L"metallic_map", prtu::toUTF16FromOSNarrow(matInfo.metallicMap)); setTexture(sb, L"normal_map", prtu::toUTF16FromOSNarrow(matInfo.normalMap)); setTexture(sb, L"roughness_map", prtu::toUTF16FromOSNarrow(matInfo.roughnessMap)); - setTexture(sb, L"opacity_map", prtu::toUTF16FromOSNarrow(matInfo.opacityMap)); + setTexture(sb, L"opacity_map", prtu::toUTF16FromOSNarrow(matInfo.opacityMap), L"opacity_map_uses_alpha_channel"); } } // namespace From 3544a01c3786745ed8a1dd2037bdf3fa01d3e0bf Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 8 Feb 2022 14:05:18 +0100 Subject: [PATCH 349/668] Cleanup: add std:: prefix to wcsstr --- src/serlio/modifiers/MayaCallbacks.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/MayaCallbacks.h b/src/serlio/modifiers/MayaCallbacks.h index 12e6ba08..235d4ab7 100644 --- a/src/serlio/modifiers/MayaCallbacks.h +++ b/src/serlio/modifiers/MayaCallbacks.h @@ -48,7 +48,7 @@ class MayaCallbacks : public IMayaCallbacks { prt::Status assetError(size_t /*isIndex*/, prt::CGAErrorLevel level, const wchar_t* /*key*/, const wchar_t* /*uri*/, const wchar_t* message) override { LOG_ERR << "ASSET ERROR: " << message; - if (message != nullptr && wcsstr(message, L"CGAC version")) { + if (message != nullptr && std::wcsstr(message, L"CGAC version")) { cgacErrors.push_back(std::make_pair(level, message)); } return prt::STATUS_OK; From 8dd0e1f438596f53a3196c882a2d827c20d513b5 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 8 Feb 2022 14:06:45 +0100 Subject: [PATCH 350/668] Cleanup: remove cached state of warnings attribute --- src/serlio/modifiers/PRTModifierNode.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierNode.cpp b/src/serlio/modifiers/PRTModifierNode.cpp index cd426c64..410fbd7e 100644 --- a/src/serlio/modifiers/PRTModifierNode.cpp +++ b/src/serlio/modifiers/PRTModifierNode.cpp @@ -217,7 +217,6 @@ MStatus PRTModifierNode::initialize() stringData.create(&stat2), &stat); MCHECK(stat2); MCHECK(stat); - MCHECK(fAttr.setCached(true)); MCHECK(fAttr.setStorable(true)); MCHECK(fAttr.setHidden(true)); MCHECK(fAttr.setConnectable(false)); From 5e840d32bd63b84c7e77c582396d5eaac04c5642 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 8 Feb 2022 14:22:41 +0100 Subject: [PATCH 351/668] Cleanup: move implementations in callbacks header to cpp file --- src/serlio/modifiers/MayaCallbacks.cpp | 44 +++++++++++++++++++++++++ src/serlio/modifiers/MayaCallbacks.h | 45 +++++++------------------- 2 files changed, 55 insertions(+), 34 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 24719ad1..040a5629 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -414,6 +414,50 @@ std::filesystem::path getAssetDir() { } } // namespace +prt::Status MayaCallbacks::generateError(size_t /*isIndex*/, prt::Status /*status*/, const wchar_t* message) { + LOG_ERR << "GENERATE ERROR: " << message; + return prt::STATUS_OK; +} + +prt::Status MayaCallbacks::assetError(size_t /*isIndex*/, prt::CGAErrorLevel level, const wchar_t* /*key*/, + const wchar_t* /*uri*/, const wchar_t* message) { + LOG_ERR << "ASSET ERROR: " << message; + if (message != nullptr && std::wcsstr(message, L"CGAC version")) { + cgacErrors.push_back(std::make_pair(level, message)); + } + return prt::STATUS_OK; +} + +prt::Status MayaCallbacks::cgaError(size_t /*isIndex*/, int32_t /*shapeID*/, prt::CGAErrorLevel /*level*/, + int32_t /*methodId*/, int32_t /*pc*/, const wchar_t* message) { + LOG_ERR << "CGA ERROR: " << message; + return prt::STATUS_OK; +} + +prt::Status MayaCallbacks::cgaPrint(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* txt) { + LOG_INF << "CGA PRINT: " << txt; + return prt::STATUS_OK; +} + +prt::Status MayaCallbacks::cgaReportBool(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* /*key*/, + bool /*value*/) { + return prt::STATUS_OK; +} + +prt::Status MayaCallbacks::cgaReportFloat(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* /*key*/, + double /*value*/) { + return prt::STATUS_OK; +} + +prt::Status MayaCallbacks::cgaReportString(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* /*key*/, + const wchar_t* /*value*/) { + return prt::STATUS_OK; +} + +const CGACErrors& MayaCallbacks::getCGACErrors() const { + return cgacErrors; +} + void MayaCallbacks::addMesh(const wchar_t*, const double* vtx, size_t vtxSize, const double* nrm, size_t nrmSize, const uint32_t* faceCounts, size_t faceCountsSize, const uint32_t* vertexIndices, size_t vertexIndicesSize, const uint32_t* normalIndices, size_t normalIndicesSize, diff --git a/src/serlio/modifiers/MayaCallbacks.h b/src/serlio/modifiers/MayaCallbacks.h index 235d4ab7..d4e7be22 100644 --- a/src/serlio/modifiers/MayaCallbacks.h +++ b/src/serlio/modifiers/MayaCallbacks.h @@ -41,39 +41,19 @@ class MayaCallbacks : public IMayaCallbacks { : inMeshObj(inMesh), outMeshObj(outMesh), mAttributeMapBuilder(amb) {} // prt::Callbacks interface - prt::Status generateError(size_t /*isIndex*/, prt::Status /*status*/, const wchar_t* message) override { - LOG_ERR << "GENERATE ERROR: " << message; - return prt::STATUS_OK; - } - prt::Status assetError(size_t /*isIndex*/, prt::CGAErrorLevel level, const wchar_t* /*key*/, - const wchar_t* /*uri*/, const wchar_t* message) override { - LOG_ERR << "ASSET ERROR: " << message; - if (message != nullptr && std::wcsstr(message, L"CGAC version")) { - cgacErrors.push_back(std::make_pair(level, message)); - } - return prt::STATUS_OK; - } + prt::Status generateError(size_t /*isIndex*/, prt::Status /*status*/, const wchar_t* message) override; + prt::Status assetError(size_t /*isIndex*/, prt::CGAErrorLevel level, const wchar_t* /*key*/, const wchar_t* /*uri*/, + const wchar_t* message) override; + prt::Status cgaError(size_t /*isIndex*/, int32_t /*shapeID*/, prt::CGAErrorLevel /*level*/, int32_t /*methodId*/, - int32_t /*pc*/, const wchar_t* message) override { - LOG_ERR << "CGA ERROR: " << message; - return prt::STATUS_OK; - } - prt::Status cgaPrint(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* txt) override { - LOG_INF << "CGA PRINT: " << txt; - return prt::STATUS_OK; - } - prt::Status cgaReportBool(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* /*key*/, - bool /*value*/) override { - return prt::STATUS_OK; - } + int32_t /*pc*/, const wchar_t* message) override; + prt::Status cgaPrint(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* txt) override; + prt::Status cgaReportBool(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* /*key*/, bool /*value*/) override; prt::Status cgaReportFloat(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* /*key*/, - double /*value*/) override { - return prt::STATUS_OK; - } + double /*value*/) override; prt::Status cgaReportString(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* /*key*/, - const wchar_t* /*value*/) override { - return prt::STATUS_OK; - } + const wchar_t* /*value*/) override; + prt::Status attrBool(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* /*key*/, bool /*value*/) override; prt::Status attrFloat(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* /*key*/, double /*value*/) override; prt::Status attrString(size_t /*isIndex*/, int32_t /*shapeID*/, const wchar_t* /*key*/, @@ -100,9 +80,7 @@ class MayaCallbacks : public IMayaCallbacks { #endif // PRT version >= 2.1 - const CGACErrors& getCGACErrors() const { - return cgacErrors; - } + const CGACErrors& getCGACErrors() const; // clang-format off void addMesh(const wchar_t* name, @@ -126,7 +104,6 @@ class MayaCallbacks : public IMayaCallbacks { void addAsset(const wchar_t* uri, const wchar_t* fileName, const uint8_t* buffer, size_t size, wchar_t* result, size_t& resultSize) override; - private: CGACErrors cgacErrors; MObject outMeshObj; From 2517ef84e035693111fc94cebd68a5b6a86b2542 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 8 Feb 2022 14:27:14 +0100 Subject: [PATCH 352/668] Cleanup: pulled cgac version missmatch detection into separate function --- src/serlio/modifiers/MayaCallbacks.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 040a5629..78d82b96 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -412,6 +412,11 @@ std::filesystem::path getAssetDir() { } return assetDir; } + +void detectAndAppendCGACErrors(prt::CGAErrorLevel level, const wchar_t* message, CGACErrors& cgacErrors) { + if (message != nullptr && std::wcsstr(message, L"CGAC version")) + cgacErrors.push_back(std::make_pair(level, message)); +} } // namespace prt::Status MayaCallbacks::generateError(size_t /*isIndex*/, prt::Status /*status*/, const wchar_t* message) { @@ -422,9 +427,7 @@ prt::Status MayaCallbacks::generateError(size_t /*isIndex*/, prt::Status /*statu prt::Status MayaCallbacks::assetError(size_t /*isIndex*/, prt::CGAErrorLevel level, const wchar_t* /*key*/, const wchar_t* /*uri*/, const wchar_t* message) { LOG_ERR << "ASSET ERROR: " << message; - if (message != nullptr && std::wcsstr(message, L"CGAC version")) { - cgacErrors.push_back(std::make_pair(level, message)); - } + detectAndAppendCGACErrors(level, message, cgacErrors); return prt::STATUS_OK; } From 30af8a77b352b88bc63966ba9b111f44a14f99be Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 8 Feb 2022 14:37:52 +0100 Subject: [PATCH 353/668] Use other warning icon to support linux verision of maya. --- src/serlio/scripts/AEserlioTemplate.mel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index a1a97c1e..8bef5924 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -125,7 +125,7 @@ global proc prtFileBrowse(string $attr, string $varname, string $filters){ string $node = prtGetNodeName($attr); - iconTextStaticLabel -st "iconOnly" -i "warningIcon.svg" -l "warning" -visible false ($node + "_CGAC_Warnings"); + iconTextStaticLabel -st "iconOnly" -i "caution.png" -l "warning" -visible false ($node + "_CGAC_Warnings"); updateCGACWarning($node); string $changeCommand = "updateCGACWarning " + $node; From ac0f7f580fe70e2b7937e96bb6ae53a68f61e61a Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 8 Feb 2022 15:40:16 +0100 Subject: [PATCH 354/668] Log generateError and newer than current warning --- src/serlio/modifiers/MayaCallbacks.cpp | 8 ++++++-- src/serlio/modifiers/MayaCallbacks.h | 2 +- src/serlio/modifiers/PRTModifierAction.cpp | 22 ++++++++++++++++++---- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 78d82b96..e9c8e855 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -414,13 +414,17 @@ std::filesystem::path getAssetDir() { } void detectAndAppendCGACErrors(prt::CGAErrorLevel level, const wchar_t* message, CGACErrors& cgacErrors) { - if (message != nullptr && std::wcsstr(message, L"CGAC version")) - cgacErrors.push_back(std::make_pair(level, message)); + if (message != nullptr && (std::wcsstr(message, L"CGAC version") || std::wcsstr(message, L"Non-recognized builtin method"))) { + const bool shouldBeLogged = + (std::wcsstr(message, L"newer than current") || (level == prt::CGAErrorLevel::CGAERROR)); + cgacErrors.push_back(std::make_tuple(level, shouldBeLogged, message)); + } } } // namespace prt::Status MayaCallbacks::generateError(size_t /*isIndex*/, prt::Status /*status*/, const wchar_t* message) { LOG_ERR << "GENERATE ERROR: " << message; + detectAndAppendCGACErrors(prt::CGAErrorLevel::CGAERROR, message, cgacErrors); return prt::STATUS_OK; } diff --git a/src/serlio/modifiers/MayaCallbacks.h b/src/serlio/modifiers/MayaCallbacks.h index d4e7be22..92022c3f 100644 --- a/src/serlio/modifiers/MayaCallbacks.h +++ b/src/serlio/modifiers/MayaCallbacks.h @@ -33,7 +33,7 @@ #include #include -using CGACErrors = std::vector>; +using CGACErrors = std::vector>; class MayaCallbacks : public IMayaCallbacks { public: diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 3e6f658b..9dae1720 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -331,7 +331,15 @@ short getDefaultEnumIdx(const prt::Annotation* annot, const PRTEnumDefaultValue& bool cgacErrorListHasErrors(CGACErrors errorList) { for (const auto& error : errorList) { - if (error.first == prt::CGAErrorLevel::CGAERROR) + if (std::get<0>(error) == prt::CGAErrorLevel::CGAERROR) + return true; + } + return false; +} + +bool cgacErrorsShouldBeLogged(CGACErrors errorList) { + for (const auto& error : errorList) { + if (std::get<1>(error)) return true; } return false; @@ -343,7 +351,7 @@ MString cgacErrorsToString(CGACErrors errorList) { if (errorString.length() > 0) errorString += "\n"; - errorString += error.second.c_str(); + errorString += std::get<2>(error).c_str(); } return errorString; } @@ -645,8 +653,14 @@ MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacWarnin MString cgacErrorString = cgacErrorsToString(mCGACErrors); if (cgacWarningData.asString() != cgacErrorString) { cgacWarningData.setString(cgacErrorString); - if (cgacErrorListHasErrors(mCGACErrors)) - MGlobal::displayError(cgacErrorString); + if (cgacErrorsShouldBeLogged(mCGACErrors)) { + if (cgacErrorListHasErrors(mCGACErrors)) { + MGlobal::displayError(cgacErrorString); + } + else { + MGlobal::displayWarning(cgacErrorString); + } + } } iterateThroughAttributesAndApply(node, mRuleAttributes, updateUIFromAttributes); From 88a5f64b1146c23865914af1e70b04175cac017f Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 10 Feb 2022 08:48:37 +0100 Subject: [PATCH 355/668] Cleanup: set storable of cgacVersionWarning to false --- src/serlio/modifiers/PRTModifierNode.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierNode.cpp b/src/serlio/modifiers/PRTModifierNode.cpp index 410fbd7e..458ccf92 100644 --- a/src/serlio/modifiers/PRTModifierNode.cpp +++ b/src/serlio/modifiers/PRTModifierNode.cpp @@ -217,7 +217,6 @@ MStatus PRTModifierNode::initialize() stringData.create(&stat2), &stat); MCHECK(stat2); MCHECK(stat); - MCHECK(fAttr.setStorable(true)); MCHECK(fAttr.setHidden(true)); MCHECK(fAttr.setConnectable(false)); MCHECK(addAttribute(cgacWarnings)); From 11e7775cb082e51aa2d4d8ccb068b27fe6a849f9 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 10 Feb 2022 11:56:03 +0100 Subject: [PATCH 356/668] Show red error icon in case of generate errors --- src/serlio/modifiers/PRTModifierAction.cpp | 1 + src/serlio/scripts/AEserlioTemplate.mel | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 9dae1720..d3cdb83a 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -351,6 +351,7 @@ MString cgacErrorsToString(CGACErrors errorList) { if (errorString.length() > 0) errorString += "\n"; + errorString += std::get<0>(error) == prt::CGAErrorLevel::CGAERROR ? "Error: " : "Warning: "; errorString += std::get<2>(error).c_str(); } return errorString; diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 8bef5924..1bc0bb8d 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -105,7 +105,13 @@ global proc updateCGACWarning(string $node){ string $control = prtControlName($node) + "_CGAC_Warnings"; prtSetVisibility($hasErrors, $control); - iconTextStaticLabel -e -ann $cgacErrors $control; + + int $displayErrorIcon = size(`match "Error:" $cgacErrors`) > 0; + string $icon = "caution.png"; + if ($displayErrorIcon) + $icon = "error.png"; + + iconTextStaticLabel -e -ann $cgacErrors -i $icon $control; } global proc prtFileBrowse(string $attr, string $varname, string $filters){ From d5d130df81183686f8da710bad9c302c0d8403f1 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 10 Feb 2022 11:57:33 +0100 Subject: [PATCH 357/668] Replace the cgac version with respective CityEngine version. --- src/serlio/modifiers/MayaCallbacks.cpp | 43 +++++++++++++++++++++++++- src/serlio/utils/Utilities.h | 11 +++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index e9c8e855..b1bc6630 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -413,11 +413,52 @@ std::filesystem::path getAssetDir() { return assetDir; } +struct cgaToCEMapping { + std::wstring cgaVersion; + std::wstring cityEngineVersion; +}; + +const std::vector cgacToCEVersion = { + // clang-format off + {L"1.17", L"2021.1"}, + {L"1.16", L"2021.0"}, + {L"1.15", L"2020.1"}, + {L"1.14", L"2020.0"}, + {L"1.13", L"2019.1"}, + {L"1.12", L"2019.0"}, + {L"1.11", L"2018.1"}, + {L"1.10", L"2018.0"}, + {L"1.9", L"2017.1"}, + {L"1.8", L"2017.0"}, + {L"1.7", L"2016.1"}, + {L"1.6", L"2016.0"}, + {L"1.5", L"2015.2"}, + {L"1.5", L"2015.1"}, + {L"1.5", L"2015.0"}, + {L"1.4", L"2014.1"}, + {L"1.3", L"2014.1"}, + {L"1.2", L"2014.0"}, + {L"1.1", L"2013.1"} + // clang-format on +}; + +void replaceCGAWithCEVersion(std::wstring& string) { + replaceAllSubstrings(string, L"CGAC version", L"CityEngine version"); + for (const auto& entry: cgacToCEVersion) { + // pad search and replace string with spaces or braces (to avoid replacing wrong substrings) + replaceAllSubstrings(string, L" " + entry.cgaVersion + L" ", L" " + entry.cityEngineVersion + L" "); + replaceAllSubstrings(string, L"(" + entry.cgaVersion + L")", L"(" + entry.cityEngineVersion + L")"); + } +} + void detectAndAppendCGACErrors(prt::CGAErrorLevel level, const wchar_t* message, CGACErrors& cgacErrors) { if (message != nullptr && (std::wcsstr(message, L"CGAC version") || std::wcsstr(message, L"Non-recognized builtin method"))) { const bool shouldBeLogged = (std::wcsstr(message, L"newer than current") || (level == prt::CGAErrorLevel::CGAERROR)); - cgacErrors.push_back(std::make_tuple(level, shouldBeLogged, message)); + + std::wstring stringMessage = message; + replaceCGAWithCEVersion(stringMessage); + cgacErrors.push_back(std::make_tuple(level, shouldBeLogged, stringMessage)); } } } // namespace diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index 7ef6acba..09adf4fb 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -230,6 +230,17 @@ inline void replace_all_not_of(std::wstring& s, const std::wstring& allowedChars } } +inline void replaceAllSubstrings(std::wstring& src, const std::wstring& search, const std::wstring& replace) { + for (size_t pos = 0;; pos += replace.length()) { + // Locate the substring to replace + pos = src.find(search, pos); + if (pos == std::wstring::npos) + break; + // Replace + src.replace(pos, search.length(), replace); + } +} + inline bool startsWithAnyOf(const std::string& s, const std::vector& sv) { for (const auto& v : sv) { if (s.find(v) == 0) From 4a746ed7de7ea3e7eb9357f25b09b476a692f1a5 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 10 Feb 2022 12:08:16 +0100 Subject: [PATCH 358/668] Cleanup: use struct instead of tuple for CGACError --- src/serlio/modifiers/MayaCallbacks.cpp | 2 +- src/serlio/modifiers/MayaCallbacks.h | 7 ++++++- src/serlio/modifiers/PRTModifierAction.cpp | 8 ++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index b1bc6630..f6df1f7d 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -458,7 +458,7 @@ void detectAndAppendCGACErrors(prt::CGAErrorLevel level, const wchar_t* message, std::wstring stringMessage = message; replaceCGAWithCEVersion(stringMessage); - cgacErrors.push_back(std::make_tuple(level, shouldBeLogged, stringMessage)); + cgacErrors.push_back({level, shouldBeLogged, stringMessage}); } } } // namespace diff --git a/src/serlio/modifiers/MayaCallbacks.h b/src/serlio/modifiers/MayaCallbacks.h index 92022c3f..4361afce 100644 --- a/src/serlio/modifiers/MayaCallbacks.h +++ b/src/serlio/modifiers/MayaCallbacks.h @@ -33,7 +33,12 @@ #include #include -using CGACErrors = std::vector>; +struct CGACError { + prt::CGAErrorLevel errorLevel; + bool shouldBeLogged; + std::wstring errorString; +}; +using CGACErrors = std::vector; class MayaCallbacks : public IMayaCallbacks { public: diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index d3cdb83a..2b83d0a5 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -331,7 +331,7 @@ short getDefaultEnumIdx(const prt::Annotation* annot, const PRTEnumDefaultValue& bool cgacErrorListHasErrors(CGACErrors errorList) { for (const auto& error : errorList) { - if (std::get<0>(error) == prt::CGAErrorLevel::CGAERROR) + if (error.errorLevel == prt::CGAErrorLevel::CGAERROR) return true; } return false; @@ -339,7 +339,7 @@ bool cgacErrorListHasErrors(CGACErrors errorList) { bool cgacErrorsShouldBeLogged(CGACErrors errorList) { for (const auto& error : errorList) { - if (std::get<1>(error)) + if (error.shouldBeLogged) return true; } return false; @@ -351,8 +351,8 @@ MString cgacErrorsToString(CGACErrors errorList) { if (errorString.length() > 0) errorString += "\n"; - errorString += std::get<0>(error) == prt::CGAErrorLevel::CGAERROR ? "Error: " : "Warning: "; - errorString += std::get<2>(error).c_str(); + errorString += error.errorLevel == prt::CGAErrorLevel::CGAERROR ? "Error: " : "Warning: "; + errorString += error.errorString.c_str(); } return errorString; } From de8a0f646234774796a4a6817c491719ad9fa36c Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 10 Feb 2022 12:55:18 +0100 Subject: [PATCH 359/668] Cleanup: rename cgacErrors to cgacProblems... This is also more in line with the problems window and avoids the ambiguity between a generic error i.e. a problem (error/warning/info) and an error type error. --- src/serlio/modifiers/PRTModifierAction.cpp | 20 ++++++++-------- src/serlio/modifiers/PRTModifierAction.h | 4 ++-- src/serlio/modifiers/PRTModifierNode.cpp | 14 +++++------ src/serlio/modifiers/PRTModifierNode.h | 2 +- src/serlio/scripts/AEserlioTemplate.mel | 28 +++++++++++----------- 5 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 2b83d0a5..94119e51 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -329,7 +329,7 @@ short getDefaultEnumIdx(const prt::Annotation* annot, const PRTEnumDefaultValue& return 0; } -bool cgacErrorListHasErrors(CGACErrors errorList) { +bool cgacProblemsHaveErrors(CGACErrors errorList) { for (const auto& error : errorList) { if (error.errorLevel == prt::CGAErrorLevel::CGAERROR) return true; @@ -337,7 +337,7 @@ bool cgacErrorListHasErrors(CGACErrors errorList) { return false; } -bool cgacErrorsShouldBeLogged(CGACErrors errorList) { +bool cgacProblemsShouldBeLogged(CGACErrors errorList) { for (const auto& error : errorList) { if (error.shouldBeLogged) return true; @@ -345,7 +345,7 @@ bool cgacErrorsShouldBeLogged(CGACErrors errorList) { return false; } -MString cgacErrorsToString(CGACErrors errorList) { +MString cgacProblemsToString(CGACErrors errorList) { MString errorString; for (const auto& error : errorList) { if (errorString.length() > 0) @@ -561,7 +561,7 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { return MStatus::kSuccess; } -MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacWarningData) { +MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacProblemData) { const auto updateUIFromAttributes = [this](const MFnDependencyNode& fnNode, const MFnAttribute& fnAttribute, const RuleAttribute& ruleAttribute, const PrtAttributeType attrType) { const AttributeMapUPtr defaultAttributeValues = @@ -651,11 +651,11 @@ MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacWarnin } }; - MString cgacErrorString = cgacErrorsToString(mCGACErrors); - if (cgacWarningData.asString() != cgacErrorString) { - cgacWarningData.setString(cgacErrorString); - if (cgacErrorsShouldBeLogged(mCGACErrors)) { - if (cgacErrorListHasErrors(mCGACErrors)) { + MString cgacErrorString = cgacProblemsToString(mCGACProblems); + if (cgacProblemData.asString() != cgacErrorString) { + cgacProblemData.setString(cgacErrorString); + if (cgacProblemsShouldBeLogged(mCGACProblems)) { + if (cgacProblemsHaveErrors(mCGACProblems)) { MGlobal::displayError(cgacErrorString); } else { @@ -854,7 +854,7 @@ MStatus PRTModifierAction::doIt() { prt::generate(shapes.data(), shapes.size(), nullptr, encIDs.data(), encIDs.size(), encOpts.data(), outputHandler.get(), PRTContext::get().mPRTCache.get(), nullptr); - mCGACErrors = outputHandler->getCGACErrors(); + mCGACProblems = outputHandler->getCGACErrors(); if (generateStatus != prt::STATUS_OK) { std::string generateFailedMessage = "prt generate failed: "; diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index b4d9feeb..51452b63 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -70,7 +70,7 @@ class PRTModifierAction : public polyModifierFty { MStatus updateRuleFiles(const MObject& node, const MString& rulePkg); MStatus fillAttributesFromNode(const MObject& node); MStatus updateUserSetAttributes(const MObject& node); - MStatus updateUI(const MObject& node, MDataHandle& cgacWarningData); + MStatus updateUI(const MObject& node, MDataHandle& cgacProblemData); void setMesh(MObject& _inMesh, MObject& _outMesh); void setRandomSeed(int32_t randomSeed) { mRandomSeed = randomSeed; @@ -94,7 +94,7 @@ class PRTModifierAction : public polyModifierFty { // Set in updateRuleFiles(rulePkg) MString mRulePkg; - CGACErrors mCGACErrors; + CGACErrors mCGACProblems; std::wstring mRuleFile; std::wstring mStartRule; const std::wstring mRuleStyle = L"Default"; // Serlio atm only supports the "Default" style diff --git a/src/serlio/modifiers/PRTModifierNode.cpp b/src/serlio/modifiers/PRTModifierNode.cpp index 458ccf92..9c4de1d9 100644 --- a/src/serlio/modifiers/PRTModifierNode.cpp +++ b/src/serlio/modifiers/PRTModifierNode.cpp @@ -38,13 +38,13 @@ namespace { const MString NAME_RULE_PKG = "Rule_Package"; const MString NAME_RANDOM_SEED = "Random_Seed"; -const MString CGAC_WARNINGS = "CGAC_Warnings"; +const MString CGAC_PROBLEMS = "CGAC_Problems"; } // namespace // Unique Node TypeId MTypeId PRTModifierNode::id(SerlioNodeIDs::SERLIO_PREFIX, SerlioNodeIDs::PRT_GEOMETRY_NODE); MObject PRTModifierNode::rulePkg; -MObject PRTModifierNode::cgacWarnings; +MObject PRTModifierNode::cgacProblems; MObject PRTModifierNode::currentRulePkg; MObject PRTModifierNode::mRandomSeed; @@ -96,8 +96,8 @@ MStatus PRTModifierNode::compute(const MPlug& plug, MDataBlock& data) { MDataHandle currentRulePkgData = data.inputValue(currentRulePkg, &status); MCheckStatus(status, "ERROR getting currentRulePkg"); - MDataHandle cgacWarningData = data.inputValue(cgacWarnings, &status); - MCheckStatus(status, "ERROR getting cgacWarnings"); + MDataHandle cgacProblemData = data.inputValue(cgacProblems, &status); + MCheckStatus(status, "ERROR getting cgacErrors"); const bool ruleFileWasChanged = (rulePkgData.asString() != currentRulePkgData.asString()); @@ -127,7 +127,7 @@ MStatus PRTModifierNode::compute(const MPlug& plug, MDataBlock& data) { // Now, perform the PRT status = fPRTModifierAction.doIt(); - fPRTModifierAction.updateUI(thisMObject(), cgacWarningData); + fPRTModifierAction.updateUI(thisMObject(), cgacProblemData); currentRulePkgData.setString(rulePkgData.asString()); @@ -213,13 +213,13 @@ MStatus PRTModifierNode::initialize() MCHECK(fAttr.setConnectable(false)); MCHECK(addAttribute(currentRulePkg)); - cgacWarnings = fAttr.create(CGAC_WARNINGS, "cgacErrors", MFnData::kString, + cgacProblems = fAttr.create(CGAC_PROBLEMS, "cgacErrors", MFnData::kString, stringData.create(&stat2), &stat); MCHECK(stat2); MCHECK(stat); MCHECK(fAttr.setHidden(true)); MCHECK(fAttr.setConnectable(false)); - MCHECK(addAttribute(cgacWarnings)); + MCHECK(addAttribute(cgacProblems)); // Set up a dependency between the input and the output. This will cause // the output to be marked dirty when the input changes. The output will diff --git a/src/serlio/modifiers/PRTModifierNode.h b/src/serlio/modifiers/PRTModifierNode.h index 7450e4ad..b09c789c 100644 --- a/src/serlio/modifiers/PRTModifierNode.h +++ b/src/serlio/modifiers/PRTModifierNode.h @@ -38,7 +38,7 @@ class PRTModifierNode : public polyModifierNode { public: // non-dynamic node attributes static MObject rulePkg; - static MObject cgacWarnings; + static MObject cgacProblems; static MObject currentRulePkg; static MTypeId id; static MObject mRandomSeed; diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 1bc0bb8d..c136cf04 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -94,24 +94,24 @@ global proc prtShowFileDialog(string $attr, string $filters) { } } -global proc updateCGACWarning(string $node){ - string $attr = $node + ".CGAC_Warnings"; +global proc updateCGACProblems(string $node){ + string $attr = $node + ".CGAC_Problems"; if (!prtAttributeExists($attr)) return; - string $cgacErrors = `getAttr ($attr)`; - int $hasErrors = (size($cgacErrors) > 0); - string $control = prtControlName($node) + "_CGAC_Warnings"; + string $cgacProblems = `getAttr ($attr)`; + int $hasProblems = (size($cgacProblems) > 0); + string $control = prtControlName($node) + "_CGAC_Problems"; - prtSetVisibility($hasErrors, $control); - - int $displayErrorIcon = size(`match "Error:" $cgacErrors`) > 0; + prtSetVisibility($hasProblems, $control); + + int $displayErrorIcon = size(`match "Error:" $cgacProblems`) > 0; string $icon = "caution.png"; if ($displayErrorIcon) $icon = "error.png"; - - iconTextStaticLabel -e -ann $cgacErrors -i $icon $control; + + iconTextStaticLabel -e -ann $cgacProblems -i $icon $control; } global proc prtFileBrowse(string $attr, string $varname, string $filters){ @@ -131,11 +131,11 @@ global proc prtFileBrowse(string $attr, string $varname, string $filters){ string $node = prtGetNodeName($attr); - iconTextStaticLabel -st "iconOnly" -i "caution.png" -l "warning" -visible false ($node + "_CGAC_Warnings"); - updateCGACWarning($node); + iconTextStaticLabel -st "iconOnly" -i "caution.png" -l "error" -visible false ($node + "_CGAC_Problems"); + updateCGACProblems($node); - string $changeCommand = "updateCGACWarning " + $node; - scriptJob -rp -p $control -ac ($node + ".CGAC_Warnings") $changeCommand; + string $changeCommand = "updateCGACProblems " + $node; + scriptJob -rp -p $control -ac ($node + ".CGAC_Problems") $changeCommand; symbolButton -image "navButtonBrowse.xpm" -c ("prtShowFileDialog(\"" + $attr + "\",\"" + $filters + "\" )") ($control + "_browse"); symbolButton -ann "reload rule package" -image "refresh.png" -c ("prtReloadRPK(\"" + $attr + "\")") ($control + "_reload"); From dc26f9af5be0752451f92ea311ceb9ae92b2eae8 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 10 Feb 2022 19:07:02 +0100 Subject: [PATCH 360/668] Cleanup: use emplace_back instead of push_back --- src/serlio/modifiers/MayaCallbacks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index f6df1f7d..f6a491ae 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -458,7 +458,7 @@ void detectAndAppendCGACErrors(prt::CGAErrorLevel level, const wchar_t* message, std::wstring stringMessage = message; replaceCGAWithCEVersion(stringMessage); - cgacErrors.push_back({level, shouldBeLogged, stringMessage}); + cgacErrors.emplace_back(CGACError{level, shouldBeLogged, stringMessage}); } } } // namespace From a4aa376351fb5810a30c20c5625a5017a2930fe9 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 11 Feb 2022 09:41:14 +0100 Subject: [PATCH 361/668] Cleanup: use replace string from prt utils --- src/serlio/modifiers/MayaCallbacks.cpp | 2 +- src/serlio/utils/Utilities.h | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index f6a491ae..a588fb3a 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -443,7 +443,7 @@ const std::vector cgacToCEVersion = { }; void replaceCGAWithCEVersion(std::wstring& string) { - replaceAllSubstrings(string, L"CGAC version", L"CityEngine version"); + replaceAllSubstrings(string, std::wstring(L"CGAC version"), std::wstring(L"CityEngine version")); for (const auto& entry: cgacToCEVersion) { // pad search and replace string with spaces or braces (to avoid replacing wrong substrings) replaceAllSubstrings(string, L" " + entry.cgaVersion + L" ", L" " + entry.cityEngineVersion + L" "); diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index 09adf4fb..ef0564f5 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -230,14 +230,13 @@ inline void replace_all_not_of(std::wstring& s, const std::wstring& allowedChars } } -inline void replaceAllSubstrings(std::wstring& src, const std::wstring& search, const std::wstring& replace) { - for (size_t pos = 0;; pos += replace.length()) { - // Locate the substring to replace - pos = src.find(search, pos); - if (pos == std::wstring::npos) - break; - // Replace - src.replace(pos, search.length(), replace); +template +void replaceAllSubstrings(std::basic_string& str, const std::basic_string& oldStr, + const std::basic_string& newStr) { + typename std::basic_string::size_type pos = 0; + while ((pos = str.find(oldStr, pos)) != std::basic_string::npos) { + str.replace(pos, oldStr.length(), newStr); + pos += newStr.length(); } } From e1df96d70d4177880bd41ca4a30e72eba3035483 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 11 Feb 2022 09:42:38 +0100 Subject: [PATCH 362/668] Cleanup: add constructor for CGAError struct --- src/serlio/modifiers/MayaCallbacks.cpp | 2 +- src/serlio/modifiers/MayaCallbacks.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index a588fb3a..93b7cc5c 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -458,7 +458,7 @@ void detectAndAppendCGACErrors(prt::CGAErrorLevel level, const wchar_t* message, std::wstring stringMessage = message; replaceCGAWithCEVersion(stringMessage); - cgacErrors.emplace_back(CGACError{level, shouldBeLogged, stringMessage}); + cgacErrors.emplace_back(level, shouldBeLogged, stringMessage); } } } // namespace diff --git a/src/serlio/modifiers/MayaCallbacks.h b/src/serlio/modifiers/MayaCallbacks.h index 4361afce..f37c7b82 100644 --- a/src/serlio/modifiers/MayaCallbacks.h +++ b/src/serlio/modifiers/MayaCallbacks.h @@ -37,6 +37,9 @@ struct CGACError { prt::CGAErrorLevel errorLevel; bool shouldBeLogged; std::wstring errorString; + + CGACError(prt::CGAErrorLevel errorLevel, bool shouldBeLogged, const std::wstring& errorString) + : errorLevel(errorLevel), shouldBeLogged(shouldBeLogged), errorString(errorString) {} }; using CGACErrors = std::vector; From 51476ec6e4465f7083043c68a11ef8ffe5e40396 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 11 Feb 2022 09:46:12 +0100 Subject: [PATCH 363/668] Cleanup: add braces to conditional in ternary operator --- src/serlio/modifiers/PRTModifierAction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 94119e51..399476f7 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -351,7 +351,7 @@ MString cgacProblemsToString(CGACErrors errorList) { if (errorString.length() > 0) errorString += "\n"; - errorString += error.errorLevel == prt::CGAErrorLevel::CGAERROR ? "Error: " : "Warning: "; + errorString += (error.errorLevel == prt::CGAErrorLevel::CGAERROR) ? "Error: " : "Warning: "; errorString += error.errorString.c_str(); } return errorString; From 7ce9277e3a081a680f5a74091979b2881bf64831 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 11 Feb 2022 10:06:34 +0100 Subject: [PATCH 364/668] Cleanup: summarize 2015 versions using the same cgac version --- src/serlio/modifiers/MayaCallbacks.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 93b7cc5c..8ce4ec17 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -432,9 +432,7 @@ const std::vector cgacToCEVersion = { {L"1.8", L"2017.0"}, {L"1.7", L"2016.1"}, {L"1.6", L"2016.0"}, - {L"1.5", L"2015.2"}, - {L"1.5", L"2015.1"}, - {L"1.5", L"2015.0"}, + {L"1.5", L"2015.0 - 2015.2"}, {L"1.4", L"2014.1"}, {L"1.3", L"2014.1"}, {L"1.2", L"2014.0"}, From 2e88f196157bd1f9a08ca484f04d8dce4d9bcff0 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 11 Feb 2022 12:10:42 +0100 Subject: [PATCH 365/668] Cleanup: use map for cgac/CE versions and dynamically search for CE versions in --- src/serlio/modifiers/MayaCallbacks.cpp | 44 +++++++++++++++++++------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 8ce4ec17..a37cffe7 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -413,12 +413,7 @@ std::filesystem::path getAssetDir() { return assetDir; } -struct cgaToCEMapping { - std::wstring cgaVersion; - std::wstring cityEngineVersion; -}; - -const std::vector cgacToCEVersion = { +const std::map cgacToCEVersion = { // clang-format off {L"1.17", L"2021.1"}, {L"1.16", L"2021.0"}, @@ -440,13 +435,38 @@ const std::vector cgacToCEVersion = { // clang-format on }; -void replaceCGAWithCEVersion(std::wstring& string) { - replaceAllSubstrings(string, std::wstring(L"CGAC version"), std::wstring(L"CityEngine version")); - for (const auto& entry: cgacToCEVersion) { - // pad search and replace string with spaces or braces (to avoid replacing wrong substrings) - replaceAllSubstrings(string, L" " + entry.cgaVersion + L" ", L" " + entry.cityEngineVersion + L" "); - replaceAllSubstrings(string, L"(" + entry.cgaVersion + L")", L"(" + entry.cityEngineVersion + L")"); +void replaceCGACVersionBetween(std::wstring& errorString, const std::wstring prefix, const std::wstring suffix) { + size_t versionStartPos = errorString.find(prefix); + if (versionStartPos != std::wstring::npos) + versionStartPos += prefix.length(); + + const size_t versionEndPos = errorString.find(suffix, versionStartPos); + + if ((versionStartPos == std::wstring::npos) || (versionEndPos == std::wstring::npos)) + return; + + const size_t versionLength = versionEndPos - versionStartPos; + const std::wstring cgacV1 = errorString.substr(versionStartPos, versionLength); + + std::wstring CEVersion; + const auto it = cgacToCEVersion.find(cgacV1); + if (it != cgacToCEVersion.end()) { + CEVersion = it->second; + } + else { + CEVersion = L"newer than 2021.1"; } + errorString.replace(versionStartPos, versionLength, CEVersion); +} + + +void replaceCGAWithCEVersion(std::wstring& errorString) { + // a typical CGAC version error string looks like: + // Potentially unsupported CGAC version X.YY : major number smaller than current (A.BB) + replaceAllSubstrings(errorString, std::wstring(L"CGAC version"), std::wstring(L"CityEngine version")); + + replaceCGACVersionBetween(errorString, L"CityEngine version ", L" "); + replaceCGACVersionBetween(errorString, L"(", L")"); } void detectAndAppendCGACErrors(prt::CGAErrorLevel level, const wchar_t* message, CGACErrors& cgacErrors) { From d0d69fc38d2ce1321c4b2c6fbd310b5cb928daa4 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 11 Feb 2022 13:03:27 +0100 Subject: [PATCH 366/668] Cleanup: move replaceCGACWithCEVersion to Utilities --- src/serlio/modifiers/MayaCallbacks.cpp | 58 +------------------------ src/serlio/utils/Utilities.cpp | 59 ++++++++++++++++++++++++++ src/serlio/utils/Utilities.h | 2 + 3 files changed, 62 insertions(+), 57 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index a37cffe7..cb06bfbf 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -413,69 +413,13 @@ std::filesystem::path getAssetDir() { return assetDir; } -const std::map cgacToCEVersion = { - // clang-format off - {L"1.17", L"2021.1"}, - {L"1.16", L"2021.0"}, - {L"1.15", L"2020.1"}, - {L"1.14", L"2020.0"}, - {L"1.13", L"2019.1"}, - {L"1.12", L"2019.0"}, - {L"1.11", L"2018.1"}, - {L"1.10", L"2018.0"}, - {L"1.9", L"2017.1"}, - {L"1.8", L"2017.0"}, - {L"1.7", L"2016.1"}, - {L"1.6", L"2016.0"}, - {L"1.5", L"2015.0 - 2015.2"}, - {L"1.4", L"2014.1"}, - {L"1.3", L"2014.1"}, - {L"1.2", L"2014.0"}, - {L"1.1", L"2013.1"} - // clang-format on -}; - -void replaceCGACVersionBetween(std::wstring& errorString, const std::wstring prefix, const std::wstring suffix) { - size_t versionStartPos = errorString.find(prefix); - if (versionStartPos != std::wstring::npos) - versionStartPos += prefix.length(); - - const size_t versionEndPos = errorString.find(suffix, versionStartPos); - - if ((versionStartPos == std::wstring::npos) || (versionEndPos == std::wstring::npos)) - return; - - const size_t versionLength = versionEndPos - versionStartPos; - const std::wstring cgacV1 = errorString.substr(versionStartPos, versionLength); - - std::wstring CEVersion; - const auto it = cgacToCEVersion.find(cgacV1); - if (it != cgacToCEVersion.end()) { - CEVersion = it->second; - } - else { - CEVersion = L"newer than 2021.1"; - } - errorString.replace(versionStartPos, versionLength, CEVersion); -} - - -void replaceCGAWithCEVersion(std::wstring& errorString) { - // a typical CGAC version error string looks like: - // Potentially unsupported CGAC version X.YY : major number smaller than current (A.BB) - replaceAllSubstrings(errorString, std::wstring(L"CGAC version"), std::wstring(L"CityEngine version")); - - replaceCGACVersionBetween(errorString, L"CityEngine version ", L" "); - replaceCGACVersionBetween(errorString, L"(", L")"); -} - void detectAndAppendCGACErrors(prt::CGAErrorLevel level, const wchar_t* message, CGACErrors& cgacErrors) { if (message != nullptr && (std::wcsstr(message, L"CGAC version") || std::wcsstr(message, L"Non-recognized builtin method"))) { const bool shouldBeLogged = (std::wcsstr(message, L"newer than current") || (level == prt::CGAErrorLevel::CGAERROR)); std::wstring stringMessage = message; - replaceCGAWithCEVersion(stringMessage); + prtu::replaceCGAWithCEVersion(stringMessage); cgacErrors.emplace_back(level, shouldBeLogged, stringMessage); } } diff --git a/src/serlio/utils/Utilities.cpp b/src/serlio/utils/Utilities.cpp index 4c3b7284..43e15447 100644 --- a/src/serlio/utils/Utilities.cpp +++ b/src/serlio/utils/Utilities.cpp @@ -42,6 +42,56 @@ struct IUnknown; #include #include #include +#include + +namespace { +const std::map cgacToCEVersion = { + // clang-format off + {L"1.17", L"2021.1"}, + {L"1.16", L"2021.0"}, + {L"1.15", L"2020.1"}, + {L"1.14", L"2020.0"}, + {L"1.13", L"2019.1"}, + {L"1.12", L"2019.0"}, + {L"1.11", L"2018.1"}, + {L"1.10", L"2018.0"}, + {L"1.9", L"2017.1"}, + {L"1.8", L"2017.0"}, + {L"1.7", L"2016.1"}, + {L"1.6", L"2016.0"}, + {L"1.5", L"2015.0 - 2015.2"}, + {L"1.4", L"2014.1"}, + {L"1.3", L"2014.1"}, + {L"1.2", L"2014.0"}, + {L"1.1", L"2013.1"}, + {L"1.0", L"2013.0"} + // clang-format on +}; + +void replaceCGACVersionBetween(std::wstring& errorString, const std::wstring prefix, const std::wstring suffix) { + size_t versionStartPos = errorString.find(prefix); + if (versionStartPos != std::wstring::npos) + versionStartPos += prefix.length(); + + const size_t versionEndPos = errorString.find(suffix, versionStartPos); + + if ((versionStartPos == std::wstring::npos) || (versionEndPos == std::wstring::npos)) + return; + + const size_t versionLength = versionEndPos - versionStartPos; + const std::wstring cgacV1 = errorString.substr(versionStartPos, versionLength); + + std::wstring CEVersion; + const auto it = cgacToCEVersion.find(cgacV1); + if (it != cgacToCEVersion.end()) { + CEVersion = it->second; + } + else { + CEVersion = L"newer than 2021.1"; + } + errorString.replace(versionStartPos, versionLength, CEVersion); +} +} namespace prtu { @@ -213,4 +263,13 @@ AttributeMapUPtr createValidatedOptions(const wchar_t* encID, const prt::Attribu return AttributeMapUPtr(validatedOptions); } +void replaceCGAWithCEVersion(std::wstring& errorString) { + // a typical CGAC version error string looks like: + // Potentially unsupported CGAC version X.YY : major number smaller than current (A.BB) + replaceAllSubstrings(errorString, std::wstring(L"CGAC version"), std::wstring(L"CityEngine version")); + + replaceCGACVersionBetween(errorString, L"CityEngine version ", L" "); + replaceCGACVersionBetween(errorString, L"(", L")"); +} + } // namespace prtu diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index ef0564f5..6f3d2f69 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -218,6 +218,8 @@ SRL_TEST_EXPORTS_API inline std::wstring getImport(const std::wstring& fqRuleNam return {}; return ruleWithoutStyle.substr(0, sepPos); } + +SRL_TEST_EXPORTS_API void replaceCGAWithCEVersion(std::wstring& errorString); } // namespace prtu inline void replace_all_not_of(std::wstring& s, const std::wstring& allowedChars) { From 052f527e61910467650f43d92df1d92e86a09c6e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 11 Feb 2022 13:04:41 +0100 Subject: [PATCH 367/668] Cleanup: fix typo in replaceCGACWithCEVersion --- src/serlio/modifiers/MayaCallbacks.cpp | 2 +- src/serlio/utils/Utilities.cpp | 2 +- src/serlio/utils/Utilities.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index cb06bfbf..8bbf592e 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -419,7 +419,7 @@ void detectAndAppendCGACErrors(prt::CGAErrorLevel level, const wchar_t* message, (std::wcsstr(message, L"newer than current") || (level == prt::CGAErrorLevel::CGAERROR)); std::wstring stringMessage = message; - prtu::replaceCGAWithCEVersion(stringMessage); + prtu::replaceCGACWithCEVersion(stringMessage); cgacErrors.emplace_back(level, shouldBeLogged, stringMessage); } } diff --git a/src/serlio/utils/Utilities.cpp b/src/serlio/utils/Utilities.cpp index 43e15447..8a652fd3 100644 --- a/src/serlio/utils/Utilities.cpp +++ b/src/serlio/utils/Utilities.cpp @@ -263,7 +263,7 @@ AttributeMapUPtr createValidatedOptions(const wchar_t* encID, const prt::Attribu return AttributeMapUPtr(validatedOptions); } -void replaceCGAWithCEVersion(std::wstring& errorString) { +void replaceCGACWithCEVersion(std::wstring& errorString) { // a typical CGAC version error string looks like: // Potentially unsupported CGAC version X.YY : major number smaller than current (A.BB) replaceAllSubstrings(errorString, std::wstring(L"CGAC version"), std::wstring(L"CityEngine version")); diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index 6f3d2f69..89569d2e 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -219,7 +219,7 @@ SRL_TEST_EXPORTS_API inline std::wstring getImport(const std::wstring& fqRuleNam return ruleWithoutStyle.substr(0, sepPos); } -SRL_TEST_EXPORTS_API void replaceCGAWithCEVersion(std::wstring& errorString); +SRL_TEST_EXPORTS_API void replaceCGACWithCEVersion(std::wstring& errorString); } // namespace prtu inline void replace_all_not_of(std::wstring& s, const std::wstring& allowedChars) { From 860700502a4df3afc40a567147f19c41b301be23 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 11 Feb 2022 13:38:59 +0100 Subject: [PATCH 368/668] Cleanup: Add space after CGAC version to avoid replacing wrong strings --- src/serlio/utils/Utilities.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/utils/Utilities.cpp b/src/serlio/utils/Utilities.cpp index 8a652fd3..763352f1 100644 --- a/src/serlio/utils/Utilities.cpp +++ b/src/serlio/utils/Utilities.cpp @@ -266,7 +266,7 @@ AttributeMapUPtr createValidatedOptions(const wchar_t* encID, const prt::Attribu void replaceCGACWithCEVersion(std::wstring& errorString) { // a typical CGAC version error string looks like: // Potentially unsupported CGAC version X.YY : major number smaller than current (A.BB) - replaceAllSubstrings(errorString, std::wstring(L"CGAC version"), std::wstring(L"CityEngine version")); + replaceAllSubstrings(errorString, std::wstring(L"CGAC version "), std::wstring(L"CityEngine version ")); replaceCGACVersionBetween(errorString, L"CityEngine version ", L" "); replaceCGACVersionBetween(errorString, L"(", L")"); From 07a102825508527d4994541f8033874fef529c97 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 11 Feb 2022 13:58:41 +0100 Subject: [PATCH 369/668] Add tests for CGAC version to CE version convertion --- src/test/tests.cpp | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/test/tests.cpp b/src/test/tests.cpp index 2b1413e3..f50f27d5 100644 --- a/src/test/tests.cpp +++ b/src/test/tests.cpp @@ -285,6 +285,43 @@ TEST_CASE("join") { CHECK(join(input3, L" ") == L""); } +TEST_CASE("replaceCGACWithCEVersion") { + SECTION("do nothing") { + std::wstring inp = L"No CGA and CGAC versions found - assuming unreleased CGA 2020.0 and CGAC 1.14"; + const std::wstring exp = L"No CGA and CGAC versions found - assuming unreleased CGA 2020.0 and CGAC 1.14"; + prtu::replaceCGACWithCEVersion(inp); + CHECK(inp == exp); + } + + SECTION("major number larger than current") { + std::wstring inp = L"Unsupported CGAC version 2.0 : major number larger than current (1.17)"; + const std::wstring exp = L"Unsupported CityEngine version newer than 2021.1 : major number larger than current (2021.1)"; + prtu::replaceCGACWithCEVersion(inp); + CHECK(inp == exp); + } + + SECTION("major number smaller than current") { + std::wstring inp = L"Potentially unsupported CGAC version 1.0 : major number smaller than current (2.0)"; + const std::wstring exp = L"Potentially unsupported CityEngine version 2013.0 : major number smaller than current (newer than 2021.1)"; + prtu::replaceCGACWithCEVersion(inp); + CHECK(inp == exp); + } + + SECTION("minor number larger than current") { + std::wstring inp = L"Potentially unsupported CGAC version 1.17 : newer than current (1.5)"; + const std::wstring exp = L"Potentially unsupported CityEngine version 2021.1 : newer than current (2015.0 - 2015.2)"; + prtu::replaceCGACWithCEVersion(inp); + CHECK(inp == exp); + } + + SECTION("problematic CityEngine version") { + std::wstring inp = L"Potentially problematic CGAC version 1.3 : recompiling with current CGA Compiler (1.17) is recommended."; + const std::wstring exp = L"Potentially problematic CityEngine version 2014.1 : recompiling with current CGA Compiler (2021.1) is recommended."; + prtu::replaceCGACWithCEVersion(inp); + CHECK(inp == exp); + } +} + TEST_CASE("toFileURI") { #if _WIN32 SECTION("windows") { From a9a9596ad8a4a7dd50eb2338ee60cdef93b3f8f4 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 14 Feb 2022 14:48:34 +0100 Subject: [PATCH 370/668] Cleanup: move enum index retrieval into addEnumParameter --- src/serlio/modifiers/PRTModifierAction.cpp | 21 +++++++++------------ src/serlio/modifiers/PRTModifierAction.h | 5 ++++- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index c4a32bdf..63b53794 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -36,7 +36,6 @@ #include "maya/MFnTypedAttribute.h" #include -#include namespace { @@ -288,8 +287,6 @@ short getDefaultEnumValue(const prt::AttributeMap& defaultAttributeValues, const return 0; } -using PRTEnumDefaultValue = std::variant; - short getDefaultEnumIdx(const prt::Annotation* annot, const PRTEnumDefaultValue& defaultValue) { short idx = 0; for (size_t arg = 0; arg < annot->getNumArguments(); arg++) { @@ -887,8 +884,7 @@ MStatus PRTModifierAction::createNodeAttributes(const RuleAttributeSet& ruleAttr const bool value = mGenerateAttrs->getBool(fqName.c_str()); if (attrTrait.first == AttributeTrait::ENUM) { mEnums.emplace_front(); - const short enumIndex = getDefaultEnumIdx(attrTrait.second.mAnnot, value); - MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, enumIndex, mEnums.front())); + MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, value, mEnums.front())); } else { MCHECK(addBoolParameter(node, attr, p, value)); @@ -901,8 +897,7 @@ MStatus PRTModifierAction::createNodeAttributes(const RuleAttributeSet& ruleAttr switch (attrTrait.first) { case AttributeTrait::ENUM: { mEnums.emplace_front(); - const short enumIndex = getDefaultEnumIdx(attrTrait.second.mAnnot, value); - MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, enumIndex, mEnums.front())); + MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, value, mEnums.front())); break; } case AttributeTrait::RANGE: { @@ -952,8 +947,7 @@ MStatus PRTModifierAction::createNodeAttributes(const RuleAttributeSet& ruleAttr switch (attrTrait.first) { case AttributeTrait::ENUM: { mEnums.emplace_front(); - const short enumIndex = getDefaultEnumIdx(attrTrait.second.mAnnot, mvalue); - MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, enumIndex, mEnums.front())); + MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, mvalue, mEnums.front())); break; } case AttributeTrait::FILE: @@ -1137,11 +1131,14 @@ MStatus PRTModifierAction::addFloatParameter(MFnDependencyNode& node, MObject& a } MStatus PRTModifierAction::addEnumParameter(const prt::Annotation* annot, MFnDependencyNode& node, MObject& attr, - const RuleAttribute& ruleAttr, short defaultValue, PRTModifierEnum& e) { + const RuleAttribute& ruleAttr, const PRTEnumDefaultValue& defaultValue, + PRTModifierEnum& e) { MStatus stat; - short plugValue = getPlugValueAndRemoveAttr(node, ruleAttr.mayaBriefName.c_str(), defaultValue); - attr = e.mAttr.create(ruleAttr.mayaFullName.c_str(), ruleAttr.mayaBriefName.c_str(), defaultValue, &stat); + const short defaultEnumIndex = getDefaultEnumIdx(annot, defaultValue); + + short plugValue = getPlugValueAndRemoveAttr(node, ruleAttr.mayaBriefName.c_str(), defaultEnumIndex); + attr = e.mAttr.create(ruleAttr.mayaFullName.c_str(), ruleAttr.mayaBriefName.c_str(), defaultEnumIndex, &stat); e.mAttr.setNiceNameOverride(ruleAttr.mayaNiceName.c_str()); MCHECK(stat); diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 2755501f..7e77c76d 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -39,10 +39,13 @@ #include #include +#include + class PRTModifierAction; using RuleAttributeMap = std::map; +using PRTEnumDefaultValue = std::variant; class PRTModifierEnum { friend class PRTModifierAction; @@ -121,7 +124,7 @@ class PRTModifierAction : public polyModifierFty { static MStatus addFileParameter(MFnDependencyNode& node, MObject& attr, const RuleAttribute& name, const MString& defaultValue, const std::wstring& ext); static MStatus addEnumParameter(const prt::Annotation* annot, MFnDependencyNode& node, MObject& attr, - const RuleAttribute& name, short defaultValue, PRTModifierEnum& e); + const RuleAttribute& name, const PRTEnumDefaultValue& defaultValue, PRTModifierEnum& e); static MStatus addColorParameter(MFnDependencyNode& node, MObject& attr, const RuleAttribute& name, const MString& defaultValue); }; From d96c7fd6b0201cdd741b746cfeeae0fe1f3aeb3f Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 15 Feb 2022 13:26:23 +0100 Subject: [PATCH 371/668] Add fuction to maya utilities to set enum options --- src/serlio/utils/MELScriptBuilder.cpp | 13 +++++++++++++ src/serlio/utils/MELScriptBuilder.h | 2 ++ src/serlio/utils/MayaUtilities.cpp | 12 ++++++++++++ src/serlio/utils/MayaUtilities.h | 3 +++ 4 files changed, 30 insertions(+) diff --git a/src/serlio/utils/MELScriptBuilder.cpp b/src/serlio/utils/MELScriptBuilder.cpp index 359dcdf1..490066f3 100644 --- a/src/serlio/utils/MELScriptBuilder.cpp +++ b/src/serlio/utils/MELScriptBuilder.cpp @@ -85,6 +85,19 @@ void MELScriptBuilder::setAttr(const MELVariable& node, const std::wstring& attr setAttr(node, attribute, color.r(), color.g(), color.b()); } +void MELScriptBuilder::setAttrEnumOptions(const MELVariable& node, const std::wstring& attribute, + const std::vector& enumOptions) { + std::wstring enumString; + + for (const std::wstring& enumOption : enumOptions) { + if (!enumString.empty()) + enumString.append(L":"); + enumString.append(enumOption); + } + + commandStream << "addAttr -e -en \"" << enumString << "\" " << composeAttributeExpression(node, attribute) << ";\n"; +} + void MELScriptBuilder::connectAttr(const MELVariable& srcNode, const std::wstring& srcAttr, const MELVariable& dstNode, const std::wstring& dstAttr) { commandStream << "connectAttr -force " << composeAttributeExpression(srcNode, srcAttr) << " " diff --git a/src/serlio/utils/MELScriptBuilder.h b/src/serlio/utils/MELScriptBuilder.h index d1aa4bd3..71a0d497 100644 --- a/src/serlio/utils/MELScriptBuilder.h +++ b/src/serlio/utils/MELScriptBuilder.h @@ -63,6 +63,8 @@ class MELScriptBuilder { void setAttr(const MELVariable& node, const std::wstring& attribute, const wchar_t* val) = delete; void setAttr(const MELVariable& node, const std::wstring& attribute, const char* val) = delete; + void setAttrEnumOptions(const MELVariable& node, const std::wstring& attribute, const std::vector& enumOptions); + void connectAttr(const MELVariable& srcNode, const std::wstring& srcAttr, const MELVariable& dstNode, const std::wstring& dstAttr); diff --git a/src/serlio/utils/MayaUtilities.cpp b/src/serlio/utils/MayaUtilities.cpp index cbecdcc5..74994650 100644 --- a/src/serlio/utils/MayaUtilities.cpp +++ b/src/serlio/utils/MayaUtilities.cpp @@ -44,4 +44,16 @@ std::filesystem::path getWorkspaceRoot(MStatus& status) { return {}; } } + +MStatus setEnumOptions(const std::wstring& node, const std::wstring& attr, const std::vector& enumOptions) { + const MELVariable melSerlioNode(L"serlioNode"); + + MELScriptBuilder scriptBuilder; + + scriptBuilder.setVar(melSerlioNode, MELStringLiteral(node)); + scriptBuilder.setAttrEnumOptions(melSerlioNode, attr, enumOptions); + + std::wstring output; + return scriptBuilder.executeSync(output); +} } // namespace mu \ No newline at end of file diff --git a/src/serlio/utils/MayaUtilities.h b/src/serlio/utils/MayaUtilities.h index 5a1f6ed7..605885d7 100644 --- a/src/serlio/utils/MayaUtilities.h +++ b/src/serlio/utils/MayaUtilities.h @@ -48,4 +48,7 @@ class NamedType { }; std::filesystem::path getWorkspaceRoot(MStatus& status); + +MStatus setEnumOptions(const std::wstring& node, const std::wstring& attr, + const std::vector& enumOptions); } // namespace mu \ No newline at end of file From aa0d794f934ff03564cfaf14da84a917f24d92c4 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 15 Feb 2022 13:54:58 +0100 Subject: [PATCH 372/668] Leave first enum option open for custom values --- src/serlio/modifiers/PRTModifierAction.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 63b53794..a4300a78 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -660,7 +660,7 @@ void PRTModifierAction::updateDynamicEnums() { currStringView = currStringView.substr(0, cutoffIndex); const MString mCurrString(currStringView.data(), static_cast(currStringView.length())); - MCHECK(e.mAttr.addField(mCurrString, enumIndex)); + MCHECK(e.mAttr.addField(mCurrString, enumIndex + 1)); } break; } @@ -672,7 +672,7 @@ void PRTModifierAction::updateDynamicEnums() { const double currDouble = doubleArray[enumIndex]; const MString mCurrString(std::to_wstring(currDouble).c_str()); - MCHECK(e.mAttr.addField(mCurrString, enumIndex)); + MCHECK(e.mAttr.addField(mCurrString, enumIndex + 1)); } break; } @@ -684,28 +684,28 @@ void PRTModifierAction::updateDynamicEnums() { const bool currBool = boolArray[enumIndex]; const MString mCurrString(std::to_wstring(currBool).c_str()); - MCHECK(e.mAttr.addField(mCurrString, enumIndex)); + MCHECK(e.mAttr.addField(mCurrString, enumIndex + 1)); } break; } case prt::Attributable::PT_STRING: { const MString mCurrString = mGenerateAttrs->getString(valuesAttr.c_str()); - MCHECK(e.mAttr.addField(mCurrString, 0)); + MCHECK(e.mAttr.addField(mCurrString, 1)); break; } case prt::Attributable::PT_FLOAT: { const bool currFloat = mGenerateAttrs->getFloat(valuesAttr.c_str()); const MString mCurrString(std::to_wstring(currFloat).c_str()); - MCHECK(e.mAttr.addField(mCurrString, 0)); + MCHECK(e.mAttr.addField(mCurrString, 1)); break; } case prt::Attributable::PT_BOOL: { const bool currBool = mGenerateAttrs->getBool(valuesAttr.c_str()); const MString mCurrString(std::to_wstring(currBool).c_str()); - MCHECK(e.mAttr.addField(mCurrString, 0)); + MCHECK(e.mAttr.addField(mCurrString, 1)); break; } default: @@ -1024,7 +1024,7 @@ MStatus PRTModifierEnum::fill(const prt::Annotation* annot) { mRestricted = true; MStatus stat; - uint32_t enumIndex = 0; + uint32_t enumIndex = 1; for (size_t arg = 0; arg < annot->getNumArguments(); arg++) { const wchar_t* key = annot->getArgument(arg)->getKey(); From 73c87a3c0e240c03540fbc6497af7db1b17613b3 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 15 Feb 2022 13:56:04 +0100 Subject: [PATCH 373/668] Update the enum options if a custom default option is removed/added --- src/serlio/modifiers/PRTModifierAction.cpp | 78 ++++++++++++++++++++-- 1 file changed, 74 insertions(+), 4 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index a4300a78..b634c06d 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -240,6 +240,74 @@ MStatus addHiddenBoolParameter(MFnDependencyNode& node, MFnAttribute& tAttr, con return stat; } +bool updateCustomEnumValue(const prt::AttributeMap& defaultAttributeValues, MFnEnumAttribute& eAttr, + const RuleAttribute& ruleAttr, const MObject& node) { + const std::wstring fqAttrName = ruleAttr.fqName; + const prt::AnnotationArgumentType ruleAttrType = ruleAttr.mType; + + short minVal; + short maxVal; + MCHECK(eAttr.getMin(minVal)); + MCHECK(eAttr.getMax(maxVal)); + + MString defMStringVal; + + switch (ruleAttrType) { + case prt::AAT_STR: { + const wchar_t* defStringVal = defaultAttributeValues.getString(fqAttrName.c_str()); + if (defStringVal == nullptr) + return false; + defMStringVal = defStringVal; + break; + } + case prt::AAT_FLOAT: { + const auto defDoubleVal = defaultAttributeValues.getFloat(fqAttrName.c_str()); + defMStringVal = std::to_wstring(defDoubleVal).c_str(); + break; + } + case prt::AAT_BOOL: { + const auto defBoolVal = defaultAttributeValues.getBool(fqAttrName.c_str()); + defMStringVal = std::to_wstring(defBoolVal).c_str(); + break; + } + default: { + LOG_ERR << "Cannot handle attribute type " << ruleAttrType << " for attr " << fqAttrName; + return false; + } + } + + MStatus status; + short idx = eAttr.fieldIndex(defMStringVal, &status); + + const bool needToAddCustomDefaultValue = (status != MStatus::kSuccess); + const bool needToRemoveCustomDefaultValue = ((idx > 0) && (minVal == 0)); + + if (needToAddCustomDefaultValue || needToRemoveCustomDefaultValue) { + std::vector enumOptions; + + for (short currIdx = 1; currIdx <= maxVal; currIdx++) + enumOptions.emplace_back(eAttr.fieldName(currIdx).asWChar()); + + MStatus stat; + const MFnDependencyNode fNode(node, &stat); + MCHECK(stat); + // clear enum options + MCHECK(mu::setEnumOptions(fNode.name().asWChar(), eAttr.name().asWChar(), {})); + // adding enums through MFnEnumAttribute does not cause issues, when the enum option string contains ":" + short currIdx = 1; + for (const MString& option : enumOptions) + MCHECK(eAttr.addField(option, currIdx++)); + } + else { + return false; + } + + if (needToAddCustomDefaultValue) + MCHECK(eAttr.addField(defMStringVal, 0)); + + return true; +} + short getDefaultEnumValue(const prt::AttributeMap& defaultAttributeValues, const MFnEnumAttribute& eAttr, const RuleAttribute& ruleAttr) { const std::wstring fqAttrName = ruleAttr.fqName; @@ -531,8 +599,9 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { } MStatus PRTModifierAction::updateUI(const MObject& node) { - const auto updateUIFromAttributes = [this](const MFnDependencyNode& fnNode, const MFnAttribute& fnAttribute, - const RuleAttribute& ruleAttribute, const PrtAttributeType attrType) { + const auto updateUIFromAttributes = [this, node](const MFnDependencyNode& fnNode, const MFnAttribute& fnAttribute, + const RuleAttribute& ruleAttribute, + const PrtAttributeType attrType) { const AttributeMapUPtr defaultAttributeValues = getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().mPRTCache, *inPrtMesh, mRandomSeed, *mGenerateAttrs); @@ -608,8 +677,9 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { MCHECK(plug.getValue(enumVal)); const bool isDefaultValue = (defEnumVal == enumVal); - - if (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue) { + const bool hasNewCustomDefault = + updateCustomEnumValue(*defaultAttributeValues, eAttr, ruleAttribute, node); + if (hasNewCustomDefault || (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue)) { plug.setShort(defEnumVal); } break; From 0f0aab836944f073a6c1fec72f9c64958e36a288 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 15 Feb 2022 13:56:58 +0100 Subject: [PATCH 374/668] Allow multiple scriptjobs to be attached to an attribute --- src/serlio/scripts/AEserlioTemplate.mel | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index e4d72e65..35ee6311 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -160,7 +160,7 @@ global proc prtAttributeControl(string $attr, string $varname){ $label = `niceName $attr`; - iconTextStaticLabel -st "iconOnly" -visible false -hlc 0.2715 0.2715 0.2715 -i "pencilCursor.png" -w 25 -h 25 -enable false ($control + "_user_set"); + iconTextStaticLabel -st "iconOnly" -visible false -i "pencilCursor.png" -w 25 -h 25 -enable false ($control + "_user_set"); if (prtIsColorAttr($attr)){ colorSliderGrp-label $label $control; @@ -179,7 +179,7 @@ global proc prtAttributeControl(string $attr, string $varname){ connectControl $control $attr; } string $changeCommand = "prtUpdateEditHighlights " + $attr; - scriptJob -rp -p $control -ac ($attr + "_user_set") $changeCommand; + scriptJob -p $control -ac ($attr + "_user_set") $changeCommand; setParent ..; setUITemplate -ppt; @@ -202,7 +202,7 @@ global proc prtAttributeControlReplace(string $attr, string $varname){ } symbolButton -edit -c ("prtForceDefault (\"" + $attr +"\")") ($control + "_force_default_reset"); string $changeCommand = "prtUpdateEditHighlights " + $attr; - scriptJob -rp -p $control -ac ($attr + "_user_set") $changeCommand; + scriptJob -p $control -ac ($attr + "_user_set") $changeCommand; } prtUpdateEditHighlights($attr); } From 03d8cb6b2082f495b83d937d4ba6d91d0cf3ee8a Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 15 Feb 2022 13:57:31 +0100 Subject: [PATCH 375/668] Add helper function to check if an attribute is an enum attribute --- src/serlio/scripts/AEserlioTemplate.mel | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 35ee6311..499300b9 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -46,6 +46,10 @@ proc int prtIsColorAttr(string $attr){ return `addAttr -q -uac ($attr)`; } +proc int prtIsEnumAttr(string $attr){ + return (`addAttr -q -at ($attr)` == "enum"); +} + proc int prtAttributeExists(string $attr){ string $node = plugNode($attr); string $attributeName = plugAttr($attr); From 433d52762739429364b7ba4bd8687c4d5321ac1e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 15 Feb 2022 15:19:22 +0100 Subject: [PATCH 376/668] Force redraw enums in attribute editor when enum options change.. This is a workaround, since enum options are only visible after the entire editor is refreshed (i.e. refreshEditorTemplates()) --- src/serlio/scripts/AEserlioTemplate.mel | 42 +++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 499300b9..55b7f389 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -150,6 +150,35 @@ global proc prtForceDefault(string $attr){ setAttr ($attr + "_force_default") true; } +global proc redrawEnum(string $attr, string $oldEnumOptions){ + //only redraw the enum if options changed + string $newEnumOptions = `addAttr -q -en $attr`; + if($newEnumOptions == $oldEnumOptions) + return; + + setUITemplate -pst attributeEditorTemplate; + string $control = prtControlName($attr); + + //remove all items in the current column layout + deleteUI ($control + "_user_set"); + deleteUI ($control + "_force_default_reset"); + deleteUI $control; + + //add all items back to the current column layout + iconTextStaticLabel -p ($control + "_column_layout") -st "iconOnly" -visible false -i "pencilCursor.png" -w 25 -h 25 -enable false ($control + "_user_set"); + + $label = `niceName $attr`; + attrEnumOptionMenuGrp -p ($control + "_column_layout") -label $label -attribute $attr $control; + symbolButton -p ($control + "_column_layout") -image "undo_s.png" -visible false -annotation "reset rule attribute to default value" -c ("prtForceDefault (\"" + $attr +"\")") ($control + "_force_default_reset"); + connectControl $control $attr; + + string $changeCommand = "prtUpdateEditHighlights " + $attr; + scriptJob -p $control -ac ($attr + "_user_set") $changeCommand; + $changeEnumSelectionCommand = "redrawEnum " + $attr + " \"" + $newEnumOptions + "\""; + scriptJob -p $control -ac $attr $changeEnumSelectionCommand; + prtUpdateEditHighlights($attr); +} + global proc prtAttributeControl(string $attr, string $varname){ setUITemplate -pst attributeEditorTemplate; string $control = prtControlName($attr); @@ -168,6 +197,12 @@ global proc prtAttributeControl(string $attr, string $varname){ if (prtIsColorAttr($attr)){ colorSliderGrp-label $label $control; + } else if (prtIsEnumAttr($attr)){ + attrEnumOptionMenuGrp -label $label -attribute $attr $control; + string $enumOptions = `addAttr -q -en $attr`; + + $changeEnumSelectionCommand = "redrawEnum " + $attr + " \"" + $enumOptions + "\""; + scriptJob -p $control -ac $attr $changeEnumSelectionCommand; } else { attrControlGrp -label $label -attribute $attr $control; } @@ -200,6 +235,13 @@ global proc prtAttributeControlReplace(string $attr, string $varname){ connectControl -index 2 $control $attr; } else if (prtIsColorAttr($attr)){ connectControl $control $attr; + } else if (prtIsEnumAttr($attr)){ + attrEnumOptionMenuGrp -edit -attribute $attr $control; + connectControl $control $attr; + + string $enumOptions = `addAttr -q -en $attr`; + $changeEnumSelectionCommand = "redrawEnum " + $attr + " \"" + $enumOptions + "\""; + scriptJob -p $control -ac $attr $changeEnumSelectionCommand; } else { attrControlGrp -edit -attribute $attr $control; connectControl $control $attr; From ce531c6c070dfd7e26351d4059ebe19f5f0ca400 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 15 Feb 2022 16:19:19 +0100 Subject: [PATCH 377/668] Cleanup: remove redundant newline --- src/serlio/modifiers/PRTModifierAction.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 7e77c76d..b6d8e296 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -41,7 +41,6 @@ #include #include - class PRTModifierAction; using RuleAttributeMap = std::map; From dbce81b6ea464e9d6dbc77f5d3ba804d6bb35fc2 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 15 Feb 2022 16:23:13 +0100 Subject: [PATCH 378/668] Cleanup: remove redundant if braces --- src/serlio/modifiers/PRTModifierAction.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index b634c06d..68ddf7c1 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -616,9 +616,8 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { const bool isDefaultValue = (defBoolVal == boolVal); - if (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue) { + if (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue) plug.setBool(defBoolVal); - } break; } case PrtAttributeType::FLOAT: { @@ -628,9 +627,8 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { const bool isDefaultValue = (defDoubleVal == doubleVal); - if (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue) { + if (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue) plug.setDouble(defDoubleVal); - } break; } case PrtAttributeType::COLOR: { @@ -651,9 +649,8 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { const bool isDefaultValue = (std::wcscmp(colStr.c_str(), defColStr) == 0); - if (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue) { + if (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue) plug.setMObject(defaultColorObj); - } break; } case PrtAttributeType::STRING: { @@ -664,9 +661,8 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { const bool isDefaultValue = (std::wcscmp(stringVal.asWChar(), defStringVal) == 0); - if (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue) { + if (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue) plug.setString(defStringVal); - } break; } case PrtAttributeType::ENUM: { @@ -679,9 +675,8 @@ MStatus PRTModifierAction::updateUI(const MObject& node) { const bool isDefaultValue = (defEnumVal == enumVal); const bool hasNewCustomDefault = updateCustomEnumValue(*defaultAttributeValues, eAttr, ruleAttribute, node); - if (hasNewCustomDefault || (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue)) { + if (hasNewCustomDefault || (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue)) plug.setShort(defEnumVal); - } break; } From 9f4ec30fb103543bde63ee304c748fd0754876d2 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 15 Feb 2022 16:25:43 +0100 Subject: [PATCH 379/668] Cleanup: remove redundant if braces --- src/serlio/modifiers/PRTModifierAction.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 68ddf7c1..60f98da3 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -1094,12 +1094,11 @@ MStatus PRTModifierEnum::fill(const prt::Annotation* annot) { const wchar_t* key = annot->getArgument(arg)->getKey(); if (std::wcscmp(key, NULL_KEY) != 0) { - if (std::wcscmp(key, RESTRICTED_KEY) == 0) { + if (std::wcscmp(key, RESTRICTED_KEY) == 0) mRestricted = annot->getArgument(arg)->getBool(); - } - if (std::wcscmp(key, VALUES_ATTR_KEY) == 0) { + + if (std::wcscmp(key, VALUES_ATTR_KEY) == 0) mValuesAttr = annot->getArgument(arg)->getStr(); - } continue; } @@ -1179,13 +1178,11 @@ MStatus PRTModifierAction::addFloatParameter(MFnDependencyNode& node, MObject& a nAttr.setNiceNameOverride(ruleAttr.mayaNiceName.c_str()); MCHECK(stat); - if (!prtu::isnan(min)) { + if (!prtu::isnan(min)) MCHECK(nAttr.setMin(min)); - } - if (!prtu::isnan(max)) { + if (!prtu::isnan(max)) MCHECK(nAttr.setMax(max)); - } stat = addParameter(node, attr, nAttr); From 0adbb29ab767ce9b6d78d53adb8799103b9f7d4b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 15 Feb 2022 16:46:35 +0100 Subject: [PATCH 380/668] Cleanup: add constexpr for constant strings --- src/serlio/utils/Utilities.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/serlio/utils/Utilities.cpp b/src/serlio/utils/Utilities.cpp index 763352f1..5ec2f8d6 100644 --- a/src/serlio/utils/Utilities.cpp +++ b/src/serlio/utils/Utilities.cpp @@ -45,6 +45,10 @@ struct IUnknown; #include namespace { +constexpr const wchar_t* TOO_NEW_CE_VERSION = L"newer than 2021.1"; +constexpr const wchar_t* CGAC_VERSION_STRING = L"CGAC version "; +constexpr const wchar_t* CE_VERSION_STRING = L"CityEngine version "; + const std::map cgacToCEVersion = { // clang-format off {L"1.17", L"2021.1"}, @@ -87,7 +91,7 @@ void replaceCGACVersionBetween(std::wstring& errorString, const std::wstring pre CEVersion = it->second; } else { - CEVersion = L"newer than 2021.1"; + CEVersion = TOO_NEW_CE_VERSION; } errorString.replace(versionStartPos, versionLength, CEVersion); } @@ -266,9 +270,9 @@ AttributeMapUPtr createValidatedOptions(const wchar_t* encID, const prt::Attribu void replaceCGACWithCEVersion(std::wstring& errorString) { // a typical CGAC version error string looks like: // Potentially unsupported CGAC version X.YY : major number smaller than current (A.BB) - replaceAllSubstrings(errorString, std::wstring(L"CGAC version "), std::wstring(L"CityEngine version ")); + replaceAllSubstrings(errorString, std::wstring(CGAC_VERSION_STRING), std::wstring(CE_VERSION_STRING)); - replaceCGACVersionBetween(errorString, L"CityEngine version ", L" "); + replaceCGACVersionBetween(errorString, CE_VERSION_STRING, L" "); replaceCGACVersionBetween(errorString, L"(", L")"); } From 4002c3478c552adaed74286eb5d69bbd235878e3 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 15 Feb 2022 16:52:35 +0100 Subject: [PATCH 381/668] Cleanup use const wstring instead of constexpr const wchar_t* --- src/serlio/utils/Utilities.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/serlio/utils/Utilities.cpp b/src/serlio/utils/Utilities.cpp index 5ec2f8d6..021e79f3 100644 --- a/src/serlio/utils/Utilities.cpp +++ b/src/serlio/utils/Utilities.cpp @@ -45,9 +45,9 @@ struct IUnknown; #include namespace { -constexpr const wchar_t* TOO_NEW_CE_VERSION = L"newer than 2021.1"; -constexpr const wchar_t* CGAC_VERSION_STRING = L"CGAC version "; -constexpr const wchar_t* CE_VERSION_STRING = L"CityEngine version "; +const std::wstring TOO_NEW_CE_VERSION = L"newer than 2021.1"; +const std::wstring CGAC_VERSION_STRING = L"CGAC version "; +const std::wstring CE_VERSION_STRING = L"CityEngine version "; const std::map cgacToCEVersion = { // clang-format off @@ -270,7 +270,7 @@ AttributeMapUPtr createValidatedOptions(const wchar_t* encID, const prt::Attribu void replaceCGACWithCEVersion(std::wstring& errorString) { // a typical CGAC version error string looks like: // Potentially unsupported CGAC version X.YY : major number smaller than current (A.BB) - replaceAllSubstrings(errorString, std::wstring(CGAC_VERSION_STRING), std::wstring(CE_VERSION_STRING)); + replaceAllSubstrings(errorString, CGAC_VERSION_STRING, CE_VERSION_STRING); replaceCGACVersionBetween(errorString, CE_VERSION_STRING, L" "); replaceCGACVersionBetween(errorString, L"(", L")"); From 2a9b18e7b3b501b1f26b5e80ea41a6dfd1dc0256 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 16 Feb 2022 11:33:29 +0100 Subject: [PATCH 382/668] Always add keys from textures and texturearrays to the attributemap... with empty values --- src/codec/encoder/MayaEncoder.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/codec/encoder/MayaEncoder.cpp b/src/codec/encoder/MayaEncoder.cpp index 9b87909a..1e21e6ab 100644 --- a/src/codec/encoder/MayaEncoder.cpp +++ b/src/codec/encoder/MayaEncoder.cpp @@ -316,9 +316,7 @@ void convertMaterialToAttributeMap(prtx::PRTUtils::AttributeMapBuilderPtr& aBuil case prtx::Material::PT_TEXTURE: { const auto& t = prtxAttr.getTexture(key); const std::wstring p = getTexturePath(t, cb, cache); - if (!p.empty()) { - aBuilder->setString(key.c_str(), p.c_str()); - } + aBuilder->setString(key.c_str(), p.c_str()); break; } @@ -334,10 +332,8 @@ void convertMaterialToAttributeMap(prtx::PRTUtils::AttributeMapBuilderPtr& aBuil texPaths.push_back(texPath); } - if (!texPaths.empty()) { - const std::vector pTexPaths = toPtrVec(texPaths); - aBuilder->setStringArray(key.c_str(), pTexPaths.data(), pTexPaths.size()); - } + const std::vector pTexPaths = toPtrVec(texPaths); + aBuilder->setStringArray(key.c_str(), pTexPaths.data(), pTexPaths.size()); break; } From 7609c79ea9e4fb60ca20ffa32078c27d3bc10973 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 16 Feb 2022 17:16:25 +0100 Subject: [PATCH 383/668] Fix transparency in serlio stingray shader Having the wrong blendmode caused small artifacts on materials that were supposed to be completely transparent. --- src/serlio/shaders/serlioShaderStingray.sfx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serlio/shaders/serlioShaderStingray.sfx b/src/serlio/shaders/serlioShaderStingray.sfx index 42f8e9ce..eca03a9c 100644 --- a/src/serlio/shaders/serlioShaderStingray.sfx +++ b/src/serlio/shaders/serlioShaderStingray.sfx @@ -135,7 +135,7 @@ NumberOfNodes=81 posx=1 v=2003 1636.499512 posy=1 v=2003 871.666687 normalspace=2 e=0 v=5012 1 - blendmode=2 e=0 v=5012 1 + blendmode=2 e=0 v=5012 2 group=-1 ISC=13 SVT=5022 3002 1 0 0 @@ -2231,7 +2231,7 @@ nodes = [ } id = "abbaabba-abba-abba-abba-abbaabbaabb9" options = [ - "dd7fcf97-0627-48ab-b29a-95b5685bb123" + "3b55d6c6-4398-4dbc-b9ef-570aff8696ae" "2b136447-676e-4943-997b-04a28ae68497" ] position = [ From 14d0b606ac8848ae64f9b6c008fb6d195e8224b6 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 17 Feb 2022 14:43:09 +0100 Subject: [PATCH 384/668] Add helper function that sets the alpha channel to the luminocity channel... in case there are no valid alpha values in the texture. --- src/serlio/utils/MELScriptBuilder.cpp | 5 +++++ src/serlio/utils/MELScriptBuilder.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/serlio/utils/MELScriptBuilder.cpp b/src/serlio/utils/MELScriptBuilder.cpp index 359dcdf1..70c195e8 100644 --- a/src/serlio/utils/MELScriptBuilder.cpp +++ b/src/serlio/utils/MELScriptBuilder.cpp @@ -132,6 +132,11 @@ void MELScriptBuilder::createTextureShadingNode(const MELVariable& nodeName) { commandStream << mel << "= `shadingNode -asTexture -skipSelect -name " << mel << " file`;\n"; } +void MELScriptBuilder::forceValidTextureAlphaChannel(const MELVariable& nodeName) { + commandStream << "setAttr " << composeAttributeExpression(nodeName, L"alphaIsLuminance") << "(!`getAttr " + << composeAttributeExpression(nodeName, L"fileHasAlpha") << "`);"; +} + void MELScriptBuilder::getUndoState(const MELVariable& undoName) { const auto mel = undoName.mel(); commandStream << mel << " = `undoInfo -q -state`;\n"; diff --git a/src/serlio/utils/MELScriptBuilder.h b/src/serlio/utils/MELScriptBuilder.h index d1aa4bd3..6bebd6b0 100644 --- a/src/serlio/utils/MELScriptBuilder.h +++ b/src/serlio/utils/MELScriptBuilder.h @@ -77,6 +77,7 @@ class MELScriptBuilder { void createShader(const std::wstring& shaderType, const MELVariable& nodeName); void createTextureShadingNode(const MELVariable& nodeName); + void forceValidTextureAlphaChannel(const MELVariable& nodeName); void getUndoState(const MELVariable& undoName); void setUndoState(const MELVariable& undoName); From c75fcc0b2e54a42d5b1d10d53cbf0b5c86a07cdb Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 17 Feb 2022 14:43:43 +0100 Subject: [PATCH 385/668] Add support for RGB based material maps for the arnold shader --- src/serlio/materials/ArnoldMaterialNode.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/serlio/materials/ArnoldMaterialNode.cpp b/src/serlio/materials/ArnoldMaterialNode.cpp index a0622d02..2e3bb431 100644 --- a/src/serlio/materials/ArnoldMaterialNode.cpp +++ b/src/serlio/materials/ArnoldMaterialNode.cpp @@ -82,8 +82,10 @@ void createMapShader(MELScriptBuilder& sb, const std::string& mapFile, const Mat sb.createShader(L"aiUvTransform", MEL_VAR_UV_TRAFO_NODE); setUvTransformAttrs(sb, uvSet, mapTrafo); - if (alpha) + if (alpha) { sb.connectAttr(MEL_VAR_MAP_NODE, L"outAlpha", MEL_VAR_UV_TRAFO_NODE, L"passthroughR"); + sb.forceValidTextureAlphaChannel(MEL_VAR_MAP_NODE); + } else sb.connectAttr(MEL_VAR_MAP_NODE, L"outColor", MEL_VAR_UV_TRAFO_NODE, L"passthrough"); } From a45ea116e18b7f3a95535b16bca84069ab26f4a3 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 17 Feb 2022 17:16:40 +0100 Subject: [PATCH 386/668] Show textures in all perspective modelEditors after adding a material.. Also activate textures if materials have already been added, just don't activate them if no valid input geometry was selected. (since we show an error) --- src/serlio/scripts/serlioCreateUI.mel | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index cff88405..c3546e38 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -123,6 +123,15 @@ global proc removePrtNode() { select -r $select; } +global proc displayTextures(){ + string $panels[] = `getPanel -typ "modelPanel"`; + for ($panel in $panels){ + string $camera = `modelEditor -q -camera $panel`; + if (`camera -q -orthographic $camera` == false) + modelEditor -e -dtx true $panel; + } +} + global proc createMaterialNode(string $materialType) { string $select[] = `ls -sl`; string $initialShapes[] = collectInitialShapes($select); @@ -137,6 +146,7 @@ global proc createMaterialNode(string $materialType) { if(hasNodeTypeInHistory($node, {"serlio"}) && !hasNodeTypeInHistory($node, {"serlioMaterial","serlioArnoldMaterial"})) serlioCreateMaterial $materialType; } + displayTextures(); select -r $select; } From 8cb1f808237eac951620e7587741e446e4fc1b4c Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 17 Feb 2022 19:56:22 +0100 Subject: [PATCH 387/668] Check for availability of opacity channel.. instead of checking if the opacity channel is used --- src/serlio/materials/ArnoldMaterialNode.cpp | 7 +++++-- src/serlio/materials/MaterialUtils.cpp | 14 ++++++++++++++ src/serlio/materials/MaterialUtils.h | 1 + src/serlio/materials/StingrayMaterialNode.cpp | 3 ++- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/serlio/materials/ArnoldMaterialNode.cpp b/src/serlio/materials/ArnoldMaterialNode.cpp index 2e3bb431..c05a40c9 100644 --- a/src/serlio/materials/ArnoldMaterialNode.cpp +++ b/src/serlio/materials/ArnoldMaterialNode.cpp @@ -65,11 +65,12 @@ void setUvTransformAttrs(MELScriptBuilder& sb, const std::wstring& uvSet, const } } -void createMapShader(MELScriptBuilder& sb, const std::string& mapFile, const MaterialTrafo& mapTrafo, +void createMapShader(MELScriptBuilder& sb, const std::string& tex, const MaterialTrafo& mapTrafo, const std::wstring& shaderName, const std::wstring& uvSet, const bool raw, const bool alpha) { sb.setVar(MEL_VAR_MAP_NODE, MELStringLiteral(shaderName)); + std::filesystem::path texPath(tex); - sb.setVar(MEL_VAR_MAP_FILE, MELStringLiteral(prtu::toUTF16FromOSNarrow(mapFile))); + sb.setVar(MEL_VAR_MAP_FILE, MELStringLiteral(prtu::toUTF16FromOSNarrow(tex))); sb.createTextureShadingNode(MEL_VAR_MAP_NODE); sb.setAttr(MEL_VAR_MAP_NODE, L"fileTextureName", MEL_VAR_MAP_FILE); @@ -85,6 +86,8 @@ void createMapShader(MELScriptBuilder& sb, const std::string& mapFile, const Mat if (alpha) { sb.connectAttr(MEL_VAR_MAP_NODE, L"outAlpha", MEL_VAR_UV_TRAFO_NODE, L"passthroughR"); sb.forceValidTextureAlphaChannel(MEL_VAR_MAP_NODE); + sb.setAttr(MEL_VAR_MAP_NODE, L"alphaIsLuminance", + !MaterialUtils::textureHasAlphaChannel(texPath)); } else sb.connectAttr(MEL_VAR_MAP_NODE, L"outColor", MEL_VAR_UV_TRAFO_NODE, L"passthrough"); diff --git a/src/serlio/materials/MaterialUtils.cpp b/src/serlio/materials/MaterialUtils.cpp index 27a5700f..4d5914cd 100644 --- a/src/serlio/materials/MaterialUtils.cpp +++ b/src/serlio/materials/MaterialUtils.cpp @@ -16,6 +16,10 @@ namespace { +constexpr const wchar_t* RGBA8_FORMAT = L"RGBA8"; +constexpr const wchar_t* FORMAT_STRING = L"format"; +constexpr const wchar_t* FILE_PREFIX = L"file:/"; + MObject findNamedObject(const std::wstring& name, MFn::Type fnType) { MStatus status; MItDependencyNodes nodeIt(fnType, &status); @@ -193,4 +197,14 @@ std::filesystem::path getStingrayShaderPath() { return sfxFile; } +bool textureHasAlphaChannel(std::wstring path) { + const prt::AttributeMap* textureMetadata = prt::createTextureMetadata((FILE_PREFIX + path).c_str()); + if (textureMetadata == nullptr) + return false; + const wchar_t* format = textureMetadata->getString(FORMAT_STRING); + if (format != nullptr && std::wcscmp(format, RGBA8_FORMAT) == 0) + return true; + return false; +} + } // namespace MaterialUtils diff --git a/src/serlio/materials/MaterialUtils.h b/src/serlio/materials/MaterialUtils.h index 559e0c2c..65d6693d 100644 --- a/src/serlio/materials/MaterialUtils.h +++ b/src/serlio/materials/MaterialUtils.h @@ -31,4 +31,5 @@ std::wstring synchronouslyCreateShadingEngine(const std::wstring& desiredShading std::filesystem::path getStingrayShaderPath(); +bool textureHasAlphaChannel(std::wstring path); } // namespace MaterialUtils \ No newline at end of file diff --git a/src/serlio/materials/StingrayMaterialNode.cpp b/src/serlio/materials/StingrayMaterialNode.cpp index fda7a829..5a93c86d 100644 --- a/src/serlio/materials/StingrayMaterialNode.cpp +++ b/src/serlio/materials/StingrayMaterialNode.cpp @@ -71,7 +71,8 @@ void setTexture(MELScriptBuilder& sb, const std::wstring& target, const std::wst sb.connectAttr(MEL_VAR_MAP_NODE, L"outColor", MEL_VAR_SHADER_NODE, L"TEX_" + target); if (!alphaTarget.empty()) - sb.connectAttr(MEL_VAR_MAP_NODE, L"fileHasAlpha", MEL_VAR_SHADER_NODE, alphaTarget); + sb.setAttr(MEL_VAR_SHADER_NODE, alphaTarget, + MaterialUtils::textureHasAlphaChannel(texPath)); sb.setAttr(MEL_VAR_SHADER_NODE, L"use_" + target, 1); } From 2ffaa539ac9cf05ba9179fa98579771c49456935 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 17 Feb 2022 19:56:51 +0100 Subject: [PATCH 388/668] Cleanup: use proper names for arnold texture nodes --- src/serlio/materials/ArnoldMaterialNode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/materials/ArnoldMaterialNode.cpp b/src/serlio/materials/ArnoldMaterialNode.cpp index c05a40c9..ddfda792 100644 --- a/src/serlio/materials/ArnoldMaterialNode.cpp +++ b/src/serlio/materials/ArnoldMaterialNode.cpp @@ -67,8 +67,8 @@ void setUvTransformAttrs(MELScriptBuilder& sb, const std::wstring& uvSet, const void createMapShader(MELScriptBuilder& sb, const std::string& tex, const MaterialTrafo& mapTrafo, const std::wstring& shaderName, const std::wstring& uvSet, const bool raw, const bool alpha) { - sb.setVar(MEL_VAR_MAP_NODE, MELStringLiteral(shaderName)); std::filesystem::path texPath(tex); + sb.setVar(MEL_VAR_MAP_NODE, MELStringLiteral(texPath.stem().wstring())); sb.setVar(MEL_VAR_MAP_FILE, MELStringLiteral(prtu::toUTF16FromOSNarrow(tex))); sb.createTextureShadingNode(MEL_VAR_MAP_NODE); From b383578bbf99cf4e5b98acf2fba0be0a1900a2b5 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 17 Feb 2022 20:06:04 +0100 Subject: [PATCH 389/668] Cleanup: explicitly convert paths to wstring --- src/serlio/materials/ArnoldMaterialNode.cpp | 2 +- src/serlio/materials/StingrayMaterialNode.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serlio/materials/ArnoldMaterialNode.cpp b/src/serlio/materials/ArnoldMaterialNode.cpp index ddfda792..edb7b631 100644 --- a/src/serlio/materials/ArnoldMaterialNode.cpp +++ b/src/serlio/materials/ArnoldMaterialNode.cpp @@ -87,7 +87,7 @@ void createMapShader(MELScriptBuilder& sb, const std::string& tex, const Materia sb.connectAttr(MEL_VAR_MAP_NODE, L"outAlpha", MEL_VAR_UV_TRAFO_NODE, L"passthroughR"); sb.forceValidTextureAlphaChannel(MEL_VAR_MAP_NODE); sb.setAttr(MEL_VAR_MAP_NODE, L"alphaIsLuminance", - !MaterialUtils::textureHasAlphaChannel(texPath)); + !MaterialUtils::textureHasAlphaChannel(texPath.wstring())); } else sb.connectAttr(MEL_VAR_MAP_NODE, L"outColor", MEL_VAR_UV_TRAFO_NODE, L"passthrough"); diff --git a/src/serlio/materials/StingrayMaterialNode.cpp b/src/serlio/materials/StingrayMaterialNode.cpp index 5a93c86d..c72f28a2 100644 --- a/src/serlio/materials/StingrayMaterialNode.cpp +++ b/src/serlio/materials/StingrayMaterialNode.cpp @@ -72,7 +72,7 @@ void setTexture(MELScriptBuilder& sb, const std::wstring& target, const std::wst if (!alphaTarget.empty()) sb.setAttr(MEL_VAR_SHADER_NODE, alphaTarget, - MaterialUtils::textureHasAlphaChannel(texPath)); + MaterialUtils::textureHasAlphaChannel(texPath.wstring())); sb.setAttr(MEL_VAR_SHADER_NODE, L"use_" + target, 1); } From 873f57c1583ce6d888878fb3891d5549cd4162ed Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 18 Feb 2022 13:45:04 +0100 Subject: [PATCH 390/668] fix compilation issue after merge --- src/serlio/modifiers/PRTModifierAction.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 86a9e73d..a28857e9 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -627,8 +627,9 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { } MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacProblemData) { - const auto updateUIFromAttributes = [this](const MFnDependencyNode& fnNode, const MFnAttribute& fnAttribute, - const RuleAttribute& ruleAttribute, const PrtAttributeType attrType) { + const auto updateUIFromAttributes = [this, node](const MFnDependencyNode& fnNode, const MFnAttribute& fnAttribute, + const RuleAttribute& ruleAttribute, + const PrtAttributeType attrType) { const AttributeMapUPtr defaultAttributeValues = getDefaultAttributeValues(mRuleFile, mStartRule, *getResolveMap(), *PRTContext::get().mPRTCache, *inPrtMesh, mRandomSeed, *mGenerateAttrs); From 926d99f2aea83e15ca62de254fa7769e10794fe1 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 18 Feb 2022 13:46:18 +0100 Subject: [PATCH 391/668] Add helper function for clearing enum values. --- src/serlio/modifiers/PRTModifierAction.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index a28857e9..6e4b690d 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -240,6 +240,13 @@ MStatus addHiddenBoolParameter(MFnDependencyNode& node, MFnAttribute& tAttr, con } return stat; } +void clearEnumValues(const MObject& node, const MFnEnumAttribute& enumAttr) { + MStatus stat; + const MFnDependencyNode fNode(node, &stat); + MCHECK(stat); + // clear enum options + MCHECK(mu::setEnumOptions(fNode.name().asWChar(), enumAttr.name().asWChar(), {})); +} bool updateCustomEnumValue(const prt::AttributeMap& defaultAttributeValues, MFnEnumAttribute& eAttr, const RuleAttribute& ruleAttr, const MObject& node) { @@ -289,11 +296,7 @@ bool updateCustomEnumValue(const prt::AttributeMap& defaultAttributeValues, MFnE for (short currIdx = 1; currIdx <= maxVal; currIdx++) enumOptions.emplace_back(eAttr.fieldName(currIdx).asWChar()); - MStatus stat; - const MFnDependencyNode fNode(node, &stat); - MCHECK(stat); - // clear enum options - MCHECK(mu::setEnumOptions(fNode.name().asWChar(), eAttr.name().asWChar(), {})); + clearEnumValues(node, eAttr); // adding enums through MFnEnumAttribute does not cause issues, when the enum option string contains ":" short currIdx = 1; for (const MString& option : enumOptions) From 300d7264114957470ea84bf2f9025b07a9944cf9 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 18 Feb 2022 13:47:36 +0100 Subject: [PATCH 392/668] Clear enum values in updateDynamicEnums before filling it --- src/serlio/modifiers/PRTModifierAction.cpp | 11 ++++++++--- src/serlio/modifiers/PRTModifierAction.h | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 6e4b690d..9c75c77d 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -734,14 +734,19 @@ MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacProble return MStatus::kSuccess; } -void PRTModifierAction::updateDynamicEnums() { +void PRTModifierAction::updateDynamicEnums(const MObject& node) { for (auto& e : mEnums) { if (e.mValuesAttr.length() == 0) continue; + short maxEnumVal; + e.mAttr.getMax(maxEnumVal); + if (maxEnumVal > 0) + clearEnumValues(node, e.mAttr); + const MString fullAttrName = e.mAttr.name(); const auto ruleAttrIt = mRuleAttributes.find(fullAttrName.asWChar()); - assert(ruleAttrIt != ruleAttributes.end()); // Rule not found + assert(ruleAttrIt != mRuleAttributes.end()); // Rule not found const RuleAttribute ruleAttr = (ruleAttrIt != mRuleAttributes.end()) ? ruleAttrIt->second : RuleAttribute(); const std::wstring attrStyle = prtu::getStyle(ruleAttr.fqName).c_str(); @@ -886,7 +891,7 @@ MStatus PRTModifierAction::updateRuleFiles(const MObject& node, const MString& r } createNodeAttributes(ruleAttributes, node, info.get()); - updateDynamicEnums(); + updateDynamicEnums(node); } return MS::kSuccess; diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 3befc5d5..c17ac2c4 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -109,7 +109,7 @@ class PRTModifierAction : public polyModifierFty { AttributeMapUPtr mGenerateAttrs; std::list mEnums; - void updateDynamicEnums(); + void updateDynamicEnums(const MObject& node); MStatus createNodeAttributes(const RuleAttributeSet& ruleAttributes, const MObject& node, const prt::RuleFileInfo* info); From 30c753c9bba465d70afcf80f64dee9c13d1a981e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 10:17:13 +0100 Subject: [PATCH 393/668] Add helper function to detect if enum is dynamic --- src/serlio/modifiers/PRTModifierAction.cpp | 4 ++++ src/serlio/modifiers/PRTModifierAction.h | 1 + 2 files changed, 5 insertions(+) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 9c75c77d..9678bc6c 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -452,6 +452,10 @@ T getPlugValueAndRemoveAttr(MFnDependencyNode& node, const MString& briefName, c } } // namespace + +bool PRTModifierEnum::isDynamic() { + return mValuesAttr.length() > 0; +} PRTModifierAction::PRTModifierAction() { AttributeMapBuilderUPtr optionsBuilder(prt::AttributeMapBuilder::create()); diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index c17ac2c4..03aa531d 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -54,6 +54,7 @@ class PRTModifierEnum { PRTModifierEnum() = default; MStatus fill(const prt::Annotation* annot); + bool isDynamic(); public: MFnEnumAttribute mAttr; From 4dbcf6b1566ba33a8960d33d700b14678166af9c Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 10:18:52 +0100 Subject: [PATCH 394/668] Add helper function to enums for getting dynamic enum options --- src/serlio/modifiers/PRTModifierAction.cpp | 86 ++++++++++++++++++++++ src/serlio/modifiers/PRTModifierAction.h | 3 + 2 files changed, 89 insertions(+) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 9678bc6c..947a25ad 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -456,6 +456,92 @@ T getPlugValueAndRemoveAttr(MFnDependencyNode& node, const MString& briefName, c bool PRTModifierEnum::isDynamic() { return mValuesAttr.length() > 0; } +const std::vector PRTModifierEnum::getDynamicEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, + const prt::AttributeMap& defaultAttributeValues) { + std::vector enumOptions; + if (!isDynamic()) + return enumOptions; + const MString fullAttrName = mAttr.name(); + + const std::wstring attrStyle = prtu::getStyle(ruleAttr.fqName).c_str(); + std::wstring attrImport = prtu::getImport(ruleAttr.fqName).c_str(); + if (!attrImport.empty()) + attrImport += prtu::IMPORT_DELIMITER; + + std::wstring valuesAttr = attrStyle + prtu::STYLE_DELIMITER + attrImport; + valuesAttr.append(mValuesAttr.asWChar()); + + const prt::Attributable::PrimitiveType type = defaultAttributeValues.getType(valuesAttr.c_str()); + + switch (type) { + case prt::Attributable::PT_STRING_ARRAY: { + size_t arr_length = 0; + const wchar_t* const* stringArray = defaultAttributeValues.getStringArray(valuesAttr.c_str(), &arr_length); + + for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { + if (stringArray[enumIndex] == nullptr) + continue; + std::wstring_view currStringView = stringArray[enumIndex]; + + // remove newlines from strings, because they break the maya UI + const size_t cutoffIndex = currStringView.find_first_of(L"\r\n"); + currStringView = currStringView.substr(0, cutoffIndex); + + const MString mCurrString(currStringView.data(), static_cast(currStringView.length())); + enumOptions.emplace_back(mCurrString); + } + break; + } + case prt::Attributable::PT_FLOAT_ARRAY: { + size_t arr_length = 0; + const double* doubleArray = defaultAttributeValues.getFloatArray(valuesAttr.c_str(), &arr_length); + + for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { + const double currDouble = doubleArray[enumIndex]; + + const MString mCurrString(std::to_wstring(currDouble).c_str()); + enumOptions.emplace_back(mCurrString); + } + break; + } + case prt::Attributable::PT_BOOL_ARRAY: { + size_t arr_length = 0; + const bool* boolArray = defaultAttributeValues.getBoolArray(valuesAttr.c_str(), &arr_length); + + for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { + const bool currBool = boolArray[enumIndex]; + + const MString mCurrString(std::to_wstring(currBool).c_str()); + enumOptions.emplace_back(mCurrString); + } + break; + } + case prt::Attributable::PT_STRING: { + const MString mCurrString = defaultAttributeValues.getString(valuesAttr.c_str()); + + enumOptions.emplace_back(mCurrString); + break; + } + case prt::Attributable::PT_FLOAT: { + const bool currFloat = defaultAttributeValues.getFloat(valuesAttr.c_str()); + + const MString mCurrString(std::to_wstring(currFloat).c_str()); + enumOptions.emplace_back(mCurrString); + break; + } + case prt::Attributable::PT_BOOL: { + const bool currBool = defaultAttributeValues.getBool(valuesAttr.c_str()); + + const MString mCurrString(std::to_wstring(currBool).c_str()); + enumOptions.emplace_back(mCurrString); + break; + } + default: + break; + } + return enumOptions; +} + PRTModifierAction::PRTModifierAction() { AttributeMapBuilderUPtr optionsBuilder(prt::AttributeMapBuilder::create()); diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 03aa531d..26bbb71c 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -62,6 +62,9 @@ class PRTModifierEnum { private: bool mRestricted = true; MString mValuesAttr; + + const std::vector getDynamicEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, + const prt::AttributeMap& defaultAttributeValues); }; // class PRTModifierEnum class PRTModifierAction : public polyModifierFty { From e18644155a353bab9e17991b0091c7b65088a095 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 10:21:05 +0100 Subject: [PATCH 395/668] Add field for storing custom default value --- src/serlio/modifiers/PRTModifierAction.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 26bbb71c..ebf4a1f6 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -62,6 +62,7 @@ class PRTModifierEnum { private: bool mRestricted = true; MString mValuesAttr; + MString mCustomDefaultValue; const std::vector getDynamicEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); From 2841970de9869c35328b7f31690bbd2bae3a63fc Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 10:21:41 +0100 Subject: [PATCH 396/668] Add helper function for updating custom enum option --- src/serlio/modifiers/PRTModifierAction.cpp | 35 ++++++++++++++++++++++ src/serlio/modifiers/PRTModifierAction.h | 1 + 2 files changed, 36 insertions(+) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 947a25ad..d627a832 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -456,6 +456,41 @@ T getPlugValueAndRemoveAttr(MFnDependencyNode& node, const MString& briefName, c bool PRTModifierEnum::isDynamic() { return mValuesAttr.length() > 0; } + +void PRTModifierEnum::updateCustomEnumValue(const RuleAttribute& ruleAttr, + const prt::AttributeMap& defaultAttributeValues) { + const std::wstring fqAttrName = ruleAttr.fqName; + const prt::AnnotationArgumentType ruleAttrType = ruleAttr.mType; + + MString defMStringVal; + + switch (ruleAttrType) { + case prt::AAT_STR: { + const wchar_t* defStringVal = defaultAttributeValues.getString(fqAttrName.c_str()); + if (defStringVal == nullptr) + return; + defMStringVal = defStringVal; + break; + } + case prt::AAT_FLOAT: { + const auto defDoubleVal = defaultAttributeValues.getFloat(fqAttrName.c_str()); + defMStringVal = std::to_wstring(defDoubleVal).c_str(); + break; + } + case prt::AAT_BOOL: { + const auto defBoolVal = defaultAttributeValues.getBool(fqAttrName.c_str()); + defMStringVal = std::to_wstring(defBoolVal).c_str(); + break; + } + default: { + LOG_ERR << "Cannot handle attribute type " << ruleAttrType << " for attr " << fqAttrName; + return; + } + } + + mCustomDefaultValue = defMStringVal; +} + const std::vector PRTModifierEnum::getDynamicEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues) { std::vector enumOptions; diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index ebf4a1f6..96d5ef96 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -66,6 +66,7 @@ class PRTModifierEnum { const std::vector getDynamicEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); + void updateCustomEnumValue(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); }; // class PRTModifierEnum class PRTModifierAction : public polyModifierFty { From e38207471ec222c5dd9cbbea72a3ce33b3ba6be4 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 10:23:53 +0100 Subject: [PATCH 397/668] Add helper function for getting current enum options --- src/serlio/modifiers/PRTModifierAction.cpp | 20 ++++++++++++++++++++ src/serlio/modifiers/PRTModifierAction.h | 2 ++ 2 files changed, 22 insertions(+) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index d627a832..8217d49c 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -457,6 +457,26 @@ bool PRTModifierEnum::isDynamic() { return mValuesAttr.length() > 0; } +const std::vector PRTModifierEnum::getEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, + const prt::AttributeMap& defaultAttributeValues) { + if (isDynamic()) { + return getDynamicEnumOptions(node, ruleAttr, defaultAttributeValues); + } + else { + std::vector enumOptions; + + short minVal; + short maxVal; + MCHECK(mAttr.getMin(minVal)); + MCHECK(mAttr.getMax(maxVal)); + + for (short currIdx = 1; currIdx <= maxVal; currIdx++) + enumOptions.emplace_back(mAttr.fieldName(currIdx)); + + return enumOptions; + } +} + void PRTModifierEnum::updateCustomEnumValue(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues) { const std::wstring fqAttrName = ruleAttr.fqName; diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 96d5ef96..9048fc11 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -64,6 +64,8 @@ class PRTModifierEnum { MString mValuesAttr; MString mCustomDefaultValue; + const std::vector getEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, + const prt::AttributeMap& defaultAttributeValues); const std::vector getDynamicEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); void updateCustomEnumValue(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); From a8b643af2c367a2548f858cc4b4e8978951df2e9 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 10:27:10 +0100 Subject: [PATCH 398/668] Add update function to enum for updating all options --- src/serlio/modifiers/PRTModifierAction.cpp | 40 ++++++++++++++++++++++ src/serlio/modifiers/PRTModifierAction.h | 4 +++ 2 files changed, 44 insertions(+) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 8217d49c..0a642d4b 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -452,6 +452,46 @@ T getPlugValueAndRemoveAttr(MFnDependencyNode& node, const MString& briefName, c } } // namespace +bool PRTModifierEnum::updateOptions(const MObject& node, const RuleAttributeMap& mRuleAttributes, + const prt::AttributeMap& defaultAttributeValues) { + const MString fullAttrName = mAttr.name(); + const auto ruleAttrIt = mRuleAttributes.find(fullAttrName.asWChar()); + assert(ruleAttrIt != mRuleAttributes.end()); // Rule not found + const RuleAttribute ruleAttr = (ruleAttrIt != mRuleAttributes.end()) ? ruleAttrIt->second : RuleAttribute(); + + const std::vector& newEnumOptions = getEnumOptions(node, ruleAttr, defaultAttributeValues); + + updateCustomEnumValue(ruleAttr, defaultAttributeValues); + + MStatus status; + const short defaultIdx = mAttr.fieldIndex(mCustomDefaultValue, &status); + if ((status == MStatus::kSuccess) && (newEnumOptions == mEnumOptions)) + return false; + + // Workaround, since setting enum values, when none are available through mel causes an Error... + MString defaultValue; + if (mAttr.getDefault(defaultValue) == MStatus::kSuccess) + clearEnumValues(node, mAttr); + + mEnumOptions = newEnumOptions; + + auto itr = std::find(mEnumOptions.cbegin(), mEnumOptions.cend(), mCustomDefaultValue); + + int customDefaultIdx = 0; + int currIdx = 1; + + for (const MString& option : mEnumOptions) { + mAttr.addField(option, currIdx); + if (option == mCustomDefaultValue) + customDefaultIdx = currIdx; + currIdx++; + } + + if (customDefaultIdx == 0) + mAttr.addField(mCustomDefaultValue, 0); + + return true; +} bool PRTModifierEnum::isDynamic() { return mValuesAttr.length() > 0; diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 9048fc11..c857c045 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -54,6 +54,9 @@ class PRTModifierEnum { PRTModifierEnum() = default; MStatus fill(const prt::Annotation* annot); + bool updateOptions(const MObject& node, const RuleAttributeMap& ruleAttributes, + const prt::AttributeMap& defaultAttributeValues); + bool isDynamic(); public: @@ -63,6 +66,7 @@ class PRTModifierEnum { bool mRestricted = true; MString mValuesAttr; MString mCustomDefaultValue; + std::vector mEnumOptions; const std::vector getEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); From 5c91c742212e15531943bcca0a6a2e7ddc2bf70f Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 10:29:24 +0100 Subject: [PATCH 399/668] Replace enum list with map and update enums directly --- src/serlio/modifiers/PRTModifierAction.cpp | 177 ++------------------- src/serlio/modifiers/PRTModifierAction.h | 3 +- 2 files changed, 12 insertions(+), 168 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 0a642d4b..7a5dcba6 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -248,70 +248,6 @@ void clearEnumValues(const MObject& node, const MFnEnumAttribute& enumAttr) { MCHECK(mu::setEnumOptions(fNode.name().asWChar(), enumAttr.name().asWChar(), {})); } -bool updateCustomEnumValue(const prt::AttributeMap& defaultAttributeValues, MFnEnumAttribute& eAttr, - const RuleAttribute& ruleAttr, const MObject& node) { - const std::wstring fqAttrName = ruleAttr.fqName; - const prt::AnnotationArgumentType ruleAttrType = ruleAttr.mType; - - short minVal; - short maxVal; - MCHECK(eAttr.getMin(minVal)); - MCHECK(eAttr.getMax(maxVal)); - - MString defMStringVal; - - switch (ruleAttrType) { - case prt::AAT_STR: { - const wchar_t* defStringVal = defaultAttributeValues.getString(fqAttrName.c_str()); - if (defStringVal == nullptr) - return false; - defMStringVal = defStringVal; - break; - } - case prt::AAT_FLOAT: { - const auto defDoubleVal = defaultAttributeValues.getFloat(fqAttrName.c_str()); - defMStringVal = std::to_wstring(defDoubleVal).c_str(); - break; - } - case prt::AAT_BOOL: { - const auto defBoolVal = defaultAttributeValues.getBool(fqAttrName.c_str()); - defMStringVal = std::to_wstring(defBoolVal).c_str(); - break; - } - default: { - LOG_ERR << "Cannot handle attribute type " << ruleAttrType << " for attr " << fqAttrName; - return false; - } - } - - MStatus status; - short idx = eAttr.fieldIndex(defMStringVal, &status); - - const bool needToAddCustomDefaultValue = (status != MStatus::kSuccess); - const bool needToRemoveCustomDefaultValue = ((idx > 0) && (minVal == 0)); - - if (needToAddCustomDefaultValue || needToRemoveCustomDefaultValue) { - std::vector enumOptions; - - for (short currIdx = 1; currIdx <= maxVal; currIdx++) - enumOptions.emplace_back(eAttr.fieldName(currIdx).asWChar()); - - clearEnumValues(node, eAttr); - // adding enums through MFnEnumAttribute does not cause issues, when the enum option string contains ":" - short currIdx = 1; - for (const MString& option : enumOptions) - MCHECK(eAttr.addField(option, currIdx++)); - } - else { - return false; - } - - if (needToAddCustomDefaultValue) - MCHECK(eAttr.addField(defMStringVal, 0)); - - return true; -} - short getDefaultEnumValue(const prt::AttributeMap& defaultAttributeValues, const MFnEnumAttribute& eAttr, const RuleAttribute& ruleAttr) { const std::wstring fqAttrName = ruleAttr.fqName; @@ -890,7 +826,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacProble const bool isDefaultValue = (defEnumVal == enumVal); const bool hasNewCustomDefault = - updateCustomEnumValue(*defaultAttributeValues, eAttr, ruleAttribute, node); + mEnums[ruleAttribute.mayaFullName].updateOptions(node, mRuleAttributes, *defaultAttributeValues); if (hasNewCustomDefault || (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue)) plug.setShort(defEnumVal); break; @@ -919,100 +855,6 @@ MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacProble return MStatus::kSuccess; } -void PRTModifierAction::updateDynamicEnums(const MObject& node) { - for (auto& e : mEnums) { - if (e.mValuesAttr.length() == 0) - continue; - - short maxEnumVal; - e.mAttr.getMax(maxEnumVal); - if (maxEnumVal > 0) - clearEnumValues(node, e.mAttr); - - const MString fullAttrName = e.mAttr.name(); - const auto ruleAttrIt = mRuleAttributes.find(fullAttrName.asWChar()); - assert(ruleAttrIt != mRuleAttributes.end()); // Rule not found - const RuleAttribute ruleAttr = (ruleAttrIt != mRuleAttributes.end()) ? ruleAttrIt->second : RuleAttribute(); - - const std::wstring attrStyle = prtu::getStyle(ruleAttr.fqName).c_str(); - std::wstring attrImport = prtu::getImport(ruleAttr.fqName).c_str(); - if (!attrImport.empty()) - attrImport += prtu::IMPORT_DELIMITER; - - std::wstring valuesAttr = attrStyle + prtu::STYLE_DELIMITER + attrImport; - valuesAttr.append(e.mValuesAttr.asWChar()); - - const prt::Attributable::PrimitiveType type = mGenerateAttrs->getType(valuesAttr.c_str()); - - switch (type) { - case prt::Attributable::PT_STRING_ARRAY: { - size_t arr_length = 0; - const wchar_t* const* stringArray = mGenerateAttrs->getStringArray(valuesAttr.c_str(), &arr_length); - - for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { - if (stringArray[enumIndex] == nullptr) - continue; - std::wstring_view currStringView = stringArray[enumIndex]; - - // remove newlines from strings, because they break the maya UI - const size_t cutoffIndex = currStringView.find_first_of(L"\r\n"); - currStringView = currStringView.substr(0, cutoffIndex); - - const MString mCurrString(currStringView.data(), static_cast(currStringView.length())); - MCHECK(e.mAttr.addField(mCurrString, enumIndex + 1)); - } - break; - } - case prt::Attributable::PT_FLOAT_ARRAY: { - size_t arr_length = 0; - const double* doubleArray = mGenerateAttrs->getFloatArray(valuesAttr.c_str(), &arr_length); - - for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { - const double currDouble = doubleArray[enumIndex]; - - const MString mCurrString(std::to_wstring(currDouble).c_str()); - MCHECK(e.mAttr.addField(mCurrString, enumIndex + 1)); - } - break; - } - case prt::Attributable::PT_BOOL_ARRAY: { - size_t arr_length = 0; - const bool* boolArray = mGenerateAttrs->getBoolArray(valuesAttr.c_str(), &arr_length); - - for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { - const bool currBool = boolArray[enumIndex]; - - const MString mCurrString(std::to_wstring(currBool).c_str()); - MCHECK(e.mAttr.addField(mCurrString, enumIndex + 1)); - } - break; - } - case prt::Attributable::PT_STRING: { - const MString mCurrString = mGenerateAttrs->getString(valuesAttr.c_str()); - - MCHECK(e.mAttr.addField(mCurrString, 1)); - break; - } - case prt::Attributable::PT_FLOAT: { - const bool currFloat = mGenerateAttrs->getFloat(valuesAttr.c_str()); - - const MString mCurrString(std::to_wstring(currFloat).c_str()); - MCHECK(e.mAttr.addField(mCurrString, 1)); - break; - } - case prt::Attributable::PT_BOOL: { - const bool currBool = mGenerateAttrs->getBool(valuesAttr.c_str()); - - const MString mCurrString(std::to_wstring(currBool).c_str()); - MCHECK(e.mAttr.addField(mCurrString, 1)); - break; - } - default: - break; - } - } -} - // Sets the mesh object for the action to operate on void PRTModifierAction::setMesh(MObject& _inMesh, MObject& _outMesh) { inMesh = _inMesh; @@ -1076,7 +918,9 @@ MStatus PRTModifierAction::updateRuleFiles(const MObject& node, const MString& r } createNodeAttributes(ruleAttributes, node, info.get()); - updateDynamicEnums(node); + for (auto& enumPair : mEnums) { + enumPair.second.updateOptions(node, mRuleAttributes, *mGenerateAttrs); + } } return MS::kSuccess; @@ -1190,8 +1034,8 @@ MStatus PRTModifierAction::createNodeAttributes(const RuleAttributeSet& ruleAttr case prt::Attributable::PT_BOOL: { const bool value = mGenerateAttrs->getBool(fqName.c_str()); if (attrTrait.first == AttributeTrait::ENUM) { - mEnums.emplace_front(); - MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, value, mEnums.front())); + mEnums.try_emplace(p.mayaFullName); + MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, value, mEnums[p.mayaFullName])); } else { MCHECK(addBoolParameter(node, attr, p, value)); @@ -1203,8 +1047,8 @@ MStatus PRTModifierAction::createNodeAttributes(const RuleAttributeSet& ruleAttr switch (attrTrait.first) { case AttributeTrait::ENUM: { - mEnums.emplace_front(); - MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, value, mEnums.front())); + mEnums.try_emplace(p.mayaFullName); + MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, value, mEnums[p.mayaFullName])); break; } case AttributeTrait::RANGE: { @@ -1253,8 +1097,9 @@ MStatus PRTModifierAction::createNodeAttributes(const RuleAttributeSet& ruleAttr switch (attrTrait.first) { case AttributeTrait::ENUM: { - mEnums.emplace_front(); - MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, mvalue, mEnums.front())); + mEnums.try_emplace(p.mayaFullName); + MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, mvalue, + mEnums[p.mayaFullName])); break; } case AttributeTrait::FILE: diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index c857c045..22059555 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -120,8 +120,7 @@ class PRTModifierAction : public polyModifierFty { // init in fillAttributesFromNode() AttributeMapUPtr mGenerateAttrs; - std::list mEnums; - void updateDynamicEnums(const MObject& node); + std::map mEnums; MStatus createNodeAttributes(const RuleAttributeSet& ruleAttributes, const MObject& node, const prt::RuleFileInfo* info); From e272f439889a7782d8e3fa67bdafca2e0394a1d7 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 10:54:48 +0100 Subject: [PATCH 400/668] Cleanup: split PRTModifierEnum implementation into separate file --- src/serlio/CMakeLists.txt | 2 + src/serlio/modifiers/PRTModifierAction.cpp | 196 +----------------- src/serlio/modifiers/PRTModifierAction.h | 33 +-- src/serlio/modifiers/PRTModifierEnum.cpp | 223 +++++++++++++++++++++ src/serlio/modifiers/PRTModifierEnum.h | 59 ++++++ 5 files changed, 289 insertions(+), 224 deletions(-) create mode 100644 src/serlio/modifiers/PRTModifierEnum.cpp create mode 100644 src/serlio/modifiers/PRTModifierEnum.h diff --git a/src/serlio/CMakeLists.txt b/src/serlio/CMakeLists.txt index 437c6561..1886ba26 100644 --- a/src/serlio/CMakeLists.txt +++ b/src/serlio/CMakeLists.txt @@ -12,6 +12,7 @@ add_library(${SERLIO_TARGET} SHARED modifiers/PRTMesh.cpp modifiers/PRTModifierAction.cpp modifiers/PRTModifierCommand.cpp + modifiers/PRTModifierEnum.cpp modifiers/PRTModifierNode.cpp modifiers/polyModifier/polyModifierCmd.cpp modifiers/polyModifier/polyModifierFty.cpp @@ -38,6 +39,7 @@ if (CMAKE_GENERATOR MATCHES "Visual Studio.+") modifiers/PRTMesh.h modifiers/PRTModifierAction.h modifiers/PRTModifierCommand.h + modifiers/PRTModifierEnum.h modifiers/PRTModifierNode.h modifiers/polyModifier/polyModifierCmd.h modifiers/polyModifier/polyModifierFty.h diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 7a5dcba6..dfccc3c5 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -240,13 +240,6 @@ MStatus addHiddenBoolParameter(MFnDependencyNode& node, MFnAttribute& tAttr, con } return stat; } -void clearEnumValues(const MObject& node, const MFnEnumAttribute& enumAttr) { - MStatus stat; - const MFnDependencyNode fNode(node, &stat); - MCHECK(stat); - // clear enum options - MCHECK(mu::setEnumOptions(fNode.name().asWChar(), enumAttr.name().asWChar(), {})); -} short getDefaultEnumValue(const prt::AttributeMap& defaultAttributeValues, const MFnEnumAttribute& eAttr, const RuleAttribute& ruleAttr) { @@ -388,191 +381,6 @@ T getPlugValueAndRemoveAttr(MFnDependencyNode& node, const MString& briefName, c } } // namespace -bool PRTModifierEnum::updateOptions(const MObject& node, const RuleAttributeMap& mRuleAttributes, - const prt::AttributeMap& defaultAttributeValues) { - const MString fullAttrName = mAttr.name(); - const auto ruleAttrIt = mRuleAttributes.find(fullAttrName.asWChar()); - assert(ruleAttrIt != mRuleAttributes.end()); // Rule not found - const RuleAttribute ruleAttr = (ruleAttrIt != mRuleAttributes.end()) ? ruleAttrIt->second : RuleAttribute(); - - const std::vector& newEnumOptions = getEnumOptions(node, ruleAttr, defaultAttributeValues); - - updateCustomEnumValue(ruleAttr, defaultAttributeValues); - - MStatus status; - const short defaultIdx = mAttr.fieldIndex(mCustomDefaultValue, &status); - if ((status == MStatus::kSuccess) && (newEnumOptions == mEnumOptions)) - return false; - - // Workaround, since setting enum values, when none are available through mel causes an Error... - MString defaultValue; - if (mAttr.getDefault(defaultValue) == MStatus::kSuccess) - clearEnumValues(node, mAttr); - - mEnumOptions = newEnumOptions; - - auto itr = std::find(mEnumOptions.cbegin(), mEnumOptions.cend(), mCustomDefaultValue); - - int customDefaultIdx = 0; - int currIdx = 1; - - for (const MString& option : mEnumOptions) { - mAttr.addField(option, currIdx); - if (option == mCustomDefaultValue) - customDefaultIdx = currIdx; - currIdx++; - } - - if (customDefaultIdx == 0) - mAttr.addField(mCustomDefaultValue, 0); - - return true; -} - -bool PRTModifierEnum::isDynamic() { - return mValuesAttr.length() > 0; -} - -const std::vector PRTModifierEnum::getEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, - const prt::AttributeMap& defaultAttributeValues) { - if (isDynamic()) { - return getDynamicEnumOptions(node, ruleAttr, defaultAttributeValues); - } - else { - std::vector enumOptions; - - short minVal; - short maxVal; - MCHECK(mAttr.getMin(minVal)); - MCHECK(mAttr.getMax(maxVal)); - - for (short currIdx = 1; currIdx <= maxVal; currIdx++) - enumOptions.emplace_back(mAttr.fieldName(currIdx)); - - return enumOptions; - } -} - -void PRTModifierEnum::updateCustomEnumValue(const RuleAttribute& ruleAttr, - const prt::AttributeMap& defaultAttributeValues) { - const std::wstring fqAttrName = ruleAttr.fqName; - const prt::AnnotationArgumentType ruleAttrType = ruleAttr.mType; - - MString defMStringVal; - - switch (ruleAttrType) { - case prt::AAT_STR: { - const wchar_t* defStringVal = defaultAttributeValues.getString(fqAttrName.c_str()); - if (defStringVal == nullptr) - return; - defMStringVal = defStringVal; - break; - } - case prt::AAT_FLOAT: { - const auto defDoubleVal = defaultAttributeValues.getFloat(fqAttrName.c_str()); - defMStringVal = std::to_wstring(defDoubleVal).c_str(); - break; - } - case prt::AAT_BOOL: { - const auto defBoolVal = defaultAttributeValues.getBool(fqAttrName.c_str()); - defMStringVal = std::to_wstring(defBoolVal).c_str(); - break; - } - default: { - LOG_ERR << "Cannot handle attribute type " << ruleAttrType << " for attr " << fqAttrName; - return; - } - } - - mCustomDefaultValue = defMStringVal; -} - -const std::vector PRTModifierEnum::getDynamicEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, - const prt::AttributeMap& defaultAttributeValues) { - std::vector enumOptions; - if (!isDynamic()) - return enumOptions; - const MString fullAttrName = mAttr.name(); - - const std::wstring attrStyle = prtu::getStyle(ruleAttr.fqName).c_str(); - std::wstring attrImport = prtu::getImport(ruleAttr.fqName).c_str(); - if (!attrImport.empty()) - attrImport += prtu::IMPORT_DELIMITER; - - std::wstring valuesAttr = attrStyle + prtu::STYLE_DELIMITER + attrImport; - valuesAttr.append(mValuesAttr.asWChar()); - - const prt::Attributable::PrimitiveType type = defaultAttributeValues.getType(valuesAttr.c_str()); - - switch (type) { - case prt::Attributable::PT_STRING_ARRAY: { - size_t arr_length = 0; - const wchar_t* const* stringArray = defaultAttributeValues.getStringArray(valuesAttr.c_str(), &arr_length); - - for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { - if (stringArray[enumIndex] == nullptr) - continue; - std::wstring_view currStringView = stringArray[enumIndex]; - - // remove newlines from strings, because they break the maya UI - const size_t cutoffIndex = currStringView.find_first_of(L"\r\n"); - currStringView = currStringView.substr(0, cutoffIndex); - - const MString mCurrString(currStringView.data(), static_cast(currStringView.length())); - enumOptions.emplace_back(mCurrString); - } - break; - } - case prt::Attributable::PT_FLOAT_ARRAY: { - size_t arr_length = 0; - const double* doubleArray = defaultAttributeValues.getFloatArray(valuesAttr.c_str(), &arr_length); - - for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { - const double currDouble = doubleArray[enumIndex]; - - const MString mCurrString(std::to_wstring(currDouble).c_str()); - enumOptions.emplace_back(mCurrString); - } - break; - } - case prt::Attributable::PT_BOOL_ARRAY: { - size_t arr_length = 0; - const bool* boolArray = defaultAttributeValues.getBoolArray(valuesAttr.c_str(), &arr_length); - - for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { - const bool currBool = boolArray[enumIndex]; - - const MString mCurrString(std::to_wstring(currBool).c_str()); - enumOptions.emplace_back(mCurrString); - } - break; - } - case prt::Attributable::PT_STRING: { - const MString mCurrString = defaultAttributeValues.getString(valuesAttr.c_str()); - - enumOptions.emplace_back(mCurrString); - break; - } - case prt::Attributable::PT_FLOAT: { - const bool currFloat = defaultAttributeValues.getFloat(valuesAttr.c_str()); - - const MString mCurrString(std::to_wstring(currFloat).c_str()); - enumOptions.emplace_back(mCurrString); - break; - } - case prt::Attributable::PT_BOOL: { - const bool currBool = defaultAttributeValues.getBool(valuesAttr.c_str()); - - const MString mCurrString(std::to_wstring(currBool).c_str()); - enumOptions.emplace_back(mCurrString); - break; - } - default: - break; - } - return enumOptions; -} - PRTModifierAction::PRTModifierAction() { AttributeMapBuilderUPtr optionsBuilder(prt::AttributeMapBuilder::create()); @@ -825,8 +633,8 @@ MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacProble MCHECK(plug.getValue(enumVal)); const bool isDefaultValue = (defEnumVal == enumVal); - const bool hasNewCustomDefault = - mEnums[ruleAttribute.mayaFullName].updateOptions(node, mRuleAttributes, *defaultAttributeValues); + const bool hasNewCustomDefault = mEnums[ruleAttribute.mayaFullName].updateOptions( + node, mRuleAttributes, *defaultAttributeValues); if (hasNewCustomDefault || (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue)) plug.setShort(defEnumVal); break; diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 22059555..0696dc03 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -21,6 +21,7 @@ #include "modifiers/MayaCallbacks.h" #include "modifiers/PRTMesh.h" +#include "modifiers/PRTModifierEnum.h" #include "modifiers/RuleAttributes.h" #include "modifiers/polyModifier/polyModifierFty.h" @@ -31,7 +32,6 @@ #include "prt/API.h" #include "maya/MDoubleArray.h" -#include "maya/MFnEnumAttribute.h" #include "maya/MIntArray.h" #include "maya/MObject.h" #include "maya/MPlugArray.h" @@ -47,34 +47,6 @@ class PRTModifierAction; using RuleAttributeMap = std::map; using PRTEnumDefaultValue = std::variant; -class PRTModifierEnum { - friend class PRTModifierAction; - -public: - PRTModifierEnum() = default; - - MStatus fill(const prt::Annotation* annot); - bool updateOptions(const MObject& node, const RuleAttributeMap& ruleAttributes, - const prt::AttributeMap& defaultAttributeValues); - - bool isDynamic(); - -public: - MFnEnumAttribute mAttr; - -private: - bool mRestricted = true; - MString mValuesAttr; - MString mCustomDefaultValue; - std::vector mEnumOptions; - - const std::vector getEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, - const prt::AttributeMap& defaultAttributeValues); - const std::vector getDynamicEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, - const prt::AttributeMap& defaultAttributeValues); - void updateCustomEnumValue(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); -}; // class PRTModifierEnum - class PRTModifierAction : public polyModifierFty { friend class PRTModifierEnum; @@ -136,7 +108,8 @@ class PRTModifierAction : public polyModifierFty { static MStatus addFileParameter(MFnDependencyNode& node, MObject& attr, const RuleAttribute& name, const MString& defaultValue, const std::wstring& ext); static MStatus addEnumParameter(const prt::Annotation* annot, MFnDependencyNode& node, MObject& attr, - const RuleAttribute& name, const PRTEnumDefaultValue& defaultValue, PRTModifierEnum& e); + const RuleAttribute& name, const PRTEnumDefaultValue& defaultValue, + PRTModifierEnum& e); static MStatus addColorParameter(MFnDependencyNode& node, MObject& attr, const RuleAttribute& name, const MString& defaultValue); }; diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp new file mode 100644 index 00000000..9f6c5ff3 --- /dev/null +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -0,0 +1,223 @@ +/** + * Serlio - Esri CityEngine Plugin for Autodesk Maya + * + * See https://github.com/esri/serlio for build and usage instructions. + * + * Copyright (c) 2012-2019 Esri R&D Center Zurich + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "modifiers/PRTModifierEnum.h" + +#include "utils/MayaUtilities.h" + +#include "maya/MFnDependencyNode.h" + +#include + +namespace { + +void clearEnumValues(const MObject& node, const MFnEnumAttribute& enumAttr) { + MStatus stat; + const MFnDependencyNode fNode(node, &stat); + MCHECK(stat); + // clear enum options + MCHECK(mu::setEnumOptions(fNode.name().asWChar(), enumAttr.name().asWChar(), {})); +} + +} // namespace + +bool PRTModifierEnum::updateOptions(const MObject& node, const RuleAttributeMap& mRuleAttributes, + const prt::AttributeMap& defaultAttributeValues) { + const MString fullAttrName = mAttr.name(); + const auto ruleAttrIt = mRuleAttributes.find(fullAttrName.asWChar()); + assert(ruleAttrIt != mRuleAttributes.end()); // Rule not found + const RuleAttribute ruleAttr = (ruleAttrIt != mRuleAttributes.end()) ? ruleAttrIt->second : RuleAttribute(); + + const std::vector& newEnumOptions = getEnumOptions(node, ruleAttr, defaultAttributeValues); + + updateCustomEnumValue(ruleAttr, defaultAttributeValues); + + MStatus status; + const short defaultIdx = mAttr.fieldIndex(mCustomDefaultValue, &status); + if ((status == MStatus::kSuccess) && (newEnumOptions == mEnumOptions)) + return false; + + // Workaround, since setting enum values, when none are available through mel causes an Error... + MString defaultValue; + if (mAttr.getDefault(defaultValue) == MStatus::kSuccess) + clearEnumValues(node, mAttr); + + mEnumOptions = newEnumOptions; + + auto itr = std::find(mEnumOptions.cbegin(), mEnumOptions.cend(), mCustomDefaultValue); + + int customDefaultIdx = 0; + int currIdx = 1; + + for (const MString& option : mEnumOptions) { + mAttr.addField(option, currIdx); + if (option == mCustomDefaultValue) + customDefaultIdx = currIdx; + currIdx++; + } + + if (customDefaultIdx == 0) + mAttr.addField(mCustomDefaultValue, 0); + + return true; +} + +bool PRTModifierEnum::isDynamic() { + return mValuesAttr.length() > 0; +} + +const std::vector PRTModifierEnum::getEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, + const prt::AttributeMap& defaultAttributeValues) { + if (isDynamic()) { + return getDynamicEnumOptions(node, ruleAttr, defaultAttributeValues); + } + else { + std::vector enumOptions; + + short minVal; + short maxVal; + MCHECK(mAttr.getMin(minVal)); + MCHECK(mAttr.getMax(maxVal)); + + for (short currIdx = 1; currIdx <= maxVal; currIdx++) + enumOptions.emplace_back(mAttr.fieldName(currIdx)); + + return enumOptions; + } +} + +void PRTModifierEnum::updateCustomEnumValue(const RuleAttribute& ruleAttr, + const prt::AttributeMap& defaultAttributeValues) { + const std::wstring fqAttrName = ruleAttr.fqName; + const prt::AnnotationArgumentType ruleAttrType = ruleAttr.mType; + + MString defMStringVal; + + switch (ruleAttrType) { + case prt::AAT_STR: { + const wchar_t* defStringVal = defaultAttributeValues.getString(fqAttrName.c_str()); + if (defStringVal == nullptr) + return; + defMStringVal = defStringVal; + break; + } + case prt::AAT_FLOAT: { + const auto defDoubleVal = defaultAttributeValues.getFloat(fqAttrName.c_str()); + defMStringVal = std::to_wstring(defDoubleVal).c_str(); + break; + } + case prt::AAT_BOOL: { + const auto defBoolVal = defaultAttributeValues.getBool(fqAttrName.c_str()); + defMStringVal = std::to_wstring(defBoolVal).c_str(); + break; + } + default: { + LOG_ERR << "Cannot handle attribute type " << ruleAttrType << " for attr " << fqAttrName; + return; + } + } + + mCustomDefaultValue = defMStringVal; +} + +const std::vector PRTModifierEnum::getDynamicEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, + const prt::AttributeMap& defaultAttributeValues) { + std::vector enumOptions; + if (!isDynamic()) + return enumOptions; + const MString fullAttrName = mAttr.name(); + + const std::wstring attrStyle = prtu::getStyle(ruleAttr.fqName).c_str(); + std::wstring attrImport = prtu::getImport(ruleAttr.fqName).c_str(); + if (!attrImport.empty()) + attrImport += prtu::IMPORT_DELIMITER; + + std::wstring valuesAttr = attrStyle + prtu::STYLE_DELIMITER + attrImport; + valuesAttr.append(mValuesAttr.asWChar()); + + const prt::Attributable::PrimitiveType type = defaultAttributeValues.getType(valuesAttr.c_str()); + + switch (type) { + case prt::Attributable::PT_STRING_ARRAY: { + size_t arr_length = 0; + const wchar_t* const* stringArray = defaultAttributeValues.getStringArray(valuesAttr.c_str(), &arr_length); + + for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { + if (stringArray[enumIndex] == nullptr) + continue; + std::wstring_view currStringView = stringArray[enumIndex]; + + // remove newlines from strings, because they break the maya UI + const size_t cutoffIndex = currStringView.find_first_of(L"\r\n"); + currStringView = currStringView.substr(0, cutoffIndex); + + const MString mCurrString(currStringView.data(), static_cast(currStringView.length())); + enumOptions.emplace_back(mCurrString); + } + break; + } + case prt::Attributable::PT_FLOAT_ARRAY: { + size_t arr_length = 0; + const double* doubleArray = defaultAttributeValues.getFloatArray(valuesAttr.c_str(), &arr_length); + + for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { + const double currDouble = doubleArray[enumIndex]; + + const MString mCurrString(std::to_wstring(currDouble).c_str()); + enumOptions.emplace_back(mCurrString); + } + break; + } + case prt::Attributable::PT_BOOL_ARRAY: { + size_t arr_length = 0; + const bool* boolArray = defaultAttributeValues.getBoolArray(valuesAttr.c_str(), &arr_length); + + for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { + const bool currBool = boolArray[enumIndex]; + + const MString mCurrString(std::to_wstring(currBool).c_str()); + enumOptions.emplace_back(mCurrString); + } + break; + } + case prt::Attributable::PT_STRING: { + const MString mCurrString = defaultAttributeValues.getString(valuesAttr.c_str()); + + enumOptions.emplace_back(mCurrString); + break; + } + case prt::Attributable::PT_FLOAT: { + const bool currFloat = defaultAttributeValues.getFloat(valuesAttr.c_str()); + + const MString mCurrString(std::to_wstring(currFloat).c_str()); + enumOptions.emplace_back(mCurrString); + break; + } + case prt::Attributable::PT_BOOL: { + const bool currBool = defaultAttributeValues.getBool(valuesAttr.c_str()); + + const MString mCurrString(std::to_wstring(currBool).c_str()); + enumOptions.emplace_back(mCurrString); + break; + } + default: + break; + } + return enumOptions; +} diff --git a/src/serlio/modifiers/PRTModifierEnum.h b/src/serlio/modifiers/PRTModifierEnum.h new file mode 100644 index 00000000..4c9220e3 --- /dev/null +++ b/src/serlio/modifiers/PRTModifierEnum.h @@ -0,0 +1,59 @@ +/** + * Serlio - Esri CityEngine Plugin for Autodesk Maya + * + * See https://github.com/esri/serlio for build and usage instructions. + * + * Copyright (c) 2012-2019 Esri R&D Center Zurich + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "modifiers/PRTModifierAction.h" +#include "modifiers/RuleAttributes.h" + +#include "utils/Utilities.h" + +#include "maya/MFnEnumAttribute.h" +#include "maya/MObject.h" +#include "maya/MString.h" + +using RuleAttributeMap = std::map; + +class PRTModifierEnum { + friend class PRTModifierAction; + +public: + PRTModifierEnum() = default; + + MStatus fill(const prt::Annotation* annot); + bool updateOptions(const MObject& node, const RuleAttributeMap& ruleAttributes, + const prt::AttributeMap& defaultAttributeValues); + + bool isDynamic(); + +public: + MFnEnumAttribute mAttr; + +private: + bool mRestricted = true; + MString mValuesAttr; + MString mCustomDefaultValue; + std::vector mEnumOptions; + + const std::vector getEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, + const prt::AttributeMap& defaultAttributeValues); + const std::vector getDynamicEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, + const prt::AttributeMap& defaultAttributeValues); + void updateCustomEnumValue(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); +}; // class PRTModifierEnum From af234e61d9c415737a7cf9d10b16205afadaee44 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 13:07:11 +0100 Subject: [PATCH 401/668] Cleanup: move empty check into helper function --- src/serlio/modifiers/PRTModifierEnum.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index 9f6c5ff3..ec6c9de6 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -28,6 +28,10 @@ namespace { void clearEnumValues(const MObject& node, const MFnEnumAttribute& enumAttr) { + // Workaround: since setting enum values through mel, when none are available causes an Error... + MString defaultValue; + if (enumAttr.getDefault(defaultValue) != MStatus::kSuccess) + return; MStatus stat; const MFnDependencyNode fNode(node, &stat); MCHECK(stat); @@ -53,10 +57,7 @@ bool PRTModifierEnum::updateOptions(const MObject& node, const RuleAttributeMap& if ((status == MStatus::kSuccess) && (newEnumOptions == mEnumOptions)) return false; - // Workaround, since setting enum values, when none are available through mel causes an Error... - MString defaultValue; - if (mAttr.getDefault(defaultValue) == MStatus::kSuccess) - clearEnumValues(node, mAttr); + clearEnumValues(node, mAttr); mEnumOptions = newEnumOptions; From 856708b2844f96207b7f38dfac43bbd3dea90e04 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 13:08:18 +0100 Subject: [PATCH 402/668] Update index when enum options change --- src/serlio/modifiers/PRTModifierAction.cpp | 15 ++++++++++----- src/serlio/modifiers/PRTModifierEnum.cpp | 14 ++++++++++---- src/serlio/modifiers/PRTModifierEnum.h | 4 ++-- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index dfccc3c5..e14be7b3 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -626,16 +626,21 @@ MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacProble break; } case PrtAttributeType::ENUM: { - MFnEnumAttribute eAttr(fnAttribute.object()); + PRTModifierEnum& modifierEnum = mEnums[ruleAttribute.mayaFullName]; - const short defEnumVal = getDefaultEnumValue(*defaultAttributeValues, eAttr, ruleAttribute); short enumVal; MCHECK(plug.getValue(enumVal)); + const auto newEnumOptions = + modifierEnum.updateOptions(node, mRuleAttributes, *defaultAttributeValues, enumVal); + const bool hasNewEnumOptions = newEnumOptions.first; + enumVal = newEnumOptions.second; + + const short defEnumVal = + getDefaultEnumValue(*defaultAttributeValues, modifierEnum.mAttr, ruleAttribute); + const bool isDefaultValue = (defEnumVal == enumVal); - const bool hasNewCustomDefault = mEnums[ruleAttribute.mayaFullName].updateOptions( - node, mRuleAttributes, *defaultAttributeValues); - if (hasNewCustomDefault || (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue)) + if (hasNewEnumOptions || (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue)) plug.setShort(defEnumVal); break; } diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index ec6c9de6..08dd070d 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -41,8 +41,9 @@ void clearEnumValues(const MObject& node, const MFnEnumAttribute& enumAttr) { } // namespace -bool PRTModifierEnum::updateOptions(const MObject& node, const RuleAttributeMap& mRuleAttributes, - const prt::AttributeMap& defaultAttributeValues) { +std::pair PRTModifierEnum::updateOptions(const MObject& node, const RuleAttributeMap& mRuleAttributes, + const prt::AttributeMap& defaultAttributeValues, + short selectedEnumIdx) { const MString fullAttrName = mAttr.name(); const auto ruleAttrIt = mRuleAttributes.find(fullAttrName.asWChar()); assert(ruleAttrIt != mRuleAttributes.end()); // Rule not found @@ -55,8 +56,10 @@ bool PRTModifierEnum::updateOptions(const MObject& node, const RuleAttributeMap& MStatus status; const short defaultIdx = mAttr.fieldIndex(mCustomDefaultValue, &status); if ((status == MStatus::kSuccess) && (newEnumOptions == mEnumOptions)) - return false; + return std::make_pair(false, selectedEnumIdx); + const MString oldSelectedOption = mAttr.fieldName(selectedEnumIdx); + clearEnumValues(node, mAttr); mEnumOptions = newEnumOptions; @@ -64,19 +67,22 @@ bool PRTModifierEnum::updateOptions(const MObject& node, const RuleAttributeMap& auto itr = std::find(mEnumOptions.cbegin(), mEnumOptions.cend(), mCustomDefaultValue); int customDefaultIdx = 0; + int newSelectedEnumIdx = 0; int currIdx = 1; for (const MString& option : mEnumOptions) { mAttr.addField(option, currIdx); if (option == mCustomDefaultValue) customDefaultIdx = currIdx; + if (option == oldSelectedOption) + newSelectedEnumIdx = currIdx; currIdx++; } if (customDefaultIdx == 0) mAttr.addField(mCustomDefaultValue, 0); - return true; + return std::make_pair(true, newSelectedEnumIdx); } bool PRTModifierEnum::isDynamic() { diff --git a/src/serlio/modifiers/PRTModifierEnum.h b/src/serlio/modifiers/PRTModifierEnum.h index 4c9220e3..5abbf31e 100644 --- a/src/serlio/modifiers/PRTModifierEnum.h +++ b/src/serlio/modifiers/PRTModifierEnum.h @@ -37,8 +37,8 @@ class PRTModifierEnum { PRTModifierEnum() = default; MStatus fill(const prt::Annotation* annot); - bool updateOptions(const MObject& node, const RuleAttributeMap& ruleAttributes, - const prt::AttributeMap& defaultAttributeValues); + std::pair updateOptions(const MObject& node, const RuleAttributeMap& ruleAttributes, + const prt::AttributeMap& defaultAttributeValues, short selectedEnumIdx = 0); bool isDynamic(); From 4d0ec7c1702c20f61a7e69bebc13e0b830b4056d Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 13:14:55 +0100 Subject: [PATCH 403/668] Cleanup: mode PRTModifierEnum::fill implementation to correct file --- src/serlio/modifiers/PRTModifierAction.cpp | 43 --------------------- src/serlio/modifiers/PRTModifierEnum.cpp | 45 ++++++++++++++++++++++ 2 files changed, 45 insertions(+), 43 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index e14be7b3..91c74ead 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -52,8 +52,6 @@ constexpr const wchar_t* FILE_CGA_PRINT = L"CGAPrint.txt"; constexpr const wchar_t* NULL_KEY = L"#NULL#"; constexpr const wchar_t* MIN_KEY = L"min"; constexpr const wchar_t* MAX_KEY = L"max"; -constexpr const wchar_t* RESTRICTED_KEY = L"restricted"; -constexpr const wchar_t* VALUES_ATTR_KEY = L"valuesAttr"; constexpr const wchar_t* ATTRIBUTE_USER_SET_SUFFIX = L"_user_set"; constexpr const wchar_t* ATTRIBUTE_FORCE_DEFAULT_SUFFIX = L"_force_default"; @@ -985,47 +983,6 @@ void PRTModifierAction::removeUnusedAttribs(MFnDependencyNode& node) { } } -MStatus PRTModifierEnum::fill(const prt::Annotation* annot) { - mRestricted = true; - MStatus stat; - - uint32_t enumIndex = 1; - for (size_t arg = 0; arg < annot->getNumArguments(); arg++) { - - const wchar_t* key = annot->getArgument(arg)->getKey(); - if (std::wcscmp(key, NULL_KEY) != 0) { - if (std::wcscmp(key, RESTRICTED_KEY) == 0) - mRestricted = annot->getArgument(arg)->getBool(); - - if (std::wcscmp(key, VALUES_ATTR_KEY) == 0) - mValuesAttr = annot->getArgument(arg)->getStr(); - continue; - } - - switch (annot->getArgument(arg)->getType()) { - case prt::AAT_BOOL: { - const bool val = annot->getArgument(arg)->getBool(); - MCHECK(mAttr.addField(MString(std::to_wstring(val).c_str()), enumIndex++)); - break; - } - case prt::AAT_FLOAT: { - const double val = annot->getArgument(arg)->getFloat(); - MCHECK(mAttr.addField(MString(std::to_wstring(val).c_str()), enumIndex++)); - break; - } - case prt::AAT_STR: { - const wchar_t* val = annot->getArgument(arg)->getStr(); - MCHECK(mAttr.addField(MString(val), enumIndex++)); - break; - } - default: - break; - } - } - - return MS::kSuccess; -} - MStatus PRTModifierAction::addParameter(MFnDependencyNode& node, MObject& attr, MFnAttribute& tAttr) { if (!(node.hasAttribute(tAttr.shortName()))) { MStatus stat; diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index 08dd070d..54550c86 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -27,6 +27,10 @@ namespace { +constexpr const wchar_t* NULL_KEY = L"#NULL#"; +constexpr const wchar_t* RESTRICTED_KEY = L"restricted"; +constexpr const wchar_t* VALUES_ATTR_KEY = L"valuesAttr"; + void clearEnumValues(const MObject& node, const MFnEnumAttribute& enumAttr) { // Workaround: since setting enum values through mel, when none are available causes an Error... MString defaultValue; @@ -228,3 +232,44 @@ const std::vector PRTModifierEnum::getDynamicEnumOptions(const MObject& } return enumOptions; } + +MStatus PRTModifierEnum::fill(const prt::Annotation* annot) { + mRestricted = true; + MStatus stat; + + uint32_t enumIndex = 1; + for (size_t arg = 0; arg < annot->getNumArguments(); arg++) { + + const wchar_t* key = annot->getArgument(arg)->getKey(); + if (std::wcscmp(key, NULL_KEY) != 0) { + if (std::wcscmp(key, RESTRICTED_KEY) == 0) + mRestricted = annot->getArgument(arg)->getBool(); + + if (std::wcscmp(key, VALUES_ATTR_KEY) == 0) + mValuesAttr = annot->getArgument(arg)->getStr(); + continue; + } + + switch (annot->getArgument(arg)->getType()) { + case prt::AAT_BOOL: { + const bool val = annot->getArgument(arg)->getBool(); + MCHECK(mAttr.addField(MString(std::to_wstring(val).c_str()), enumIndex++)); + break; + } + case prt::AAT_FLOAT: { + const double val = annot->getArgument(arg)->getFloat(); + MCHECK(mAttr.addField(MString(std::to_wstring(val).c_str()), enumIndex++)); + break; + } + case prt::AAT_STR: { + const wchar_t* val = annot->getArgument(arg)->getStr(); + MCHECK(mAttr.addField(MString(val), enumIndex++)); + break; + } + default: + break; + } + } + + return MS::kSuccess; +} \ No newline at end of file From 4c578ea3eb796488e853e28a03a3e6e990197afc Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 13:20:12 +0100 Subject: [PATCH 404/668] Cleanup: avoid using m_ names for local variables --- src/serlio/modifiers/PRTModifierEnum.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index 54550c86..7c9481eb 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -178,8 +178,8 @@ const std::vector PRTModifierEnum::getDynamicEnumOptions(const MObject& const size_t cutoffIndex = currStringView.find_first_of(L"\r\n"); currStringView = currStringView.substr(0, cutoffIndex); - const MString mCurrString(currStringView.data(), static_cast(currStringView.length())); - enumOptions.emplace_back(mCurrString); + const MString currMString(currStringView.data(), static_cast(currStringView.length())); + enumOptions.emplace_back(currMString); } break; } @@ -190,8 +190,8 @@ const std::vector PRTModifierEnum::getDynamicEnumOptions(const MObject& for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { const double currDouble = doubleArray[enumIndex]; - const MString mCurrString(std::to_wstring(currDouble).c_str()); - enumOptions.emplace_back(mCurrString); + const MString currMString(std::to_wstring(currDouble).c_str()); + enumOptions.emplace_back(currMString); } break; } @@ -202,29 +202,29 @@ const std::vector PRTModifierEnum::getDynamicEnumOptions(const MObject& for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { const bool currBool = boolArray[enumIndex]; - const MString mCurrString(std::to_wstring(currBool).c_str()); - enumOptions.emplace_back(mCurrString); + const MString currMString(std::to_wstring(currBool).c_str()); + enumOptions.emplace_back(currMString); } break; } case prt::Attributable::PT_STRING: { - const MString mCurrString = defaultAttributeValues.getString(valuesAttr.c_str()); + const MString currMString = defaultAttributeValues.getString(valuesAttr.c_str()); - enumOptions.emplace_back(mCurrString); + enumOptions.emplace_back(currMString); break; } case prt::Attributable::PT_FLOAT: { const bool currFloat = defaultAttributeValues.getFloat(valuesAttr.c_str()); - const MString mCurrString(std::to_wstring(currFloat).c_str()); - enumOptions.emplace_back(mCurrString); + const MString currMString(std::to_wstring(currFloat).c_str()); + enumOptions.emplace_back(currMString); break; } case prt::Attributable::PT_BOOL: { const bool currBool = defaultAttributeValues.getBool(valuesAttr.c_str()); - const MString mCurrString(std::to_wstring(currBool).c_str()); - enumOptions.emplace_back(mCurrString); + const MString currMString(std::to_wstring(currBool).c_str()); + enumOptions.emplace_back(currMString); break; } default: From 529f4b0d51662e956f037234c05c5fcc2db4580c Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 13:21:56 +0100 Subject: [PATCH 405/668] Cleanup: use MCHECK when adding enum fields --- src/serlio/modifiers/PRTModifierEnum.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index 7c9481eb..a73cf15a 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -75,7 +75,7 @@ std::pair PRTModifierEnum::updateOptions(const MObject& node, const int currIdx = 1; for (const MString& option : mEnumOptions) { - mAttr.addField(option, currIdx); + MCHECK(mAttr.addField(option, currIdx)); if (option == mCustomDefaultValue) customDefaultIdx = currIdx; if (option == oldSelectedOption) @@ -84,7 +84,7 @@ std::pair PRTModifierEnum::updateOptions(const MObject& node, const } if (customDefaultIdx == 0) - mAttr.addField(mCustomDefaultValue, 0); + MCHECK(mAttr.addField(mCustomDefaultValue, 0)); return std::make_pair(true, newSelectedEnumIdx); } From 76f7303514fbe21c8b493cae1c1bfe5ddda4f5c9 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 13:23:22 +0100 Subject: [PATCH 406/668] Cleanup: remove unused iterator --- src/serlio/modifiers/PRTModifierEnum.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index a73cf15a..ce15aa19 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -68,8 +68,6 @@ std::pair PRTModifierEnum::updateOptions(const MObject& node, const mEnumOptions = newEnumOptions; - auto itr = std::find(mEnumOptions.cbegin(), mEnumOptions.cend(), mCustomDefaultValue); - int customDefaultIdx = 0; int newSelectedEnumIdx = 0; int currIdx = 1; From db099c18439368b6ad539c788d4db11ff6a3213f Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 13:25:09 +0100 Subject: [PATCH 407/668] Cleanup: use more accurate variable name --- src/serlio/modifiers/PRTModifierAction.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 91c74ead..7c23c9b1 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -628,10 +628,10 @@ MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacProble short enumVal; MCHECK(plug.getValue(enumVal)); - const auto newEnumOptions = + const auto newEnumInfo = modifierEnum.updateOptions(node, mRuleAttributes, *defaultAttributeValues, enumVal); - const bool hasNewEnumOptions = newEnumOptions.first; - enumVal = newEnumOptions.second; + const bool hasNewEnumOptions = newEnumInfo.first; + enumVal = newEnumInfo.second; const short defEnumVal = getDefaultEnumValue(*defaultAttributeValues, modifierEnum.mAttr, ruleAttribute); From ecde35d4e70505d3483961da898d79859190a6e8 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 13:29:32 +0100 Subject: [PATCH 408/668] Cleanup: add comment to describe output of PRTModifierEnum::updateOptions --- src/serlio/modifiers/PRTModifierEnum.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index ce15aa19..e66f328b 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -45,9 +45,11 @@ void clearEnumValues(const MObject& node, const MFnEnumAttribute& enumAttr) { } // namespace +// This function updates all enum options and returns a pair, where the first argument indicates if the options have +// changed and the second argument corresponds to the new index of the currently selected item std::pair PRTModifierEnum::updateOptions(const MObject& node, const RuleAttributeMap& mRuleAttributes, - const prt::AttributeMap& defaultAttributeValues, - short selectedEnumIdx) { + const prt::AttributeMap& defaultAttributeValues, + short selectedEnumIdx) { const MString fullAttrName = mAttr.name(); const auto ruleAttrIt = mRuleAttributes.find(fullAttrName.asWChar()); assert(ruleAttrIt != mRuleAttributes.end()); // Rule not found From 086fcb2b59ed37ff8a6e35a35dfa31a5be7de0f9 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 13:42:04 +0100 Subject: [PATCH 409/668] Cleanup: move RuleAttributeMap definition to RuleAttributes.h --- src/serlio/modifiers/PRTModifierAction.h | 1 - src/serlio/modifiers/PRTModifierEnum.h | 2 -- src/serlio/modifiers/RuleAttributes.h | 1 + 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.h b/src/serlio/modifiers/PRTModifierAction.h index 0696dc03..bc462ae6 100644 --- a/src/serlio/modifiers/PRTModifierAction.h +++ b/src/serlio/modifiers/PRTModifierAction.h @@ -44,7 +44,6 @@ class PRTModifierAction; -using RuleAttributeMap = std::map; using PRTEnumDefaultValue = std::variant; class PRTModifierAction : public polyModifierFty { diff --git a/src/serlio/modifiers/PRTModifierEnum.h b/src/serlio/modifiers/PRTModifierEnum.h index 5abbf31e..f15e6b8c 100644 --- a/src/serlio/modifiers/PRTModifierEnum.h +++ b/src/serlio/modifiers/PRTModifierEnum.h @@ -28,8 +28,6 @@ #include "maya/MObject.h" #include "maya/MString.h" -using RuleAttributeMap = std::map; - class PRTModifierEnum { friend class PRTModifierAction; diff --git a/src/serlio/modifiers/RuleAttributes.h b/src/serlio/modifiers/RuleAttributes.h index 3c7a7dbf..981d525a 100644 --- a/src/serlio/modifiers/RuleAttributes.h +++ b/src/serlio/modifiers/RuleAttributes.h @@ -73,6 +73,7 @@ struct RuleAttributeCmp { using RuleAttributeVec = std::vector; using RuleAttributeSet = std::set; +using RuleAttributeMap = std::map; SRL_TEST_EXPORTS_API RuleAttributeSet getRuleAttributes(const std::wstring& ruleFile, const prt::RuleFileInfo* ruleFileInfo); From 5fa228f57f426de3d265d02af8e58f7f5a8aab7c Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 13:56:24 +0100 Subject: [PATCH 410/668] Cleanup: remove unused function parameter --- src/serlio/modifiers/PRTModifierEnum.cpp | 8 ++++---- src/serlio/modifiers/PRTModifierEnum.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index e66f328b..060acb3c 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -55,7 +55,7 @@ std::pair PRTModifierEnum::updateOptions(const MObject& node, const assert(ruleAttrIt != mRuleAttributes.end()); // Rule not found const RuleAttribute ruleAttr = (ruleAttrIt != mRuleAttributes.end()) ? ruleAttrIt->second : RuleAttribute(); - const std::vector& newEnumOptions = getEnumOptions(node, ruleAttr, defaultAttributeValues); + const std::vector& newEnumOptions = getEnumOptions(ruleAttr, defaultAttributeValues); updateCustomEnumValue(ruleAttr, defaultAttributeValues); @@ -93,10 +93,10 @@ bool PRTModifierEnum::isDynamic() { return mValuesAttr.length() > 0; } -const std::vector PRTModifierEnum::getEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, +const std::vector PRTModifierEnum::getEnumOptions(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues) { if (isDynamic()) { - return getDynamicEnumOptions(node, ruleAttr, defaultAttributeValues); + return getDynamicEnumOptions(ruleAttr, defaultAttributeValues); } else { std::vector enumOptions; @@ -147,7 +147,7 @@ void PRTModifierEnum::updateCustomEnumValue(const RuleAttribute& ruleAttr, mCustomDefaultValue = defMStringVal; } -const std::vector PRTModifierEnum::getDynamicEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, +const std::vector PRTModifierEnum::getDynamicEnumOptions(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues) { std::vector enumOptions; if (!isDynamic()) diff --git a/src/serlio/modifiers/PRTModifierEnum.h b/src/serlio/modifiers/PRTModifierEnum.h index f15e6b8c..76ac096d 100644 --- a/src/serlio/modifiers/PRTModifierEnum.h +++ b/src/serlio/modifiers/PRTModifierEnum.h @@ -36,7 +36,7 @@ class PRTModifierEnum { MStatus fill(const prt::Annotation* annot); std::pair updateOptions(const MObject& node, const RuleAttributeMap& ruleAttributes, - const prt::AttributeMap& defaultAttributeValues, short selectedEnumIdx = 0); + const prt::AttributeMap& defaultAttributeValues, short selectedEnumIdx = 0); bool isDynamic(); @@ -49,9 +49,9 @@ class PRTModifierEnum { MString mCustomDefaultValue; std::vector mEnumOptions; - const std::vector getEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, + const std::vector getEnumOptions(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); - const std::vector getDynamicEnumOptions(const MObject& node, const RuleAttribute& ruleAttr, + const std::vector getDynamicEnumOptions(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); void updateCustomEnumValue(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); }; // class PRTModifierEnum From 9396ddd5eb15e866cbf4671cdb546c7c544d733a Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 13:56:43 +0100 Subject: [PATCH 411/668] Cleanup: run clang-format --- src/serlio/modifiers/PRTModifierEnum.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index 060acb3c..49049027 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -65,7 +65,7 @@ std::pair PRTModifierEnum::updateOptions(const MObject& node, const return std::make_pair(false, selectedEnumIdx); const MString oldSelectedOption = mAttr.fieldName(selectedEnumIdx); - + clearEnumValues(node, mAttr); mEnumOptions = newEnumOptions; From a9ac4c1e70bda44521d0543ce677fbd25521042c Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 14:10:39 +0100 Subject: [PATCH 412/668] Cleanup: remove unused index variable --- src/serlio/modifiers/PRTModifierEnum.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index 49049027..1a411ad5 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -60,7 +60,6 @@ std::pair PRTModifierEnum::updateOptions(const MObject& node, const updateCustomEnumValue(ruleAttr, defaultAttributeValues); MStatus status; - const short defaultIdx = mAttr.fieldIndex(mCustomDefaultValue, &status); if ((status == MStatus::kSuccess) && (newEnumOptions == mEnumOptions)) return std::make_pair(false, selectedEnumIdx); From 4aa01390a91a095bc4f662fd5ca22c174bf92f1f Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 14:14:39 +0100 Subject: [PATCH 413/668] Cleanup: remove unnecessary import --- src/serlio/modifiers/PRTModifierEnum.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.h b/src/serlio/modifiers/PRTModifierEnum.h index 76ac096d..e14bf79b 100644 --- a/src/serlio/modifiers/PRTModifierEnum.h +++ b/src/serlio/modifiers/PRTModifierEnum.h @@ -19,7 +19,6 @@ #pragma once -#include "modifiers/PRTModifierAction.h" #include "modifiers/RuleAttributes.h" #include "utils/Utilities.h" From f5c9ea319c9d94bd1fd7d2fbc3cbd13dc968df1d Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 21 Feb 2022 14:22:33 +0100 Subject: [PATCH 414/668] Cleanup: check if string is nullptr before assigning it to MString --- src/serlio/modifiers/PRTModifierEnum.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index 1a411ad5..aa034867 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -207,9 +207,11 @@ const std::vector PRTModifierEnum::getDynamicEnumOptions(const RuleAttr break; } case prt::Attributable::PT_STRING: { - const MString currMString = defaultAttributeValues.getString(valuesAttr.c_str()); + const wchar_t* currString = defaultAttributeValues.getString(valuesAttr.c_str()); + if (currString == nullptr) + break; - enumOptions.emplace_back(currMString); + enumOptions.emplace_back(currString); break; } case prt::Attributable::PT_FLOAT: { From 42f773a7652c907115f17f4cafc8a6347251fea5 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 22 Feb 2022 14:49:40 +0100 Subject: [PATCH 415/668] Correctly set the transparency mode. (Transparent_Fade instead of Transparent) --- src/serlio/materials/StingrayMaterialNode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/materials/StingrayMaterialNode.cpp b/src/serlio/materials/StingrayMaterialNode.cpp index fda7a829..f113232f 100644 --- a/src/serlio/materials/StingrayMaterialNode.cpp +++ b/src/serlio/materials/StingrayMaterialNode.cpp @@ -99,7 +99,7 @@ void appendToMaterialScriptBuilder(MELScriptBuilder& sb, const MaterialInfo& mat sb.addCmdLine(MEL_VAR_SHADING_NODE_INDEX.mel() + L" = `shaderfx -sfxnode " + MEL_VAR_SHADER_NODE.mel() + L" -getNodeIDByName " + nodeIDName.mel() + L"`;"); - const std::wstring blendMode = (matInfo.opacityMap.empty() && (matInfo.opacity >= 1.0)) ? L"0" : L"1"; + const std::wstring blendMode = (matInfo.opacityMap.empty() && (matInfo.opacity >= 1.0)) ? L"0" : L"2"; sb.addCmdLine(L"shaderfx -sfxnode " + MEL_VAR_SHADER_NODE.mel() + L" -edit_stringlist " + MEL_VAR_SHADING_NODE_INDEX.mel() + L" blendmode " + blendMode + L";"); From 5122f3c25496bde319ae26256bc15e4ea3b9d4b7 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 22 Feb 2022 14:58:18 +0100 Subject: [PATCH 416/668] Cleanup: use toFileURI from utilities --- src/serlio/materials/MaterialUtils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serlio/materials/MaterialUtils.cpp b/src/serlio/materials/MaterialUtils.cpp index 4d5914cd..e1c0e9f6 100644 --- a/src/serlio/materials/MaterialUtils.cpp +++ b/src/serlio/materials/MaterialUtils.cpp @@ -4,6 +4,7 @@ #include "utils/MELScriptBuilder.h" #include "utils/MItDependencyNodesWrapper.h" #include "utils/MayaUtilities.h" +#include "utils/Utilities.h" #include "PRTContext.h" @@ -18,7 +19,6 @@ namespace { constexpr const wchar_t* RGBA8_FORMAT = L"RGBA8"; constexpr const wchar_t* FORMAT_STRING = L"format"; -constexpr const wchar_t* FILE_PREFIX = L"file:/"; MObject findNamedObject(const std::wstring& name, MFn::Type fnType) { MStatus status; @@ -198,7 +198,7 @@ std::filesystem::path getStingrayShaderPath() { } bool textureHasAlphaChannel(std::wstring path) { - const prt::AttributeMap* textureMetadata = prt::createTextureMetadata((FILE_PREFIX + path).c_str()); + const prt::AttributeMap* textureMetadata = prt::createTextureMetadata(prtu::toFileURI(path).c_str()); if (textureMetadata == nullptr) return false; const wchar_t* format = textureMetadata->getString(FORMAT_STRING); From 4635434e5af7acfaad565490164d4c37f7caa473 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 22 Feb 2022 14:59:21 +0100 Subject: [PATCH 417/668] Use smart pointer for attribute map --- src/serlio/materials/MaterialUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/materials/MaterialUtils.cpp b/src/serlio/materials/MaterialUtils.cpp index e1c0e9f6..0e8bc73b 100644 --- a/src/serlio/materials/MaterialUtils.cpp +++ b/src/serlio/materials/MaterialUtils.cpp @@ -198,7 +198,7 @@ std::filesystem::path getStingrayShaderPath() { } bool textureHasAlphaChannel(std::wstring path) { - const prt::AttributeMap* textureMetadata = prt::createTextureMetadata(prtu::toFileURI(path).c_str()); + const AttributeMapUPtr textureMetadata(prt::createTextureMetadata(prtu::toFileURI(path).c_str())); if (textureMetadata == nullptr) return false; const wchar_t* format = textureMetadata->getString(FORMAT_STRING); From 48fc4f12285d2f3c20ce3a6d85b294ed713a45e6 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 22 Feb 2022 17:05:48 +0100 Subject: [PATCH 418/668] Stash files in separate initial stage --- Jenkinsfile | 4 ++++ pipeline.groovy | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index f1dc56d5..011c67cb 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -28,6 +28,10 @@ node { impl = load('pipeline.groovy') } +stage('serlio-checkout'){ + impl.checkout() +} + stage('serlio') { impl.pipeline() } diff --git a/pipeline.groovy b/pipeline.groovy index 24afbbe4..e156bc76 100644 --- a/pipeline.groovy +++ b/pipeline.groovy @@ -18,6 +18,7 @@ import com.esri.zrh.jenkins.ce.PrtAppPipelineLibrary @Field final String REPO_CREDS = 'jenkins-github-serlio-deployer-key' @Field final String SOURCES = "serlio.git/src" @Field final String BUILD_TARGET = 'package' +@Field final String SOURCE_STASH = 'serlio-src' // TODO: abusing grp field to distinguish maya versions per task @Field final List CONFIGS = [ @@ -41,12 +42,31 @@ import com.esri.zrh.jenkins.ce.PrtAppPipelineLibrary @Field String myBranch = env.BRANCH_NAME +// checkout and stash repo +def checkout(){ + cepl.runParallel(taskGenSourceCheckout()) +} + // entry point for standalone pipeline def pipeline(String branchName = null) { cepl.runParallel(getTasks(branchName)) papl.finalizeRun('serlio', myBranch) } +Map taskGenSourceCheckout(){ + final List srcCfgs = [ [ ba: psl.BA_CHECKOUT ] ] + Map tasks = [:] + // checkout serlio.git + def taskSRL = { + cepl.cleanCurrentDir() + final String localDir = 'srl' + papl.checkout(REPO, myBranch, REPO_CREDS) + stash(name: SOURCE_STASH) + } + tasks << cepl.generateTasks("checkout-and-stash-srl", taskSRL, srcCfgs) + return tasks +} + // entry point for embedded pipeline Map getTasks(String branchName = null) { if (branchName) myBranch = branchName From 7dbba7ee8da21b393f235b22fe677307bd53d174 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 22 Feb 2022 17:06:31 +0100 Subject: [PATCH 419/668] Use unstash instead of checkout --- pipeline.groovy | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/pipeline.groovy b/pipeline.groovy index e156bc76..1bdceb26 100644 --- a/pipeline.groovy +++ b/pipeline.groovy @@ -97,13 +97,18 @@ Map taskGenSerlioInstallers() { def taskBuildSerlio(cfg) { final String appName = 'serlio' - final List DEPS = [ cfg.maya ] + final List deps = [ cfg.maya ] List defs = [ [ key: 'maya_DIR', val: cfg.maya.p ], [ key: 'SRL_VERSION_BUILD', val: env.BUILD_NUMBER ] ] - papl.buildConfig(REPO, myBranch, SOURCES, BUILD_TARGET, cfg, DEPS, defs, REPO_CREDS) - + cepl.cleanCurrentDir() + unstash(name: SOURCE_STASH) + deps.each { d -> papl.fetchDependency(d, cfg) } + papl.jpe.dir(path: 'build') { + papl.runCMakeBuild(SOURCES, BUILD_TARGET, cfg, defs) + } + def buildProps = papl.jpe.readProperties(file: 'build/deployment.properties') final String artifactPattern = "${buildProps.package_file}.*" final def artifactVersion = { p -> buildProps.package_version_base } @@ -117,20 +122,24 @@ def taskBuildSerlio(cfg) { def taskBuildSerlioTests(cfg) { final String appName = 'serlio-test' - final List DEPS = [ cfg.maya ] + final List deps = [ cfg.maya ] List defs = [ [ key: 'maya_DIR', val: cfg.maya.p ], [ key: 'SRL_VERSION_BUILD', val: env.BUILD_NUMBER ] ] - - papl.buildConfig(REPO, myBranch, SOURCES, 'build_and_run_tests', cfg, DEPS, defs, REPO_CREDS) + cepl.cleanCurrentDir() + unstash(name: SOURCE_STASH) + deps.each { d -> papl.fetchDependency(d, cfg) } + papl.jpe.dir(path: 'build') { + papl.runCMakeBuild(SOURCES, 'build_and_run_tests', cfg, defs) + } papl.jpe.junit('build/test/serlio_test_report.xml') } def taskBuildSerlioInstaller(cfg) { final String appName = 'serlio-installer' cepl.cleanCurrentDir() - papl.checkout(REPO, myBranch, REPO_CREDS) + unstash(name: SOURCE_STASH) final List deps = [ cfg.maya ] deps.each { d -> papl.fetchDependency(d, cfg) } From 2d749e6725cd0c4d758a498ffc7bcac85f3a4bc0 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 22 Feb 2022 17:11:27 +0100 Subject: [PATCH 420/668] Cleanup: add helper function for running cmake build --- pipeline.groovy | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/pipeline.groovy b/pipeline.groovy index 1bdceb26..16cd1381 100644 --- a/pipeline.groovy +++ b/pipeline.groovy @@ -94,9 +94,7 @@ Map taskGenSerlioInstallers() { } // -- TASK BUILDERS - -def taskBuildSerlio(cfg) { - final String appName = 'serlio' +def taskBuildCMake(cfg, target){ final List deps = [ cfg.maya ] List defs = [ [ key: 'maya_DIR', val: cfg.maya.p ], @@ -106,9 +104,14 @@ def taskBuildSerlio(cfg) { unstash(name: SOURCE_STASH) deps.each { d -> papl.fetchDependency(d, cfg) } papl.jpe.dir(path: 'build') { - papl.runCMakeBuild(SOURCES, BUILD_TARGET, cfg, defs) + papl.runCMakeBuild(SOURCES, target, cfg, defs) } - +} + +def taskBuildSerlio(cfg) { + final String appName = 'serlio' + + taskBuildCMake(cfg, BUILD_TARGET) def buildProps = papl.jpe.readProperties(file: 'build/deployment.properties') final String artifactPattern = "${buildProps.package_file}.*" final def artifactVersion = { p -> buildProps.package_version_base } @@ -122,17 +125,7 @@ def taskBuildSerlio(cfg) { def taskBuildSerlioTests(cfg) { final String appName = 'serlio-test' - final List deps = [ cfg.maya ] - List defs = [ - [ key: 'maya_DIR', val: cfg.maya.p ], - [ key: 'SRL_VERSION_BUILD', val: env.BUILD_NUMBER ] - ] - cepl.cleanCurrentDir() - unstash(name: SOURCE_STASH) - deps.each { d -> papl.fetchDependency(d, cfg) } - papl.jpe.dir(path: 'build') { - papl.runCMakeBuild(SOURCES, 'build_and_run_tests', cfg, defs) - } + taskBuildCMake(cfg, 'build_and_run_tests') papl.jpe.junit('build/test/serlio_test_report.xml') } From 26d8f139adad03eeaa2afe920f8b0eb9e346cab5 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 23 Feb 2022 09:33:30 +0100 Subject: [PATCH 421/668] Cleanup: remove unnecessary c_str conversion --- src/serlio/modifiers/PRTModifierEnum.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index aa034867..8f01e914 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -153,8 +153,8 @@ const std::vector PRTModifierEnum::getDynamicEnumOptions(const RuleAttr return enumOptions; const MString fullAttrName = mAttr.name(); - const std::wstring attrStyle = prtu::getStyle(ruleAttr.fqName).c_str(); - std::wstring attrImport = prtu::getImport(ruleAttr.fqName).c_str(); + const std::wstring attrStyle = prtu::getStyle(ruleAttr.fqName); + std::wstring attrImport = prtu::getImport(ruleAttr.fqName); if (!attrImport.empty()) attrImport += prtu::IMPORT_DELIMITER; From b57893d4693519cb49bd421905615c2d0385bec8 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 23 Feb 2022 09:35:30 +0100 Subject: [PATCH 422/668] Cleanup: remove const for return by value functions --- src/serlio/modifiers/PRTModifierEnum.cpp | 4 ++-- src/serlio/modifiers/PRTModifierEnum.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index 8f01e914..66a6b805 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -92,7 +92,7 @@ bool PRTModifierEnum::isDynamic() { return mValuesAttr.length() > 0; } -const std::vector PRTModifierEnum::getEnumOptions(const RuleAttribute& ruleAttr, +std::vector PRTModifierEnum::getEnumOptions(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues) { if (isDynamic()) { return getDynamicEnumOptions(ruleAttr, defaultAttributeValues); @@ -146,7 +146,7 @@ void PRTModifierEnum::updateCustomEnumValue(const RuleAttribute& ruleAttr, mCustomDefaultValue = defMStringVal; } -const std::vector PRTModifierEnum::getDynamicEnumOptions(const RuleAttribute& ruleAttr, +std::vector PRTModifierEnum::getDynamicEnumOptions(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues) { std::vector enumOptions; if (!isDynamic()) diff --git a/src/serlio/modifiers/PRTModifierEnum.h b/src/serlio/modifiers/PRTModifierEnum.h index e14bf79b..538cb2dd 100644 --- a/src/serlio/modifiers/PRTModifierEnum.h +++ b/src/serlio/modifiers/PRTModifierEnum.h @@ -48,9 +48,9 @@ class PRTModifierEnum { MString mCustomDefaultValue; std::vector mEnumOptions; - const std::vector getEnumOptions(const RuleAttribute& ruleAttr, + std::vector getEnumOptions(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); - const std::vector getDynamicEnumOptions(const RuleAttribute& ruleAttr, + std::vector getDynamicEnumOptions(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); void updateCustomEnumValue(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); }; // class PRTModifierEnum From 76ad6cf2e81a73868a2bceccfa06263a1a4a14ba Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 23 Feb 2022 09:51:57 +0100 Subject: [PATCH 423/668] Cleanup: avoid second lookup by using returned iterator --- src/serlio/modifiers/PRTModifierAction.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 7c23c9b1..a04b9bd5 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -845,8 +845,9 @@ MStatus PRTModifierAction::createNodeAttributes(const RuleAttributeSet& ruleAttr case prt::Attributable::PT_BOOL: { const bool value = mGenerateAttrs->getBool(fqName.c_str()); if (attrTrait.first == AttributeTrait::ENUM) { - mEnums.try_emplace(p.mayaFullName); - MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, value, mEnums[p.mayaFullName])); + auto currEnumItr = mEnums.try_emplace(p.mayaFullName); + PRTModifierEnum& currEnum = currEnumItr.first->second; + MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, value, currEnum)); } else { MCHECK(addBoolParameter(node, attr, p, value)); @@ -858,8 +859,9 @@ MStatus PRTModifierAction::createNodeAttributes(const RuleAttributeSet& ruleAttr switch (attrTrait.first) { case AttributeTrait::ENUM: { - mEnums.try_emplace(p.mayaFullName); - MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, value, mEnums[p.mayaFullName])); + auto currEnumItr = mEnums.try_emplace(p.mayaFullName); + PRTModifierEnum& currEnum = currEnumItr.first->second; + MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, value, currEnum)); break; } case AttributeTrait::RANGE: { @@ -908,9 +910,9 @@ MStatus PRTModifierAction::createNodeAttributes(const RuleAttributeSet& ruleAttr switch (attrTrait.first) { case AttributeTrait::ENUM: { - mEnums.try_emplace(p.mayaFullName); - MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, mvalue, - mEnums[p.mayaFullName])); + auto currEnumItr = mEnums.try_emplace(p.mayaFullName); + PRTModifierEnum& currEnum = currEnumItr.first->second; + MCHECK(addEnumParameter(attrTrait.second.mAnnot, node, attr, p, mvalue, currEnum)); break; } case AttributeTrait::FILE: From 766a11c2b05c1de0ce3d4382abb277e845d9bf82 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 23 Feb 2022 10:35:15 +0100 Subject: [PATCH 424/668] Fix custom default enum option update regression --- src/serlio/modifiers/PRTModifierEnum.cpp | 14 ++++++++------ src/serlio/modifiers/PRTModifierEnum.h | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index 66a6b805..45d0fb3d 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -57,10 +57,10 @@ std::pair PRTModifierEnum::updateOptions(const MObject& node, const const std::vector& newEnumOptions = getEnumOptions(ruleAttr, defaultAttributeValues); - updateCustomEnumValue(ruleAttr, defaultAttributeValues); + const bool hasNewCustomDefaultValue = updateCustomEnumValue(ruleAttr, defaultAttributeValues); MStatus status; - if ((status == MStatus::kSuccess) && (newEnumOptions == mEnumOptions)) + if ((status == MStatus::kSuccess) && (newEnumOptions == mEnumOptions) && !hasNewCustomDefaultValue) return std::make_pair(false, selectedEnumIdx); const MString oldSelectedOption = mAttr.fieldName(selectedEnumIdx); @@ -112,7 +112,7 @@ std::vector PRTModifierEnum::getEnumOptions(const RuleAttribute& ruleAt } } -void PRTModifierEnum::updateCustomEnumValue(const RuleAttribute& ruleAttr, +bool PRTModifierEnum::updateCustomEnumValue(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues) { const std::wstring fqAttrName = ruleAttr.fqName; const prt::AnnotationArgumentType ruleAttrType = ruleAttr.mType; @@ -123,7 +123,7 @@ void PRTModifierEnum::updateCustomEnumValue(const RuleAttribute& ruleAttr, case prt::AAT_STR: { const wchar_t* defStringVal = defaultAttributeValues.getString(fqAttrName.c_str()); if (defStringVal == nullptr) - return; + return false; defMStringVal = defStringVal; break; } @@ -139,11 +139,13 @@ void PRTModifierEnum::updateCustomEnumValue(const RuleAttribute& ruleAttr, } default: { LOG_ERR << "Cannot handle attribute type " << ruleAttrType << " for attr " << fqAttrName; - return; + return false; } } - + if (mCustomDefaultValue == defMStringVal) + return false; mCustomDefaultValue = defMStringVal; + return true; } std::vector PRTModifierEnum::getDynamicEnumOptions(const RuleAttribute& ruleAttr, diff --git a/src/serlio/modifiers/PRTModifierEnum.h b/src/serlio/modifiers/PRTModifierEnum.h index 538cb2dd..447c9b90 100644 --- a/src/serlio/modifiers/PRTModifierEnum.h +++ b/src/serlio/modifiers/PRTModifierEnum.h @@ -52,5 +52,5 @@ class PRTModifierEnum { const prt::AttributeMap& defaultAttributeValues); std::vector getDynamicEnumOptions(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); - void updateCustomEnumValue(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); + bool updateCustomEnumValue(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); }; // class PRTModifierEnum From 917686350eb41c8a3ce605ba206504b276a358a7 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 23 Feb 2022 15:12:01 +0100 Subject: [PATCH 425/668] Cleanup: remove redundant status --- src/serlio/modifiers/PRTModifierEnum.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index 45d0fb3d..3278c50c 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -59,8 +59,7 @@ std::pair PRTModifierEnum::updateOptions(const MObject& node, const const bool hasNewCustomDefaultValue = updateCustomEnumValue(ruleAttr, defaultAttributeValues); - MStatus status; - if ((status == MStatus::kSuccess) && (newEnumOptions == mEnumOptions) && !hasNewCustomDefaultValue) + if ((newEnumOptions == mEnumOptions) && !hasNewCustomDefaultValue) return std::make_pair(false, selectedEnumIdx); const MString oldSelectedOption = mAttr.fieldName(selectedEnumIdx); From 72d06f08c9fc0c4c36f73998fc92e025910947bd Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 23 Feb 2022 16:46:22 +0100 Subject: [PATCH 426/668] Remove helper function for clearing enums --- src/serlio/modifiers/PRTModifierEnum.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index 3278c50c..f309ce8f 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -31,18 +31,6 @@ constexpr const wchar_t* NULL_KEY = L"#NULL#"; constexpr const wchar_t* RESTRICTED_KEY = L"restricted"; constexpr const wchar_t* VALUES_ATTR_KEY = L"valuesAttr"; -void clearEnumValues(const MObject& node, const MFnEnumAttribute& enumAttr) { - // Workaround: since setting enum values through mel, when none are available causes an Error... - MString defaultValue; - if (enumAttr.getDefault(defaultValue) != MStatus::kSuccess) - return; - MStatus stat; - const MFnDependencyNode fNode(node, &stat); - MCHECK(stat); - // clear enum options - MCHECK(mu::setEnumOptions(fNode.name().asWChar(), enumAttr.name().asWChar(), {})); -} - } // namespace // This function updates all enum options and returns a pair, where the first argument indicates if the options have @@ -64,8 +52,6 @@ std::pair PRTModifierEnum::updateOptions(const MObject& node, const const MString oldSelectedOption = mAttr.fieldName(selectedEnumIdx); - clearEnumValues(node, mAttr); - mEnumOptions = newEnumOptions; int customDefaultIdx = 0; From 76bf8788882b2634b8362b46726b431a18d40eaf Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 23 Feb 2022 16:47:44 +0100 Subject: [PATCH 427/668] Extend setEnumOptions to directly set options.. from node, attr, enumOptions and customDefaultOption --- src/serlio/utils/MELScriptBuilder.cpp | 17 +++++++++++++---- src/serlio/utils/MELScriptBuilder.h | 4 +++- src/serlio/utils/MayaUtilities.cpp | 19 +++++++++++++------ src/serlio/utils/MayaUtilities.h | 8 ++++++-- 4 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/serlio/utils/MELScriptBuilder.cpp b/src/serlio/utils/MELScriptBuilder.cpp index 490066f3..62416f13 100644 --- a/src/serlio/utils/MELScriptBuilder.cpp +++ b/src/serlio/utils/MELScriptBuilder.cpp @@ -26,7 +26,7 @@ namespace { -constexpr bool MEL_ENABLE_DISPLAY = false; +constexpr bool MEL_ENABLE_DISPLAY = true; std::wstring composeAttributeExpression(const MELVariable& node, const std::wstring& attribute) { assert(!attribute.empty() && attribute[0] != L'.'); // to catch refactoring bugs @@ -86,16 +86,23 @@ void MELScriptBuilder::setAttr(const MELVariable& node, const std::wstring& attr } void MELScriptBuilder::setAttrEnumOptions(const MELVariable& node, const std::wstring& attribute, - const std::vector& enumOptions) { + const std::vector& enumOptions, + const std::optional& customDefaultOption) { std::wstring enumString; + if (customDefaultOption.has_value()) + enumString.append(customDefaultOption.value() + L"=0"); + int idx = 1; for (const std::wstring& enumOption : enumOptions) { if (!enumString.empty()) enumString.append(L":"); - enumString.append(enumOption); + enumString.append(enumOption + L"=" + std::to_wstring(idx++)); } + // Don't update to an empty enum + if (enumString.empty()) + enumString.append(L" "); - commandStream << "addAttr -e -en \"" << enumString << "\" " << composeAttributeExpression(node, attribute) << ";\n"; + commandStream << "addAttr -e -en " << MELStringLiteral(enumString).mel() << " " << composeAttributeExpression(node, attribute) << ";\n"; } void MELScriptBuilder::connectAttr(const MELVariable& srcNode, const std::wstring& srcAttr, const MELVariable& dstNode, @@ -172,6 +179,8 @@ MStatus MELScriptBuilder::executeSync(std::wstring& output) { MStatus status; MString result = MGlobal::executeCommandStringResult(commandStream.str().c_str(), MEL_ENABLE_DISPLAY, false, &status); + MString statusString = status.errorString(); + bool hasError = status.error(); commandStream.clear(); output.assign(result.asWChar()); return status; diff --git a/src/serlio/utils/MELScriptBuilder.h b/src/serlio/utils/MELScriptBuilder.h index 71a0d497..c040baeb 100644 --- a/src/serlio/utils/MELScriptBuilder.h +++ b/src/serlio/utils/MELScriptBuilder.h @@ -63,7 +63,9 @@ class MELScriptBuilder { void setAttr(const MELVariable& node, const std::wstring& attribute, const wchar_t* val) = delete; void setAttr(const MELVariable& node, const std::wstring& attribute, const char* val) = delete; - void setAttrEnumOptions(const MELVariable& node, const std::wstring& attribute, const std::vector& enumOptions); + void setAttrEnumOptions(const MELVariable& node, const std::wstring& attribute, + const std::vector& enumOptions, + const std::optional& customDefaultOption); void connectAttr(const MELVariable& srcNode, const std::wstring& srcAttr, const MELVariable& dstNode, const std::wstring& dstAttr); diff --git a/src/serlio/utils/MayaUtilities.cpp b/src/serlio/utils/MayaUtilities.cpp index 74994650..db6b62c0 100644 --- a/src/serlio/utils/MayaUtilities.cpp +++ b/src/serlio/utils/MayaUtilities.cpp @@ -45,15 +45,22 @@ std::filesystem::path getWorkspaceRoot(MStatus& status) { } } -MStatus setEnumOptions(const std::wstring& node, const std::wstring& attr, const std::vector& enumOptions) { +MStatus setEnumOptions(const MObject& node, MFnEnumAttribute& enumAttr, + const std::vector& enumOptions, + const std::optional& customDefaultOption) { + MStatus stat; + const MFnDependencyNode fNode(node, &stat); + if (stat != MStatus::kSuccess) + return stat; + const MELVariable melSerlioNode(L"serlioNode"); MELScriptBuilder scriptBuilder; + const std::wstring nodeName = fNode.name().asWChar(); + const std::wstring attrName = fNode.name().asWChar(); + scriptBuilder.setVar(melSerlioNode, MELStringLiteral(nodeName)); + scriptBuilder.setAttrEnumOptions(melSerlioNode, enumAttr.name().asWChar(), enumOptions, customDefaultOption); - scriptBuilder.setVar(melSerlioNode, MELStringLiteral(node)); - scriptBuilder.setAttrEnumOptions(melSerlioNode, attr, enumOptions); - - std::wstring output; - return scriptBuilder.executeSync(output); + return scriptBuilder.execute(); } } // namespace mu \ No newline at end of file diff --git a/src/serlio/utils/MayaUtilities.h b/src/serlio/utils/MayaUtilities.h index 605885d7..ba0eda20 100644 --- a/src/serlio/utils/MayaUtilities.h +++ b/src/serlio/utils/MayaUtilities.h @@ -5,10 +5,13 @@ #include "maya/MFloatPointArray.h" #include "maya/MFnAttribute.h" #include "maya/MFnDependencyNode.h" +#include "maya/MFnEnumAttribute.h" #include "maya/MObject.h" #include "maya/MStatus.h" #include "maya/MString.h" +#include + #define MCHECK(status) mu::statusCheck((status), __FILE__, __LINE__); // utility functions with dependencies on the Maya API @@ -49,6 +52,7 @@ class NamedType { std::filesystem::path getWorkspaceRoot(MStatus& status); -MStatus setEnumOptions(const std::wstring& node, const std::wstring& attr, - const std::vector& enumOptions); +MStatus setEnumOptions(const MObject& node, MFnEnumAttribute& enumAttr, + const std::vector& enumOptions, + const std::optional& customDefaultOption); } // namespace mu \ No newline at end of file From 670117b746cc4e245c62209e20556cbd6929ff26 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 23 Feb 2022 16:48:35 +0100 Subject: [PATCH 428/668] Make sure dynamic enums are not emtpy after initialization Otherwise maya will break, when we try to update them via the addAttr mel command --- src/serlio/modifiers/PRTModifierEnum.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index f309ce8f..b3805300 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -233,8 +233,11 @@ MStatus PRTModifierEnum::fill(const prt::Annotation* annot) { if (std::wcscmp(key, RESTRICTED_KEY) == 0) mRestricted = annot->getArgument(arg)->getBool(); - if (std::wcscmp(key, VALUES_ATTR_KEY) == 0) + if (std::wcscmp(key, VALUES_ATTR_KEY) == 0) { mValuesAttr = annot->getArgument(arg)->getStr(); + // Add empty option, because it is not considered an enum by addAttr in mel otherwise (bug) + MCHECK(mAttr.addField(" ", 1)); + } continue; } From 5c65d90131ae52035fde96392db9f055fe5d7a38 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 23 Feb 2022 16:56:11 +0100 Subject: [PATCH 429/668] Use the helper function for setting the enum options --- src/serlio/modifiers/PRTModifierEnum.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index b3805300..27262d03 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -58,8 +58,10 @@ std::pair PRTModifierEnum::updateOptions(const MObject& node, const int newSelectedEnumIdx = 0; int currIdx = 1; + std::vector wstringOptions; + for (const MString& option : mEnumOptions) { - MCHECK(mAttr.addField(option, currIdx)); + wstringOptions.emplace_back(option.asWChar()); if (option == mCustomDefaultValue) customDefaultIdx = currIdx; if (option == oldSelectedOption) @@ -67,8 +69,12 @@ std::pair PRTModifierEnum::updateOptions(const MObject& node, const currIdx++; } - if (customDefaultIdx == 0) - MCHECK(mAttr.addField(mCustomDefaultValue, 0)); + if (customDefaultIdx == 0) { + MCHECK(mu::setEnumOptions(node, mAttr, wstringOptions, mCustomDefaultValue.asWChar())); + } + else { + MCHECK(mu::setEnumOptions(node, mAttr, wstringOptions, {})); + } return std::make_pair(true, newSelectedEnumIdx); } From 9770f7d37084dc13c09d5f21c1c7af688907383b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 23 Feb 2022 16:57:05 +0100 Subject: [PATCH 430/668] Cleanup: change type of PRTModifierEnum from MString to std::wstring --- src/serlio/modifiers/PRTModifierEnum.cpp | 51 ++++++++++-------------- src/serlio/modifiers/PRTModifierEnum.h | 10 ++--- 2 files changed, 27 insertions(+), 34 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index 27262d03..af96bf69 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -43,14 +43,14 @@ std::pair PRTModifierEnum::updateOptions(const MObject& node, const assert(ruleAttrIt != mRuleAttributes.end()); // Rule not found const RuleAttribute ruleAttr = (ruleAttrIt != mRuleAttributes.end()) ? ruleAttrIt->second : RuleAttribute(); - const std::vector& newEnumOptions = getEnumOptions(ruleAttr, defaultAttributeValues); + const std::vector& newEnumOptions = getEnumOptions(ruleAttr, defaultAttributeValues); const bool hasNewCustomDefaultValue = updateCustomEnumValue(ruleAttr, defaultAttributeValues); if ((newEnumOptions == mEnumOptions) && !hasNewCustomDefaultValue) return std::make_pair(false, selectedEnumIdx); - const MString oldSelectedOption = mAttr.fieldName(selectedEnumIdx); + const std::wstring oldSelectedOption = mAttr.fieldName(selectedEnumIdx).asWChar(); mEnumOptions = newEnumOptions; @@ -58,10 +58,8 @@ std::pair PRTModifierEnum::updateOptions(const MObject& node, const int newSelectedEnumIdx = 0; int currIdx = 1; - std::vector wstringOptions; - for (const MString& option : mEnumOptions) { - wstringOptions.emplace_back(option.asWChar()); + for (const std::wstring& option : mEnumOptions) { if (option == mCustomDefaultValue) customDefaultIdx = currIdx; if (option == oldSelectedOption) @@ -70,10 +68,10 @@ std::pair PRTModifierEnum::updateOptions(const MObject& node, const } if (customDefaultIdx == 0) { - MCHECK(mu::setEnumOptions(node, mAttr, wstringOptions, mCustomDefaultValue.asWChar())); + MCHECK(mu::setEnumOptions(node, mAttr, mEnumOptions, mCustomDefaultValue)); } else { - MCHECK(mu::setEnumOptions(node, mAttr, wstringOptions, {})); + MCHECK(mu::setEnumOptions(node, mAttr, mEnumOptions, {})); } return std::make_pair(true, newSelectedEnumIdx); @@ -83,13 +81,13 @@ bool PRTModifierEnum::isDynamic() { return mValuesAttr.length() > 0; } -std::vector PRTModifierEnum::getEnumOptions(const RuleAttribute& ruleAttr, +std::vector PRTModifierEnum::getEnumOptions(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues) { if (isDynamic()) { return getDynamicEnumOptions(ruleAttr, defaultAttributeValues); } else { - std::vector enumOptions; + std::vector enumOptions; short minVal; short maxVal; @@ -97,7 +95,7 @@ std::vector PRTModifierEnum::getEnumOptions(const RuleAttribute& ruleAt MCHECK(mAttr.getMax(maxVal)); for (short currIdx = 1; currIdx <= maxVal; currIdx++) - enumOptions.emplace_back(mAttr.fieldName(currIdx)); + enumOptions.emplace_back(mAttr.fieldName(currIdx).asWChar()); return enumOptions; } @@ -108,24 +106,24 @@ bool PRTModifierEnum::updateCustomEnumValue(const RuleAttribute& ruleAttr, const std::wstring fqAttrName = ruleAttr.fqName; const prt::AnnotationArgumentType ruleAttrType = ruleAttr.mType; - MString defMStringVal; + std::wstring defWStringVal; switch (ruleAttrType) { case prt::AAT_STR: { const wchar_t* defStringVal = defaultAttributeValues.getString(fqAttrName.c_str()); if (defStringVal == nullptr) return false; - defMStringVal = defStringVal; + defWStringVal = defStringVal; break; } case prt::AAT_FLOAT: { const auto defDoubleVal = defaultAttributeValues.getFloat(fqAttrName.c_str()); - defMStringVal = std::to_wstring(defDoubleVal).c_str(); + defWStringVal = std::to_wstring(defDoubleVal).c_str(); break; } case prt::AAT_BOOL: { const auto defBoolVal = defaultAttributeValues.getBool(fqAttrName.c_str()); - defMStringVal = std::to_wstring(defBoolVal).c_str(); + defWStringVal = std::to_wstring(defBoolVal).c_str(); break; } default: { @@ -133,15 +131,15 @@ bool PRTModifierEnum::updateCustomEnumValue(const RuleAttribute& ruleAttr, return false; } } - if (mCustomDefaultValue == defMStringVal) + if (mCustomDefaultValue == defWStringVal) return false; - mCustomDefaultValue = defMStringVal; + mCustomDefaultValue = defWStringVal; return true; } -std::vector PRTModifierEnum::getDynamicEnumOptions(const RuleAttribute& ruleAttr, +std::vector PRTModifierEnum::getDynamicEnumOptions(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues) { - std::vector enumOptions; + std::vector enumOptions; if (!isDynamic()) return enumOptions; const MString fullAttrName = mAttr.name(); @@ -152,7 +150,7 @@ std::vector PRTModifierEnum::getDynamicEnumOptions(const RuleAttribute& attrImport += prtu::IMPORT_DELIMITER; std::wstring valuesAttr = attrStyle + prtu::STYLE_DELIMITER + attrImport; - valuesAttr.append(mValuesAttr.asWChar()); + valuesAttr.append(mValuesAttr); const prt::Attributable::PrimitiveType type = defaultAttributeValues.getType(valuesAttr.c_str()); @@ -170,8 +168,7 @@ std::vector PRTModifierEnum::getDynamicEnumOptions(const RuleAttribute& const size_t cutoffIndex = currStringView.find_first_of(L"\r\n"); currStringView = currStringView.substr(0, cutoffIndex); - const MString currMString(currStringView.data(), static_cast(currStringView.length())); - enumOptions.emplace_back(currMString); + enumOptions.emplace_back(currStringView); } break; } @@ -182,8 +179,7 @@ std::vector PRTModifierEnum::getDynamicEnumOptions(const RuleAttribute& for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { const double currDouble = doubleArray[enumIndex]; - const MString currMString(std::to_wstring(currDouble).c_str()); - enumOptions.emplace_back(currMString); + enumOptions.emplace_back(std::to_wstring(currDouble)); } break; } @@ -194,8 +190,7 @@ std::vector PRTModifierEnum::getDynamicEnumOptions(const RuleAttribute& for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { const bool currBool = boolArray[enumIndex]; - const MString currMString(std::to_wstring(currBool).c_str()); - enumOptions.emplace_back(currMString); + enumOptions.emplace_back(std::to_wstring(currBool)); } break; } @@ -210,15 +205,13 @@ std::vector PRTModifierEnum::getDynamicEnumOptions(const RuleAttribute& case prt::Attributable::PT_FLOAT: { const bool currFloat = defaultAttributeValues.getFloat(valuesAttr.c_str()); - const MString currMString(std::to_wstring(currFloat).c_str()); - enumOptions.emplace_back(currMString); + enumOptions.emplace_back(std::to_wstring(currFloat)); break; } case prt::Attributable::PT_BOOL: { const bool currBool = defaultAttributeValues.getBool(valuesAttr.c_str()); - const MString currMString(std::to_wstring(currBool).c_str()); - enumOptions.emplace_back(currMString); + enumOptions.emplace_back(std::to_wstring(currBool)); break; } default: diff --git a/src/serlio/modifiers/PRTModifierEnum.h b/src/serlio/modifiers/PRTModifierEnum.h index 447c9b90..a392f79b 100644 --- a/src/serlio/modifiers/PRTModifierEnum.h +++ b/src/serlio/modifiers/PRTModifierEnum.h @@ -44,13 +44,13 @@ class PRTModifierEnum { private: bool mRestricted = true; - MString mValuesAttr; - MString mCustomDefaultValue; - std::vector mEnumOptions; + std::wstring mValuesAttr; + std::wstring mCustomDefaultValue; + std::vector mEnumOptions; - std::vector getEnumOptions(const RuleAttribute& ruleAttr, + std::vector getEnumOptions(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); - std::vector getDynamicEnumOptions(const RuleAttribute& ruleAttr, + std::vector getDynamicEnumOptions(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); bool updateCustomEnumValue(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); }; // class PRTModifierEnum From 173ff02cc41d0413b59b2a8a3c31c4ed0f3d87af Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 23 Feb 2022 16:58:57 +0100 Subject: [PATCH 431/668] Cleanup: remove newlines that are nolonger needed for readability --- src/serlio/modifiers/PRTModifierEnum.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index af96bf69..dd4a9b55 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -58,7 +58,6 @@ std::pair PRTModifierEnum::updateOptions(const MObject& node, const int newSelectedEnumIdx = 0; int currIdx = 1; - for (const std::wstring& option : mEnumOptions) { if (option == mCustomDefaultValue) customDefaultIdx = currIdx; @@ -167,7 +166,6 @@ std::vector PRTModifierEnum::getDynamicEnumOptions(const RuleAttri // remove newlines from strings, because they break the maya UI const size_t cutoffIndex = currStringView.find_first_of(L"\r\n"); currStringView = currStringView.substr(0, cutoffIndex); - enumOptions.emplace_back(currStringView); } break; @@ -178,7 +176,6 @@ std::vector PRTModifierEnum::getDynamicEnumOptions(const RuleAttri for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { const double currDouble = doubleArray[enumIndex]; - enumOptions.emplace_back(std::to_wstring(currDouble)); } break; @@ -189,7 +186,6 @@ std::vector PRTModifierEnum::getDynamicEnumOptions(const RuleAttri for (short enumIndex = 0; enumIndex < arr_length; enumIndex++) { const bool currBool = boolArray[enumIndex]; - enumOptions.emplace_back(std::to_wstring(currBool)); } break; @@ -198,19 +194,16 @@ std::vector PRTModifierEnum::getDynamicEnumOptions(const RuleAttri const wchar_t* currString = defaultAttributeValues.getString(valuesAttr.c_str()); if (currString == nullptr) break; - enumOptions.emplace_back(currString); break; } case prt::Attributable::PT_FLOAT: { const bool currFloat = defaultAttributeValues.getFloat(valuesAttr.c_str()); - enumOptions.emplace_back(std::to_wstring(currFloat)); break; } case prt::Attributable::PT_BOOL: { const bool currBool = defaultAttributeValues.getBool(valuesAttr.c_str()); - enumOptions.emplace_back(std::to_wstring(currBool)); break; } From 146c77bdef85a2c400a906e973c472406097fc54 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 23 Feb 2022 17:16:48 +0100 Subject: [PATCH 432/668] Add helper functions for getting index and name for a specific option. --- src/serlio/modifiers/PRTModifierEnum.cpp | 19 +++++++++++++++++++ src/serlio/modifiers/PRTModifierEnum.h | 2 ++ 2 files changed, 21 insertions(+) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index dd4a9b55..69926672 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -80,6 +80,25 @@ bool PRTModifierEnum::isDynamic() { return mValuesAttr.length() > 0; } +size_t PRTModifierEnum::getOptionIndex(std::wstring fieldName) { + const auto iter = std::find(mEnumOptions.begin(), mEnumOptions.end(), fieldName); + //return first index, if element is not available + if (iter == mEnumOptions.end()) + return 0; + return std::distance(mEnumOptions.begin(), iter); +} + +std::wstring PRTModifierEnum::getOptionName(size_t fieldIndex) { + if (fieldIndex == 0) { + return mCustomDefaultValue; + } + else { + if ((fieldIndex > mEnumOptions.size()) || (fieldIndex <= 0)) + return {}; + return mEnumOptions[fieldIndex - 1]; + } +} + std::vector PRTModifierEnum::getEnumOptions(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues) { if (isDynamic()) { diff --git a/src/serlio/modifiers/PRTModifierEnum.h b/src/serlio/modifiers/PRTModifierEnum.h index a392f79b..988ca08f 100644 --- a/src/serlio/modifiers/PRTModifierEnum.h +++ b/src/serlio/modifiers/PRTModifierEnum.h @@ -38,6 +38,8 @@ class PRTModifierEnum { const prt::AttributeMap& defaultAttributeValues, short selectedEnumIdx = 0); bool isDynamic(); + size_t getOptionIndex(std::wstring fieldName); + std::wstring getOptionName(size_t fieldIndex); public: MFnEnumAttribute mAttr; From 1506a971ae8faef09e205241f0ca212125670ce8 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 23 Feb 2022 17:25:43 +0100 Subject: [PATCH 433/668] Add helper function for getting number of options --- src/serlio/modifiers/PRTModifierEnum.cpp | 4 ++++ src/serlio/modifiers/PRTModifierEnum.h | 1 + 2 files changed, 5 insertions(+) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index 69926672..7a8ad7b9 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -99,6 +99,10 @@ std::wstring PRTModifierEnum::getOptionName(size_t fieldIndex) { } } +size_t PRTModifierEnum::getOptionCount() { + return mEnumOptions.size() + 1; +} + std::vector PRTModifierEnum::getEnumOptions(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues) { if (isDynamic()) { diff --git a/src/serlio/modifiers/PRTModifierEnum.h b/src/serlio/modifiers/PRTModifierEnum.h index 988ca08f..6ce3ad81 100644 --- a/src/serlio/modifiers/PRTModifierEnum.h +++ b/src/serlio/modifiers/PRTModifierEnum.h @@ -40,6 +40,7 @@ class PRTModifierEnum { bool isDynamic(); size_t getOptionIndex(std::wstring fieldName); std::wstring getOptionName(size_t fieldIndex); + size_t getOptionCount(); public: MFnEnumAttribute mAttr; From 0f306d6f501f7ace2de7a2da44470ef8fb2414d3 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 09:07:11 +0100 Subject: [PATCH 434/668] Return MString in getOptionName This makes it easier to convert the result to bool/int if needed. --- src/serlio/modifiers/PRTModifierEnum.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index 7a8ad7b9..5a702cce 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -88,14 +88,14 @@ size_t PRTModifierEnum::getOptionIndex(std::wstring fieldName) { return std::distance(mEnumOptions.begin(), iter); } -std::wstring PRTModifierEnum::getOptionName(size_t fieldIndex) { +MString PRTModifierEnum::getOptionName(size_t fieldIndex) { if (fieldIndex == 0) { - return mCustomDefaultValue; + return MString(mCustomDefaultValue.c_str()); } else { if ((fieldIndex > mEnumOptions.size()) || (fieldIndex <= 0)) return {}; - return mEnumOptions[fieldIndex - 1]; + return MString(mEnumOptions[fieldIndex - 1].c_str()); } } From ab91d5fcc40222d55c4dc8bf303418907372811a Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 09:13:38 +0100 Subject: [PATCH 435/668] Cleanup: make getter functions available for const PRTModifierEnums --- src/serlio/modifiers/PRTModifierEnum.cpp | 8 ++++---- src/serlio/modifiers/PRTModifierEnum.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index 5a702cce..cbc10e56 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -76,11 +76,11 @@ std::pair PRTModifierEnum::updateOptions(const MObject& node, const return std::make_pair(true, newSelectedEnumIdx); } -bool PRTModifierEnum::isDynamic() { +bool PRTModifierEnum::isDynamic() const { return mValuesAttr.length() > 0; } -size_t PRTModifierEnum::getOptionIndex(std::wstring fieldName) { +size_t PRTModifierEnum::getOptionIndex(std::wstring fieldName) const { const auto iter = std::find(mEnumOptions.begin(), mEnumOptions.end(), fieldName); //return first index, if element is not available if (iter == mEnumOptions.end()) @@ -88,7 +88,7 @@ size_t PRTModifierEnum::getOptionIndex(std::wstring fieldName) { return std::distance(mEnumOptions.begin(), iter); } -MString PRTModifierEnum::getOptionName(size_t fieldIndex) { +MString PRTModifierEnum::getOptionName(size_t fieldIndex) const { if (fieldIndex == 0) { return MString(mCustomDefaultValue.c_str()); } @@ -99,7 +99,7 @@ MString PRTModifierEnum::getOptionName(size_t fieldIndex) { } } -size_t PRTModifierEnum::getOptionCount() { +size_t PRTModifierEnum::getOptionCount() const { return mEnumOptions.size() + 1; } diff --git a/src/serlio/modifiers/PRTModifierEnum.h b/src/serlio/modifiers/PRTModifierEnum.h index 6ce3ad81..b321702f 100644 --- a/src/serlio/modifiers/PRTModifierEnum.h +++ b/src/serlio/modifiers/PRTModifierEnum.h @@ -37,10 +37,10 @@ class PRTModifierEnum { std::pair updateOptions(const MObject& node, const RuleAttributeMap& ruleAttributes, const prt::AttributeMap& defaultAttributeValues, short selectedEnumIdx = 0); - bool isDynamic(); - size_t getOptionIndex(std::wstring fieldName); - std::wstring getOptionName(size_t fieldIndex); - size_t getOptionCount(); + bool isDynamic() const; + size_t getOptionIndex(std::wstring fieldName) const; + MString getOptionName(size_t fieldIndex) const; + size_t getOptionCount() const; public: MFnEnumAttribute mAttr; From 6440c02539d99aa80c583c0b55aaf629d7ae3aae Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 09:15:14 +0100 Subject: [PATCH 436/668] Use PRTModifierEnum::getOptionName instead of MFnEnumAttribute::fieldName --- src/serlio/modifiers/PRTModifierAction.cpp | 31 ++++++++++------------ 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index a04b9bd5..776abe5c 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -239,22 +239,19 @@ MStatus addHiddenBoolParameter(MFnDependencyNode& node, MFnAttribute& tAttr, con return stat; } -short getDefaultEnumValue(const prt::AttributeMap& defaultAttributeValues, const MFnEnumAttribute& eAttr, +short getDefaultEnumValue(const prt::AttributeMap& defaultAttributeValues, const PRTModifierEnum& mAttr, const RuleAttribute& ruleAttr) { const std::wstring fqAttrName = ruleAttr.fqName; const prt::AnnotationArgumentType ruleAttrType = ruleAttr.mType; - short minVal; - short maxVal; - MCHECK(eAttr.getMin(minVal)); - MCHECK(eAttr.getMax(maxVal)); + size_t maxVal = mAttr.getOptionCount(); switch (ruleAttrType) { case prt::AAT_STR: { const wchar_t* defStringVal = defaultAttributeValues.getString(fqAttrName.c_str()); - for (short currIdx = minVal; currIdx <= maxVal; currIdx++) { - const wchar_t* currString = eAttr.fieldName(currIdx).asWChar(); + for (short currIdx = 0; currIdx <= maxVal; currIdx++) { + const wchar_t* currString = mAttr.getOptionName(currIdx).asWChar(); if (std::wcscmp(currString, defStringVal) == 0) return currIdx; } @@ -263,8 +260,8 @@ short getDefaultEnumValue(const prt::AttributeMap& defaultAttributeValues, const case prt::AAT_FLOAT: { const auto defDoubleVal = defaultAttributeValues.getFloat(fqAttrName.c_str()); - for (short currIdx = minVal; currIdx <= maxVal; currIdx++) { - const double currDouble = eAttr.fieldName(currIdx).asDouble(); + for (short currIdx = 0; currIdx <= maxVal; currIdx++) { + const double currDouble = mAttr.getOptionName(currIdx).asDouble(); if (currDouble == defDoubleVal) return currIdx; } @@ -273,8 +270,8 @@ short getDefaultEnumValue(const prt::AttributeMap& defaultAttributeValues, const case prt::AAT_BOOL: { const auto defBoolVal = defaultAttributeValues.getBool(fqAttrName.c_str()); - for (short currIdx = minVal; currIdx <= maxVal; currIdx++) { - const bool currBool = (eAttr.fieldName(currIdx).asInt() != 0); + for (short currIdx = 0; currIdx <= maxVal; currIdx++) { + const bool currBool = mAttr.getOptionName(currIdx).asInt(); if (currBool == defBoolVal) return currIdx; } @@ -443,7 +440,7 @@ MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { break; } case PrtAttributeType::ENUM: { - MFnEnumAttribute eAttr(fnAttribute.object()); + const PRTModifierEnum& currEnum = mEnums[ruleAttribute.mayaFullName]; short enumVal; MCHECK(plug.getValue(enumVal)); @@ -451,13 +448,13 @@ MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { if (getIsUserSet(fnNode, fnAttribute)) { switch (ruleAttrType) { case prt::AAT_STR: - aBuilder->setString(fqAttrName.c_str(), eAttr.fieldName(enumVal).asWChar()); + aBuilder->setString(fqAttrName.c_str(), currEnum.getOptionName(enumVal).asWChar()); break; case prt::AAT_FLOAT: - aBuilder->setFloat(fqAttrName.c_str(), eAttr.fieldName(enumVal).asDouble()); + aBuilder->setFloat(fqAttrName.c_str(), currEnum.getOptionName(enumVal).asDouble()); break; case prt::AAT_BOOL: - aBuilder->setBool(fqAttrName.c_str(), eAttr.fieldName(enumVal).asInt() != 0); + aBuilder->setBool(fqAttrName.c_str(), currEnum.getOptionName(enumVal).asInt() != 0); break; default: LOG_ERR << "Cannot handle attribute type " << ruleAttrType << " for attr " << fqAttrName; @@ -535,7 +532,7 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { case PrtAttributeType::ENUM: { MFnEnumAttribute eAttr(fnAttribute.object()); - const short defEnumVal = getDefaultEnumValue(*defaultAttributeValues, eAttr, ruleAttribute); + const short defEnumVal = getDefaultEnumValue(*defaultAttributeValues, mEnums[ruleAttribute.mayaFullName], ruleAttribute); short enumVal; MCHECK(plug.getValue(enumVal)); @@ -634,7 +631,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacProble enumVal = newEnumInfo.second; const short defEnumVal = - getDefaultEnumValue(*defaultAttributeValues, modifierEnum.mAttr, ruleAttribute); + getDefaultEnumValue(*defaultAttributeValues, modifierEnum, ruleAttribute); const bool isDefaultValue = (defEnumVal == enumVal); From dcbbf5be4fb1850df40b125b73eeac130e6a49ce Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 09:41:53 +0100 Subject: [PATCH 437/668] Add helper function for replacing banned chars --- src/serlio/utils/Utilities.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index 89569d2e..1541bfd8 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -232,6 +232,16 @@ inline void replace_all_not_of(std::wstring& s, const std::wstring& allowedChars } } +inline void replaceAllOf(std::wstring& s, const std::wstring& bannedChars) { + std::wstring::size_type pos = 0; + while (pos < s.size()) { + pos = s.find_first_of(bannedChars, pos); + if (pos == std::wstring::npos) + break; + s[pos++] = L'_'; + } +} + template void replaceAllSubstrings(std::basic_string& str, const std::basic_string& oldStr, const std::basic_string& newStr) { From 39e7c346b3ced70762a3743f7bb6f8b6c0fa3326 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 09:47:43 +0100 Subject: [PATCH 438/668] Replace banned chars in options.. This also includes passing an empty string as option --- src/serlio/utils/MELScriptBuilder.cpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/serlio/utils/MELScriptBuilder.cpp b/src/serlio/utils/MELScriptBuilder.cpp index 62416f13..c9444668 100644 --- a/src/serlio/utils/MELScriptBuilder.cpp +++ b/src/serlio/utils/MELScriptBuilder.cpp @@ -27,6 +27,7 @@ namespace { constexpr bool MEL_ENABLE_DISPLAY = true; +const std::wstring ENUM_BANNED_CHARS = L"=:\\;\r\n"; std::wstring composeAttributeExpression(const MELVariable& node, const std::wstring& attribute) { assert(!attribute.empty() && attribute[0] != L'.'); // to catch refactoring bugs @@ -90,13 +91,27 @@ void MELScriptBuilder::setAttrEnumOptions(const MELVariable& node, const std::ws const std::optional& customDefaultOption) { std::wstring enumString; - if (customDefaultOption.has_value()) - enumString.append(customDefaultOption.value() + L"=0"); + if (customDefaultOption.has_value()) { + std::wstring customDefaultOptionString = customDefaultOption.value(); + if (customDefaultOptionString == L"") { + enumString.append(L" =0"); + } + else { + replaceAllOf(customDefaultOptionString, ENUM_BANNED_CHARS); + enumString.append(customDefaultOptionString + L"=0"); + } + } int idx = 1; - for (const std::wstring& enumOption : enumOptions) { + for (std::wstring enumOption : enumOptions) { if (!enumString.empty()) enumString.append(L":"); - enumString.append(enumOption + L"=" + std::to_wstring(idx++)); + if (enumOption == L"") { + enumString.append(enumOption + L" =" + std::to_wstring(idx++)); + } + else { + replaceAllOf(enumOption, ENUM_BANNED_CHARS); + enumString.append(enumOption + L"=" + std::to_wstring(idx++)); + } } // Don't update to an empty enum if (enumString.empty()) From 68b116b913ec21a1ff0de57d86374286488a7d71 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 09:50:29 +0100 Subject: [PATCH 439/668] Cleanup: move code for cleaning enum options into helper function --- src/serlio/utils/MELScriptBuilder.cpp | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/serlio/utils/MELScriptBuilder.cpp b/src/serlio/utils/MELScriptBuilder.cpp index c9444668..340000bd 100644 --- a/src/serlio/utils/MELScriptBuilder.cpp +++ b/src/serlio/utils/MELScriptBuilder.cpp @@ -36,6 +36,15 @@ std::wstring composeAttributeExpression(const MELVariable& node, const std::wstr return out.str(); } +void cleanEnumOptionName(std::wstring& optionName) { + if (optionName == L"") { + optionName = L" "; + } + else { + replaceAllOf(optionName, ENUM_BANNED_CHARS); + } +} + } // namespace void MELScriptBuilder::setAttr(const MELVariable& node, const std::wstring& attribute, const bool val) { @@ -93,25 +102,15 @@ void MELScriptBuilder::setAttrEnumOptions(const MELVariable& node, const std::ws if (customDefaultOption.has_value()) { std::wstring customDefaultOptionString = customDefaultOption.value(); - if (customDefaultOptionString == L"") { - enumString.append(L" =0"); - } - else { - replaceAllOf(customDefaultOptionString, ENUM_BANNED_CHARS); - enumString.append(customDefaultOptionString + L"=0"); - } + cleanEnumOptionName(customDefaultOptionString); + enumString.append(customDefaultOptionString + L"=0"); } int idx = 1; for (std::wstring enumOption : enumOptions) { if (!enumString.empty()) enumString.append(L":"); - if (enumOption == L"") { - enumString.append(enumOption + L" =" + std::to_wstring(idx++)); - } - else { - replaceAllOf(enumOption, ENUM_BANNED_CHARS); - enumString.append(enumOption + L"=" + std::to_wstring(idx++)); - } + cleanEnumOptionName(enumOption); + enumString.append(enumOption + L"=" + std::to_wstring(idx++)); } // Don't update to an empty enum if (enumString.empty()) From 547f5c85a897af3c52429e28c1eea5347e70a601 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 09:50:43 +0100 Subject: [PATCH 440/668] Cleanup: remove newline --- src/serlio/modifiers/PRTModifierAction.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 776abe5c..3149d4a3 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -633,7 +633,6 @@ MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacProble const short defEnumVal = getDefaultEnumValue(*defaultAttributeValues, modifierEnum, ruleAttribute); - const bool isDefaultValue = (defEnumVal == enumVal); if (hasNewEnumOptions || (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue)) plug.setShort(defEnumVal); From 174030a0899d94287fe48c35e90f513e8bc0f53b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 10:52:44 +0100 Subject: [PATCH 441/668] Cleanup: rename fieldXX to optionXX --- src/serlio/modifiers/PRTModifierEnum.cpp | 12 ++++++------ src/serlio/modifiers/PRTModifierEnum.h | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index cbc10e56..e9a2c983 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -80,22 +80,22 @@ bool PRTModifierEnum::isDynamic() const { return mValuesAttr.length() > 0; } -size_t PRTModifierEnum::getOptionIndex(std::wstring fieldName) const { - const auto iter = std::find(mEnumOptions.begin(), mEnumOptions.end(), fieldName); +size_t PRTModifierEnum::getOptionIndex(std::wstring optionName) const { + const auto iter = std::find(mEnumOptions.begin(), mEnumOptions.end(), optionName); //return first index, if element is not available if (iter == mEnumOptions.end()) return 0; return std::distance(mEnumOptions.begin(), iter); } -MString PRTModifierEnum::getOptionName(size_t fieldIndex) const { - if (fieldIndex == 0) { +MString PRTModifierEnum::getOptionName(size_t optionIndex) const { + if (optionIndex == 0) { return MString(mCustomDefaultValue.c_str()); } else { - if ((fieldIndex > mEnumOptions.size()) || (fieldIndex <= 0)) + if ((optionIndex > mEnumOptions.size()) || (optionIndex <= 0)) return {}; - return MString(mEnumOptions[fieldIndex - 1].c_str()); + return MString(mEnumOptions[optionIndex - 1].c_str()); } } diff --git a/src/serlio/modifiers/PRTModifierEnum.h b/src/serlio/modifiers/PRTModifierEnum.h index b321702f..4935f5eb 100644 --- a/src/serlio/modifiers/PRTModifierEnum.h +++ b/src/serlio/modifiers/PRTModifierEnum.h @@ -38,8 +38,8 @@ class PRTModifierEnum { const prt::AttributeMap& defaultAttributeValues, short selectedEnumIdx = 0); bool isDynamic() const; - size_t getOptionIndex(std::wstring fieldName) const; - MString getOptionName(size_t fieldIndex) const; + size_t getOptionIndex(std::wstring optionName) const; + MString getOptionName(size_t optionIndex) const; size_t getOptionCount() const; public: From 0afd58d2b3269e8e078bdbbf05bc2890503f9dd9 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 10:53:13 +0100 Subject: [PATCH 442/668] Cleanup: don't use auto for bool/double --- src/serlio/modifiers/PRTModifierEnum.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index e9a2c983..5ab25fd8 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -139,12 +139,12 @@ bool PRTModifierEnum::updateCustomEnumValue(const RuleAttribute& ruleAttr, break; } case prt::AAT_FLOAT: { - const auto defDoubleVal = defaultAttributeValues.getFloat(fqAttrName.c_str()); + const double defDoubleVal = defaultAttributeValues.getFloat(fqAttrName.c_str()); defWStringVal = std::to_wstring(defDoubleVal).c_str(); break; } case prt::AAT_BOOL: { - const auto defBoolVal = defaultAttributeValues.getBool(fqAttrName.c_str()); + const bool defBoolVal = defaultAttributeValues.getBool(fqAttrName.c_str()); defWStringVal = std::to_wstring(defBoolVal).c_str(); break; } From b9ba9330c81d10753f47ac1e61f40ca77931bb56 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 11:16:04 +0100 Subject: [PATCH 443/668] Fix index in getOptionIndex --- src/serlio/modifiers/PRTModifierEnum.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index 5ab25fd8..c56b9faf 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -85,7 +85,7 @@ size_t PRTModifierEnum::getOptionIndex(std::wstring optionName) const { //return first index, if element is not available if (iter == mEnumOptions.end()) return 0; - return std::distance(mEnumOptions.begin(), iter); + return std::distance(mEnumOptions.begin(), iter) + 1; } MString PRTModifierEnum::getOptionName(size_t optionIndex) const { From afab269c5358b65037958bfcc6c60cd354ac646e Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 11:16:46 +0100 Subject: [PATCH 444/668] Cleanup: use getOptionIndex instead of iterating over getOptionName --- src/serlio/modifiers/PRTModifierAction.cpp | 25 ++++++---------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 3149d4a3..f19bf89e 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -250,31 +250,18 @@ short getDefaultEnumValue(const prt::AttributeMap& defaultAttributeValues, const case prt::AAT_STR: { const wchar_t* defStringVal = defaultAttributeValues.getString(fqAttrName.c_str()); - for (short currIdx = 0; currIdx <= maxVal; currIdx++) { - const wchar_t* currString = mAttr.getOptionName(currIdx).asWChar(); - if (std::wcscmp(currString, defStringVal) == 0) - return currIdx; - } + if (defStringVal != 0) + return mAttr.getOptionIndex(defStringVal); break; } case prt::AAT_FLOAT: { - const auto defDoubleVal = defaultAttributeValues.getFloat(fqAttrName.c_str()); - - for (short currIdx = 0; currIdx <= maxVal; currIdx++) { - const double currDouble = mAttr.getOptionName(currIdx).asDouble(); - if (currDouble == defDoubleVal) - return currIdx; - } + const float defDoubleVal = defaultAttributeValues.getFloat(fqAttrName.c_str()); + return mAttr.getOptionIndex(std::to_wstring(defDoubleVal)); break; } case prt::AAT_BOOL: { - const auto defBoolVal = defaultAttributeValues.getBool(fqAttrName.c_str()); - - for (short currIdx = 0; currIdx <= maxVal; currIdx++) { - const bool currBool = mAttr.getOptionName(currIdx).asInt(); - if (currBool == defBoolVal) - return currIdx; - } + const bool defBoolVal = defaultAttributeValues.getBool(fqAttrName.c_str()); + return mAttr.getOptionIndex(std::to_wstring(defBoolVal)); break; } default: From e0c49ea7b3ce0804b947647fe4eb56c35895a76a Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 11:25:55 +0100 Subject: [PATCH 445/668] Cleanup: don't use auto for double and bool variables --- src/serlio/modifiers/PRTModifierAction.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index f19bf89e..1177b285 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -479,7 +479,7 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { switch (attrType) { case PrtAttributeType::BOOL: { - const auto defBoolVal = defaultAttributeValues->getBool(fqAttrName.c_str()); + const bool defBoolVal = defaultAttributeValues->getBool(fqAttrName.c_str()); bool boolVal; MCHECK(plug.getValue(boolVal)); @@ -487,7 +487,7 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { break; } case PrtAttributeType::FLOAT: { - const auto defDoubleVal = defaultAttributeValues->getFloat(fqAttrName.c_str()); + const double defDoubleVal = defaultAttributeValues->getFloat(fqAttrName.c_str()); double doubleVal; MCHECK(plug.getValue(doubleVal)); @@ -552,7 +552,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacProble switch (attrType) { case PrtAttributeType::BOOL: { - const auto defBoolVal = defaultAttributeValues->getBool(fqAttrName.c_str()); + const bool defBoolVal = defaultAttributeValues->getBool(fqAttrName.c_str()); bool boolVal; MCHECK(plug.getValue(boolVal)); @@ -563,7 +563,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacProble break; } case PrtAttributeType::FLOAT: { - const auto defDoubleVal = defaultAttributeValues->getFloat(fqAttrName.c_str()); + const double defDoubleVal = defaultAttributeValues->getFloat(fqAttrName.c_str()); double doubleVal; MCHECK(plug.getValue(doubleVal)); From 3a9d6f6b5a9f734a7d4df87c7817f0cc3fcbb07b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 11:26:52 +0100 Subject: [PATCH 446/668] Cleanup: use static cast to convert index type to short --- src/serlio/modifiers/PRTModifierAction.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 1177b285..b6c26012 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -251,17 +251,17 @@ short getDefaultEnumValue(const prt::AttributeMap& defaultAttributeValues, const const wchar_t* defStringVal = defaultAttributeValues.getString(fqAttrName.c_str()); if (defStringVal != 0) - return mAttr.getOptionIndex(defStringVal); + return static_cast(mAttr.getOptionIndex(defStringVal)); break; } case prt::AAT_FLOAT: { const float defDoubleVal = defaultAttributeValues.getFloat(fqAttrName.c_str()); - return mAttr.getOptionIndex(std::to_wstring(defDoubleVal)); + return static_cast(mAttr.getOptionIndex(std::to_wstring(defDoubleVal))); break; } case prt::AAT_BOOL: { const bool defBoolVal = defaultAttributeValues.getBool(fqAttrName.c_str()); - return mAttr.getOptionIndex(std::to_wstring(defBoolVal)); + return static_cast(mAttr.getOptionIndex(std::to_wstring(defBoolVal))); break; } default: From 93d11fd558c3f0d459b5a41e4a09a04163c5cc87 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 11:27:42 +0100 Subject: [PATCH 447/668] Cleanup: change variable type from float to double for getFloat --- src/serlio/modifiers/PRTModifierAction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index b6c26012..4716e94d 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -255,7 +255,7 @@ short getDefaultEnumValue(const prt::AttributeMap& defaultAttributeValues, const break; } case prt::AAT_FLOAT: { - const float defDoubleVal = defaultAttributeValues.getFloat(fqAttrName.c_str()); + const double defDoubleVal = defaultAttributeValues.getFloat(fqAttrName.c_str()); return static_cast(mAttr.getOptionIndex(std::to_wstring(defDoubleVal))); break; } From 037d94698981afed2231d1c615a1e4f13473962d Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 11:28:19 +0100 Subject: [PATCH 448/668] Cleanup: revert mel script logging to false --- src/serlio/utils/MELScriptBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/utils/MELScriptBuilder.cpp b/src/serlio/utils/MELScriptBuilder.cpp index 340000bd..4c44b923 100644 --- a/src/serlio/utils/MELScriptBuilder.cpp +++ b/src/serlio/utils/MELScriptBuilder.cpp @@ -26,7 +26,7 @@ namespace { -constexpr bool MEL_ENABLE_DISPLAY = true; +constexpr bool MEL_ENABLE_DISPLAY = false; const std::wstring ENUM_BANNED_CHARS = L"=:\\;\r\n"; std::wstring composeAttributeExpression(const MELVariable& node, const std::wstring& attribute) { From 78678840f510dc3e95c65a218bef902d841a8f0c Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 11:33:54 +0100 Subject: [PATCH 449/668] Cleanup: move enum helper function implementation into PRTModifierEnum --- src/serlio/modifiers/PRTModifierAction.cpp | 37 ++-------------------- src/serlio/modifiers/PRTModifierEnum.cpp | 31 ++++++++++++++++++ src/serlio/modifiers/PRTModifierEnum.h | 1 + 3 files changed, 35 insertions(+), 34 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 4716e94d..643b659d 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -239,37 +239,6 @@ MStatus addHiddenBoolParameter(MFnDependencyNode& node, MFnAttribute& tAttr, con return stat; } -short getDefaultEnumValue(const prt::AttributeMap& defaultAttributeValues, const PRTModifierEnum& mAttr, - const RuleAttribute& ruleAttr) { - const std::wstring fqAttrName = ruleAttr.fqName; - const prt::AnnotationArgumentType ruleAttrType = ruleAttr.mType; - - size_t maxVal = mAttr.getOptionCount(); - - switch (ruleAttrType) { - case prt::AAT_STR: { - const wchar_t* defStringVal = defaultAttributeValues.getString(fqAttrName.c_str()); - - if (defStringVal != 0) - return static_cast(mAttr.getOptionIndex(defStringVal)); - break; - } - case prt::AAT_FLOAT: { - const double defDoubleVal = defaultAttributeValues.getFloat(fqAttrName.c_str()); - return static_cast(mAttr.getOptionIndex(std::to_wstring(defDoubleVal))); - break; - } - case prt::AAT_BOOL: { - const bool defBoolVal = defaultAttributeValues.getBool(fqAttrName.c_str()); - return static_cast(mAttr.getOptionIndex(std::to_wstring(defBoolVal))); - break; - } - default: - LOG_ERR << "Cannot handle attribute type " << ruleAttrType << " for attr " << fqAttrName; - } - return 0; -} - short getDefaultEnumIdx(const prt::Annotation* annot, const PRTEnumDefaultValue& defaultValue) { short idx = 0; for (size_t arg = 0; arg < annot->getNumArguments(); arg++) { @@ -519,7 +488,8 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { case PrtAttributeType::ENUM: { MFnEnumAttribute eAttr(fnAttribute.object()); - const short defEnumVal = getDefaultEnumValue(*defaultAttributeValues, mEnums[ruleAttribute.mayaFullName], ruleAttribute); + const short defEnumVal = + mEnums[ruleAttribute.mayaFullName].getDefaultEnumValue(*defaultAttributeValues, ruleAttribute); short enumVal; MCHECK(plug.getValue(enumVal)); @@ -617,8 +587,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacProble const bool hasNewEnumOptions = newEnumInfo.first; enumVal = newEnumInfo.second; - const short defEnumVal = - getDefaultEnumValue(*defaultAttributeValues, modifierEnum, ruleAttribute); + const short defEnumVal = modifierEnum.getDefaultEnumValue(*defaultAttributeValues, ruleAttribute); const bool isDefaultValue = (defEnumVal == enumVal); if (hasNewEnumOptions || (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue)) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index c56b9faf..8a01710f 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -103,6 +103,37 @@ size_t PRTModifierEnum::getOptionCount() const { return mEnumOptions.size() + 1; } +short PRTModifierEnum::getDefaultEnumValue(const prt::AttributeMap& defaultAttributeValues, + const RuleAttribute& ruleAttr) const { + const std::wstring fqAttrName = ruleAttr.fqName; + const prt::AnnotationArgumentType ruleAttrType = ruleAttr.mType; + + size_t maxVal = getOptionCount(); + + switch (ruleAttrType) { + case prt::AAT_STR: { + const wchar_t* defStringVal = defaultAttributeValues.getString(fqAttrName.c_str()); + + if (defStringVal != 0) + return static_cast(getOptionIndex(defStringVal)); + break; + } + case prt::AAT_FLOAT: { + const double defDoubleVal = defaultAttributeValues.getFloat(fqAttrName.c_str()); + return static_cast(getOptionIndex(std::to_wstring(defDoubleVal))); + break; + } + case prt::AAT_BOOL: { + const bool defBoolVal = defaultAttributeValues.getBool(fqAttrName.c_str()); + return static_cast(getOptionIndex(std::to_wstring(defBoolVal))); + break; + } + default: + LOG_ERR << "Cannot handle attribute type " << ruleAttrType << " for attr " << fqAttrName; + } + return 0; +} + std::vector PRTModifierEnum::getEnumOptions(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues) { if (isDynamic()) { diff --git a/src/serlio/modifiers/PRTModifierEnum.h b/src/serlio/modifiers/PRTModifierEnum.h index 4935f5eb..88301b53 100644 --- a/src/serlio/modifiers/PRTModifierEnum.h +++ b/src/serlio/modifiers/PRTModifierEnum.h @@ -41,6 +41,7 @@ class PRTModifierEnum { size_t getOptionIndex(std::wstring optionName) const; MString getOptionName(size_t optionIndex) const; size_t getOptionCount() const; + short getDefaultEnumValue(const prt::AttributeMap& defaultAttributeValues, const RuleAttribute& ruleAttr) const; public: MFnEnumAttribute mAttr; From e259bc70389de0d5b3345b3c80f21da141a0a596 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 11:34:52 +0100 Subject: [PATCH 450/668] Cleanup: run clang-format --- src/serlio/modifiers/PRTModifierEnum.cpp | 6 +++--- src/serlio/modifiers/PRTModifierEnum.h | 4 ++-- src/serlio/utils/MELScriptBuilder.cpp | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index 8a01710f..05018991 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -82,7 +82,7 @@ bool PRTModifierEnum::isDynamic() const { size_t PRTModifierEnum::getOptionIndex(std::wstring optionName) const { const auto iter = std::find(mEnumOptions.begin(), mEnumOptions.end(), optionName); - //return first index, if element is not available + // return first index, if element is not available if (iter == mEnumOptions.end()) return 0; return std::distance(mEnumOptions.begin(), iter) + 1; @@ -135,7 +135,7 @@ short PRTModifierEnum::getDefaultEnumValue(const prt::AttributeMap& defaultAttri } std::vector PRTModifierEnum::getEnumOptions(const RuleAttribute& ruleAttr, - const prt::AttributeMap& defaultAttributeValues) { + const prt::AttributeMap& defaultAttributeValues) { if (isDynamic()) { return getDynamicEnumOptions(ruleAttr, defaultAttributeValues); } @@ -191,7 +191,7 @@ bool PRTModifierEnum::updateCustomEnumValue(const RuleAttribute& ruleAttr, } std::vector PRTModifierEnum::getDynamicEnumOptions(const RuleAttribute& ruleAttr, - const prt::AttributeMap& defaultAttributeValues) { + const prt::AttributeMap& defaultAttributeValues) { std::vector enumOptions; if (!isDynamic()) return enumOptions; diff --git a/src/serlio/modifiers/PRTModifierEnum.h b/src/serlio/modifiers/PRTModifierEnum.h index 88301b53..7d137e7c 100644 --- a/src/serlio/modifiers/PRTModifierEnum.h +++ b/src/serlio/modifiers/PRTModifierEnum.h @@ -53,8 +53,8 @@ class PRTModifierEnum { std::vector mEnumOptions; std::vector getEnumOptions(const RuleAttribute& ruleAttr, - const prt::AttributeMap& defaultAttributeValues); + const prt::AttributeMap& defaultAttributeValues); std::vector getDynamicEnumOptions(const RuleAttribute& ruleAttr, - const prt::AttributeMap& defaultAttributeValues); + const prt::AttributeMap& defaultAttributeValues); bool updateCustomEnumValue(const RuleAttribute& ruleAttr, const prt::AttributeMap& defaultAttributeValues); }; // class PRTModifierEnum diff --git a/src/serlio/utils/MELScriptBuilder.cpp b/src/serlio/utils/MELScriptBuilder.cpp index 4c44b923..eb998b8e 100644 --- a/src/serlio/utils/MELScriptBuilder.cpp +++ b/src/serlio/utils/MELScriptBuilder.cpp @@ -116,7 +116,8 @@ void MELScriptBuilder::setAttrEnumOptions(const MELVariable& node, const std::ws if (enumString.empty()) enumString.append(L" "); - commandStream << "addAttr -e -en " << MELStringLiteral(enumString).mel() << " " << composeAttributeExpression(node, attribute) << ";\n"; + commandStream << "addAttr -e -en " << MELStringLiteral(enumString).mel() << " " + << composeAttributeExpression(node, attribute) << ";\n"; } void MELScriptBuilder::connectAttr(const MELVariable& srcNode, const std::wstring& srcAttr, const MELVariable& dstNode, From 8156d1684257375fba29b82dd71277289e1d7d2b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 11:36:33 +0100 Subject: [PATCH 451/668] Cleanup: remove unused getOptionCount --- src/serlio/modifiers/PRTModifierEnum.cpp | 6 ------ src/serlio/modifiers/PRTModifierEnum.h | 1 - 2 files changed, 7 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index 05018991..e2527612 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -99,17 +99,11 @@ MString PRTModifierEnum::getOptionName(size_t optionIndex) const { } } -size_t PRTModifierEnum::getOptionCount() const { - return mEnumOptions.size() + 1; -} - short PRTModifierEnum::getDefaultEnumValue(const prt::AttributeMap& defaultAttributeValues, const RuleAttribute& ruleAttr) const { const std::wstring fqAttrName = ruleAttr.fqName; const prt::AnnotationArgumentType ruleAttrType = ruleAttr.mType; - size_t maxVal = getOptionCount(); - switch (ruleAttrType) { case prt::AAT_STR: { const wchar_t* defStringVal = defaultAttributeValues.getString(fqAttrName.c_str()); diff --git a/src/serlio/modifiers/PRTModifierEnum.h b/src/serlio/modifiers/PRTModifierEnum.h index 7d137e7c..8457cdd4 100644 --- a/src/serlio/modifiers/PRTModifierEnum.h +++ b/src/serlio/modifiers/PRTModifierEnum.h @@ -40,7 +40,6 @@ class PRTModifierEnum { bool isDynamic() const; size_t getOptionIndex(std::wstring optionName) const; MString getOptionName(size_t optionIndex) const; - size_t getOptionCount() const; short getDefaultEnumValue(const prt::AttributeMap& defaultAttributeValues, const RuleAttribute& ruleAttr) const; public: From 9e11ed3a089945b74b39fdf88b187d4fb1952cc9 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 13:33:29 +0100 Subject: [PATCH 452/668] Cleanup: fix type of double variable --- src/serlio/modifiers/PRTModifierEnum.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index e2527612..00f4217c 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -246,7 +246,7 @@ std::vector PRTModifierEnum::getDynamicEnumOptions(const RuleAttri break; } case prt::Attributable::PT_FLOAT: { - const bool currFloat = defaultAttributeValues.getFloat(valuesAttr.c_str()); + const double currFloat = defaultAttributeValues.getFloat(valuesAttr.c_str()); enumOptions.emplace_back(std::to_wstring(currFloat)); break; } From a28e68b26a77841b47066b16fa25bf1f3c207de2 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 14:29:19 +0100 Subject: [PATCH 453/668] Cleanup: move groovy pipeline implementation into Jenkins file --- Jenkinsfile | 165 +++++++++++++++++++++++++++++++++++++++++---- pipeline.groovy | 175 ------------------------------------------------ 2 files changed, 153 insertions(+), 187 deletions(-) delete mode 100644 pipeline.groovy diff --git a/Jenkinsfile b/Jenkinsfile index 011c67cb..5a1af59e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,16 +1,22 @@ #!/usr/bin/env groovy -import groovy.transform.Field - +// This pipeline is designed to run on Esri-internal CI infrastructure. // -- PIPELINE LIBRARIES @Library('psl') import com.esri.zrh.jenkins.PipelineSupportLibrary import com.esri.zrh.jenkins.PslFactory +import com.esri.zrh.jenkins.psl.UploadTrackingPsl +import com.esri.zrh.jenkins.JenkinsTools +import com.esri.zrh.jenkins.ToolInfo +import com.esri.zrh.jenkins.ce.CityEnginePipelineLibrary +import com.esri.zrh.jenkins.ce.PrtAppPipelineLibrary +import groovy.transform.Field -@Field def psl = PslFactory.create(this) - +@Field def psl = PslFactory.create(this, UploadTrackingPsl.ID) +@Field def cepl = new CityEnginePipelineLibrary(this, psl) +@Field def papl = new PrtAppPipelineLibrary(cepl) // -- SETUP @@ -18,20 +24,155 @@ psl.runsHere('production') env.PIPELINE_ARCHIVING_ALLOWED = "true" properties([ disableConcurrentBuilds() ]) +// -- GLOBAL DEFINITIONS -// -- LOAD & RUN PIPELINE +@Field final String REPO = 'git@github.com:esri/serlio.git' +@Field final String REPO_CREDS = 'jenkins-github-serlio-deployer-key' +@Field final String SOURCES = "serlio.git/src" +@Field final String BUILD_TARGET = 'package' +@Field final String SOURCE_STASH = 'serlio-src' +// TODO: abusing grp field to distinguish maya versions per task +@Field final List CONFIGS = [ + [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2018', maya: PrtAppPipelineLibrary.Dependencies.MAYA2018 ], + [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2019', maya: PrtAppPipelineLibrary.Dependencies.MAYA2019 ], + [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2020', maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], + [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2022', maya: PrtAppPipelineLibrary.Dependencies.MAYA2022 ], + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2018', maya: PrtAppPipelineLibrary.Dependencies.MAYA2018 ], + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2019', maya: PrtAppPipelineLibrary.Dependencies.MAYA2019 ], + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2020', maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2022', maya: PrtAppPipelineLibrary.Dependencies.MAYA2022 ], +] -def impl +@Field final List TEST_CONFIGS = [ + [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, maya: PrtAppPipelineLibrary.Dependencies.MAYA2022 ], + [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, maya: PrtAppPipelineLibrary.Dependencies.MAYA2022 ], +] +@Field String myBranch = env.BRANCH_NAME -node { - checkout scm - impl = load('pipeline.groovy') -} +// -- PIPELINE stage('serlio-checkout'){ - impl.checkout() + cepl.runParallel(taskGenSourceCheckout()) } stage('serlio') { - impl.pipeline() + cepl.runParallel(getTasks(branchName)) + papl.finalizeRun('serlio', myBranch) +} + +// -- TASK GENERATORS + +Map taskGenSourceCheckout(){ + final List srcCfgs = [ [ ba: psl.BA_CHECKOUT ] ] + Map tasks = [:] + // checkout serlio.git + def taskSRL = { + cepl.cleanCurrentDir() + final String localDir = 'srl' + papl.checkout(REPO, myBranch, REPO_CREDS) + stash(name: SOURCE_STASH) + } + tasks << cepl.generateTasks("checkout-and-stash-srl", taskSRL, srcCfgs) + return tasks +} + +// entry point for embedded pipeline +Map getTasks(String branchName = null) { + if (branchName) myBranch = branchName + + Map tasks = [:] + tasks << taskGenSerlio() + tasks << taskGenSerlioTests() + tasks << taskGenSerlioInstallers() + return tasks +} + +Map taskGenSerlio() { + return cepl.generateTasks('srl', this.&taskBuildSerlio, CONFIGS) +} + +Map taskGenSerlioTests() { + return cepl.generateTasks('srl-tst', this.&taskBuildSerlioTests, TEST_CONFIGS) +} + +Map taskGenSerlioInstallers() { + return cepl.generateTasks('srl-msi', this.&taskBuildSerlioInstaller, CONFIGS.findAll { it.os == cepl.CFG_OS_WIN10}) +} + +// -- TASK BUILDERS + +def taskBuildCMake(cfg, target){ + final List deps = [ cfg.maya ] + List defs = [ + [ key: 'maya_DIR', val: cfg.maya.p ], + [ key: 'SRL_VERSION_BUILD', val: env.BUILD_NUMBER ] + ] + cepl.cleanCurrentDir() + unstash(name: SOURCE_STASH) + deps.each { d -> papl.fetchDependency(d, cfg) } + papl.jpe.dir(path: 'build') { + papl.runCMakeBuild(SOURCES, target, cfg, defs) + } +} + +def taskBuildSerlio(cfg) { + final String appName = 'serlio' + + taskBuildCMake(cfg, BUILD_TARGET) + def buildProps = papl.jpe.readProperties(file: 'build/deployment.properties') + final String artifactPattern = "${buildProps.package_file}.*" + final def artifactVersion = { p -> buildProps.package_version_base } + + // TODO: abusing grp again to label artifact + def classifierExtractor = { p -> + return cepl.getArchiveClassifier(cfg) + '-' + cfg.grp + } + papl.publish(appName, myBranch, artifactPattern, artifactVersion, cfg, classifierExtractor) } + +def taskBuildSerlioTests(cfg) { + final String appName = 'serlio-test' + taskBuildCMake(cfg, 'build_and_run_tests') + papl.jpe.junit('build/test/serlio_test_report.xml') +} + +def taskBuildSerlioInstaller(cfg) { + final String appName = 'serlio-installer' + cepl.cleanCurrentDir() + unstash(name: SOURCE_STASH) + final List deps = [ cfg.maya ] + deps.each { d -> papl.fetchDependency(d, cfg) } + + // Toolchain definition for building MSI installers. + final JenkinsTools compiler = cepl.getToolchainTool(cfg) + final def toolchain = [ + new ToolInfo(JenkinsTools.CMAKE313, cfg), + new ToolInfo(JenkinsTools.NINJA, cfg), + new ToolInfo(JenkinsTools.WIX, cfg), + new ToolInfo(compiler, cfg) + ] + + // Setup environment according to above toolchain definition. + withTool(toolchain) { + dir('serlio.git') { + String cmd = JenkinsTools.generateSetupPreamble(this, cfg, toolchain.collect { it.tool }) + cmd += "\n" + cmd += "powershell .\\build.ps1" + cmd += " -MAYA_DIR ${cfg.maya.p.call(cfg)}" // <-- !!! + cmd += " -BUILD_NO ${env.BUILD_NUMBER}" + cmd += " -TARGET InstallerFromScratch" + + psl.runCmd(cmd) + + def buildProps = papl.jpe.readProperties(file: 'build/build_msi/deployment.properties') + final String artifactPattern = "out/${buildProps.package_file}.*" + final def artifactVersion = { p -> buildProps.package_version_base } + + // TODO: abusing grp again to label artifact + def classifierExtractor = { p -> + return cepl.getArchiveClassifier(cfg) + '-' + cfg.grp + } + papl.publish(appName, myBranch, artifactPattern, artifactVersion, cfg, classifierExtractor) + } + } +} \ No newline at end of file diff --git a/pipeline.groovy b/pipeline.groovy deleted file mode 100644 index 16cd1381..00000000 --- a/pipeline.groovy +++ /dev/null @@ -1,175 +0,0 @@ -import groovy.transform.Field -import com.esri.zrh.jenkins.PipelineSupportLibrary -import com.esri.zrh.jenkins.PslFactory -import com.esri.zrh.jenkins.psl.UploadTrackingPsl -import com.esri.zrh.jenkins.JenkinsTools -import com.esri.zrh.jenkins.ToolInfo -import com.esri.zrh.jenkins.ce.CityEnginePipelineLibrary -import com.esri.zrh.jenkins.ce.PrtAppPipelineLibrary - -@Field def psl = PslFactory.create(this, UploadTrackingPsl.ID) -@Field def cepl = new CityEnginePipelineLibrary(this, psl) -@Field def papl = new PrtAppPipelineLibrary(cepl) - - -// -- GLOBAL DEFINITIONS - -@Field final String REPO = 'git@github.com:esri/serlio.git' -@Field final String REPO_CREDS = 'jenkins-github-serlio-deployer-key' -@Field final String SOURCES = "serlio.git/src" -@Field final String BUILD_TARGET = 'package' -@Field final String SOURCE_STASH = 'serlio-src' - -// TODO: abusing grp field to distinguish maya versions per task -@Field final List CONFIGS = [ - [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2018', maya: PrtAppPipelineLibrary.Dependencies.MAYA2018 ], - [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2019', maya: PrtAppPipelineLibrary.Dependencies.MAYA2019 ], - [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2020', maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], - [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2022', maya: PrtAppPipelineLibrary.Dependencies.MAYA2022 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2018', maya: PrtAppPipelineLibrary.Dependencies.MAYA2018 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2019', maya: PrtAppPipelineLibrary.Dependencies.MAYA2019 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2020', maya: PrtAppPipelineLibrary.Dependencies.MAYA2020 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2022', maya: PrtAppPipelineLibrary.Dependencies.MAYA2022 ], -] - -@Field final List TEST_CONFIGS = [ - [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, maya: PrtAppPipelineLibrary.Dependencies.MAYA2022 ], - [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, maya: PrtAppPipelineLibrary.Dependencies.MAYA2022 ], -] - - -// -- PIPELINE - -@Field String myBranch = env.BRANCH_NAME - -// checkout and stash repo -def checkout(){ - cepl.runParallel(taskGenSourceCheckout()) -} - -// entry point for standalone pipeline -def pipeline(String branchName = null) { - cepl.runParallel(getTasks(branchName)) - papl.finalizeRun('serlio', myBranch) -} - -Map taskGenSourceCheckout(){ - final List srcCfgs = [ [ ba: psl.BA_CHECKOUT ] ] - Map tasks = [:] - // checkout serlio.git - def taskSRL = { - cepl.cleanCurrentDir() - final String localDir = 'srl' - papl.checkout(REPO, myBranch, REPO_CREDS) - stash(name: SOURCE_STASH) - } - tasks << cepl.generateTasks("checkout-and-stash-srl", taskSRL, srcCfgs) - return tasks -} - -// entry point for embedded pipeline -Map getTasks(String branchName = null) { - if (branchName) myBranch = branchName - - Map tasks = [:] - tasks << taskGenSerlio() - tasks << taskGenSerlioTests() - tasks << taskGenSerlioInstallers() - return tasks -} - - -// -- TASK GENERATORS - -Map taskGenSerlio() { - return cepl.generateTasks('srl', this.&taskBuildSerlio, CONFIGS) -} - -Map taskGenSerlioTests() { - return cepl.generateTasks('srl-tst', this.&taskBuildSerlioTests, TEST_CONFIGS) -} - -Map taskGenSerlioInstallers() { - return cepl.generateTasks('srl-msi', this.&taskBuildSerlioInstaller, CONFIGS.findAll { it.os == cepl.CFG_OS_WIN10}) -} - -// -- TASK BUILDERS -def taskBuildCMake(cfg, target){ - final List deps = [ cfg.maya ] - List defs = [ - [ key: 'maya_DIR', val: cfg.maya.p ], - [ key: 'SRL_VERSION_BUILD', val: env.BUILD_NUMBER ] - ] - cepl.cleanCurrentDir() - unstash(name: SOURCE_STASH) - deps.each { d -> papl.fetchDependency(d, cfg) } - papl.jpe.dir(path: 'build') { - papl.runCMakeBuild(SOURCES, target, cfg, defs) - } -} - -def taskBuildSerlio(cfg) { - final String appName = 'serlio' - - taskBuildCMake(cfg, BUILD_TARGET) - def buildProps = papl.jpe.readProperties(file: 'build/deployment.properties') - final String artifactPattern = "${buildProps.package_file}.*" - final def artifactVersion = { p -> buildProps.package_version_base } - - // TODO: abusing grp again to label artifact - def classifierExtractor = { p -> - return cepl.getArchiveClassifier(cfg) + '-' + cfg.grp - } - papl.publish(appName, myBranch, artifactPattern, artifactVersion, cfg, classifierExtractor) -} - -def taskBuildSerlioTests(cfg) { - final String appName = 'serlio-test' - taskBuildCMake(cfg, 'build_and_run_tests') - papl.jpe.junit('build/test/serlio_test_report.xml') -} - -def taskBuildSerlioInstaller(cfg) { - final String appName = 'serlio-installer' - cepl.cleanCurrentDir() - unstash(name: SOURCE_STASH) - final List deps = [ cfg.maya ] - deps.each { d -> papl.fetchDependency(d, cfg) } - - // Toolchain definition for building MSI installers. - final JenkinsTools compiler = cepl.getToolchainTool(cfg) - final def toolchain = [ - new ToolInfo(JenkinsTools.CMAKE313, cfg), - new ToolInfo(JenkinsTools.NINJA, cfg), - new ToolInfo(JenkinsTools.WIX, cfg), - new ToolInfo(compiler, cfg) - ] - - // Setup environment according to above toolchain definition. - withTool(toolchain) { - dir('serlio.git') { - String cmd = JenkinsTools.generateSetupPreamble(this, cfg, toolchain.collect { it.tool }) - cmd += "\n" - cmd += "powershell .\\build.ps1" - cmd += " -MAYA_DIR ${cfg.maya.p.call(cfg)}" // <-- !!! - cmd += " -BUILD_NO ${env.BUILD_NUMBER}" - cmd += " -TARGET InstallerFromScratch" - - psl.runCmd(cmd) - - def buildProps = papl.jpe.readProperties(file: 'build/build_msi/deployment.properties') - final String artifactPattern = "out/${buildProps.package_file}.*" - final def artifactVersion = { p -> buildProps.package_version_base } - - // TODO: abusing grp again to label artifact - def classifierExtractor = { p -> - return cepl.getArchiveClassifier(cfg) + '-' + cfg.grp - } - papl.publish(appName, myBranch, artifactPattern, artifactVersion, cfg, classifierExtractor) - } - } -} - -// -- make embeddable - -return this From 76d4b376c684ca3b586d520ab0567291d7fb59fb Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 14:41:01 +0100 Subject: [PATCH 454/668] Cleanup: move finalizeRun out of serlio stage --- Jenkinsfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 5a1af59e..d6688e1c 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -57,9 +57,10 @@ stage('serlio-checkout'){ stage('serlio') { cepl.runParallel(getTasks(branchName)) - papl.finalizeRun('serlio', myBranch) } +papl.finalizeRun('serlio', myBranch) + // -- TASK GENERATORS Map taskGenSourceCheckout(){ From af57c399fcaf123ac63a200ab40d7ed794a950d8 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 14:42:21 +0100 Subject: [PATCH 455/668] Cleanup: rename prepare stage analogous to pyprt --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index d6688e1c..f7eec7db 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -51,7 +51,7 @@ properties([ disableConcurrentBuilds() ]) // -- PIPELINE -stage('serlio-checkout'){ +stage('prepare'){ cepl.runParallel(taskGenSourceCheckout()) } @@ -73,7 +73,7 @@ Map taskGenSourceCheckout(){ papl.checkout(REPO, myBranch, REPO_CREDS) stash(name: SOURCE_STASH) } - tasks << cepl.generateTasks("checkout-and-stash-srl", taskSRL, srcCfgs) + tasks << cepl.generateTasks("prepare", taskSRL, srcCfgs) return tasks } From 9ac2a696f96402d8cdfab26f5d2abec80963a8c3 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 16:01:38 +0100 Subject: [PATCH 456/668] Cleanup: don't use a local variable for env.BRANCH_NAME --- Jenkinsfile | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index f7eec7db..7b8856de 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -47,7 +47,6 @@ properties([ disableConcurrentBuilds() ]) [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, maya: PrtAppPipelineLibrary.Dependencies.MAYA2022 ], [ os: cepl.CFG_OS_WIN10, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_VC1427, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, maya: PrtAppPipelineLibrary.Dependencies.MAYA2022 ], ] -@Field String myBranch = env.BRANCH_NAME // -- PIPELINE @@ -59,7 +58,7 @@ stage('serlio') { cepl.runParallel(getTasks(branchName)) } -papl.finalizeRun('serlio', myBranch) +papl.finalizeRun('serlio', env.BRANCH_NAME) // -- TASK GENERATORS @@ -70,7 +69,7 @@ Map taskGenSourceCheckout(){ def taskSRL = { cepl.cleanCurrentDir() final String localDir = 'srl' - papl.checkout(REPO, myBranch, REPO_CREDS) + papl.checkout(REPO, env.BRANCH_NAME, REPO_CREDS) stash(name: SOURCE_STASH) } tasks << cepl.generateTasks("prepare", taskSRL, srcCfgs) @@ -79,7 +78,7 @@ Map taskGenSourceCheckout(){ // entry point for embedded pipeline Map getTasks(String branchName = null) { - if (branchName) myBranch = branchName + if (branchName) env.BRANCH_NAME = branchName Map tasks = [:] tasks << taskGenSerlio() @@ -128,7 +127,7 @@ def taskBuildSerlio(cfg) { def classifierExtractor = { p -> return cepl.getArchiveClassifier(cfg) + '-' + cfg.grp } - papl.publish(appName, myBranch, artifactPattern, artifactVersion, cfg, classifierExtractor) + papl.publish(appName, env.BRANCH_NAME, artifactPattern, artifactVersion, cfg, classifierExtractor) } def taskBuildSerlioTests(cfg) { @@ -173,7 +172,7 @@ def taskBuildSerlioInstaller(cfg) { def classifierExtractor = { p -> return cepl.getArchiveClassifier(cfg) + '-' + cfg.grp } - papl.publish(appName, myBranch, artifactPattern, artifactVersion, cfg, classifierExtractor) + papl.publish(appName, env.BRANCH_NAME, artifactPattern, artifactVersion, cfg, classifierExtractor) } } } \ No newline at end of file From e9fbbb787dd55d7d1e8f6847f62aa75f862ae36b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 16:10:04 +0100 Subject: [PATCH 457/668] Cleanup: remove code remnants from embedded pipeline --- Jenkinsfile | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 7b8856de..e9eb12b8 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -55,7 +55,7 @@ stage('prepare'){ } stage('serlio') { - cepl.runParallel(getTasks(branchName)) + cepl.runParallel(getTasks()) } papl.finalizeRun('serlio', env.BRANCH_NAME) @@ -76,10 +76,7 @@ Map taskGenSourceCheckout(){ return tasks } -// entry point for embedded pipeline Map getTasks(String branchName = null) { - if (branchName) env.BRANCH_NAME = branchName - Map tasks = [:] tasks << taskGenSerlio() tasks << taskGenSerlioTests() From 5e08701c064f7aa1b51a004f1afd5e044c70b3a1 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 24 Feb 2022 16:17:39 +0100 Subject: [PATCH 458/668] Cleanup: add helper function for source checkout --- Jenkinsfile | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index e9eb12b8..81c563f5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -31,6 +31,8 @@ properties([ disableConcurrentBuilds() ]) @Field final String SOURCES = "serlio.git/src" @Field final String BUILD_TARGET = 'package' @Field final String SOURCE_STASH = 'serlio-src' + +@Field final List CHECKOUT_CONFIG = [ [ ba: psl.BA_CHECKOUT ] ] // TODO: abusing grp field to distinguish maya versions per task @Field final List CONFIGS = [ [ os: cepl.CFG_OS_RHEL7, bc: cepl.CFG_BC_REL, tc: cepl.CFG_TC_GCC93, cc: cepl.CFG_CC_OPT, arch: cepl.CFG_ARCH_X86_64, grp: 'maya2018', maya: PrtAppPipelineLibrary.Dependencies.MAYA2018 ], @@ -63,16 +65,8 @@ papl.finalizeRun('serlio', env.BRANCH_NAME) // -- TASK GENERATORS Map taskGenSourceCheckout(){ - final List srcCfgs = [ [ ba: psl.BA_CHECKOUT ] ] Map tasks = [:] - // checkout serlio.git - def taskSRL = { - cepl.cleanCurrentDir() - final String localDir = 'srl' - papl.checkout(REPO, env.BRANCH_NAME, REPO_CREDS) - stash(name: SOURCE_STASH) - } - tasks << cepl.generateTasks("prepare", taskSRL, srcCfgs) + tasks << taskGenSerlioSourceCheckout() return tasks } @@ -84,6 +78,10 @@ Map getTasks(String branchName = null) { return tasks } +Map taskGenSerlioSourceCheckout() { + return cepl.generateTasks('srl', this.&taskSourceCheckout, CHECKOUT_CONFIG) +} + Map taskGenSerlio() { return cepl.generateTasks('srl', this.&taskBuildSerlio, CONFIGS) } @@ -98,6 +96,12 @@ Map taskGenSerlioInstallers() { // -- TASK BUILDERS +def taskSourceCheckout(cfg) { + cepl.cleanCurrentDir() + papl.checkout(REPO, env.BRANCH_NAME, REPO_CREDS) + stash(name: SOURCE_STASH) +} + def taskBuildCMake(cfg, target){ final List deps = [ cfg.maya ] List defs = [ From 2d9566df690db1d3ace6c821085c0d80a31907e9 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 28 Feb 2022 09:10:51 +0100 Subject: [PATCH 459/668] Add cgaErrors to the problems list. --- src/serlio/modifiers/MayaCallbacks.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 8bbf592e..3b1e00ce 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -438,9 +438,10 @@ prt::Status MayaCallbacks::assetError(size_t /*isIndex*/, prt::CGAErrorLevel lev return prt::STATUS_OK; } -prt::Status MayaCallbacks::cgaError(size_t /*isIndex*/, int32_t /*shapeID*/, prt::CGAErrorLevel /*level*/, +prt::Status MayaCallbacks::cgaError(size_t /*isIndex*/, int32_t /*shapeID*/, prt::CGAErrorLevel level, int32_t /*methodId*/, int32_t /*pc*/, const wchar_t* message) { LOG_ERR << "CGA ERROR: " << message; + detectAndAppendCGACErrors(level, message, cgacErrors); return prt::STATUS_OK; } From ee4cf54375fabd24199db681f2649c669e33fe4b Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 28 Feb 2022 09:12:38 +0100 Subject: [PATCH 460/668] Use a map to store the error count --- src/serlio/modifiers/MayaCallbacks.cpp | 4 +++- src/serlio/modifiers/MayaCallbacks.h | 6 +++++- src/serlio/modifiers/PRTModifierAction.cpp | 10 +++++----- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 3b1e00ce..5c394aa1 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -420,7 +420,9 @@ void detectAndAppendCGACErrors(prt::CGAErrorLevel level, const wchar_t* message, std::wstring stringMessage = message; prtu::replaceCGACWithCEVersion(stringMessage); - cgacErrors.emplace_back(level, shouldBeLogged, stringMessage); + const auto [it, wasInserted] = cgacErrors.try_emplace({level, shouldBeLogged, stringMessage}, 1); + if (!wasInserted) + it->second++; } } } // namespace diff --git a/src/serlio/modifiers/MayaCallbacks.h b/src/serlio/modifiers/MayaCallbacks.h index f37c7b82..0d75bbb7 100644 --- a/src/serlio/modifiers/MayaCallbacks.h +++ b/src/serlio/modifiers/MayaCallbacks.h @@ -40,8 +40,12 @@ struct CGACError { CGACError(prt::CGAErrorLevel errorLevel, bool shouldBeLogged, const std::wstring& errorString) : errorLevel(errorLevel), shouldBeLogged(shouldBeLogged), errorString(errorString) {} + + bool operator<(const CGACError& other) const { + return errorString < other.errorString; + } }; -using CGACErrors = std::vector; +using CGACErrors = std::map; class MayaCallbacks : public IMayaCallbacks { public: diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 399476f7..fe8ce696 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -330,7 +330,7 @@ short getDefaultEnumIdx(const prt::Annotation* annot, const PRTEnumDefaultValue& } bool cgacProblemsHaveErrors(CGACErrors errorList) { - for (const auto& error : errorList) { + for (const auto& [error, count] : errorList) { if (error.errorLevel == prt::CGAErrorLevel::CGAERROR) return true; } @@ -338,7 +338,7 @@ bool cgacProblemsHaveErrors(CGACErrors errorList) { } bool cgacProblemsShouldBeLogged(CGACErrors errorList) { - for (const auto& error : errorList) { + for (const auto& [error, count] : errorList) { if (error.shouldBeLogged) return true; } @@ -347,11 +347,11 @@ bool cgacProblemsShouldBeLogged(CGACErrors errorList) { MString cgacProblemsToString(CGACErrors errorList) { MString errorString; - for (const auto& error : errorList) { + for (const auto& [error, count] : errorList) { if (errorString.length() > 0) errorString += "\n"; - - errorString += (error.errorLevel == prt::CGAErrorLevel::CGAERROR) ? "Error: " : "Warning: "; + errorString += count; + errorString += (error.errorLevel == prt::CGAErrorLevel::CGAERROR) ? " Error: " : " Warning: "; errorString += error.errorString.c_str(); } return errorString; From 96d11ac324ab4f42eacf8b08c49088269f4505b1 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 28 Feb 2022 09:13:32 +0100 Subject: [PATCH 461/668] Cleanup: only replace CGAC version with CE version in case of CGAC version error --- src/serlio/modifiers/MayaCallbacks.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/serlio/modifiers/MayaCallbacks.cpp b/src/serlio/modifiers/MayaCallbacks.cpp index 5c394aa1..fd66442b 100644 --- a/src/serlio/modifiers/MayaCallbacks.cpp +++ b/src/serlio/modifiers/MayaCallbacks.cpp @@ -414,12 +414,15 @@ std::filesystem::path getAssetDir() { } void detectAndAppendCGACErrors(prt::CGAErrorLevel level, const wchar_t* message, CGACErrors& cgacErrors) { - if (message != nullptr && (std::wcsstr(message, L"CGAC version") || std::wcsstr(message, L"Non-recognized builtin method"))) { - const bool shouldBeLogged = - (std::wcsstr(message, L"newer than current") || (level == prt::CGAErrorLevel::CGAERROR)); - + if (message != nullptr) { + bool shouldBeLogged = (level == prt::CGAErrorLevel::CGAERROR); std::wstring stringMessage = message; - prtu::replaceCGACWithCEVersion(stringMessage); + + if (std::wcsstr(message, L"CGAC version")) { + shouldBeLogged = shouldBeLogged || (std::wcsstr(message, L"newer than current")); + prtu::replaceCGACWithCEVersion(stringMessage); + } + const auto [it, wasInserted] = cgacErrors.try_emplace({level, shouldBeLogged, stringMessage}, 1); if (!wasInserted) it->second++; From d1eed98ed7cf40afc228ec3e6ef3f9ea83a6d7b5 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 28 Feb 2022 09:14:22 +0100 Subject: [PATCH 462/668] Show problems view window, when pressing error Icon --- src/serlio/scripts/AEserlioTemplate.mel | 51 ++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index c136cf04..4e1863dc 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -94,13 +94,54 @@ global proc prtShowFileDialog(string $attr, string $filters) { } } -global proc updateCGACProblems(string $node){ +proc string getProblemString(string $node){ string $attr = $node + ".CGAC_Problems"; if (!prtAttributeExists($attr)) - return; + return ""; + string $problemString = `getAttr($attr)`; + return $problemString; +} + +global proc prtShowProblemView(string $node){ + $cgacProblems = getProblemString($node); + + string $errors[] = stringToStringArray($cgacProblems, "\n"); + string $window = `window -title "Serlio: Problems" + -resizeToFitChildren true + -iconName "Serlio" + -widthHeight 500 210`; + string $scrollLayout = `scrollLayout + -horizontalScrollBarThickness 16 + -verticalScrollBarThickness 16 + -childResizable true`; + rowColumnLayout + -numberOfColumns 3 + -adjustableColumn 3 + -columnSpacing 1 5 + -columnSpacing 2 5 + -columnSpacing 3 5; + int $index; + for ($error in $errors) { + int $displayErrorIcon = size(`match "Error:" $error`) > 0; + string $icon = "caution.png"; + if ($displayErrorIcon) + $icon = "error.png"; + + string $errorCount = `match "[0-9]+" $error`; + string $stringAfterColon = `match ":.*" $error`; + string $errorString = `match "[^: ].*" $stringAfterColon`; + + string $serlioImage = `iconTextStaticLabel -h 32 -w 28 -st "iconOnly" -i $icon`; + text -label ("count: " + $errorCount); + textField -bgc 0.15 0.15 0.15 -font "fixedWidthFont" -text $errorString -editable false -ip 1; + } + showWindow $window; +} - string $cgacProblems = `getAttr ($attr)`; +global proc updateCGACProblems(string $node){ + string $cgacProblems = getProblemString($node); + int $isVisible = ($cgacProblems != ""); int $hasProblems = (size($cgacProblems) > 0); string $control = prtControlName($node) + "_CGAC_Problems"; @@ -111,7 +152,7 @@ global proc updateCGACProblems(string $node){ if ($displayErrorIcon) $icon = "error.png"; - iconTextStaticLabel -e -ann $cgacProblems -i $icon $control; + symbolButton -e -visible $isVisible -ann $cgacProblems -i $icon $control; } global proc prtFileBrowse(string $attr, string $varname, string $filters){ @@ -131,7 +172,7 @@ global proc prtFileBrowse(string $attr, string $varname, string $filters){ string $node = prtGetNodeName($attr); - iconTextStaticLabel -st "iconOnly" -i "caution.png" -l "error" -visible false ($node + "_CGAC_Problems"); + symbolButton -i "caution.png" -visible false -c ("prtShowProblemView(\"" + $node + "\")") ($node + "_CGAC_Problems"); updateCGACProblems($node); string $changeCommand = "updateCGACProblems " + $node; From 8efa592cb461e326cba5a6ecede14d602a389acd Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 28 Feb 2022 09:27:55 +0100 Subject: [PATCH 463/668] Remove problems from annotation in problem/warning icon --- src/serlio/scripts/AEserlioTemplate.mel | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 4e1863dc..2d51e0bb 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -139,7 +139,7 @@ global proc prtShowProblemView(string $node){ showWindow $window; } -global proc updateCGACProblems(string $node){ +global proc updateCGACProblemIcon(string $node){ string $cgacProblems = getProblemString($node); int $isVisible = ($cgacProblems != ""); int $hasProblems = (size($cgacProblems) > 0); @@ -152,7 +152,7 @@ global proc updateCGACProblems(string $node){ if ($displayErrorIcon) $icon = "error.png"; - symbolButton -e -visible $isVisible -ann $cgacProblems -i $icon $control; + symbolButton -e -visible $isVisible -i $icon $control; } global proc prtFileBrowse(string $attr, string $varname, string $filters){ @@ -172,10 +172,10 @@ global proc prtFileBrowse(string $attr, string $varname, string $filters){ string $node = prtGetNodeName($attr); - symbolButton -i "caution.png" -visible false -c ("prtShowProblemView(\"" + $node + "\")") ($node + "_CGAC_Problems"); - updateCGACProblems($node); + symbolButton -ann "show problems" -i "caution.png" -visible false -c ("prtShowProblemView(\"" + $node + "\")") ($node + "_CGAC_Problems"); + updateCGACProblemIcon($node); - string $changeCommand = "updateCGACProblems " + $node; + string $changeCommand = "updateCGACProblemIcon " + $node; scriptJob -rp -p $control -ac ($node + ".CGAC_Problems") $changeCommand; symbolButton -image "navButtonBrowse.xpm" -c ("prtShowFileDialog(\"" + $attr + "\",\"" + $filters + "\" )") ($control + "_browse"); From 6e1266174894b659fd2e7693306bb162c1e10b50 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 28 Feb 2022 09:47:18 +0100 Subject: [PATCH 464/668] Cleanup: make helper function for logging errors.. Also only log required error/warning instead of all issues --- src/serlio/modifiers/PRTModifierAction.cpp | 27 +++++++--------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index fe8ce696..a406a7a6 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -329,18 +329,14 @@ short getDefaultEnumIdx(const prt::Annotation* annot, const PRTEnumDefaultValue& return 0; } -bool cgacProblemsHaveErrors(CGACErrors errorList) { +bool cgacLogProblems(CGACErrors errorList) { for (const auto& [error, count] : errorList) { - if (error.errorLevel == prt::CGAErrorLevel::CGAERROR) - return true; - } - return false; -} - -bool cgacProblemsShouldBeLogged(CGACErrors errorList) { - for (const auto& [error, count] : errorList) { - if (error.shouldBeLogged) - return true; + if (error.shouldBeLogged) { + if (error.errorLevel == prt::CGAErrorLevel::CGAERROR) + MGlobal::displayError(error.errorString.c_str()); + else + MGlobal::displayWarning(error.errorString.c_str()); + } } return false; } @@ -654,14 +650,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacProble MString cgacErrorString = cgacProblemsToString(mCGACProblems); if (cgacProblemData.asString() != cgacErrorString) { cgacProblemData.setString(cgacErrorString); - if (cgacProblemsShouldBeLogged(mCGACProblems)) { - if (cgacProblemsHaveErrors(mCGACProblems)) { - MGlobal::displayError(cgacErrorString); - } - else { - MGlobal::displayWarning(cgacErrorString); - } - } + cgacLogProblems(mCGACProblems); } iterateThroughAttributesAndApply(node, mRuleAttributes, updateUIFromAttributes); From cad6115a19e7b2f039298830b97891bbb95737c7 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 28 Feb 2022 12:41:59 +0100 Subject: [PATCH 465/668] Add utility function for comparing MStringArrays --- src/serlio/utils/MayaUtilities.cpp | 10 ++++++++++ src/serlio/utils/MayaUtilities.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/src/serlio/utils/MayaUtilities.cpp b/src/serlio/utils/MayaUtilities.cpp index cbecdcc5..ec570ca4 100644 --- a/src/serlio/utils/MayaUtilities.cpp +++ b/src/serlio/utils/MayaUtilities.cpp @@ -44,4 +44,14 @@ std::filesystem::path getWorkspaceRoot(MStatus& status) { return {}; } } +bool mStringArraysAreEqual(const MStringArray& lhs, const MStringArray& rhs) { + if (lhs.length() != rhs.length()) + return false; + uint32_t index; + for (uint32_t index = 0; index < lhs.length(); index++) { + if (lhs[index] != rhs[index]) + return false; + } + return true; +} } // namespace mu \ No newline at end of file diff --git a/src/serlio/utils/MayaUtilities.h b/src/serlio/utils/MayaUtilities.h index 5a1f6ed7..afdd8929 100644 --- a/src/serlio/utils/MayaUtilities.h +++ b/src/serlio/utils/MayaUtilities.h @@ -48,4 +48,6 @@ class NamedType { }; std::filesystem::path getWorkspaceRoot(MStatus& status); + +bool mStringArraysAreEqual(const MStringArray& lhs, const MStringArray& rhs); } // namespace mu \ No newline at end of file From 811a344584116ec3c536d4ac4a8602efb93528a5 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 28 Feb 2022 12:54:22 +0100 Subject: [PATCH 466/668] Cleanup: Use MStringArrays to store problems/errors.. and avoid string parsing --- src/serlio/modifiers/PRTModifierAction.cpp | 39 ++++++++++++-------- src/serlio/modifiers/PRTModifierNode.cpp | 6 ++-- src/serlio/scripts/AEserlioTemplate.mel | 42 ++++++++++++---------- 3 files changed, 53 insertions(+), 34 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index a406a7a6..960d542f 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -33,6 +33,7 @@ #include "maya/MFnMesh.h" #include "maya/MFnNumericAttribute.h" #include "maya/MFnStringData.h" +#include "maya/MFnStringArrayData.h" #include "maya/MFnTypedAttribute.h" #include "maya/MGlobal.h" @@ -341,16 +342,31 @@ bool cgacLogProblems(CGACErrors errorList) { return false; } -MString cgacProblemsToString(CGACErrors errorList) { - MString errorString; +MStringArray cgacProblemsToStringArray(CGACErrors errorList) { + MStringArray errorStringArrray; for (const auto& [error, count] : errorList) { - if (errorString.length() > 0) - errorString += "\n"; - errorString += count; - errorString += (error.errorLevel == prt::CGAErrorLevel::CGAERROR) ? " Error: " : " Warning: "; - errorString += error.errorString.c_str(); + errorStringArrray.append(std::to_wstring(count).c_str()); + const MString errorLevel = (error.errorLevel == prt::CGAErrorLevel::CGAERROR) ? "Error" : "Warning"; + errorStringArrray.append(errorLevel); + errorStringArrray.append(error.errorString.c_str()); + } + return errorStringArrray; +} + +void updateCgacProblemData(const CGACErrors& cgacProblems, MDataHandle& cgacProblemData) { + MStringArray newCgacErrorStringArray = cgacProblemsToStringArray(cgacProblems); + + MObject errorDataObject = cgacProblemData.data(); + MFnStringArrayData stringArrayData(errorDataObject); + MStringArray oldCgacErrorStringArray = stringArrayData.array(); + + if (!mu::mStringArraysAreEqual(oldCgacErrorStringArray, newCgacErrorStringArray)) { + cgacLogProblems(cgacProblems); + + MFnStringArrayData newStringArrayData; + MObject newErrorDataObject = newStringArrayData.create(newCgacErrorStringArray); + cgacProblemData.setMObject(newErrorDataObject); } - return errorString; } template @@ -647,12 +663,7 @@ MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacProble } }; - MString cgacErrorString = cgacProblemsToString(mCGACProblems); - if (cgacProblemData.asString() != cgacErrorString) { - cgacProblemData.setString(cgacErrorString); - cgacLogProblems(mCGACProblems); - } - + updateCgacProblemData(mCGACProblems, cgacProblemData); iterateThroughAttributesAndApply(node, mRuleAttributes, updateUIFromAttributes); return MStatus::kSuccess; diff --git a/src/serlio/modifiers/PRTModifierNode.cpp b/src/serlio/modifiers/PRTModifierNode.cpp index 9c4de1d9..63cfa6ac 100644 --- a/src/serlio/modifiers/PRTModifierNode.cpp +++ b/src/serlio/modifiers/PRTModifierNode.cpp @@ -27,6 +27,7 @@ #include "maya/MFnMeshData.h" #include "maya/MFnNumericAttribute.h" #include "maya/MFnStringData.h" +#include "maya/MFnStringArrayData.h" #include "maya/MFnTypedAttribute.h" #define MCheckStatus(status, message) \ @@ -181,6 +182,7 @@ MStatus PRTModifierNode::initialize() MStatus stat2; MStatus stat; MFnStringData stringData; + MFnStringArrayData stringArrayData; MFnTypedAttribute fAttr; rulePkg = fAttr.create(NAME_RULE_PKG, "rulePkg", MFnData::kString, stringData.create(&stat2), &stat); @@ -213,8 +215,8 @@ MStatus PRTModifierNode::initialize() MCHECK(fAttr.setConnectable(false)); MCHECK(addAttribute(currentRulePkg)); - cgacProblems = fAttr.create(CGAC_PROBLEMS, "cgacErrors", MFnData::kString, - stringData.create(&stat2), &stat); + cgacProblems = + fAttr.create(CGAC_PROBLEMS, "cgacErrors", MFnData::kStringArray, stringArrayData.create(&stat2), &stat); MCHECK(stat2); MCHECK(stat); MCHECK(fAttr.setHidden(true)); diff --git a/src/serlio/scripts/AEserlioTemplate.mel b/src/serlio/scripts/AEserlioTemplate.mel index 2d51e0bb..59390fe3 100644 --- a/src/serlio/scripts/AEserlioTemplate.mel +++ b/src/serlio/scripts/AEserlioTemplate.mel @@ -94,19 +94,27 @@ global proc prtShowFileDialog(string $attr, string $filters) { } } -proc string getProblemString(string $node){ +proc string[] getProblems(string $node){ string $attr = $node + ".CGAC_Problems"; if (!prtAttributeExists($attr)) - return ""; - string $problemString = `getAttr($attr)`; - return $problemString; + return {}; + + string $cgacProblems[] = `getAttr($attr)`; + return $cgacProblems; +} + +proc int problemsHaveErrors(string $cgacProblems[]){ + for($i = 0; $i < size($cgacProblems); $i += 3){ + if($cgacProblems[$i + 1] == "Error") + return true; + } + return false; } global proc prtShowProblemView(string $node){ - $cgacProblems = getProblemString($node); + string $cgacProblems[] = getProblems($node); - string $errors[] = stringToStringArray($cgacProblems, "\n"); string $window = `window -title "Serlio: Problems" -resizeToFitChildren true -iconName "Serlio" @@ -122,15 +130,15 @@ global proc prtShowProblemView(string $node){ -columnSpacing 2 5 -columnSpacing 3 5; int $index; - for ($error in $errors) { - int $displayErrorIcon = size(`match "Error:" $error`) > 0; + for ($i = 0; $i < size($cgacProblems); $i += 3) { + string $errorCount = $cgacProblems[$i]; + string $errorType = $cgacProblems[$i + 1]; + string $errorString = $cgacProblems[$i + 2]; + + int $displayErrorIcon = ($errorType == "Error"); string $icon = "caution.png"; if ($displayErrorIcon) $icon = "error.png"; - - string $errorCount = `match "[0-9]+" $error`; - string $stringAfterColon = `match ":.*" $error`; - string $errorString = `match "[^: ].*" $stringAfterColon`; string $serlioImage = `iconTextStaticLabel -h 32 -w 28 -st "iconOnly" -i $icon`; text -label ("count: " + $errorCount); @@ -140,19 +148,17 @@ global proc prtShowProblemView(string $node){ } global proc updateCGACProblemIcon(string $node){ - string $cgacProblems = getProblemString($node); - int $isVisible = ($cgacProblems != ""); + string $cgacProblems[] = getProblems($node); int $hasProblems = (size($cgacProblems) > 0); + string $control = prtControlName($node) + "_CGAC_Problems"; - prtSetVisibility($hasProblems, $control); - - int $displayErrorIcon = size(`match "Error:" $cgacProblems`) > 0; + int $displayErrorIcon = problemsHaveErrors($cgacProblems); string $icon = "caution.png"; if ($displayErrorIcon) $icon = "error.png"; - symbolButton -e -visible $isVisible -i $icon $control; + symbolButton -e -visible $hasProblems -i $icon $control; } global proc prtFileBrowse(string $attr, string $varname, string $filters){ From e7375f1ca34803ae3f80df046c091d25122551a1 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 28 Feb 2022 13:03:33 +0100 Subject: [PATCH 467/668] Make sure errors are sorted in before of warnings This will make errors appear before warnings in problem view --- src/serlio/modifiers/MayaCallbacks.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/serlio/modifiers/MayaCallbacks.h b/src/serlio/modifiers/MayaCallbacks.h index 0d75bbb7..85940630 100644 --- a/src/serlio/modifiers/MayaCallbacks.h +++ b/src/serlio/modifiers/MayaCallbacks.h @@ -42,6 +42,10 @@ struct CGACError { : errorLevel(errorLevel), shouldBeLogged(shouldBeLogged), errorString(errorString) {} bool operator<(const CGACError& other) const { + // make sure errors are in front + if (errorLevel != other.errorLevel) + return errorLevel < other.errorLevel; + // sort alphabetically if both have the same error level return errorString < other.errorString; } }; From d17a6ba0938ee5caef652cbe8c168749e785a2e3 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 28 Feb 2022 13:16:02 +0100 Subject: [PATCH 468/668] Rename window title for about page in serlio. --- src/serlio/scripts/serlioCreateUI.mel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index c3546e38..80993717 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -170,7 +170,7 @@ global proc createAboutDialog(){ "Redistribution or web service offerings are not allowed unless expressly permitted.\n" + "Serlio is under the same license as the included CityEngine SDK."; - string $window = `window -title "Serlio: About" + string $window = `window -title "About Serlio" -resizeToFitChildren true -iconName "Serlio" -widthHeight 500 210`; From 02a3eb65ed54724b41d950d7c763401d6fa37848 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Mon, 28 Feb 2022 16:06:10 +0100 Subject: [PATCH 469/668] Add Helper function to retrieve filecontent from a webpage --- src/serlio/utils/MELScriptBuilder.cpp | 8 ++++++++ src/serlio/utils/MELScriptBuilder.h | 1 + src/serlio/utils/MayaUtilities.cpp | 15 +++++++++++++++ src/serlio/utils/MayaUtilities.h | 1 + 4 files changed, 25 insertions(+) diff --git a/src/serlio/utils/MELScriptBuilder.cpp b/src/serlio/utils/MELScriptBuilder.cpp index 70c195e8..720229be 100644 --- a/src/serlio/utils/MELScriptBuilder.cpp +++ b/src/serlio/utils/MELScriptBuilder.cpp @@ -160,6 +160,14 @@ void MELScriptBuilder::getWorkspaceDir() { commandStream << L"workspace -q -rd;\n"; } +void MELScriptBuilder::getStringFromURL(const std::wstring& url) { + python(L"import urllib.request"); + python(L"url = \\\"" + url + L"\\\""); + python(L"response = urllib.request.urlopen(url)"); + python(L"import urllib.request"); + python(L"response.read()"); +} + MStatus MELScriptBuilder::executeSync(std::wstring& output) { MStatus status; MString result = diff --git a/src/serlio/utils/MELScriptBuilder.h b/src/serlio/utils/MELScriptBuilder.h index 6bebd6b0..6c8d9f1b 100644 --- a/src/serlio/utils/MELScriptBuilder.h +++ b/src/serlio/utils/MELScriptBuilder.h @@ -84,6 +84,7 @@ class MELScriptBuilder { void setUndoState(bool undoState); void getWorkspaceDir(); + void getStringFromURL(const std::wstring& url); void python(const std::wstring& pythonCmd); void addCmdLine(const std::wstring& line); diff --git a/src/serlio/utils/MayaUtilities.cpp b/src/serlio/utils/MayaUtilities.cpp index cbecdcc5..5f93677e 100644 --- a/src/serlio/utils/MayaUtilities.cpp +++ b/src/serlio/utils/MayaUtilities.cpp @@ -44,4 +44,19 @@ std::filesystem::path getWorkspaceRoot(MStatus& status) { return {}; } } + +std::wstring getStringFromURL(const std::wstring& url, MStatus& status) { + MELScriptBuilder scriptBuilder; + scriptBuilder.getStringFromURL(url); + + std::wstring output; + status = scriptBuilder.executeSync(output); + + if (status == MS::kSuccess) { + return output; + } + else { + return {}; + } +} } // namespace mu \ No newline at end of file diff --git a/src/serlio/utils/MayaUtilities.h b/src/serlio/utils/MayaUtilities.h index 5a1f6ed7..80798a08 100644 --- a/src/serlio/utils/MayaUtilities.h +++ b/src/serlio/utils/MayaUtilities.h @@ -48,4 +48,5 @@ class NamedType { }; std::filesystem::path getWorkspaceRoot(MStatus& status); +std::wstring getStringFromURL(const std::wstring& url, MStatus& status); } // namespace mu \ No newline at end of file From 0624b232bacd6382c5beedf054c082fb81296a1c Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 1 Mar 2022 13:44:58 +0100 Subject: [PATCH 470/668] Add project name "serlio" to defines --- src/common.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common.cmake b/src/common.cmake index 5d7e925d..0b85899f 100644 --- a/src/common.cmake +++ b/src/common.cmake @@ -25,6 +25,7 @@ function(set_common_target_definitions TGT) set_target_properties(${TGT} PROPERTIES CXX_STANDARD 17) target_compile_definitions(${TGT} PRIVATE -DSRL_VERSION=\"${SRL_VERSION}\" # quoted to use it as string literal + -DSRL_PROJECT_NAME=\"${SRL_PROJECT_NAME}\" -DPRT_VERSION_MAJOR=${PRT_VERSION_MAJOR} -DPRT_VERSION_MINOR=${PRT_VERSION_MINOR}) endfunction() From ba7245cb4ff555d2101ef008794a2a9df736e7c6 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 1 Mar 2022 13:46:26 +0100 Subject: [PATCH 471/668] Add helper function to register Help pages as string resource --- src/serlio/utils/MayaUtilities.cpp | 57 ++++++++++++++++++++++++++++++ src/serlio/utils/MayaUtilities.h | 3 ++ 2 files changed, 60 insertions(+) diff --git a/src/serlio/utils/MayaUtilities.cpp b/src/serlio/utils/MayaUtilities.cpp index 5f93677e..d3978a7b 100644 --- a/src/serlio/utils/MayaUtilities.cpp +++ b/src/serlio/utils/MayaUtilities.cpp @@ -1,7 +1,46 @@ #include "utils/MayaUtilities.h" #include "utils/MELScriptBuilder.h" +#include "maya/MStringResource.h" +#include "maya/MStringResourceId.h" + +#include #include +#include + +namespace { +constexpr const wchar_t KEY_URL_SEPARATOR = L'='; +const std::wstring INDIRECTION_URL = L"https://raw.githubusercontent.com/Esri/serlio/indirection_urls/urls.txt"; + +const std::map fallbackKeyToUrlMap = { + {"SERLIO_HOME", "https://esri.github.io/cityengine/serlio"}, + {"CGA_REFERENCE", "https://doc.arcgis.com/en/cityengine/latest/cga/cityengine-cga-introduction.htm"}, + {"RPK_MANUAL", "https://doc.arcgis.com/en/cityengine/latest/help/help-rule-package.htm"}}; + +std::map getKeyToUrlMap() { + MStatus status; + const std::wstring& indirectionWString = mu::getStringFromURL(INDIRECTION_URL, status); + if (status != MStatus::kSuccess) + return {}; + + const std::string& indirectionString = prtu::toUTF8FromUTF16(indirectionWString); + std::map keyToUrlMap; + + std::istringstream wiss(indirectionString); + for (std::string line; std::getline(wiss, line);) { + size_t idx = line.find_first_of(KEY_URL_SEPARATOR); + if ((idx == std::string::npos) || ((idx + 1) >= line.length())) + continue; + + const std::string key = line.substr(0, idx); + const std::string url = line.substr(idx + 1); + + keyToUrlMap[key] = url; + } + return keyToUrlMap; +} + +} // namespace namespace mu { @@ -59,4 +98,22 @@ std::wstring getStringFromURL(const std::wstring& url, MStatus& status) { return {}; } } + +MStatus registerMStringResources() { + std::map keyToUrlMap = getKeyToUrlMap(); + + for (const auto& [key, url] : fallbackKeyToUrlMap) { + auto it = keyToUrlMap.find(key); + if (it == keyToUrlMap.end()) { + const MStringResourceId SerlioHomeURL(SRL_PROJECT_NAME, key.c_str(), url.c_str()); + MStringResource::registerString(SerlioHomeURL); + } + else { + const MStringResourceId SerlioHomeURL(SRL_PROJECT_NAME, key.c_str(), it->second.c_str()); + MStringResource::registerString(SerlioHomeURL); + } + } + + return MS::kSuccess; +} } // namespace mu \ No newline at end of file diff --git a/src/serlio/utils/MayaUtilities.h b/src/serlio/utils/MayaUtilities.h index 80798a08..aa981ac8 100644 --- a/src/serlio/utils/MayaUtilities.h +++ b/src/serlio/utils/MayaUtilities.h @@ -48,5 +48,8 @@ class NamedType { }; std::filesystem::path getWorkspaceRoot(MStatus& status); + std::wstring getStringFromURL(const std::wstring& url, MStatus& status); + +MStatus registerMStringResources(); } // namespace mu \ No newline at end of file From 3856f5c69e4817f4e8e056e6dad00c408de72f23 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 1 Mar 2022 13:47:22 +0100 Subject: [PATCH 472/668] Register string resources --- src/serlio/serlioPlugin.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/serlio/serlioPlugin.cpp b/src/serlio/serlioPlugin.cpp index 1ba16440..8f69afcb 100644 --- a/src/serlio/serlioPlugin.cpp +++ b/src/serlio/serlioPlugin.cpp @@ -89,6 +89,8 @@ MStatus initializePlugin(MObject obj) { MCHECK(plugin.registerUI(MEL_PROC_CREATE_UI, MEL_PROC_DELETE_UI)); + MCHECK(plugin.registerUIStrings(mu::registerMStringResources, "")); + return MStatus::kSuccess; } From f47c419a3e1d3dddf2d5f440605e0ba4214e18db Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 1 Mar 2022 13:48:11 +0100 Subject: [PATCH 473/668] Use string resource that is retrieved from github.. instead of hardcoded urls for helper pages --- src/serlio/scripts/serlioCreateUI.mel | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/serlio/scripts/serlioCreateUI.mel b/src/serlio/scripts/serlioCreateUI.mel index c3546e38..9f3de527 100644 --- a/src/serlio/scripts/serlioCreateUI.mel +++ b/src/serlio/scripts/serlioCreateUI.mel @@ -151,9 +151,9 @@ global proc createMaterialNode(string $materialType) { } global proc createHelpMenuItems(){ - string $serlioWebsite = "https://esri.github.io/cityengine/serlio"; - string $cgaReference = "https://doc.arcgis.com/en/cityengine/latest/cga/cityengine-cga-introduction.htm"; - string $rulePackageManual = "https://doc.arcgis.com/en/cityengine/latest/help/help-rule-package.htm"; + string $serlioWebsite = getPluginResource("serlio", "SERLIO_HOME"); + string $cgaReference = getPluginResource("serlio", "CGA_REFERENCE"); + string $rulePackageManual = getPluginResource("serlio", "RPK_MANUAL"); menuItem -label "Serlio Website" -c ("showHelp -absolute \"" + $serlioWebsite + "\""); menuItem -label "CityEngine CGA Reference" -c ("showHelp -absolute \"" + $cgaReference + "\""); From 81c3af27704904f1b855d8f94484a09ab464d8fa Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 1 Mar 2022 14:40:21 +0100 Subject: [PATCH 474/668] Cleanup: remove duplicate import --- src/serlio/utils/MELScriptBuilder.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/serlio/utils/MELScriptBuilder.cpp b/src/serlio/utils/MELScriptBuilder.cpp index 720229be..acf1941b 100644 --- a/src/serlio/utils/MELScriptBuilder.cpp +++ b/src/serlio/utils/MELScriptBuilder.cpp @@ -164,7 +164,6 @@ void MELScriptBuilder::getStringFromURL(const std::wstring& url) { python(L"import urllib.request"); python(L"url = \\\"" + url + L"\\\""); python(L"response = urllib.request.urlopen(url)"); - python(L"import urllib.request"); python(L"response.read()"); } From 6b0a32ae7f2284453df3565bb5743ace8b7a7ab4 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 1 Mar 2022 15:19:35 +0100 Subject: [PATCH 475/668] Make compatible with both python 2 and 3 --- src/serlio/utils/MELScriptBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/utils/MELScriptBuilder.cpp b/src/serlio/utils/MELScriptBuilder.cpp index acf1941b..1bd0d8fc 100644 --- a/src/serlio/utils/MELScriptBuilder.cpp +++ b/src/serlio/utils/MELScriptBuilder.cpp @@ -161,7 +161,7 @@ void MELScriptBuilder::getWorkspaceDir() { } void MELScriptBuilder::getStringFromURL(const std::wstring& url) { - python(L"import urllib.request"); + python(L"from six.moves import urllib"); python(L"url = \\\"" + url + L"\\\""); python(L"response = urllib.request.urlopen(url)"); python(L"response.read()"); From aaba4d234bd57bcd9d41aed5a04c69c64fc3aec3 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Tue, 1 Mar 2022 15:19:55 +0100 Subject: [PATCH 476/668] Add timeout of 3 seconds --- src/serlio/utils/MELScriptBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/utils/MELScriptBuilder.cpp b/src/serlio/utils/MELScriptBuilder.cpp index 1bd0d8fc..6a41ccdc 100644 --- a/src/serlio/utils/MELScriptBuilder.cpp +++ b/src/serlio/utils/MELScriptBuilder.cpp @@ -163,7 +163,7 @@ void MELScriptBuilder::getWorkspaceDir() { void MELScriptBuilder::getStringFromURL(const std::wstring& url) { python(L"from six.moves import urllib"); python(L"url = \\\"" + url + L"\\\""); - python(L"response = urllib.request.urlopen(url)"); + python(L"response = urllib.request.urlopen(url, timeout=3)"); python(L"response.read()"); } From 135db60d9f66460fa4b6d8ea6720101a0a8d8e60 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 2 Mar 2022 11:35:25 +0100 Subject: [PATCH 477/668] Improve attribute readability by adding "_" after PRT prefix --- src/serlio/modifiers/RuleAttributes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/RuleAttributes.cpp b/src/serlio/modifiers/RuleAttributes.cpp index e1bca3ea..b9cfafea 100644 --- a/src/serlio/modifiers/RuleAttributes.cpp +++ b/src/serlio/modifiers/RuleAttributes.cpp @@ -35,7 +35,7 @@ namespace { constexpr bool DBG = false; -constexpr const wchar_t* PRT_ATTR_FULL_NAME_PREFIX = L"PRT"; +constexpr const wchar_t* PRT_ATTR_FULL_NAME_PREFIX = L"PRT_"; const std::wstring MAYA_COMPATIBLE_CHARS = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; std::wstring cleanForMaya(const std::wstring& name) { From 980d47adecb0e18d0c6be6b413d3496331f528a2 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 2 Mar 2022 11:35:57 +0100 Subject: [PATCH 478/668] Cleanup: add "_" to maya compatible chars --- src/serlio/modifiers/RuleAttributes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/RuleAttributes.cpp b/src/serlio/modifiers/RuleAttributes.cpp index b9cfafea..cfe50fbc 100644 --- a/src/serlio/modifiers/RuleAttributes.cpp +++ b/src/serlio/modifiers/RuleAttributes.cpp @@ -37,7 +37,7 @@ constexpr bool DBG = false; constexpr const wchar_t* PRT_ATTR_FULL_NAME_PREFIX = L"PRT_"; -const std::wstring MAYA_COMPATIBLE_CHARS = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; +const std::wstring MAYA_COMPATIBLE_CHARS = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; std::wstring cleanForMaya(const std::wstring& name) { auto r = name; replace_all_not_of(r, MAYA_COMPATIBLE_CHARS); From 4d4bb1851dd8adac0c3a0f8a644a99da06f86686 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Wed, 2 Mar 2022 11:37:34 +0100 Subject: [PATCH 479/668] Make maya names unique by adding duplicate count suffix --- src/serlio/modifiers/RuleAttributes.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/serlio/modifiers/RuleAttributes.cpp b/src/serlio/modifiers/RuleAttributes.cpp index cfe50fbc..1a52a452 100644 --- a/src/serlio/modifiers/RuleAttributes.cpp +++ b/src/serlio/modifiers/RuleAttributes.cpp @@ -56,6 +56,13 @@ std::wstring getNiceName(const std::wstring& fqAttrName) { return cleanForMaya(prtu::removeImport(prtu::removeStyle(fqAttrName))); } +std::wstring getDuplicateCountSuffix(const std::wstring& mayaName, std::map& mayaNameDuplicateCountMap) { + auto [iterator, isFirstEntry] = mayaNameDuplicateCountMap.try_emplace(mayaName, 0); + if (!isFirstEntry) + iterator->second++; + return L"_" + std::to_wstring(iterator->second); +} + } // namespace std::map getImportOrderMap(const prt::RuleFileInfo* ruleFileInfo) { @@ -107,6 +114,8 @@ RuleAttributeSet getRuleAttributes(const std::wstring& ruleFile, const prt::Rule const std::map importOrderMap = getImportOrderMap(ruleFileInfo); + std::map mayaNameDuplicateCountMap; + for (size_t i = 0; i < ruleFileInfo->getNumAttributes(); i++) { const prt::RuleFileInfo::Entry* attr = ruleFileInfo->getAttribute(i); @@ -115,11 +124,15 @@ RuleAttributeSet getRuleAttributes(const std::wstring& ruleFile, const prt::Rule RuleAttribute p; p.fqName = attr->getName(); - p.mayaBriefName = getBriefName(p.fqName); - p.mayaFullName = getFullName(p.fqName); p.mayaNiceName = getNiceName(p.fqName); p.mType = attr->getReturnType(); + //make sure maya names are unique + const std::wstring mayaBriefName = getBriefName(p.fqName); + const std::wstring mayaFullName = getFullName(p.fqName); + p.mayaBriefName = mayaBriefName + getDuplicateCountSuffix(mayaBriefName, mayaNameDuplicateCountMap); + p.mayaFullName = mayaFullName + getDuplicateCountSuffix(mayaFullName, mayaNameDuplicateCountMap); + // TODO: is this correct? import name != rule file name std::wstring ruleName = p.fqName; size_t idxStyle = ruleName.find(L'$'); From 9b2d04c4527b37cf799b9f2bbf0cce65fb2320a2 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 3 Mar 2022 16:06:00 +0100 Subject: [PATCH 480/668] Add helper function in MaterialUtils for resetting material --- src/serlio/materials/MaterialUtils.cpp | 10 ++++++++++ src/serlio/materials/MaterialUtils.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/src/serlio/materials/MaterialUtils.cpp b/src/serlio/materials/MaterialUtils.cpp index 0e8bc73b..249ffd58 100644 --- a/src/serlio/materials/MaterialUtils.cpp +++ b/src/serlio/materials/MaterialUtils.cpp @@ -16,6 +16,7 @@ #include "maya/adskDataAssociations.h" namespace { +const MELVariable MEL_UNDO_STATE(L"materialUndoState"); constexpr const wchar_t* RGBA8_FORMAT = L"RGBA8"; constexpr const wchar_t* FORMAT_STRING = L"format"; @@ -207,4 +208,13 @@ bool textureHasAlphaChannel(std::wstring path) { return false; } +void resetMaterial(std::wstring meshName) { + MELScriptBuilder scriptBuilder; + scriptBuilder.declInt(MEL_UNDO_STATE); + scriptBuilder.getUndoState(MEL_UNDO_STATE); + scriptBuilder.setUndoState(false); + scriptBuilder.setsUseInitialShadingGroup(meshName); + scriptBuilder.setUndoState(MEL_UNDO_STATE); + scriptBuilder.execute(); +} } // namespace MaterialUtils diff --git a/src/serlio/materials/MaterialUtils.h b/src/serlio/materials/MaterialUtils.h index 65d6693d..040a3025 100644 --- a/src/serlio/materials/MaterialUtils.h +++ b/src/serlio/materials/MaterialUtils.h @@ -32,4 +32,6 @@ std::wstring synchronouslyCreateShadingEngine(const std::wstring& desiredShading std::filesystem::path getStingrayShaderPath(); bool textureHasAlphaChannel(std::wstring path); + +void resetMaterial(std::wstring meshName); } // namespace MaterialUtils \ No newline at end of file From 2a531b6a935a8f007b848494f10f5c1e018e7059 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 3 Mar 2022 16:07:01 +0100 Subject: [PATCH 481/668] Reset materials to default, when no valid material stream is available --- src/serlio/materials/ArnoldMaterialNode.cpp | 14 ++++++++------ src/serlio/materials/StingrayMaterialNode.cpp | 14 ++++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/serlio/materials/ArnoldMaterialNode.cpp b/src/serlio/materials/ArnoldMaterialNode.cpp index edb7b631..edc5733c 100644 --- a/src/serlio/materials/ArnoldMaterialNode.cpp +++ b/src/serlio/materials/ArnoldMaterialNode.cpp @@ -319,20 +319,22 @@ MStatus ArnoldMaterialNode::compute(const MPlug& plug, MDataBlock& data) { MaterialUtils::forwardGeometry(aInMesh, aOutMesh, data); + MString meshName; + const MStatus meshNameStatus = MaterialUtils::getMeshName(meshName, plug); + if (meshNameStatus != MStatus::kSuccess || meshName.length() == 0) + return meshNameStatus; + adsk::Data::Stream* inMatStream = MaterialUtils::getMaterialStream(aInMesh, data); - if (inMatStream == nullptr) + if (inMatStream == nullptr) { + MaterialUtils::resetMaterial(meshName.asWChar()); return MStatus::kSuccess; + } const adsk::Data::Structure* materialStructure = adsk::Data::Structure::structureByName(PRT_MATERIAL_STRUCTURE.c_str()); if (materialStructure == nullptr) return MStatus::kFailure; - MString meshName; - const MStatus meshNameStatus = MaterialUtils::getMeshName(meshName, plug); - if (meshNameStatus != MStatus::kSuccess || meshName.length() == 0) - return meshNameStatus; - MaterialUtils::MaterialCache matCache = MaterialUtils::getMaterialsByStructure(*materialStructure, MATERIAL_BASE_NAME); diff --git a/src/serlio/materials/StingrayMaterialNode.cpp b/src/serlio/materials/StingrayMaterialNode.cpp index aaefdd62..a90bdfae 100644 --- a/src/serlio/materials/StingrayMaterialNode.cpp +++ b/src/serlio/materials/StingrayMaterialNode.cpp @@ -182,20 +182,22 @@ MStatus StingrayMaterialNode::compute(const MPlug& plug, MDataBlock& data) { MaterialUtils::forwardGeometry(aInMesh, aOutMesh, data); + MString meshName; + MStatus meshNameStatus = MaterialUtils::getMeshName(meshName, plug); + if (meshNameStatus != MStatus::kSuccess || meshName.length() == 0) + return meshNameStatus; + adsk::Data::Stream* inMatStream = MaterialUtils::getMaterialStream(aInMesh, data); - if (inMatStream == nullptr) + if (inMatStream == nullptr) { + MaterialUtils::resetMaterial(meshName.asWChar()); return MStatus::kSuccess; + } const adsk::Data::Structure* materialStructure = adsk::Data::Structure::structureByName(PRT_MATERIAL_STRUCTURE.c_str()); if (materialStructure == nullptr) return MStatus::kFailure; - MString meshName; - MStatus meshNameStatus = MaterialUtils::getMeshName(meshName, plug); - if (meshNameStatus != MStatus::kSuccess || meshName.length() == 0) - return meshNameStatus; - MaterialUtils::MaterialCache matCache = MaterialUtils::getMaterialsByStructure(*materialStructure, MATERIAL_BASE_NAME); From 1e9fa5513b61a8d3b906ebe864d9a2aed836ee7a Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 3 Mar 2022 17:34:40 +0100 Subject: [PATCH 482/668] Cleanup: remove unused function argument --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 81c563f5..c683513a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -70,7 +70,7 @@ Map taskGenSourceCheckout(){ return tasks } -Map getTasks(String branchName = null) { +Map getTasks() { Map tasks = [:] tasks << taskGenSerlio() tasks << taskGenSerlioTests() From 312ec84daeea6fc659dad0696fa7306dcd6ca76c Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 3 Mar 2022 17:37:19 +0100 Subject: [PATCH 483/668] Cleanup: rename second stage to 'build' --- Jenkinsfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index c683513a..b4d25658 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -56,8 +56,8 @@ stage('prepare'){ cepl.runParallel(taskGenSourceCheckout()) } -stage('serlio') { - cepl.runParallel(getTasks()) +stage('build') { + cepl.runParallel(getBuildTasks()) } papl.finalizeRun('serlio', env.BRANCH_NAME) @@ -70,7 +70,7 @@ Map taskGenSourceCheckout(){ return tasks } -Map getTasks() { +Map getBuildTasks() { Map tasks = [:] tasks << taskGenSerlio() tasks << taskGenSerlioTests() From 1a99462f8421d48d1729b22968ffd00f17582ede Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 3 Mar 2022 17:46:36 +0100 Subject: [PATCH 484/668] Cleanup: rename helper function to get checkout task --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index b4d25658..e787857b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -53,7 +53,7 @@ properties([ disableConcurrentBuilds() ]) // -- PIPELINE stage('prepare'){ - cepl.runParallel(taskGenSourceCheckout()) + cepl.runParallel(getCheckoutTask()) } stage('build') { @@ -64,7 +64,7 @@ papl.finalizeRun('serlio', env.BRANCH_NAME) // -- TASK GENERATORS -Map taskGenSourceCheckout(){ +Map getCheckoutTask(){ Map tasks = [:] tasks << taskGenSerlioSourceCheckout() return tasks From b6173e2058307373036ffc6869a288ffb592aecb Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 3 Mar 2022 17:46:57 +0100 Subject: [PATCH 485/668] Separate test into separate stage --- Jenkinsfile | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index e787857b..2c8c7fe5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -56,6 +56,10 @@ stage('prepare'){ cepl.runParallel(getCheckoutTask()) } +stage('test') { + cepl.runParallel(getTestTasks()) +} + stage('build') { cepl.runParallel(getBuildTasks()) } @@ -70,10 +74,15 @@ Map getCheckoutTask(){ return tasks } +Map getTestTasks() { + Map tasks = [:] + tasks << taskGenSerlioTests() + return tasks +} + Map getBuildTasks() { Map tasks = [:] tasks << taskGenSerlio() - tasks << taskGenSerlioTests() tasks << taskGenSerlioInstallers() return tasks } From 8c31a3caaac2a94a2eb8e57525ada0f1d787a6d9 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 3 Mar 2022 17:49:08 +0100 Subject: [PATCH 486/668] Separate installer generation into separate stage --- Jenkinsfile | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 2c8c7fe5..2cf58e62 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -64,6 +64,10 @@ stage('build') { cepl.runParallel(getBuildTasks()) } +stage('installers') { + cepl.runParallel(getInstallersTasks()) +} + papl.finalizeRun('serlio', env.BRANCH_NAME) // -- TASK GENERATORS @@ -83,6 +87,11 @@ Map getTestTasks() { Map getBuildTasks() { Map tasks = [:] tasks << taskGenSerlio() + return tasks +} + +Map getInstallersTasks() { + Map tasks = [:] tasks << taskGenSerlioInstallers() return tasks } From a6c0c7eb4bd62dcd9818e497d1adca32ab0a9dac Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Thu, 3 Mar 2022 17:51:08 +0100 Subject: [PATCH 487/668] Cleanup: rename prefixes of build/checkout --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 2cf58e62..5c22b7e9 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -97,11 +97,11 @@ Map getInstallersTasks() { } Map taskGenSerlioSourceCheckout() { - return cepl.generateTasks('srl', this.&taskSourceCheckout, CHECKOUT_CONFIG) + return cepl.generateTasks('srl-src', this.&taskSourceCheckout, CHECKOUT_CONFIG) } Map taskGenSerlio() { - return cepl.generateTasks('srl', this.&taskBuildSerlio, CONFIGS) + return cepl.generateTasks('srl-bld', this.&taskBuildSerlio, CONFIGS) } Map taskGenSerlioTests() { From 7ea78bd2445016eb113050d3046cab45adb8aac2 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 4 Mar 2022 09:32:00 +0100 Subject: [PATCH 488/668] Cleanup: use unused local variable --- src/serlio/utils/MayaUtilities.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serlio/utils/MayaUtilities.cpp b/src/serlio/utils/MayaUtilities.cpp index db6b62c0..3b43d517 100644 --- a/src/serlio/utils/MayaUtilities.cpp +++ b/src/serlio/utils/MayaUtilities.cpp @@ -57,9 +57,9 @@ MStatus setEnumOptions(const MObject& node, MFnEnumAttribute& enumAttr, MELScriptBuilder scriptBuilder; const std::wstring nodeName = fNode.name().asWChar(); - const std::wstring attrName = fNode.name().asWChar(); + const std::wstring attrName = enumAttr.name().asWChar(); scriptBuilder.setVar(melSerlioNode, MELStringLiteral(nodeName)); - scriptBuilder.setAttrEnumOptions(melSerlioNode, enumAttr.name().asWChar(), enumOptions, customDefaultOption); + scriptBuilder.setAttrEnumOptions(melSerlioNode, attrName, enumOptions, customDefaultOption); return scriptBuilder.execute(); } From 16069af07419eacb176a1a2df8af5b2f9afd14e4 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 4 Mar 2022 09:34:49 +0100 Subject: [PATCH 489/668] Cleanup: remove redundant condition in if clause --- src/serlio/modifiers/PRTModifierEnum.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index 00f4217c..62966b1f 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -93,7 +93,7 @@ MString PRTModifierEnum::getOptionName(size_t optionIndex) const { return MString(mCustomDefaultValue.c_str()); } else { - if ((optionIndex > mEnumOptions.size()) || (optionIndex <= 0)) + if (optionIndex > mEnumOptions.size()) return {}; return MString(mEnumOptions[optionIndex - 1].c_str()); } From 966d1a8c60fc7318ef73e10eb07736f8c433d16a Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 4 Mar 2022 09:35:57 +0100 Subject: [PATCH 490/668] Cleanup: use const ref string argument --- src/serlio/modifiers/PRTModifierEnum.cpp | 2 +- src/serlio/modifiers/PRTModifierEnum.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index 62966b1f..bcc9d35d 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -80,7 +80,7 @@ bool PRTModifierEnum::isDynamic() const { return mValuesAttr.length() > 0; } -size_t PRTModifierEnum::getOptionIndex(std::wstring optionName) const { +size_t PRTModifierEnum::getOptionIndex(const std::wstring& optionName) const { const auto iter = std::find(mEnumOptions.begin(), mEnumOptions.end(), optionName); // return first index, if element is not available if (iter == mEnumOptions.end()) diff --git a/src/serlio/modifiers/PRTModifierEnum.h b/src/serlio/modifiers/PRTModifierEnum.h index 8457cdd4..60f7db57 100644 --- a/src/serlio/modifiers/PRTModifierEnum.h +++ b/src/serlio/modifiers/PRTModifierEnum.h @@ -38,7 +38,7 @@ class PRTModifierEnum { const prt::AttributeMap& defaultAttributeValues, short selectedEnumIdx = 0); bool isDynamic() const; - size_t getOptionIndex(std::wstring optionName) const; + size_t getOptionIndex(const std::wstring& optionName) const; MString getOptionName(size_t optionIndex) const; short getDefaultEnumValue(const prt::AttributeMap& defaultAttributeValues, const RuleAttribute& ruleAttr) const; From 58b6b37c7777348cc7357df09a19fa8d413b03cd Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 4 Mar 2022 09:37:38 +0100 Subject: [PATCH 491/668] Cleanup: use std:string::empty --- src/serlio/utils/MELScriptBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/utils/MELScriptBuilder.cpp b/src/serlio/utils/MELScriptBuilder.cpp index eb998b8e..3d9d8229 100644 --- a/src/serlio/utils/MELScriptBuilder.cpp +++ b/src/serlio/utils/MELScriptBuilder.cpp @@ -37,7 +37,7 @@ std::wstring composeAttributeExpression(const MELVariable& node, const std::wstr } void cleanEnumOptionName(std::wstring& optionName) { - if (optionName == L"") { + if (optionName.empty()) { optionName = L" "; } else { From c5448c3cc4eebf40834f305709e161d2ea72743a Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 4 Mar 2022 09:40:14 +0100 Subject: [PATCH 492/668] Cleanup: use classic for instead of increasing idx over foreach --- src/serlio/utils/MELScriptBuilder.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/serlio/utils/MELScriptBuilder.cpp b/src/serlio/utils/MELScriptBuilder.cpp index 3d9d8229..e6923c51 100644 --- a/src/serlio/utils/MELScriptBuilder.cpp +++ b/src/serlio/utils/MELScriptBuilder.cpp @@ -105,12 +105,12 @@ void MELScriptBuilder::setAttrEnumOptions(const MELVariable& node, const std::ws cleanEnumOptionName(customDefaultOptionString); enumString.append(customDefaultOptionString + L"=0"); } - int idx = 1; - for (std::wstring enumOption : enumOptions) { + for (int idx = 0; idx < enumOptions.size(); idx++) { + std::wstring enumOption = enumOptions[idx]; if (!enumString.empty()) enumString.append(L":"); cleanEnumOptionName(enumOption); - enumString.append(enumOption + L"=" + std::to_wstring(idx++)); + enumString.append(enumOption + L"=" + std::to_wstring(idx)); } // Don't update to an empty enum if (enumString.empty()) From 6d3c994216a8afd8e3d55334a589e19fdd20d9f5 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 4 Mar 2022 09:46:45 +0100 Subject: [PATCH 493/668] Cleanup: compare against nullptr instead of 0 --- src/serlio/modifiers/PRTModifierEnum.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/modifiers/PRTModifierEnum.cpp b/src/serlio/modifiers/PRTModifierEnum.cpp index bcc9d35d..240801ae 100644 --- a/src/serlio/modifiers/PRTModifierEnum.cpp +++ b/src/serlio/modifiers/PRTModifierEnum.cpp @@ -108,7 +108,7 @@ short PRTModifierEnum::getDefaultEnumValue(const prt::AttributeMap& defaultAttri case prt::AAT_STR: { const wchar_t* defStringVal = defaultAttributeValues.getString(fqAttrName.c_str()); - if (defStringVal != 0) + if (defStringVal != nullptr) return static_cast(getOptionIndex(defStringVal)); break; } From ed72a387e5b60f69a23a26ee22b4af64ca2b4eb3 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 4 Mar 2022 09:50:21 +0100 Subject: [PATCH 494/668] Cleanup: remove unused debug variables --- src/serlio/utils/MELScriptBuilder.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/serlio/utils/MELScriptBuilder.cpp b/src/serlio/utils/MELScriptBuilder.cpp index e6923c51..793a1da1 100644 --- a/src/serlio/utils/MELScriptBuilder.cpp +++ b/src/serlio/utils/MELScriptBuilder.cpp @@ -194,8 +194,6 @@ MStatus MELScriptBuilder::executeSync(std::wstring& output) { MStatus status; MString result = MGlobal::executeCommandStringResult(commandStream.str().c_str(), MEL_ENABLE_DISPLAY, false, &status); - MString statusString = status.errorString(); - bool hasError = status.error(); commandStream.clear(); output.assign(result.asWChar()); return status; From 1023a66e94c4fcdd36565b7fb013a2aca71f909f Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 4 Mar 2022 10:10:52 +0100 Subject: [PATCH 495/668] Cleanup: check if enum is available before accessing it (it might be a user-added enum) --- src/serlio/modifiers/PRTModifierAction.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/serlio/modifiers/PRTModifierAction.cpp b/src/serlio/modifiers/PRTModifierAction.cpp index 643b659d..14005982 100644 --- a/src/serlio/modifiers/PRTModifierAction.cpp +++ b/src/serlio/modifiers/PRTModifierAction.cpp @@ -396,7 +396,10 @@ MStatus PRTModifierAction::fillAttributesFromNode(const MObject& node) { break; } case PrtAttributeType::ENUM: { - const PRTModifierEnum& currEnum = mEnums[ruleAttribute.mayaFullName]; + auto it = mEnums.find(ruleAttribute.mayaFullName); + if (it == mEnums.end()) + break; + const PRTModifierEnum& currEnum = it->second; short enumVal; MCHECK(plug.getValue(enumVal)); @@ -486,10 +489,12 @@ MStatus PRTModifierAction::updateUserSetAttributes(const MObject& node) { break; } case PrtAttributeType::ENUM: { - MFnEnumAttribute eAttr(fnAttribute.object()); + auto it = mEnums.find(ruleAttribute.mayaFullName); + if (it == mEnums.end()) + break; + const PRTModifierEnum& currEnum = it->second; - const short defEnumVal = - mEnums[ruleAttribute.mayaFullName].getDefaultEnumValue(*defaultAttributeValues, ruleAttribute); + const short defEnumVal = currEnum.getDefaultEnumValue(*defaultAttributeValues, ruleAttribute); short enumVal; MCHECK(plug.getValue(enumVal)); @@ -578,16 +583,19 @@ MStatus PRTModifierAction::updateUI(const MObject& node, MDataHandle& cgacProble break; } case PrtAttributeType::ENUM: { - PRTModifierEnum& modifierEnum = mEnums[ruleAttribute.mayaFullName]; + auto it = mEnums.find(ruleAttribute.mayaFullName); + if (it == mEnums.end()) + break; + PRTModifierEnum& currEnum = it->second; short enumVal; MCHECK(plug.getValue(enumVal)); const auto newEnumInfo = - modifierEnum.updateOptions(node, mRuleAttributes, *defaultAttributeValues, enumVal); + currEnum.updateOptions(node, mRuleAttributes, *defaultAttributeValues, enumVal); const bool hasNewEnumOptions = newEnumInfo.first; enumVal = newEnumInfo.second; - const short defEnumVal = modifierEnum.getDefaultEnumValue(*defaultAttributeValues, ruleAttribute); + const short defEnumVal = currEnum.getDefaultEnumValue(*defaultAttributeValues, ruleAttribute); const bool isDefaultValue = (defEnumVal == enumVal); if (hasNewEnumOptions || (!getIsUserSet(fnNode, fnAttribute) && !isDefaultValue)) From df0b8773f2feefd93e29406a52afd9d23de16172 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 4 Mar 2022 10:23:24 +0100 Subject: [PATCH 496/668] Cleanup: fix index offset --- src/serlio/utils/MELScriptBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serlio/utils/MELScriptBuilder.cpp b/src/serlio/utils/MELScriptBuilder.cpp index 793a1da1..1733dd53 100644 --- a/src/serlio/utils/MELScriptBuilder.cpp +++ b/src/serlio/utils/MELScriptBuilder.cpp @@ -110,7 +110,7 @@ void MELScriptBuilder::setAttrEnumOptions(const MELVariable& node, const std::ws if (!enumString.empty()) enumString.append(L":"); cleanEnumOptionName(enumOption); - enumString.append(enumOption + L"=" + std::to_wstring(idx)); + enumString.append(enumOption + L"=" + std::to_wstring(idx + 1)); } // Don't update to an empty enum if (enumString.empty()) From 91a676b502b15b3798371e615f99bfba5410c783 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 4 Mar 2022 10:25:00 +0100 Subject: [PATCH 497/668] Cleanup: rename helper function to camelCase --- src/serlio/modifiers/RuleAttributes.cpp | 2 +- src/serlio/utils/Utilities.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serlio/modifiers/RuleAttributes.cpp b/src/serlio/modifiers/RuleAttributes.cpp index e1bca3ea..c3c30b83 100644 --- a/src/serlio/modifiers/RuleAttributes.cpp +++ b/src/serlio/modifiers/RuleAttributes.cpp @@ -40,7 +40,7 @@ constexpr const wchar_t* PRT_ATTR_FULL_NAME_PREFIX = L"PRT"; const std::wstring MAYA_COMPATIBLE_CHARS = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; std::wstring cleanForMaya(const std::wstring& name) { auto r = name; - replace_all_not_of(r, MAYA_COMPATIBLE_CHARS); + replaceAllNotOf(r, MAYA_COMPATIBLE_CHARS); return r; } diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index 1541bfd8..adfcc7de 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -222,7 +222,7 @@ SRL_TEST_EXPORTS_API inline std::wstring getImport(const std::wstring& fqRuleNam SRL_TEST_EXPORTS_API void replaceCGACWithCEVersion(std::wstring& errorString); } // namespace prtu -inline void replace_all_not_of(std::wstring& s, const std::wstring& allowedChars) { +inline void replaceAllNotOf(std::wstring& s, const std::wstring& allowedChars) { std::wstring::size_type pos = 0; while (pos < s.size()) { pos = s.find_first_not_of(allowedChars, pos); From 345560fbbfa3c310679563b6cdb65bf68b30bcc1 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 4 Mar 2022 11:12:29 +0100 Subject: [PATCH 498/668] Add tests for replaceAllOf and replaceSomeOf --- src/serlio/utils/Utilities.h | 4 +-- src/test/tests.cpp | 56 ++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index adfcc7de..c7e3190f 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -222,7 +222,7 @@ SRL_TEST_EXPORTS_API inline std::wstring getImport(const std::wstring& fqRuleNam SRL_TEST_EXPORTS_API void replaceCGACWithCEVersion(std::wstring& errorString); } // namespace prtu -inline void replaceAllNotOf(std::wstring& s, const std::wstring& allowedChars) { +SRL_TEST_EXPORTS_API inline void replaceAllNotOf(std::wstring& s, const std::wstring& allowedChars) { std::wstring::size_type pos = 0; while (pos < s.size()) { pos = s.find_first_not_of(allowedChars, pos); @@ -232,7 +232,7 @@ inline void replaceAllNotOf(std::wstring& s, const std::wstring& allowedChars) { } } -inline void replaceAllOf(std::wstring& s, const std::wstring& bannedChars) { +SRL_TEST_EXPORTS_API inline void replaceAllOf(std::wstring& s, const std::wstring& bannedChars) { std::wstring::size_type pos = 0; while (pos < s.size()) { pos = s.find_first_of(bannedChars, pos); diff --git a/src/test/tests.cpp b/src/test/tests.cpp index f50f27d5..e81c849e 100644 --- a/src/test/tests.cpp +++ b/src/test/tests.cpp @@ -337,6 +337,62 @@ TEST_CASE("toFileURI") { #endif } +TEST_CASE("replaceAllNotOf") { + const std::wstring allowedChars = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + SECTION("empty") { + std::wstring testString = L""; + const std::wstring expected = L""; + replaceAllNotOf(testString, allowedChars); + CHECK(testString == expected); + } + SECTION("replace nothing") { + std::wstring testString = L"The_quick_brown_fox_jumps_over_the_lazy_dog"; + const std::wstring expected = L"The_quick_brown_fox_jumps_over_the_lazy_dog"; + replaceAllNotOf(testString, allowedChars); + CHECK(testString == expected); + } + SECTION("replace some") { + std::wstring testString = L"Replace:all\r\nbut=alpha^numerical.characters;"; + const std::wstring expected = L"Replace_all__but_alpha_numerical_characters_"; + replaceAllNotOf(testString, allowedChars); + CHECK(testString == expected); + } + SECTION("replace all") { + std::wstring testString = L"/:\r^?=-\\%`*\"+-"; + const std::wstring expected = L"______________"; + replaceAllNotOf(testString, allowedChars); + CHECK(testString == expected); + } +} + +TEST_CASE("replaceAllOf") { + const std::wstring bannedChars = L"=:\\;\r\n"; + SECTION("empty") { + std::wstring testString = L""; + const std::wstring expected = L""; + replaceAllOf(testString, bannedChars); + CHECK(testString == expected); + } + SECTION("replace nothing") { + std::wstring testString = L"The quick brown fox jumps over the lazy dog"; + const std::wstring expected = L"The quick brown fox jumps over the lazy dog"; + replaceAllOf(testString, bannedChars); + CHECK(testString == expected); + } + SECTION("replace some") { + std::wstring testString = L"A=B+C;\r\nE:F"; + const std::wstring expected = L"A_B+C___E_F"; + replaceAllOf(testString, bannedChars); + CHECK(testString == expected); + } + SECTION("replace all") { + std::wstring testString = L"=:\\;\r\n"; + const std::wstring expected = L"______"; + replaceAllOf(testString, bannedChars); + CHECK(testString == expected); + } +} + // we use a custom main function to manage PRT lifetime int main(int argc, char* argv[]) { const std::vector addExtDirs = { From 10936f8eddd9b79ae0d33457d97a52274f37f038 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 4 Mar 2022 11:59:10 +0100 Subject: [PATCH 499/668] Cleanup: ove getDuplicateCountSuffix into utilities --- src/serlio/modifiers/RuleAttributes.cpp | 11 ++--------- src/serlio/utils/Utilities.cpp | 8 ++++++++ src/serlio/utils/Utilities.h | 4 ++++ 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/serlio/modifiers/RuleAttributes.cpp b/src/serlio/modifiers/RuleAttributes.cpp index 1a52a452..ba464467 100644 --- a/src/serlio/modifiers/RuleAttributes.cpp +++ b/src/serlio/modifiers/RuleAttributes.cpp @@ -56,13 +56,6 @@ std::wstring getNiceName(const std::wstring& fqAttrName) { return cleanForMaya(prtu::removeImport(prtu::removeStyle(fqAttrName))); } -std::wstring getDuplicateCountSuffix(const std::wstring& mayaName, std::map& mayaNameDuplicateCountMap) { - auto [iterator, isFirstEntry] = mayaNameDuplicateCountMap.try_emplace(mayaName, 0); - if (!isFirstEntry) - iterator->second++; - return L"_" + std::to_wstring(iterator->second); -} - } // namespace std::map getImportOrderMap(const prt::RuleFileInfo* ruleFileInfo) { @@ -130,8 +123,8 @@ RuleAttributeSet getRuleAttributes(const std::wstring& ruleFile, const prt::Rule //make sure maya names are unique const std::wstring mayaBriefName = getBriefName(p.fqName); const std::wstring mayaFullName = getFullName(p.fqName); - p.mayaBriefName = mayaBriefName + getDuplicateCountSuffix(mayaBriefName, mayaNameDuplicateCountMap); - p.mayaFullName = mayaFullName + getDuplicateCountSuffix(mayaFullName, mayaNameDuplicateCountMap); + p.mayaBriefName = mayaBriefName + prtu::getDuplicateCountSuffix(mayaBriefName, mayaNameDuplicateCountMap); + p.mayaFullName = mayaFullName + prtu::getDuplicateCountSuffix(mayaFullName, mayaNameDuplicateCountMap); // TODO: is this correct? import name != rule file name std::wstring ruleName = p.fqName; diff --git a/src/serlio/utils/Utilities.cpp b/src/serlio/utils/Utilities.cpp index 021e79f3..bef07f0f 100644 --- a/src/serlio/utils/Utilities.cpp +++ b/src/serlio/utils/Utilities.cpp @@ -276,4 +276,12 @@ void replaceCGACWithCEVersion(std::wstring& errorString) { replaceCGACVersionBetween(errorString, L"(", L")"); } +std::wstring getDuplicateCountSuffix(const std::wstring& mayaName, + std::map& mayaNameDuplicateCountMap) { + auto [iterator, isFirstEntry] = mayaNameDuplicateCountMap.try_emplace(mayaName, 0); + if (!isFirstEntry) + iterator->second++; + return L"_" + std::to_wstring(iterator->second); +} + } // namespace prtu diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index 89569d2e..2cb5f0e5 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -220,6 +221,9 @@ SRL_TEST_EXPORTS_API inline std::wstring getImport(const std::wstring& fqRuleNam } SRL_TEST_EXPORTS_API void replaceCGACWithCEVersion(std::wstring& errorString); + +SRL_TEST_EXPORTS_API std::wstring getDuplicateCountSuffix(const std::wstring& mayaName, + std::map& mayaNameDuplicateCountMap); } // namespace prtu inline void replace_all_not_of(std::wstring& s, const std::wstring& allowedChars) { From dce85220c6573f96d253aa53d720f9b25b2e4131 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 4 Mar 2022 12:00:58 +0100 Subject: [PATCH 500/668] Cleanup: use more generic names in helper function --- src/serlio/utils/Utilities.cpp | 6 +++--- src/serlio/utils/Utilities.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/serlio/utils/Utilities.cpp b/src/serlio/utils/Utilities.cpp index bef07f0f..0be0bb31 100644 --- a/src/serlio/utils/Utilities.cpp +++ b/src/serlio/utils/Utilities.cpp @@ -276,9 +276,9 @@ void replaceCGACWithCEVersion(std::wstring& errorString) { replaceCGACVersionBetween(errorString, L"(", L")"); } -std::wstring getDuplicateCountSuffix(const std::wstring& mayaName, - std::map& mayaNameDuplicateCountMap) { - auto [iterator, isFirstEntry] = mayaNameDuplicateCountMap.try_emplace(mayaName, 0); +std::wstring getDuplicateCountSuffix(const std::wstring& name, + std::map& duplicateCountMap) { + auto [iterator, isFirstEntry] = duplicateCountMap.try_emplace(name, 0); if (!isFirstEntry) iterator->second++; return L"_" + std::to_wstring(iterator->second); diff --git a/src/serlio/utils/Utilities.h b/src/serlio/utils/Utilities.h index 2cb5f0e5..54329b18 100644 --- a/src/serlio/utils/Utilities.h +++ b/src/serlio/utils/Utilities.h @@ -222,8 +222,8 @@ SRL_TEST_EXPORTS_API inline std::wstring getImport(const std::wstring& fqRuleNam SRL_TEST_EXPORTS_API void replaceCGACWithCEVersion(std::wstring& errorString); -SRL_TEST_EXPORTS_API std::wstring getDuplicateCountSuffix(const std::wstring& mayaName, - std::map& mayaNameDuplicateCountMap); +SRL_TEST_EXPORTS_API std::wstring getDuplicateCountSuffix(const std::wstring& name, + std::map& duplicateCountMap); } // namespace prtu inline void replace_all_not_of(std::wstring& s, const std::wstring& allowedChars) { From 2d1282563ac0ee3333d374dd4cd73e9c955bc606 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 4 Mar 2022 12:05:13 +0100 Subject: [PATCH 501/668] Cleanup: move name clash resolution into getter functions --- src/serlio/modifiers/RuleAttributes.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/serlio/modifiers/RuleAttributes.cpp b/src/serlio/modifiers/RuleAttributes.cpp index ba464467..a3aea2fe 100644 --- a/src/serlio/modifiers/RuleAttributes.cpp +++ b/src/serlio/modifiers/RuleAttributes.cpp @@ -44,12 +44,16 @@ std::wstring cleanForMaya(const std::wstring& name) { return r; } -std::wstring getFullName(const std::wstring& fqAttrName) { - return PRT_ATTR_FULL_NAME_PREFIX + cleanForMaya(fqAttrName); +std::wstring getFullName(const std::wstring& fqAttrName, std::map& mayaNameDuplicateCountMap) { + const std::wstring fullName = PRT_ATTR_FULL_NAME_PREFIX + cleanForMaya(fqAttrName); + // make sure maya names are unique + return fullName + prtu::getDuplicateCountSuffix(fullName, mayaNameDuplicateCountMap); } -std::wstring getBriefName(const std::wstring& fqAttrName) { - return cleanForMaya(prtu::removeStyle(fqAttrName)); +std::wstring getBriefName(const std::wstring& fqAttrName, std::map& mayaNameDuplicateCountMap) { + const std::wstring briefName = cleanForMaya(prtu::removeStyle(fqAttrName)); + // make sure maya names are unique + return briefName + prtu::getDuplicateCountSuffix(briefName, mayaNameDuplicateCountMap); } std::wstring getNiceName(const std::wstring& fqAttrName) { @@ -119,12 +123,8 @@ RuleAttributeSet getRuleAttributes(const std::wstring& ruleFile, const prt::Rule p.fqName = attr->getName(); p.mayaNiceName = getNiceName(p.fqName); p.mType = attr->getReturnType(); - - //make sure maya names are unique - const std::wstring mayaBriefName = getBriefName(p.fqName); - const std::wstring mayaFullName = getFullName(p.fqName); - p.mayaBriefName = mayaBriefName + prtu::getDuplicateCountSuffix(mayaBriefName, mayaNameDuplicateCountMap); - p.mayaFullName = mayaFullName + prtu::getDuplicateCountSuffix(mayaFullName, mayaNameDuplicateCountMap); + p.mayaBriefName = getBriefName(p.fqName, mayaNameDuplicateCountMap); + p.mayaFullName = getFullName(p.fqName, mayaNameDuplicateCountMap); // TODO: is this correct? import name != rule file name std::wstring ruleName = p.fqName; From e08e6b5d54872977cd03b07fddc38c1c4fd8fc30 Mon Sep 17 00:00:00 2001 From: Christian Knieling Date: Fri, 4 Mar 2022 13:19:50 +0100 Subject: [PATCH 502/668] Add tests for getDuplicateCountSuffix --- src/test/tests.cpp | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/test/tests.cpp b/src/test/tests.cpp index f50f27d5..39828f3f 100644 --- a/src/test/tests.cpp +++ b/src/test/tests.cpp @@ -337,6 +337,40 @@ TEST_CASE("toFileURI") { #endif } +TEST_CASE("getDuplicateCountSuffix") { + std::map duplicateCountMap; + + // Fully quantified attributename: Default$import1.myAttr + const std::wstring briefAttr1 = L"import1_myAttr"; + const std::wstring fullAttr1 = L"Default_import1_myAttr"; + const std::wstring briefAttr1Suffix = prtu::getDuplicateCountSuffix(briefAttr1, duplicateCountMap); + const std::wstring fullAttr1Suffix = prtu::getDuplicateCountSuffix(fullAttr1, duplicateCountMap); + const std::wstring expectedBriefAttr1Suffix = L"_0"; + const std::wstring expectedfullAttr1Suffix = L"_0"; + CHECK(briefAttr1Suffix == expectedBriefAttr1Suffix); + CHECK(fullAttr1Suffix == expectedfullAttr1Suffix); + + // Fully quantified attributename: Default$import1_myAttr + const std::wstring briefAttr2 = L"import1_myAttr"; + const std::wstring fullAttr2 = L"Default_import1_myAttr"; + const std::wstring briefAttr2Suffix = prtu::getDuplicateCountSuffix(briefAttr2, duplicateCountMap); + const std::wstring fullAttr2Suffix = prtu::getDuplicateCountSuffix(fullAttr2, duplicateCountMap); + const std::wstring expectedBriefAttr2Suffix = L"_1"; + const std::wstring expectedfullAttr2Suffix = L"_1"; + CHECK(briefAttr2Suffix == expectedBriefAttr2Suffix); + CHECK(fullAttr2Suffix == expectedfullAttr2Suffix); + + // Fully quantified attributename: Default_import1$myAttr + const std::wstring briefAttr3 = L"myAttr"; + const std::wstring fullAttr3 = L"Default_import1_myAttr"; + const std::wstring briefAttr3Suffix = prtu::getDuplicateCountSuffix(briefAttr3, duplicateCountMap); + const std::wstring fullAttr3Suffix = prtu::getDuplicateCountSuffix(fullAttr3, duplicateCountMap); + const std::wstring expectedBriefAttr3Suffix = L"_0"; + const std::wstring expectedfullAttr3Suffix = L"_2"; + CHECK(briefAttr3Suffix == expectedBriefAttr3Suffix); + CHECK(fullAttr3Suffix == expectedfullAttr3Suffix); +} + // we use a custom main function to manage PRT lifetime int main(int argc, char* argv[]) { const std::vector addExtDirs = { From 57e4421d9d16c66fcd6eeaba9350b52b02b75dac Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 7 Mar 2022 10:39:37 +0100 Subject: [PATCH 503/668] Add cmake function to get catch2 from GitHub --- src/common.cmake | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/common.cmake b/src/common.cmake index 5d7e925d..a38897a1 100644 --- a/src/common.cmake +++ b/src/common.cmake @@ -132,6 +132,24 @@ function(srl_add_dependency_maya TGT) endfunction() +### Catch Test Framework + +function(srl_add_dependency_catch TGT) + FetchContent_Declare( + catch + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v2.13.8) + + FetchContent_GetProperties(catch) + if(NOT catch_POPULATED) + FetchContent_Populate(catch) + add_subdirectory(${catch_SOURCE_DIR} ${catch_BINARY_DIR}) + endif() + + target_include_directories(${TGT} PRIVATE ${Catch2_SOURCE_DIR}/single_include) +endfunction() + + ### targets installation location if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) From a974356725d365abe6e1ba4dc126ae28553d15f6 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 7 Mar 2022 10:45:31 +0100 Subject: [PATCH 504/668] Switch to new catch2 version --- src/test/CMakeLists.txt | 1 + src/test/tests.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 1f856e58..f9de00fa 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -32,6 +32,7 @@ target_include_directories(${TEST_TARGET} PRIVATE $) srl_add_dependency_prt(${TEST_TARGET}) +srl_add_dependency_catch(${TEST_TARGET}) # copy libraries next to test excutable so they can be found # TODO: do a proper install of the test executable alongside its dependencies diff --git a/src/test/tests.cpp b/src/test/tests.cpp index f50f27d5..573ba85c 100644 --- a/src/test/tests.cpp +++ b/src/test/tests.cpp @@ -25,7 +25,7 @@ #include "utils/Utilities.h" #define CATCH_CONFIG_RUNNER -#include "catch/catch.hpp" +#include "catch2/catch.hpp" #include From 9e795ffef7e8b1685b4862498550886a33072d87 Mon Sep 17 00:00:00 2001 From: Simon Haegler Date: Mon, 7 Mar 2022 10:38:59 +0100 Subject: [PATCH 505/668] Remove old, bundled catch2 --- src/test/catch/.clang-format | 1 - src/test/catch/catch.hpp | 16865 --------------------------------- 2 files changed, 16866 deletions(-) delete mode 100644 src/test/catch/.clang-format delete mode 100644 src/test/catch/catch.hpp diff --git a/src/test/catch/.clang-format b/src/test/catch/.clang-format deleted file mode 100644 index 5e192e0c..00000000 --- a/src/test/catch/.clang-format +++ /dev/null @@ -1 +0,0 @@ -BasedOnStyle: none diff --git a/src/test/catch/catch.hpp b/src/test/catch/catch.hpp deleted file mode 100644 index 303f664f..00000000 --- a/src/test/catch/catch.hpp +++ /dev/null @@ -1,16865 +0,0 @@ -/* - * Catch v2.9.1 - * Generated: 2019-06-17 11:59:24.363643 - * ---------------------------------------------------------- - * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2019 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED -#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED -// start catch.hpp - - -#define CATCH_VERSION_MAJOR 2 -#define CATCH_VERSION_MINOR 9 -#define CATCH_VERSION_PATCH 1 - -#ifdef __clang__ -# pragma clang system_header -#elif defined __GNUC__ -# pragma GCC system_header -#endif - -// start catch_suppress_warnings.h - -#ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(push) -# pragma warning(disable: 161 1682) -# else // __ICC -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wswitch-enum" -# pragma clang diagnostic ignored "-Wcovered-switch-default" -# endif -#elif defined __GNUC__ - // Because REQUIREs trigger GCC's -Wparentheses, and because still - // supported version of g++ have only buggy support for _Pragmas, - // Wparentheses have to be suppressed globally. -# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details - -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-variable" -# pragma GCC diagnostic ignored "-Wpadded" -#endif -// end catch_suppress_warnings.h -#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) -# define CATCH_IMPL -# define CATCH_CONFIG_ALL_PARTS -#endif - -// In the impl file, we want to have access to all parts of the headers -// Can also be used to sanely support PCHs -#if defined(CATCH_CONFIG_ALL_PARTS) -# define CATCH_CONFIG_EXTERNAL_INTERFACES -# if defined(CATCH_CONFIG_DISABLE_MATCHERS) -# undef CATCH_CONFIG_DISABLE_MATCHERS -# endif -# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) -# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER -# endif -#endif - -#if !defined(CATCH_CONFIG_IMPL_ONLY) -// start catch_platform.h - -#ifdef __APPLE__ -# include -# if TARGET_OS_OSX == 1 -# define CATCH_PLATFORM_MAC -# elif TARGET_OS_IPHONE == 1 -# define CATCH_PLATFORM_IPHONE -# endif - -#elif defined(linux) || defined(__linux) || defined(__linux__) -# define CATCH_PLATFORM_LINUX - -#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) -# define CATCH_PLATFORM_WINDOWS -#endif - -// end catch_platform.h - -#ifdef CATCH_IMPL -# ifndef CLARA_CONFIG_MAIN -# define CLARA_CONFIG_MAIN_NOT_DEFINED -# define CLARA_CONFIG_MAIN -# endif -#endif - -// start catch_user_interfaces.h - -namespace Catch { - unsigned int rngSeed(); -} - -// end catch_user_interfaces.h -// start catch_tag_alias_autoregistrar.h - -// start catch_common.h - -// start catch_compiler_capabilities.h - -// Detect a number of compiler features - by compiler -// The following features are defined: -// -// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? -// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? -// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? -// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? -// **************** -// Note to maintainers: if new toggles are added please document them -// in configuration.md, too -// **************** - -// In general each macro has a _NO_ form -// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. -// Many features, at point of detection, define an _INTERNAL_ macro, so they -// can be combined, en-mass, with the _NO_ forms later. - -#ifdef __cplusplus - -# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) -# define CATCH_CPP14_OR_GREATER -# endif - -# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define CATCH_CPP17_OR_GREATER -# endif - -#endif - -#if defined(CATCH_CPP17_OR_GREATER) -# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -#endif - -#ifdef __clang__ - -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ - _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") -# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic pop" ) - -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) -# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic pop" ) - -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) -# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \ - _Pragma( "clang diagnostic pop" ) - -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) -# define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \ - _Pragma( "clang diagnostic pop" ) - -#endif // __clang__ - -//////////////////////////////////////////////////////////////////////////////// -// Assume that non-Windows platforms support posix signals by default -#if !defined(CATCH_PLATFORM_WINDOWS) - #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS -#endif - -//////////////////////////////////////////////////////////////////////////////// -// We know some environments not to support full POSIX signals -#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) - #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -#endif - -#ifdef __OS400__ -# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -# define CATCH_CONFIG_COLOUR_NONE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Android somehow still does not support std::to_string -#if defined(__ANDROID__) -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Not all Windows environments support SEH properly -#if defined(__MINGW32__) -# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH -#endif - -//////////////////////////////////////////////////////////////////////////////// -// PS4 -#if defined(__ORBIS__) -# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Cygwin -#ifdef __CYGWIN__ - -// Required for some versions of Cygwin to declare gettimeofday -// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin -# define _BSD_SOURCE -// some versions of cygwin (most) do not support std::to_string. Use the libstd check. -// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 -# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ - && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) - -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING - -# endif -#endif // __CYGWIN__ - -//////////////////////////////////////////////////////////////////////////////// -// Visual C++ -#ifdef _MSC_VER - -# if _MSC_VER >= 1900 // Visual Studio 2015 or newer -# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -# endif - -// Universal Windows platform does not support SEH -// Or console colours (or console at all...) -# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) -# define CATCH_CONFIG_COLOUR_NONE -# else -# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH -# endif - -// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ -// _MSVC_TRADITIONAL == 0 means new conformant preprocessor -// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor -# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) -# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -# endif -#endif // _MSC_VER - -#if defined(_REENTRANT) || defined(_MSC_VER) -// Enable async processing, as -pthread is specified or no additional linking is required -# define CATCH_INTERNAL_CONFIG_USE_ASYNC -#endif // _MSC_VER - -//////////////////////////////////////////////////////////////////////////////// -// Check if we are compiled with -fno-exceptions or equivalent -#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) -# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED -#endif - -//////////////////////////////////////////////////////////////////////////////// -// DJGPP -#ifdef __DJGPP__ -# define CATCH_INTERNAL_CONFIG_NO_WCHAR -#endif // __DJGPP__ - -//////////////////////////////////////////////////////////////////////////////// -// Embarcadero C++Build -#if defined(__BORLANDC__) - #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN -#endif - -//////////////////////////////////////////////////////////////////////////////// - -// Use of __COUNTER__ is suppressed during code analysis in -// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly -// handled by it. -// Otherwise all supported compilers support COUNTER macro, -// but user still might want to turn it off -#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) - #define CATCH_INTERNAL_CONFIG_COUNTER -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Check if string_view is available and usable -// The check is split apart to work around v140 (VS2015) preprocessor issue... -#if defined(__has_include) -#if __has_include() && defined(CATCH_CPP17_OR_GREATER) -# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW -#endif -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Check if optional is available and usable -#if defined(__has_include) -# if __has_include() && defined(CATCH_CPP17_OR_GREATER) -# define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL -# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // __has_include - -//////////////////////////////////////////////////////////////////////////////// -// Check if variant is available and usable -#if defined(__has_include) -# if __has_include() && defined(CATCH_CPP17_OR_GREATER) -# if defined(__clang__) && (__clang_major__ < 8) - // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 - // fix should be in clang 8, workaround in libstdc++ 8.2 -# include -# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) -# define CATCH_CONFIG_NO_CPP17_VARIANT -# else -# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT -# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) -# else -# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT -# endif // defined(__clang__) && (__clang_major__ < 8) -# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // __has_include - -#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) -# define CATCH_CONFIG_COUNTER -#endif -#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) -# define CATCH_CONFIG_WINDOWS_SEH -#endif -// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. -#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) -# define CATCH_CONFIG_POSIX_SIGNALS -#endif -// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. -#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) -# define CATCH_CONFIG_WCHAR -#endif - -#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) -# define CATCH_CONFIG_CPP11_TO_STRING -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) -# define CATCH_CONFIG_CPP17_OPTIONAL -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) -# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) -# define CATCH_CONFIG_CPP17_STRING_VIEW -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) -# define CATCH_CONFIG_CPP17_VARIANT -#endif - -#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) -# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE -#endif - -#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) -# define CATCH_CONFIG_NEW_CAPTURE -#endif - -#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -# define CATCH_CONFIG_DISABLE_EXCEPTIONS -#endif - -#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) -# define CATCH_CONFIG_POLYFILL_ISNAN -#endif - -#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) -# define CATCH_CONFIG_USE_ASYNC -#endif - -#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS -#endif - -#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -#define CATCH_TRY if ((true)) -#define CATCH_CATCH_ALL if ((false)) -#define CATCH_CATCH_ANON(type) if ((false)) -#else -#define CATCH_TRY try -#define CATCH_CATCH_ALL catch (...) -#define CATCH_CATCH_ANON(type) catch (type) -#endif - -#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) -#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#endif - -// end catch_compiler_capabilities.h -#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line -#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) -#ifdef CATCH_CONFIG_COUNTER -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) -#else -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) -#endif - -#include -#include -#include - -// We need a dummy global operator<< so we can bring it into Catch namespace later -struct Catch_global_namespace_dummy {}; -std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); - -namespace Catch { - - struct CaseSensitive { enum Choice { - Yes, - No - }; }; - - class NonCopyable { - NonCopyable( NonCopyable const& ) = delete; - NonCopyable( NonCopyable && ) = delete; - NonCopyable& operator = ( NonCopyable const& ) = delete; - NonCopyable& operator = ( NonCopyable && ) = delete; - - protected: - NonCopyable(); - virtual ~NonCopyable(); - }; - - struct SourceLineInfo { - - SourceLineInfo() = delete; - SourceLineInfo( char const* _file, std::size_t _line ) noexcept - : file( _file ), - line( _line ) - {} - - SourceLineInfo( SourceLineInfo const& other ) = default; - SourceLineInfo& operator = ( SourceLineInfo const& ) = default; - SourceLineInfo( SourceLineInfo&& ) noexcept = default; - SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; - - bool empty() const noexcept; - bool operator == ( SourceLineInfo const& other ) const noexcept; - bool operator < ( SourceLineInfo const& other ) const noexcept; - - char const* file; - std::size_t line; - }; - - std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); - - // Bring in operator<< from global namespace into Catch namespace - // This is necessary because the overload of operator<< above makes - // lookup stop at namespace Catch - using ::operator<<; - - // Use this in variadic streaming macros to allow - // >> +StreamEndStop - // as well as - // >> stuff +StreamEndStop - struct StreamEndStop { - std::string operator+() const; - }; - template - T const& operator + ( T const& value, StreamEndStop ) { - return value; - } -} - -#define CATCH_INTERNAL_LINEINFO \ - ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) - -// end catch_common.h -namespace Catch { - - struct RegistrarForTagAliases { - RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - }; - -} // end namespace Catch - -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ - CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS - -// end catch_tag_alias_autoregistrar.h -// start catch_test_registry.h - -// start catch_interfaces_testcase.h - -#include - -namespace Catch { - - class TestSpec; - - struct ITestInvoker { - virtual void invoke () const = 0; - virtual ~ITestInvoker(); - }; - - class TestCase; - struct IConfig; - - struct ITestCaseRegistry { - virtual ~ITestCaseRegistry(); - virtual std::vector const& getAllTests() const = 0; - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; - }; - - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector const& getAllTestCasesSorted( IConfig const& config ); - -} - -// end catch_interfaces_testcase.h -// start catch_stringref.h - -#include -#include -#include - -namespace Catch { - - /// A non-owning string class (similar to the forthcoming std::string_view) - /// Note that, because a StringRef may be a substring of another string, - /// it may not be null terminated. c_str() must return a null terminated - /// string, however, and so the StringRef will internally take ownership - /// (taking a copy), if necessary. In theory this ownership is not externally - /// visible - but it does mean (substring) StringRefs should not be shared between - /// threads. - class StringRef { - public: - using size_type = std::size_t; - - private: - friend struct StringRefTestAccess; - - char const* m_start; - size_type m_size; - - char* m_data = nullptr; - - void takeOwnership(); - - static constexpr char const* const s_empty = ""; - - public: // construction/ assignment - StringRef() noexcept - : StringRef( s_empty, 0 ) - {} - - StringRef( StringRef const& other ) noexcept - : m_start( other.m_start ), - m_size( other.m_size ) - {} - - StringRef( StringRef&& other ) noexcept - : m_start( other.m_start ), - m_size( other.m_size ), - m_data( other.m_data ) - { - other.m_data = nullptr; - } - - StringRef( char const* rawChars ) noexcept; - - StringRef( char const* rawChars, size_type size ) noexcept - : m_start( rawChars ), - m_size( size ) - {} - - StringRef( std::string const& stdString ) noexcept - : m_start( stdString.c_str() ), - m_size( stdString.size() ) - {} - - ~StringRef() noexcept { - delete[] m_data; - } - - auto operator = ( StringRef const &other ) noexcept -> StringRef& { - delete[] m_data; - m_data = nullptr; - m_start = other.m_start; - m_size = other.m_size; - return *this; - } - - operator std::string() const; - - void swap( StringRef& other ) noexcept; - - public: // operators - auto operator == ( StringRef const& other ) const noexcept -> bool; - auto operator != ( StringRef const& other ) const noexcept -> bool; - - auto operator[] ( size_type index ) const noexcept -> char; - - public: // named queries - auto empty() const noexcept -> bool { - return m_size == 0; - } - auto size() const noexcept -> size_type { - return m_size; - } - - auto numberOfCharacters() const noexcept -> size_type; - auto c_str() const -> char const*; - - public: // substrings and searches - auto substr( size_type start, size_type size ) const noexcept -> StringRef; - - // Returns the current start pointer. - // Note that the pointer can change when if the StringRef is a substring - auto currentData() const noexcept -> char const*; - - private: // ownership queries - may not be consistent between calls - auto isOwned() const noexcept -> bool; - auto isSubstring() const noexcept -> bool; - }; - - auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; - auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; - auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; - - auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; - auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; - - inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { - return StringRef( rawChars, size ); - } - -} // namespace Catch - -inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { - return Catch::StringRef( rawChars, size ); -} - -// end catch_stringref.h -// start catch_type_traits.hpp - - -#include - -namespace Catch{ - -#ifdef CATCH_CPP17_OR_GREATER - template - inline constexpr auto is_unique = std::true_type{}; - - template - inline constexpr auto is_unique = std::bool_constant< - (!std::is_same_v && ...) && is_unique - >{}; -#else - -template -struct is_unique : std::true_type{}; - -template -struct is_unique : std::integral_constant -::value - && is_unique::value - && is_unique::value ->{}; - -#endif -} - -// end catch_type_traits.hpp -// start catch_preprocessor.hpp - - -#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ -#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) - -#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ -// MSVC needs more evaluations -#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) -#else -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) -#endif - -#define CATCH_REC_END(...) -#define CATCH_REC_OUT - -#define CATCH_EMPTY() -#define CATCH_DEFER(id) id CATCH_EMPTY() - -#define CATCH_REC_GET_END2() 0, CATCH_REC_END -#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 -#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 -#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT -#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) -#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) - -#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) - -#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) - -// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, -// and passes userdata as the first parameter to each invocation, -// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) -#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) -#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ -#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ -#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF -#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) -#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ -#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) -#else -// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF -#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) -#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ -#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) -#endif - -#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ -#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) - -#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) - -#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) -#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) -#else -#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) -#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) -#endif - -#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ - CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) - -#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) -#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) -#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) -#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) -#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) -#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) -#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _4, _5, _6) -#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) -#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) -#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) -#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) - -#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N - -#define INTERNAL_CATCH_TYPE_GEN\ - template struct TypeList {};\ - template\ - constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ - \ - template class L1, typename...E1, template class L2, typename...E2> \ - constexpr auto append(L1, L2) noexcept -> L1 { return {}; }\ - template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ - constexpr auto append(L1, L2, Rest...) noexcept -> decltype(append(L1{}, Rest{}...)) { return {}; }\ - template< template class L1, typename...E1, typename...Rest>\ - constexpr auto append(L1, TypeList, Rest...) noexcept -> L1 { return {}; }\ - \ - template< template class Container, template class List, typename...elems>\ - constexpr auto rewrap(List) noexcept -> TypeList> { return {}; }\ - template< template class Container, template class List, class...Elems, typename...Elements>\ - constexpr auto rewrap(List,Elements...) noexcept -> decltype(append(TypeList>{}, rewrap(Elements{}...))) { return {}; }\ - \ - template

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≪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 Oi}a%Qv#SLh$Jk#$cQLB~ literal 156864 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