3MFConsortium / lib3mf

lib3mf is an implementation of the 3D Manufacturing Format file standard
http://3mf.io
BSD 2-Clause "Simplified" License
236 stars 94 forks source link

Reading bad vertex values when using in combination with Qt #282

Closed TheJKM closed 2 months ago

TheJKM commented 3 years ago

Dear lib3mf developers,

I encountered a problem using lib3mf to read 3mf files, especially vertex values. When reading raw vertex values within my application, I get the values having zero on every position after the decimal point. This leads to broken meshes containing no roundness.

After some investigation, I was able to reduce the issue to the following environment:

Which version of lib3mf am I using? Currently the latest 2.2.0, but it also happens with 2.1.1

Which operating system am I using? Linux, all tested variants (Manjaro KDE, Ubuntu) are affected.

What did I do? I simply read a 3mf file, get the first mesh object, and print all vertices. Before reading, I created a QApplicaion object because I use Qt for GUI stuff.

What did I expect to see? The correct vertex numbers (showing the last 5 vertices).

11,000000 -0,000000 5,000000
11,000000 0,000000 1,000000
11,000000 0,000000 5,000000
12,000000 0,000000 1,000000
12,000000 0,000000 5,000000

What did I see instead? Wrong numbers - missing all numbers after the decimal point (showing the last 5 vertices).

11.993439 -0.396760 5.500000
11.993439 0.396760 1.000000
11.993439 0.396760 5.500000
12.000000 0.000000 1.000000
12.000000 0.000000 5.500000

I know that this problem is rather weird and I don't know if Qt or lib3mf is to blame here. But, as you know the code of lib3mf, I hope that you guys can provide some advice where the root cause for this issue is.

For reference, I'll include a minimal example to reproduce the issue. For building it, Qt6 has to be installed (it can be changed to Qt5 easily). I add my CMake file, it it taken from the examples using the precompiled version of lib3mf, but rather picky getting to work, at least on my machine. Also, I added a part of the outputs from the example, and two images showing the result. Also, I attached the 3mf file I used for testing. The example project is also included in the ZIP archive.

Thanks in advance, TheJKM

Minimum example source (main.cpp):

// Include dependencies
#include <QApplication>
#include "lib3mf_implicit.hpp"

int main(int argc, char** argv) {
  Lib3MF::PWrapper _wrapper = Lib3MF::CWrapper::loadLibrary();
  Lib3MF::PModel _project = _wrapper->CreateModel();
  Lib3MF::PReader reader = _project->QueryReader("3mf");

  QApplication application(argc, argv); // Comment this line out to make it work

  // Test file reading
  reader->ReadFromFile("../testmodel.3mf");

  Lib3MF::PMeshObjectIterator meshIterator = _project->GetMeshObjects();
  if (meshIterator->Count() != 1) {
    printf("Not 1 mesh: %d\n", meshIterator->Count());
  }
  meshIterator->MoveNext();
  Lib3MF::PMeshObject mesh = meshIterator->GetCurrentMeshObject();
  Lib3MF_uint32 verticeCount = mesh->GetVertexCount();

  for (int i = 0; i < verticeCount; i++) {
    Lib3MF::sPosition p = mesh->GetVertex(i);
    printf("%f %f %f\n", p.m_Coordinates[0], p.m_Coordinates[1], p.m_Coordinates[2]);
  }

  printf("\n-------- Now correct results using the data structures, without reading from a file:\n\n");

  // Test data structures
  std::vector<Lib3MF::sPosition> positions;
  std::vector<Lib3MF::sTriangle> tri;
  Lib3MF::sPosition test = {1.2356226352435452, 4.256324, -24.462456};
  printf("%f %f %f\n", test.m_Coordinates[0], test.m_Coordinates[1], test.m_Coordinates[2]);
  for (int i = 0; i < 10; i++) {
    positions.push_back(test);
  }
  for (Lib3MF::sPosition test: positions) {
    printf("%f %f %f\n", test.m_Coordinates[0], test.m_Coordinates[1], test.m_Coordinates[2]);
  }
}

CMakeLists.txt using the precompiled lib3mf version:

CMAKE_MINIMUM_REQUIRED(VERSION 3.16.3)
PROJECT(lib3mfqt VERSION 0.0.1 LANGUAGES CXX)

# Build lib3mftest
SET(CMAKE_CXX_STANDARD 11)

SET(CMAKE_CURRENT_BINDING_DIR ${CMAKE_CURRENT_SOURCE_DIR}/.)

FIND_PACKAGE(Qt6 COMPONENTS Widgets REQUIRED)

INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/Bindings/Cpp)
FIND_LIBRARY(LIB3MF 3mf HINTS ${CMAKE_CURRENT_SOURCE_DIR}/Bin)

message(STATUS "${LIB3MF}")

# The executable we want to build
ADD_EXECUTABLE(lib3mftest
  main.cpp
)

TARGET_LINK_LIBRARIES(lib3mftest
  ${LIB3MF}
  Qt6::Widgets
)

Broken output: LinuxBugDefect

Correct output (rendered on macOS):

LinuxBugWorking

ZIP archive containing the example project: lib3mftest.zip

bubnikv commented 3 years ago

It looks like a locales / decimal separator issue.

On Mon, Sep 6, 2021 at 1:32 AM Johannes Kreutz @.***> wrote:

Dear lib3mf developers,

I encountered a problem using lib3mf to read 3mf files, especially vertex values. When reading raw vertex values within my application, I get the values having zero on every position after the decimal point. This leads to broken meshes containing no roundness.

After some investigation, I was able to reduce the issue to the following environment:

  • The application uses Qt for building its GUI. Without Qt, everything works as it should. Especially, as soon as the creation of a QApplication object, which is mandatory in a Qt application, happens before calling ReadFromFile, the error occurs.
  • Using the data structures without reading from a file works just fine.
  • The error only occurs on Linux. I was able to reproduce it using Manjaro (Rolling release, fully updated as of 06.09.2021) using KDE/X11, Ubuntu 20.04 LTS using Gnome/X11 and Ubuntu 21.04 using Gnome/Wayland. It happens with both gcc and clang compilers.
  • Other operating systems are not affected. I testes macOS 11.5.2 and Windows 10 (i think it was 20H2), the latter using mingw-gcc.
  • It happens with Qt 6 and 5, tested using Qt 6.1, Qt 6.0.1 and Qt 5.15.2.

Which version of lib3mf am I using? Currently the latest 2.2.0, but it also happens with 2.1.1

Which operating system am I using? Linux, all tested variants (Manjaro KDE, Ubuntu) are affected.

What did I do? I simply read a 3mf file, get the first mesh object, and print all vertices. Before reading, I created a QApplicaion object because I use Qt for GUI stuff.

What did I expect to see? The correct vertex numbers (showing the last 5 vertices).

11,000000 -0,000000 5,000000 11,000000 0,000000 1,000000 11,000000 0,000000 5,000000 12,000000 0,000000 1,000000 12,000000 0,000000 5,000000

What did I see instead? Wrong numbers - missing all numbers after the decimal point (showing the last 5 vertices).

11.993439 -0.396760 5.500000 11.993439 0.396760 1.000000 11.993439 0.396760 5.500000 12.000000 0.000000 1.000000 12.000000 0.000000 5.500000

I know that this problem is rather weird and I don't know if Qt or lib3mf is to blame here. But, as you know the code of lib3mf, I hope that you guys can provide some advice where the root cause for this issue is.

For reference, I'll include a minimal example to reproduce the issue. For building it, Qt6 has to be installed (it can be changed to Qt5 easily). I add my CMake file, it it taken from the examples using the precompiled version of lib3mf, but rather picky getting to work, at least on my machine. Also, I added a part of the outputs from the example, and two images showing the result. Also, I attached the 3mf file I used for testing. The example project is also included in the ZIP archive.

Thanks in advance, TheJKM

Minimum example source (main.cpp):

// Include dependencies

include

include "lib3mf_implicit.hpp"

int main(int argc, char** argv) { Lib3MF::PWrapper _wrapper = Lib3MF::CWrapper::loadLibrary(); Lib3MF::PModel _project = _wrapper->CreateModel(); Lib3MF::PReader reader = _project->QueryReader("3mf");

QApplication application(argc, argv); // Comment this line out to make it work

// Test file reading reader->ReadFromFile("../testmodel.3mf");

Lib3MF::PMeshObjectIterator meshIterator = _project->GetMeshObjects(); if (meshIterator->Count() != 1) { printf("Not 1 mesh: %d\n", meshIterator->Count()); } meshIterator->MoveNext(); Lib3MF::PMeshObject mesh = meshIterator->GetCurrentMeshObject(); Lib3MF_uint32 verticeCount = mesh->GetVertexCount();

for (int i = 0; i < verticeCount; i++) { Lib3MF::sPosition p = mesh->GetVertex(i); printf("%f %f %f\n", p.m_Coordinates[0], p.m_Coordinates[1], p.m_Coordinates[2]); }

printf("\n-------- Now correct results using the data structures, without reading from a file:\n\n");

// Test data structures std::vector positions; std::vector tri; Lib3MF::sPosition test = {1.2356226352435452, 4.256324, -24.462456}; printf("%f %f %f\n", test.m_Coordinates[0], test.m_Coordinates[1], test.m_Coordinates[2]); for (int i = 0; i < 10; i++) { positions.push_back(test); } for (Lib3MF::sPosition test: positions) { printf("%f %f %f\n", test.m_Coordinates[0], test.m_Coordinates[1], test.m_Coordinates[2]); } }

CMakeLists.txt using the precompiled lib3mf version:

CMAKE_MINIMUM_REQUIRED(VERSION 3.16.3) PROJECT(lib3mfqt VERSION 0.0.1 LANGUAGES CXX)

Build lib3mftest

SET(CMAKE_CXX_STANDARD 11)

SET(CMAKE_CURRENT_BINDING_DIR ${CMAKE_CURRENT_SOURCE_DIR}/.)

FIND_PACKAGE(Qt6 COMPONENTS Widgets REQUIRED)

INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/Bindings/Cpp) FIND_LIBRARY(LIB3MF 3mf HINTS ${CMAKE_CURRENT_SOURCE_DIR}/Bin)

message(STATUS "${LIB3MF}")

The executable we want to build

ADD_EXECUTABLE(lib3mftest main.cpp )

TARGET_LINK_LIBRARIES(lib3mftest ${LIB3MF} Qt6::Widgets )

Broken output: [image: LinuxBugDefect] https://user-images.githubusercontent.com/21987613/132144274-dbe840e5-3011-466e-8b2e-79c7756c14cc.png

Correct output (rendered on macOS): [image: LinuxBugWorking] https://user-images.githubusercontent.com/21987613/132144278-ce246860-5f81-4616-b69a-8e770d583963.png

ZIP archive containing the example project: lib3mftest.zip https://github.com/3MFConsortium/lib3mf/files/7112513/lib3mftest.zip

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/3MFConsortium/lib3mf/issues/282, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABMPSI6P6RRHYA64SEBUXBLUAP4XFANCNFSM5DPJWA6A . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

izaias-atlantico commented 2 years ago

I'm having the same issue, could you to explain more about you talking @bubnikv ?

bubnikv commented 2 years ago

@izaias-atlantico We are not using lib3mf in house, however similar issues happen if one parses floats with a locales specific function that expects or emits numbers with commas (',') as a decimal separator, while 3MF XML files should be produced with dots ('.') as a decimal separator.

izaias-atlantico commented 2 years ago

@TheJKM, I found the problem for me, my file 3mf has a param called transform in my .model

like this: ...

  <build>
    <item objectid="3" transform="0.0393701 0 0 0 0.0393701 0 0 0 0.0393701 0 0 -0.0163594" />
  </build>

...

So, when I remove that or I change "." for "," inside the file (its needs compress after for .3mf) I can open the file after QApplication. I tryed change Locale using QLocale::setDefault but the problem still happens :/ . Someone knows if there are a good way to use the same Local decimal sparator of my system in the context of QApplication?

izaias-atlantico commented 2 years ago

Guys, I solved the problem. I just have changed the locale from std. Finally is just put this command std::locale::global(std::locale::classic()) before read the 3mf mesh and voilà, that's working fine for me!

NorbertEff commented 2 years ago

Also encountered this, and can confirm that the C locale setting right after QApplication creation fixes it.

This might be exatly the cause of the problem: https://doc.qt.io/qt-5/qcoreapplication.html#locale-settings