I want to use ActiViz .NET and C# to multitexture an object created from .obj file.
As long as I know somehow to use texture with single texture, I have problem with more than one texture. Based on what I found on VTK GitHub I started writing a code, but there comes problem with SetBlendingMode and MapDataArrayToMultiTextureAttribute methods.
I use SetBlendingMode like this:
texture.SetBlendingMode(vtkTexture.VTKTextureBlendingMode.VTK_TEXTURE_BLENDING_MODE_REPLACE);
which results with:
The best overloaded method match for 'Kitware.VTK.vtkTexture.SetBlendingMode(int)' has some invalid arguments
With MapDataArrayToMultiTextureAttribute it looks like this:
mapper.MapDataArrayToMultiTextureAttribute(vtkProperty.VTKTextureUnit.VTK_TEXTURE_UNIT_0, "TCoords", vtkDataObject.FIELD_ASSOCIATION, 0);
which ends with similar message:
The best overloaded method match for 'Kitware.VTK.vtkPolyDataMapper.MapDataArrayToMultiTextureAttribute(int, string, int, int)' has some invalid arguments
The whole code:
private void ReadOBJ(string path)
{
vtkTesting test = vtkTesting.New();
// Varibles for obj file and texture
string filePath = path;
string texturePath0 = #"C:\Users\admin\Desktop\Fox-Skull-obj\Fox Skull_0.jpg";
string texturePath1 = #"C:\Users\admin\Desktop\Fox-Skull-obj\Fox Skull_1.jpg";
string texturePath2 = #"C:\Users\admin\Desktop\Fox-Skull-obj\Fox Skull_2.jpg";
string texturePath3 = #"C:\Users\admin\Desktop\Fox-Skull-obj\Fox Skull_3.jpg";
// Open jpeg file including texture
vtkJPEGReader jpegReader = new vtkJPEGReader();
jpegReader.SetFileName(texturePath0);
jpegReader.Update();
vtkJPEGReader jpegReader1 = new vtkJPEGReader();
jpegReader1.SetFileName(texturePath1);
jpegReader1.Update();
vtkJPEGReader jpegReader2 = new vtkJPEGReader();
jpegReader2.SetFileName(texturePath2);
jpegReader2.Update();
vtkJPEGReader jpegReader3 = new vtkJPEGReader();
jpegReader3.SetFileName(texturePath3);
jpegReader3.Update();
// Open obj file
vtkOBJReader reader = new vtkOBJReader();
if (!File.Exists(filePath))
{
MessageBox.Show("Cannot read file \"" + filePath + "\"", "Error", MessageBoxButtons.OK);
return;
}
reader.SetFileName(filePath);
reader.Update();
vtkTriangleFilter triangleFilter = vtkTriangleFilter.New();
triangleFilter.SetInputConnection(reader.GetOutputPort());
vtkStripper stripper = vtkStripper.New();
stripper.SetInputConnection(triangleFilter.GetOutputPort());
stripper.Update();
vtkPolyData polydata = stripper.GetOutput();
polydata.Register(null);
polydata.GetPointData().SetNormals(null);
vtkFloatArray TCoords = vtkFloatArray.New();
TCoords.SetNumberOfComponents(2);
TCoords.Allocate(8, 0);
TCoords.InsertNextTuple2(0.0, 0.0);
TCoords.InsertNextTuple2(1.0, 0.0);
TCoords.InsertNextTuple2(0.0, 1.0);
TCoords.InsertNextTuple2(1.0, 1.0);
TCoords.SetName("TCoords");
polydata.GetPointData().AddArray(TCoords);
// Create texture
vtkTexture texture = new vtkTexture();
vtkTexture texture1 = new vtkTexture();
vtkTexture texture2 = new vtkTexture();
vtkTexture texture3 = new vtkTexture();
texture.SetInputConnection(jpegReader.GetOutputPort());
texture1.SetInputConnection(jpegReader1.GetOutputPort());
texture2.SetInputConnection(jpegReader2.GetOutputPort());
texture3.SetInputConnection(jpegReader3.GetOutputPort());
texture.SetBlendingMode((int)vtkTexture.VTKTextureBlendingMode.VTK_TEXTURE_BLENDING_MODE_REPLACE);
texture1.SetBlendingMode((int)vtkTexture.VTKTextureBlendingMode.VTK_TEXTURE_BLENDING_MODE_ADD);
texture2.SetBlendingMode((int)vtkTexture.VTKTextureBlendingMode.VTK_TEXTURE_BLENDING_MODE_ADD);
texture3.SetBlendingMode((int)vtkTexture.VTKTextureBlendingMode.VTK_TEXTURE_BLENDING_MODE_ADD);
// Mapping textures
vtkTextureMapToCylinder mapSphere = new vtkTextureMapToCylinder();
mapSphere.SetInputConnection(reader.GetOutputPort());
// Visualize
vtkPolyDataMapper mapper = vtkPolyDataMapper.New();
mapper.SetInput(polydata);
// Get a reference to the renderwindow of our renderWindowControl1
vtkRenderWindow renderWindow = renderWindowControl1.RenderWindow;
// Renderer
vtkRenderer renderer = renderWindow.GetRenderers().GetFirstRenderer();
vtkRenderWindowInteractor iren = vtkRenderWindowInteractor.New();
iren.SetRenderWindow(renderWindow);
// Create actor and add mapper with texture
vtkActor actor = vtkActor.New();
vtkOpenGLHardwareSupport hardware = vtkOpenGLRenderWindow.SafeDownCast(renderWindow).GetHardwareSupport();
bool supported = hardware.GetSupportsMultiTexturing();
int tu = 0;
if (supported)
{
tu = hardware.GetNumberOfFixedTextureUnits();
}
if (supported && tu > 2)
{
mapper.MapDataArrayToMultiTextureAttribute((int)vtkProperty.VTKTextureUnit.VTK_TEXTURE_UNIT_0, "TCoords", (int)vtkDataObject.FieldAssociations.FIELD_ASSOCIATION_POINTS, -1);
mapper.MapDataArrayToMultiTextureAttribute((int)vtkProperty.VTKTextureUnit.VTK_TEXTURE_UNIT_1, "TCoords", (int)vtkDataObject.FieldAssociations.FIELD_ASSOCIATION_POINTS, -1);
mapper.MapDataArrayToMultiTextureAttribute((int)vtkProperty.VTKTextureUnit.VTK_TEXTURE_UNIT_2, "TCoords", (int)vtkDataObject.FieldAssociations.FIELD_ASSOCIATION_POINTS, -1);
mapper.MapDataArrayToMultiTextureAttribute((int)vtkProperty.VTKTextureUnit.VTK_TEXTURE_UNIT_3, "TCoords", (int)vtkDataObject.FieldAssociations.FIELD_ASSOCIATION_POINTS, -1);
actor.GetProperty().SetTexture((int)vtkProperty.VTKTextureUnit.VTK_TEXTURE_UNIT_0, texture);
actor.GetProperty().SetTexture((int)vtkProperty.VTKTextureUnit.VTK_TEXTURE_UNIT_1, texture1);
actor.GetProperty().SetTexture((int)vtkProperty.VTKTextureUnit.VTK_TEXTURE_UNIT_2, texture2);
actor.GetProperty().SetTexture((int)vtkProperty.VTKTextureUnit.VTK_TEXTURE_UNIT_3, texture3);
}
else
{
if (supported)
{
mapper.MapDataArrayToMultiTextureAttribute((int)vtkProperty.VTKTextureUnit.VTK_TEXTURE_UNIT_0, "TCoords", (int)vtkDataObject.AttributeTypes.POINT, 0);
}
actor.SetTexture(texture);
}
actor.SetMapper(mapper);
renderWindow.AddRenderer(renderer);
// Hide current actor
renderer.RemoveAllViewProps();
// Set background color
renderer.SetBackground(0.3, 0.6, 0.3);
// Add new actor to the renderer
renderer.AddActor(actor);
// Render
renderWindow.Render();
}
Does anybody have any experience with multitexture with C# and VTK and can help me? Do you know any other solutions that can help me?
EDIT 01.12.2015
Thanks to JohnnyQ answer I probably make functions mentioned above working. I say "probably" because right now when I'm running code and select .obj file program stops working with one of two errors:
An unhandled exception of type 'System.AccessViolationException' occurred in Kitware.VTK.dll with information about attempt of read or write protected memory or program just stops with vshost32.exe stop working message.
I've updated code above to actual version. Any suggestions are still appreciated.
You have to explicit cast from the enum type to an integral type.
e.g.
texture.SetBlendingMode((int)vtkTexture.VTKTextureBlendingMode.VTK_TEXTURE_BLENDING_MODE_REPLACE);
Related
Background: I'm trying to write a program to insert an image into a cell of a spreadsheet. LibreOffice recently changed how this is done, and all the samples I could find use the old method which no longer works.
Technically I know that you can't "insert" an image into a cell and that such an image is an overlay on a DrawPage that sits on top of the spreadsheet to "decorate" it.
One of the first steps in doing this (the new way) is to create an XGraphic object which contains the image. The process is to create an XGraphicProvider and call it with MediaProperties that specify the image file URL to be loaded. I have a program that is supposed to do this but the resulting XGraphic is null. The LO SDK gives pretty much no information when you do something wrong; it just doesn't work.
Here is the code I have, with all the headers removed:
// addpic
// add picture to spreadsheet - debug version
class OpenOfficeApp {
[STAThread]
static void Main(string[] args) {
bool lreadonly;
string pqfile;
string pqURL;
string pqpic;
pqfile = "file:///D:/Documents/NSexeye/ODS%20File%20Access/"+
"addpix/addpic.ods";
pqpic = "addpic2";
pqURL = pqpic+".jpg";
lreadonly = false;
Console.WriteLine("Using: "+pqfile);
// get the desktop
XComponentContext XCC = uno.util.Bootstrap.bootstrap();
XMultiComponentFactory XMCF =
(XMultiComponentFactory)XCC.getServiceManager();
XMultiServiceFactory XMSF = (XMultiServiceFactory)XCC.getServiceManager();
XComponentLoader XCL =
(XComponentLoader)XMSF.createInstance("com.sun.star.frame.Desktop");
// open the spreadsheet
PropertyValue[] pPV = new PropertyValue[2];
pPV[0] = new PropertyValue();
pPV[0].Name = "Hidden";
pPV[0].Value = new uno.Any(true);
pPV[1] = new PropertyValue();
pPV[1].Name = "ReadOnly";
if (lreadonly) pPV[1].Value = new uno.Any(true);
else pPV[1].Value = new uno.Any(false);
XComponent XCo = XCL.loadComponentFromURL(pqfile,"_blank",0,pPV);
// create graphic object containing image
object oGP = XMCF.createInstanceWithContext(
"com.sun.star.graphic.GraphicProvider",XCC);
if (oGP == null) {
Console.WriteLine("oGP is null. Aborting.");
return;
}
XGraphicProvider XGP = (XGraphicProvider)oGP;
if (XGP == null) {
Console.WriteLine("XGP is null. Aborting.");
return;
}
pPV = new PropertyValue[1];
pPV[0] = new PropertyValue();
pPV[0].Name = "URL";
pPV[0].Value = new uno.Any(pqURL);
Console.WriteLine("Creating XGraphic containing "+pqURL);
XGraphic XG = XGP.queryGraphic(pPV);
// *** XG is null here
if (XG == null) {
Console.WriteLine("XG is null. Aborting.");
return;
}
// ... lots of stuff to be added here
// save and close the spreadsheet
XModifiable XM = (XModifiable)XCo;
XM.setModified(true);
XStorable XSt = (XStorable)XCo;
XSt.store();
XCloseable XCl = (XCloseable)XCo;
XCl.close(true);
// terminate LibreOffice
// *** I want this to not terminate it if something else is open
XDesktop XD = (XDesktop)XCL;
if (XD != null) XD.terminate();
}
}
I get a null for the XGraphic, in the place indicated in the comments. I don't know if the call to create it is failing, or if one of the earlier steps of the process are incorrect.
My goal here, in addition to getting my program working, is to create a sample program showing how to add an image to a Calc spreadsheet cell, and to manipulate such images. There are a fair number of people asking questions about this and none of the examples I've found will work. I think a good working sample will be of value.
I've spent a lot of time searching for information and code samples for this, with nothing that helps. I've tried to find ways to verify the validity of the XGraphicProvider interface with no luck. I've run out of things to try.
I'm hoping someone who knows about the LibreOffice SDK can take a look and maybe see what I'm doing wrong.
Update: I figured out what I was doing wrong: I was passing a bare filename in the "URL" property to XGraphicProvider. It has to be the same format (starting with "file:///") as the spreadsheet's file name specification.
Now I'm stuck with another property problem. The XGraphic has to be specified as a parameter to the GraphicObjectShape's Graphic property, but the setPropertyValue() function requires that it be a uno.Any type. I can't figure out how to specify an interface name like XGraphic as a uno.Any.
Here is the piece of code that won't compile, complaining that it can't convert an XGraphic to a uno.Any, in the first setPropertyValue call:
// set image XGraphic
XPropertySet XPS = (XPropertySet)XS;
XPS.setPropertyValue("Graphic",XG);
XPS.setPropertyValue("Name",new uno.Any(pqpic));
XG is an XGraphic type. Using "new uno.Any(XG)" doesn't work either, giving a similar compiler error.
After trying unsuccessfully for a few hours to get the latest LO SDK up and running, let me offer some untested ideas.
First of all, here is some working Basic code, no doubt similar to what you're translating from. The important line is oShape.Graphic = oProvider.queryGraphic(Props()).
oDoc = ThisComponent
oSheet = oDoc.CurrentController.ActiveSheet
pqURL = "file:///C:/Users/JimK/Desktop/addpic.jpg"
oProvider = createUnoService("com.sun.star.graphic.GraphicProvider")
oShape = oDoc.createInstance("com.sun.star.drawing.GraphicObjectShape")
Dim Props(0) as new com.sun.star.beans.PropertyValue
Props(0).Name= "URL"
Props(0).Value = pqURL
oShape.Graphic = oProvider.queryGraphic(Props())
oCell = oSheet.getCellByPosition(5,5)
oShape.Name = oCell.AbsoluteName + "##" + Props(0).Value
oShape.Anchor = oCell
oSheet.DrawPage.add(oShape)
'Resize
w = oShape.Graphic.Size.Width
h = oShape.Graphic.Size.Height
wcl = oCell.Size.Width
hcl = oCell.Size.Height
If w<>0 and h<>0 then
oCell.String=""
Dim Size as new com.sun.star.awt.Size
Size.Width = wcl
Size.Height = h*wcl/w
If Size.Height > hcl then
Size.Width = hcl*w/h
Size.Height = hcl
Endif
oShape.setSize(Size)
oShape.setPosition(oCell.Position)
erase oShape
Else
oShape.dispose()
Endif
Now, how to translate this to C#? It looks like you may need to explicitly specify the type. In the SDK example, there are calls like this.
xFieldProp.setPropertyValue(
"Orientation",
new uno.Any(
typeof (unoidl.com.sun.star.sheet.DataPilotFieldOrientation),
unoidl.com.sun.star.sheet.DataPilotFieldOrientation.DATA ) );
So in your case, something like this:
XPS.setPropertyValue(
"Graphic"
new uno.Any(
typeof(unoidl.com.sun.star.graphic.XGraphic),
XG));
Alternatively, follow the suggestion here: set GraphicURL, which should load the image and set Graphic for you.
I want to get map box tiles from database but it does not work. I get MBTilesMapProvider class from here.
It is invoked like below:
map.Manager.Mode = AccessMode.ServerAndCache;
map.MapProvider = new MBTilesMapProvider("C:\\Users\\NPC\\Desktop\\test\\ne.mbtiles");
result:
but if google maps used as map provider like below it works well
map.Manager.Mode = AccessMode.ServerAndCache;
map.MapProvider = GoogleSatelliteMapProvider.Instance;
When i debuged i noticed that GetTiles method is invoked never.
Note: I think there is no problem about finding database because it reads meta data from database.
I solved solution by make a bit changes at MBTilesHelper.cs.
First i realized that when reading metadata from database MinZoom and MaxZoom values, they always stay as zero because it does not contain MinZoom or MaxZoom. Therefore, i set them manually.
and secondly i changed a bit "getImage" method.
private PureImage getImage(GPoint pos, int zoom)
{
PureImage retval = null;
var resultImage = _mbtiles.GetTileStream(pos.X, pos.Y, zoom);
if (resultImage != null && resultImage.Length > 0)
{
//resultImage.Position = 0L;
retval = GetTileImageFromArray(resultImage);
}
return retval;
}
I'm working from the docs on trying to create a sheet with a view on it using the Revit API in C#
here is the docs URL link. You can find the code at the bottom in the first C# block.
I get a red squiggly under the view3D.Id:
Viewport.Create(doc, viewSheet.Id, view3D.Id, new XYZ(location.U, location.V, 0));
I can't find that it was deprecated nor can I figure out how to resolve it. I'm a bit confused about why it's trying to grab its own elementID. Also, just getting into the revit-API. it seems like "views" in Revit are called "viewports" in the API. i need to read more on this.
here is the entire code block:
private void CreateSheetView(Autodesk.Revit.DB.Document document, View3D view3D)
{
// Get an available title block from document
FilteredElementCollector collector = new FilteredElementCollector(document);
collector.OfClass(typeof(FamilySymbol));
collector.OfCategory(BuiltInCategory.OST_TitleBlocks);
FamilySymbol fs = collector.FirstElement() as FamilySymbol;
if (fs != null)
{
using (Transaction t = new Transaction(document, "Create a new ViewSheet"))
{
t.Start();
try
{
// Create a sheet view
ViewSheet viewSheet = ViewSheet.Create(document, fs.Id);
if (null == viewSheet)
{
throw new Exception("Failed to create new ViewSheet.");
}
// Add passed in view onto the center of the sheet
UV location = new UV((viewSheet.Outline.Max.U - viewSheet.Outline.Min.U) / 2,
(viewSheet.Outline.Max.V - viewSheet.Outline.Min.V) / 2);
//viewSheet.AddView(view3D, location);
Viewport.Create(document, viewSheet.Id, view3D.Id, new XYZ(location.U, location.V, 0));
^ERROR HAPPENS IN LINE ABOVE AT view3D.Id
// Print the sheet out
if (viewSheet.CanBePrinted)
{
TaskDialog taskDialog = new TaskDialog("Revit");
taskDialog.MainContent = "Print the sheet?";
TaskDialogCommonButtons buttons = TaskDialogCommonButtons.Yes | TaskDialogCommonButtons.No;
taskDialog.CommonButtons = buttons;
TaskDialogResult result = taskDialog.Show();
if (result == TaskDialogResult.Yes)
{
viewSheet.Print();
}
}
t.Commit();
}
catch
{
t.RollBack();
}
}
}
}
The Building Coder discussion of exact viewport positioning includes some woking sample calls to ViewSheet.Create and Viewport.Create.
I want to show c# source code with syntax highlighting and theme coloring inside a wpf control. This is for preview only and I don't need any editing capabilities.
I found some code samples on how to embed a code editor which receives a file path to load.
I loaded it with a temp file I created - and it works, well almost...
The problem is that the loaded code have parsing errors which shows up in the error list.
Is there a way to set those errors to not appear in the error list?
Here is the code:
IVsInvisibleEditorManager invisibleEditorManager = (IVsInvisibleEditorManager)ServiceProvider.GlobalProvider.GetService(typeof(SVsInvisibleEditorManager));
ErrorHandler.ThrowOnFailure(invisibleEditorManager.RegisterInvisibleEditor(csTempFilePath, pProject: null,dwFlags: (uint)_EDITORREGFLAGS.RIEF_ENABLECACHING,
pFactory: null, ppEditor: out this.invisibleEditor));
//The doc data is the IVsTextLines that represents the in-memory version of the file we opened in our invisibe editor, we need
//to extract that so that we can create our real (visible) editor.
IntPtr docDataPointer = IntPtr.Zero;
Guid guidIVSTextLines = typeof(IVsTextLines).GUID;
ErrorHandler.ThrowOnFailure(this.invisibleEditor.GetDocData(fEnsureWritable: 1, riid: ref guidIVSTextLines, ppDocData: out docDataPointer));
try
{
IVsTextLines docData = (IVsTextLines)Marshal.GetObjectForIUnknown(docDataPointer);
//Get the component model so we can request the editor adapter factory which we can use to spin up an editor instance.
IComponentModel componentModel = (IComponentModel)ServiceProvider.GlobalProvider.GetService(typeof(SComponentModel));
IVsEditorAdaptersFactoryService editorAdapterFactoryService = componentModel.GetService<IVsEditorAdaptersFactoryService>();
//Create a code window adapter.
this.codeWindow = editorAdapterFactoryService.CreateVsCodeWindowAdapter(OleServiceProvider);
IVsCodeWindowEx codeWindowEx = (IVsCodeWindowEx)this.codeWindow;
INITVIEW[] initView = new INITVIEW[1];
codeWindowEx.Initialize((uint)_codewindowbehaviorflags.CWB_DISABLESPLITTER,
VSUSERCONTEXTATTRIBUTEUSAGE.VSUC_Usage_Filter,
szNameAuxUserContext: "",
szValueAuxUserContext: "",
InitViewFlags: 0,
pInitView: initView);
ErrorHandler.ThrowOnFailure(this.codeWindow.SetBuffer((IVsTextLines)docData));
//Get our text view for our editor which we will use to get the WPF control that hosts said editor.
ErrorHandler.ThrowOnFailure(this.codeWindow.GetPrimaryView(out this.textView));
//Get our WPF host from our text view (from our code window).
IWpfTextViewHost textViewHost = editorAdapterFactoryService.GetWpfTextViewHost(this.textView);
textViewHost.TextView.Options.SetOptionValue(DefaultTextViewHostOptions.ChangeTrackingId, false);
textViewHost.TextView.Options.SetOptionValue(DefaultTextViewOptions.ViewProhibitUserInputId, true);
var dte = ContainerFactory.GetContainer().Resolve<DTE2>();
var projectItem = dte.Solution.FindProjectItem(csTempFilePath);
projectItem.Properties.Item("BuildAction").Value = prjBuildAction.prjBuildActionNone;
return textViewHost.HostControl;
}
finally
{
if (docDataPointer != IntPtr.Zero)
{
//Release the doc data from the invisible editor since it gave us a ref-counted copy.
Marshal.Release(docDataPointer);
}
}
I've tried to remove the errors from the error list manually. But it didn't work - I think it's because i can only remove errors that I added previously. Here is the code I tried using to remove the errors:
public void RemoveTempFileErrors()
{
var provider = new ErrorListProvider(ServiceProvider.GlobalProvider)
{
ProviderName = "MyProvider",
ProviderGuid = new Guid("41C0915D-A0F4-42B2-985F-D1CC5F65BFFC") // my provider guid
};
var vsTaskList1 = (IVsTaskList) Package.GetGlobalService(typeof (IVsTaskList));
uint providerCookie;
vsTaskList1.RegisterTaskProvider(provider, out providerCookie);
vsTaskList1.RefreshTasks(providerCookie);
var vsTaskList2 = (IVsTaskList2)Package.GetGlobalService(typeof(IVsTaskList));
provider.SuspendRefresh();
IVsEnumTaskItems enumerator;
vsTaskList1.EnumTaskItems(out enumerator);
IVsTaskItem[] arr = new IVsTaskItem[1];
while (enumerator.Next(1, arr, null) == 0)
{
string doc;
arr[0].Document(out doc);
if (doc == csTempFilePath)
{
vsTaskList2.RemoveTasks(providerCookie, 1, arr);
}
}
provider.ResumeRefresh();
provider.Refresh();
vsTaskList1.UnregisterTaskProvider(providerCookie);
}
I solved it partially -
The parsing errors were caused because the code was a method without a class. So i wrapped the method in a class and used an elision buffer to show only the method without the wrapper class
The elision buffer code goes like this:
var subsetSnapshot = new SnapshotSpan(textSnapshot.Lines.First().EndIncludingLineBreak, textSnapshot.Lines.Last().Start);
var projectionBufferFactory = componentModel.GetService<IProjectionBufferFactoryService>();
var projBuffer = projectionBufferFactory.CreateElisionBuffer(null,
new NormalizedSnapshotSpanCollection(subsetSnapshot), ElisionBufferOptions.None);
IVsTextBuffer bufferAdapter = editorAdapterFactoryService.CreateVsTextBufferAdapterForSecondaryBuffer(OleServiceProvider, projBuffer);
projTextView = editorAdapterFactoryService.CreateVsTextViewAdapter(OleServiceProvider);
projTextView.Initialize((IVsTextLines)bufferAdapter, IntPtr.Zero,
(uint)TextViewInitFlags.VIF_HSCROLL | (uint)TextViewInitFlags.VIF_VSCROLL | (uint)TextViewInitFlags3.VIF_NO_HWND_SUPPORT,
new[] { new INITVIEW { fSelectionMargin = 0, fWidgetMargin = 0, fVirtualSpace = 0, fDragDropMove = 0 } };
IWpfTextViewHost projTextViewHost = editorAdapterFactoryService.GetWpfTextViewHost(projTextView);
Sounds a little bit scary isn't it?
Some background information, I want to load a tar archive which contains some lua modules into my C# application using LuaInterface. The easiest way would be to extract these files to a temp folder, modify the lua module search path and read them with require as usual. But I do not want to put these scripts somewhere on the file system.
So I thought it should be possible to load the tar-archive with the #ziplib I know there are a lot of lua implementations for tar and stuff like that. But the #zlib is already part of the project.
After successfully loading the file as strings(streams) out of the archive I should be able to pass them into lua.DoString(...) in C# via LuaInterface.
But simply loading modules by a dostring or dofile does not work if modules have a line like this: "module(..., package.seeall)" There is a error reportet like passing argument 1 a nil, but string expected.
The other problem is a module may depend on other modules which are also located in the tar archive.
One possible solution should be to define a custom loader as described here.
My idea is to implement such a loader in C# with the #ziplib and map this loader into the lua stack of my C# application.
Does anyone of you had a similar task to this?
Are there any ready to use solutions which already address problems like this?
The tar file is not must have but a nice to have package format.
Is this idea feasible or totally unfeasible?
I've written some example class to extract the lua files from the archive. This method works as loader and return a lua function.
namespace LuaInterfaceTest
{
class LuaTarModuleLoader
{
private LuaTarModuleLoader() { }
~LuaTarModuleLoader()
{
in_stream_.Close();
}
public LuaTarModuleLoader(Stream in_stream,Lua lua )
{
in_stream_ = in_stream;
lua_ = lua;
}
public LuaFunction load(string modulename, out string error_message)
{
string lua_chunk = "test=hello";
string filename = modulename + ".lua";
error_message = "Unable to locate the file";
in_stream_.Position = 0; // rewind
Stream gzipStream = new BZip2InputStream(in_stream_);
TarInputStream tar = new TarInputStream(gzipStream);
TarEntry tarEntry;
LuaFunction func = null;
while ((tarEntry = tar.GetNextEntry()) != null)
{
if (tarEntry.IsDirectory)
{
continue;
}
if (filename == tarEntry.Name)
{
MemoryStream out_stream = new MemoryStream();
tar.CopyEntryContents(out_stream);
out_stream.Position = 0; // rewind
StreamReader stream_reader = new StreamReader(out_stream);
lua_chunk = stream_reader.ReadToEnd();
func = lua_.LoadString(lua_chunk, filename);
string dum = func.ToString();
error_message = "No Error!";
break;
}
}
return func;
}
private Stream in_stream_;
private Lua lua_;
}
}
I try to register the load method like this in the LuaInterface
Lua lua = new Lua();
GC.Collect();
Stream inStream = File.OpenRead("c:\\tmp\\lua_scripts.tar.bz2");
LuaTarModuleLoader tar_loader = new LuaTarModuleLoader(inStream, lua);
lua.DoString("require 'CLRPackage'");
lua.DoString("import \"ICSharpCode.SharpZipLib.dll\"");
lua.DoString("import \"System\"");
lua["container_module_loader"] = tar_loader;
lua.DoString("table.insert(package.loaders, 2, container_module_loader.load)");
lua.DoString("require 'def_sensor'");
If I try it this way I'll get an exception while the call to require :
"instance method 'load' requires a non null target object"
I tried to call the load method directly, here I have to use the ":" notation.
lua.DoString("container_module_loader:load('def_sensor')");
If I call the method like that I hit a breakpoint in the debugger which is place on top of the method so everything works as expected.
But If I try to register the method with ":" notation I get an exception while registering the method:
lua.DoString("table.insert(package.loaders, 2, container_module_loader:load)");
"[string "chunk"]:1: function arguments expected near ')'"
In LÖVE they have that working. All Lua files are inside one zip file, and they work, even if ... is used. The library they use is PhysicsFS.
Have a look at the source. Probably /modules/filesystem will get you started.
I finally got the trick ;-)
One Problem I currently not really understand is that my loader should not return any string.
Here is my solution:
The loader Class itself:
namespace LuaInterfaceTest
{
class LuaTarModuleLoader
{
private LuaTarModuleLoader() { }
~LuaTarModuleLoader()
{
in_stream_.Close();
}
public LuaTarModuleLoader(Stream in_stream,Lua lua )
{
in_stream_ = in_stream;
lua_ = lua;
}
public LuaFunction load(string modulename)
{
string lua_chunk = "";
string filename = modulename + ".lua";
in_stream_.Position = 0; // rewind
Stream gzipStream = new BZip2InputStream(in_stream_);
TarInputStream tar = new TarInputStream(gzipStream);
TarEntry tarEntry;
LuaFunction func = null;
while ((tarEntry = tar.GetNextEntry()) != null)
{
if (tarEntry.IsDirectory)
{
continue;
}
if (filename == tarEntry.Name)
{
MemoryStream out_stream = new MemoryStream();
tar.CopyEntryContents(out_stream);
out_stream.Position = 0; // rewind
StreamReader stream_reader = new StreamReader(out_stream);
lua_chunk = stream_reader.ReadToEnd();
func = lua_.LoadString(lua_chunk, modulename);
string dum = func.ToString();
break;
}
}
return func;
}
private Stream in_stream_;
private Lua lua_;
}
}
And how to use the loader, I am not sure if all the package stuff is really needed. But I had to wrap up the call with ":" notation and hide it behind my "load_wrapper" function.
string load_wrapper = "local function load_wrapper(modname)\n return container_module_loader:load(modname)\n end";
Lua lua = new Lua();
GC.Collect();
Stream inStream = File.OpenRead("c:\\tmp\\lua_scripts.tar.bz2");
LuaTarModuleLoader tar_loader = new LuaTarModuleLoader(inStream, lua);
lua.DoString("require 'CLRPackage'");
lua.DoString("import \"System\"");
lua["container_module_loader"] = tar_loader;
lua.DoString(load_wrapper);
string loader_package = "module('my_loader', package.seeall) \n";
loader_package += load_wrapper + "\n";
loader_package += "table.insert(package.loaders, 2, load_wrapper)";
lua.DoString(loader_package);
lua.DoFile("./load_modules.lua");
I hope this may also helps some other