Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix typo corrections and signature mismatches in Pascal binding (also helpful for Delphi users) #385

Open
GridLogicMichigan opened this issue Aug 7, 2024 · 21 comments
Assignees

Comments

@GridLogicMichigan
Copy link

I tried to convert the Pascal binding unit to work in Delphi, but I cannot seem to resolve the circular dependencies of the many classes. Will this be provided as another option?

@vijaiaeroastro
Copy link
Collaborator

@alexanderoster Perhaps you can comment on this as I am not familiar with the Pascal bindings.

@GridLogicMichigan
Copy link
Author

I actually have a unit that I am now testing I think I resolved my issues but not sure... Here is what I have so far. But the other challenge is since I am ne to 3MF the documentation is not easy to follow, I just want to render the model in a Viewport and build a list of objects and use the object names.

Unit_Lib3MF.zip

@vijaiaeroastro
Copy link
Collaborator

@GridLogicMichigan Have you seen our examples?
https://github.com/3MFConsortium/lib3mf/blob/develop/SDK/Examples/Cpp/Source/ExtractInfo.cpp

This would be the perfect example for you. If you still have difficulty, share your script, and I will ensure you get the help needed. Unfortunately, I have never written Pascal, but I can get someone to help you.

@GridLogicMichigan
Copy link
Author

Well originally, I just created my own custom Lib3MF unit (see attached Delphi project), I was able to render the models in my viewport but then I broke the code when I tried to get Object Names... so then I decided to take the Pascal unit and make it into a Delphis specific unit... which I attached in a previous comment. Now I want to take the app that I made in the attached zip of the delphi project and use the proper unit so I can have access to the full features of the dll.

OPLCADViewer.zip

@GridLogicMichigan
Copy link
Author

I made some typo corrections, which originated in the Pascal version some of the dll function exports did not match the names in the Pascal unit... by the way.. this one attached is Delphi specific,,, but likely you will need to correct your Pascal one too.

[
Unit_Lib3MF.zip
](url)

@vijaiaeroastro
Copy link
Collaborator

@GridLogicMichigan Thanks. I will check it out. These are all autogenerated, by the way. Currently, the confident bindings are C, C++, Python, and to some extent, Golang.

@vijaiaeroastro vijaiaeroastro changed the title Delphi binding Fix typo corrections and signature mismatches in Pascal binding (also helpful for Delphi users) Aug 7, 2024
@GridLogicMichigan
Copy link
Author

GridLogicMichigan commented Aug 7, 2024

Also this: function GetPrereleaseInformation(out APrereleaseInfo: String): Boolean;
function GetBuildInformation(out ABuildInformation: String): Boolean; was changed to: function GetPrereleaseInformation(out APrereleaseInfo: WideString): Boolean;
function GetBuildInformation(out ABuildInformation: WideString): Boolean;

for whatever the reason their was a pointer exception, but changing the string to widestring allowed me to build a basic app:

unit Unit1;

interface

uses
Windows, System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
FMX.Controls.Presentation, FMX.StdCtrls, FMX.Viewport3D, FMX.Memo.Types,
FMX.ScrollBox, FMX.Memo, Unit_Lib3MF;

type
TForm1 = class(TForm)
Viewport3D1: TViewport3D;
Button1: TButton;
OpenDialog1: TOpenDialog;
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure OutputLib3MFVersion;
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.FormCreate(Sender: TObject);
begin
OutputLib3MFVersion;
end;
procedure TForm1.OutputLib3MFVersion;
var
Wrapper: TLib3MFWrapper;
Major, Minor, Micro: Cardinal;
PreReleaseInfo, BuildInfo: Widestring;
DLLName: String;
begin
DLLName := 'lib3mf.dll';
Wrapper := TLib3MFWrapper.Create(DLLName);
try
if(Assigned(Wrapper)) then
begin
Wrapper.GetLibraryVersion(Major, Minor, Micro);
Memo1.Lines.Add(Format('Lib3MF.Version = %d.%d.%d', [Major, Minor, Micro]));

    if Wrapper.GetPrereleaseInformation(PreReleaseInfo) then
      Memo1.Lines.Add('-' + PreReleaseInfo);


    if Wrapper.GetBuildInformation(BuildInfo) then
      Memo1.Lines.Add('+' + BuildInfo);
  end;

finally
Wrapper.Destroy;
end;
end;

end.

@GridLogicMichigan
Copy link
Author

@GridLogicMichigan Have you seen our examples? https://github.com/3MFConsortium/lib3mf/blob/develop/SDK/Examples/Cpp/Source/ExtractInfo.cpp

This would be the perfect example for you. If you still have difficulty, share your script, and I will ensure you get the help needed. Unfortunately, I have never written Pascal, but I can get someone to help you.

I am getting: 3MF Read example
Lib3MF.Version = 2.3.2

Error loading 3MF file: the 3MF Library Error - the queried reader class is unknown (#100, )
Failed to load 3MF file.

@vijaiaeroastro
Copy link
Collaborator

Did you use 3mf as the type for reader ?

@GridLogicMichigan
Copy link
Author

Did you use 3mf as the type for reader ?

yes:

procedure TForm1.ExtractInfoExample(FileName: string);
var
  ObjectIterator: TLib3MFObjectIterator;
  Obj: TLib3MFObject;
  BuildItemIterator: TLib3MFBuildItemIterator;
  BuildItem: TLib3MFBuildItem;
  SliceStackIterator: TLib3MFSliceStackIterator;
  SliceStack: TLib3MFSliceStack;
begin
  Memo1.Lines.Add('------------------------------------------------------------------');
  Memo1.Lines.Add('3MF Read example');
  OutputLib3MFVersion;
  Memo1.Lines.Add('------------------------------------------------------------------');

  Model := Lib3MF.CreateModel;

  if not OpenFile3MF(FileName) then
  begin
    Memo1.Lines.Add('Failed to load 3MF file.');
    Exit;
  end;

  ShowThumbnailInformation(Model);
  ShowMetaDataInformation(Model.GetMetaDataGroup);

  SliceStackIterator := Model.GetSliceStacks;
  while SliceStackIterator.MoveNext do
  begin
    SliceStack := SliceStackIterator.GetCurrentSliceStack;
    ShowSliceStack(SliceStack, '');
  end;

  ObjectIterator := Model.GetObjects;
  while ObjectIterator.MoveNext do
  begin
    Obj := ObjectIterator.GetCurrentObject;
    if Obj.IsMeshObject then
      ShowMeshObjectInformation(Model.GetMeshObjectByID(Obj.GetResourceID))
    else if Obj.IsComponentsObject then
      ShowComponentsObjectInformation(Model.GetComponentsObjectByID(Obj.GetResourceID))
    else
      Memo1.Lines.Add(Format('unknown object #%d:', [Obj.GetResourceID]));
  end;

  BuildItemIterator := Model.GetBuildItems;
  while BuildItemIterator.MoveNext do
  begin
    BuildItem := BuildItemIterator.GetCurrent;
    Memo1.Lines.Add(Format('Build item (Object #%d):', [BuildItem.GetObjectResourceID]));
    if BuildItem.HasObjectTransform then
      ShowTransform(BuildItem.GetObjectTransform, '   ')
    else
      Memo1.Lines.Add('   Transformation: none');
    Memo1.Lines.Add(Format('   Part number: "%s"', [BuildItem.GetPartNumber]));
    if BuildItem.GetMetaDataGroup.GetMetaDataCount > 0 then
      ShowMetaDataInformation(BuildItem.GetMetaDataGroup);
  end;

  Memo1.Lines.Add('done');
end;

and

function TForm1.OpenFile3MF(FN: String): Boolean;
var
  i: Integer;
  WarningCount: Cardinal;
  WarningCode: Cardinal;
  WarningMessage: Widestring;
begin
  Result := False;
  if Assigned(Lib3MF) and FileExists(FN) then
  begin
    try
      Model := Lib3MF.CreateModel;
      Lib3MFReader := Model.QueryReader('3mf');
      Lib3MFReader.SetStrictModeActive(False);
      Lib3MFReader.ReadFromFile(FN);

      WarningCount := Lib3MFReader.GetWarningCount;
      for i := 0 to WarningCount - 1 do
      begin
        WarningMessage := Lib3MFReader.GetWarning(i, WarningCode);
        Memo1.Lines.Add(Format('Encountered warning #%d : %s', [WarningCode, WarningMessage]));
      end;

      Result := True;
    except
      on E: ELib3MFException do
      begin
        Memo1.Lines.Add(Format('Error loading 3MF file: %s', [E.Message]));
        Exit;
      end;
    end;
  end
  else
    Memo1.Lines.Add('Library not initialized or file not found.');
end;

@GridLogicMichigan
Copy link
Author

The problem seems to be in identifying ClassTypeId maybe?? But my Delphi class function TLib3MFPolymorphicFactory.Make(Wrapper: TLib3MFWrapper; Handle: TLib3MFHandle): T; method pretty much matches the Cpp version, but the class type id seems to be the $5A8164ECEDB03F09 value (0x5A8164ECEDB03F09UL in cpp), seems like it should be the reader case... but I am new to this library...

@GridLogicMichigan
Copy link
Author

I figured it out. the Pascal and Delphi versions should use AnsiString (instead of String) everywhere in those units.. Stilll looking through the unit for other flaws.

@vijaiaeroastro
Copy link
Collaborator

@GridLogicMichigan Really appreciate the amount of time you have put into this binding. Kindly share the final working version and I will try to patch the bindings based on your work in the very next release.

@GridLogicMichigan
Copy link
Author

you're welcome. I had to work on another project so I have set this aside a few days, but I hope to finished up soon.

@GridLogicMichigan
Copy link
Author

I think this should work for Delphi. I am working on a FireMonkey (Delphi) that is similar to the C++ test app (in fact I had one that worked , but adding a Viewport to allow for viewing the models).

Unit_Lib3MF.zip

@GridLogicMichigan
Copy link
Author

GridLogicMichigan commented Aug 23, 2024

Unit_Lib3MF.zip
I made another fix the Delphi version of the unit that changes this:

function TLib3MFObject.GetName(): AnsiString; var bytesNeededName: Cardinal; bytesWrittenName: Cardinal; bufferName: array of Char; begin bytesNeededName:= 0; bytesWrittenName:= 0; FWrapper.CheckError(Self, FWrapper.Lib3MFObject_GetNameFunc(FHandle, 0, bytesNeededName, nil)); SetLength(bufferName, bytesNeededName); FWrapper.CheckError(Self, FWrapper.Lib3MFObject_GetNameFunc(FHandle, bytesNeededName, bytesWrittenName, @bufferName[0])); Result := String(@bufferName[0]); end;

to this:
` function TLib3MFObject.GetName(): AnsiString;
var
bytesNeededName: Cardinal;
bytesWrittenName: Cardinal;
bufferName: array of AnsiChar;
begin
bytesNeededName := 0;
bytesWrittenName := 0;

// Get the size needed for the name buffer
FWrapper.CheckError(Self, FWrapper.Lib3MFObject_GetNameFunc(FHandle, 0, bytesNeededName, nil));

if bytesNeededName = 0 then
  begin
    Result := '';
    Exit;
  end;

// Allocate buffer for the name
SetLength(bufferName, bytesNeededName);
// Retrieve the name
FWrapper.CheckError(Self, FWrapper.Lib3MFObject_GetNameFunc(FHandle, bytesNeededName, bytesWrittenName, @bufferName[0]));
// Convert the buffer to a string, considering the length
Result := AnsiString(PAnsiChar(@bufferName[0]));

end;`

also I can share my new Delphi/FireMoney app that renders the model in a Viewport.

@vijaiaeroastro
Copy link
Collaborator

@GridLogicMichigan It will definitely help fix the issue. I have already asked someone to look into these bindings.

@GridLogicMichigan
Copy link
Author

Unit_Lib3MF.zip
A few other fixes in the Delphi binding.

@alexanderoster
Copy link
Contributor

Hi @GridLogicMichigan:

We have looked through your changes, and they look really reasonable.
As we only have FreePascal available for testing a new version, would you be able to come on a live video call to check the implementation against your setup?

Best,

Alex

@GridLogicMichigan
Copy link
Author

GridLogicMichigan commented Sep 4, 2024 via email

@vijaiaeroastro
Copy link
Collaborator

@GridLogicMichigan Send me an email at github@kaditham.meshing.engineer and we will schedule a call based on everyone's availability.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants