I have a GLScene project. In the SceneViewer I import some stl files as freeform.
The user can interact with this objects (move and rotate them with mouse).
Now I have to export this whole Scene to one stl file, so the position and the rotation of the freeforms should be like in the Scene after this export ("merge").
I found this thread about the same problem: Export "Scene" to STL File but this creates an broken stl file (tried open with meshlab).
I hope there is some idea for creating a solution.
If somebody knows a solution for another 3d-file format, it will be great too.
I have found the solution :-)
the link in the answer is nearly right,
but i have to calculate the header information (count of faceletts) in an other way.
now i use a loop an calculate the header before writing thats all.
Sometimes the solution is so near.
var j :integer ;
var i:integer;
var header: TSTLHeader;
var dataFace: TSTLFace;
var list: TaffineVectorlist;
//objects = list of steFreeform objects
//astream = created stream
for j := 0 to objects.count - 1 do
begin
list := TGLFREEForm(objects[j].MeshObjects.ExtractTriangles;
header.nbfaces :0 header.nbFaces + list.count div 3 ;
end;
aStream.write(header.SizeOf(header));
//rest see above link in the question
Related
I am using Delphi 10 Seattle Subscription Update 1 and TeeChart Standard v2015.15.150420 which came bundled with Delphi.
I drop a TDBChart component on a new VCL application's blank form. I then use the sample code as outlined in the "Adding a Function" tutorial found at http://www.teechart.net/docs/teechart/vclfmx/tutorials/UserGuide/Tutorials/tutorial7.htm#AddFunction in the form's OnCreate event. With this code everything works as it should and I get two bar series populated with sample values and one line series which represents the average of the two bar series.
The problem comes in if I don't want the average represented by a line series, but rather by, say, a bar series. If I change the TLineSeries to a TBarSeries and run the program, it causes an "access violation at 0x0066d665: read of address 0x00000198" on adding the first bar series as a datasource to the function series (tmpLineSeries), eg. tmpLineSeries.DataSources.Add( tmpBarSeries1 );.
Here's the problem code (see "AV occurs here" below). Note that the only code that changed from the working tutorial example was, as explained, tmpLineSeries that had been changed to a TBarSeries type instead of a TLineSeries type :
procedure TForm1.FormCreate(Sender: TObject);
var tmpBarSeries1,
tmpBarSeries2 : TBarSeries;
tmpLineSeries : TBarSeries;
begin
//Add 2 data Series
tmpBarSeries1:=TBarSeries.Create(Self);
tmpBarSeries2:=TBarSeries.Create(Self);
DBChart1.AddSeries(tmpBarSeries1);
DBChart1.AddSeries(tmpBarSeries2);
//Populate them with data (here random)
tmpBarSeries1.FillSampleValues(10);
tmpBarSeries2.FillSampleValues(10);
//Add a series to be used for an Average Function
tmpLineSeries:=TBarSeries.Create(Self);
DBChart1.AddSeries(tmpLineSeries);
//Define the Function Type for the new Series
tmpLineSeries.SetFunction(TAverageTeeFunction.Create(Self));
//Define the Datasource for the new Function Series
//Datasource accepts the Series titles of the other 2 Series
tmpLineSeries.DataSources.Clear;
tmpLineSeries.DataSources.Add( tmpBarSeries1 ); ////// AV occurs here!!!
tmpLineSeries.DataSources.Add( tmpBarSeries2 );
// *Note - When populating your input Series manually you will need to
// use the Checkdatasource method
// - See the section entitled 'Defining a Datasource'
//Change the Period of the Function so that it groups averages
//every 2 Points
tmpLineSeries.FunctionType.Period := 2;
end;
It seems to either be a bug in TeeChart or I am missing a configuration step necessary for BarSeries that is not necessary for LineSeries.
Can anyone see what I am doing wrong, or alternatively suggest a workaround for the bug? I don't think upgrading to the latest version of TeeChart is an option at this stage because, as I understand it, this can only be done by upgrading Delphi (I'm already at the latest update of Delphi), or alternatively purchasing the standalone version of TeeChart.
This looks like a bug to me. I have add it (bug #1484) to Steema Software's bugzilla. The only workaround I can think of is setting TTeeFunction.Period property to a value greater than zero so the offending code is not executed. For example:
//workaround
tmpLineSeries.FunctionType.Period:=1;
tmpLineSeries.DataSources.Add( tmpBarSeries1 ); ////// AV occurs here!!!
tmpLineSeries.DataSources.Add( tmpBarSeries2 );
UPDATE: This has been fixed for the next TeeChart release.
I have downloaded opensource delphi twain component (TDelphiTwain).
The interesting thing is, that when placed and saved on the form it creates bad dfm entry for itself.
object DelphiTwain: TDelphiTwain
OnSourceDisable = DelphiTwainSourceDisable
OnSourceSetupFileXfer = DelphiTwainSourceSetupFileXfer
TransferMode = ttmMemory
SourceCount = 0
Info.MajorVersion = 1
Info.MinorVersion = 0
Info.Language = tlDanish
Info.CountryCode = 1
Info.Groups = [tgControl, tgImage, tgAudio, MinorVersion]
Info.VersionInfo = 'Application name'
Info.Manufacturer = 'Application manufacturer'
Info.ProductFamily = 'App product family'
Info.ProductName = 'App product name'
LibraryLoaded = False
SourceManagerLoaded = False
Left = 520
Top = 136
end
The problem is with the line:
Info.Groups = [tgControl, tgImage, tgAudio, MinorVersion]
There are only three possible elements:
tgControl, tgImage and tgAudio
It adds MinorVersion everytime I Save the form.
When the app is run I get the error that there is invalid property for Info.Groups.
When i rmeove the bad part manually and without leaving dfm file the app starts ok.
I looked in the internet and there was one inquire regarding these strange issue, unfortunately it hasn't been resolved.
I think that there is some sort of memory corruption. In the post in teh internet, strange signs were displayed ...
Has anyone worked with that component or could give me some hint how this could be fixed?
The error seems to be in TTwainIdentity.GetGroups where result is not initialized. You can try to change the code by replacing
Include(Result, tgControl);
with
Result := [tgControl];
You have to recompile the package to make this change work inside the IDE.
I don't know the component, but I think the problem lies in the TTwainIdentity.GetGroups method. It starts like this:
begin
Include(Result, tgControl);
This means that it assumes that Result is initialized to an empty set. However, Result may contain garbage, and not necessarily an empty set. Change this method to look like this:
function TTwainIdentity.GetGroups(): TTwainGroups;
{Convert from Structure.SupportedGroups to TTwainGroups}
begin
Result := [tgControl];
if DG_IMAGE AND Structure.SupportedGroups <> 0 then
Include(Result, tgImage);
if DG_AUDIO AND Structure.SupportedGroups <> 0 then
Include(Result, tgAudio);
end;
Some result types will not throw a compiler warning about not being initialized, but that doesn't mean they are empty. Same goes, for instance, for strings.
See also: http://qc.embarcadero.com/wc/qcmain.aspx?d=894
But still, it is odd that this happens. Apparently, Delphi tries to find the name of the given item in the set and accidentally finds the name of another property. It seems to me that quite some checks in writing the dfm are missing if this happens. :)
Given stage coordinates (x,y), I want to make my flash app behave just as if the user clicked at position (x,y). That is, something like
function simulateClick(x:Number, y:Number):void{
var e:MouseEvent = new MouseEvent(MouseEvent.CLICK, true, false, x, y)
stage.dispatchEvent(e);
}
I've found a bunch of pages talking about this kind of thing, and they all give solutions similar to the above. However, this isn't equivalent to the user clicking at (x,y). There are two problems:
The first is that e.stageX and e.stageY are both 0. I can't set them directly. The documentation says they are calculated when e.localX and e.localY are set, but this isn't happening when I set e.localX before dispatchEvent, nor in the event listener.
I could rewrite all of my event listeners with something like this:
var p:Point = e.target.localToGlobal(new Point(e.localX, e.localY));
Is this the only option?
The second problem is that my event listeners are registered with children of stage, not stage itself. So I need to find out what target to call dispatchEvent on. Clearly Flash is capable of determining what the target should be, ie which object owns the topmost pixel visible at position (x,y), because it does so when the user actually clicks. Is there an easy way to get at this information, or should I just write my own recursive function to do the same thing? I'm using DisplayObjectContainer.getObjectsUnderPoint at the moment, but it's not quite right.
I'm writing in FlashDevelop, if that makes any difference.
e.stageX/Y is populated correctly for me... also getObjectsUnderPoint() seems to work fine. I'm assuming that the x/y values passed to simulateClick are global coordinates?
edit: as pointed out in the comments, the mouse event must be dispatched on InteractiveObject instances... modified the code accordingly.
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.InteractiveObject;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.geom.Point;
public function simulateClick(x:Number, y:Number):void
{
var objects:Array = stage.getObjectsUnderPoint(new Point(x, y));
var target:DisplayObject;
while(target = objects.pop())
{
if(target is InteractiveObject)
{
break;
}
}
if(target !== null)
{
var local:Point = target.globalToLocal(new Point(x, y));
var e:MouseEvent = new MouseEvent(MouseEvent.CLICK, true, false, local.x, local.y);
target.dispatchEvent(e);
}
}
public function addedToStage():void
{
var parent:Sprite = new Sprite();
stage.addChild(parent);
var child:Sprite = new Sprite();
child.name = 'child 1';
child.graphics.beginFill(0xff0000, 1);
child.graphics.drawRect(0, 0, 200, 200);
child.graphics.endFill();
var child2:Sprite = new Sprite();
child2.name = 'child 2';
child2.graphics.beginFill(0xff00ff, 1);
child2.graphics.drawRect(0, 0, 100, 100);
child2.graphics.endFill();
child2.x = 150;
child2.y = 150;
var bmpData:BitmapData = new BitmapData(80, 80, false, 0x00ff00);
var bmp:Bitmap = new Bitmap(bmpData);
bmp.name = 'bitmap';
child2.addChild(bmp);
parent.addChild(child);
parent.addChild(child2);
child2.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void
{
trace('target: ' + e.target.name);
trace('localX: ' + e.localX);
trace('localY: ' + e.localY);
trace('stageX: ' + e.stageX);
trace('stageY: ' + e.stageY);
});
simulateClick(190, 190);
}
Output:
target: child 2
localX: 40
localY: 40
stageX: 190
stageY: 190
For question 1: After you create the MouseEvent (assigning it a local x,y) you should be able to directly reference e.stageX and set it to what you want prior to dispatching the event. It's just a property of the MouseEvent instance.
For #2, currentTarget is always the thing that is topmost under the mouse, while target is the thing that is dispatching the event -- assuming the event is genuinely being dispatched by mouse interaction. In your case, you can set the target to be whatever object you have dispatching the event, and set the currentTarget arbitrarily. The question really is whether this is the most efficient way to deal with what's under the mouse right now; and the answer is, probably not. You'd be a lot better off using a MOUSE_OVER event to keep tabs on what the mouse is over right now, store that as a variable you can use when you want to call this, and don't try to iterate the whole display chain all the time (because Flash natively does that much faster than you can do it in a loop). If you put a mouseOver on the stage, and just check the currentTarget, you'll be getting whatever the topmost item is under the mouse on every frame where it changes.
You should be aware that (to prevent some obvious nasty scripts), certain actions cannot be triggered by mouse events that are generated dynamically by actionscript. These include opening a file reference and going fullscreen.
I have faced this issue too, gave me a bit of a headache.
In my situation I was creating the event, performing a bit of complex computations, but I couldn't retrieve global coordinates even though I had already set local coordinates.
Actually the solution was quite obvious in my case...
Global coordinates are populated only AFTER the event is dispatched, otherwise how can the event know how to translate local to global?
This is another pitfall, on top of not checking for the object used to dispatch event being an InteractiveObject.
I post this because someone else may face this issue due to both pitfalls. A quick answer easy to read.
SOLUTION BELOW
I've been looking all over the net to find a solution for this, but it seems quite hard to get an answer for this in Delphi...
Skip this if you're familiar with Outlook
Some explanation before:
The Contacts Folder in Outlook is organized like a foldertree in Windows. The Contacts are stored in the Contacts Folder itself or within subfolders.
My Code does add Contacts from an external Database into the Outlook contacts Database. To prevent double entries the programm is supposed to check all contacts and see if it can find an 'older' version of the contact entry and update it, or if not, create a new one.
Therefore I wrote a recursion which loops through the folders and checks the contacts.
Within a folder you can get the subfolder by (besides Next, Previous and Last)
Contacts:= Contacts.Folders.Getfirst
//The now selected Folder is the first subfolder within the previous selected one
If I am trying to get any property of this Subfolder like 'Items.Count' or anything else, an error occurs because this folder doesn't exist.
Therefore I want to check if the Folder exists or not, and skip to loop through this subfolder because otherwise the loop would break here and the program stops.
Skip until here if you're familiar with Outlook workings
THE PROBLEM:
In Debugger this Contacts/Folder Variable (an OleVariant, Pointer to the now selected Folder) contains values similar to this: '$0074974C'.
If there is no subfolder this value returns '$00000000'. This seems to be a pointer.
How should I check if a folder exists or not?
const
olFolderContacts = $0000000A;
var
outlook, NameSpace, Contact, ContactsRoot, Contacts: OleVariant;
begin
Outlook := CreateOleObject('Outlook.Application');
NameSpace := Outlook.GetNameSpace('MAPI');
ContactsRoot := NameSpace.GetDefaultFolder(olFolderContacts);
Contacts:= ContactsRoot;
//We're now in the Contacts Folder
Contacts:= Contacts.folders.getfirst;
//First Subfolder
What didn't work:
Check if
Contacts = '$00000000' (As string)
Contacts = '$00000000' (As OleVariant)
var
val:TVarRec;
code:
val:=Contacts;
string(Contacts.VWideChar) = '$00000000'
var
vntNothing: OLEVariant;
code:
TVarData(vntNothing).VType := varDispatch;
TVarData(vntNothing).VDispatch := Nil;
Contacts = vntNothing
Contacts = unassigned
...
...
In VBA this problem has a simple solution
if Contacts = Nothing
But there is no 'Nothing' in Delphi...
Ideas?
You could first check the count on the Folders collection:
if Contacts.Folders.Count = 0 then
or
Contacts := Contacts.Folders.GetFirst;
if VarIsClear(Contacts) then
You could try this:
if IUnknown(Contacts) = nil then
//
var
x: string;
in code:
x:= format('%p%',[Pointer(TVarData(contacts).VDispatch)]);
if x = '00000000' then
'New Contact'
else
'open folder and search within this one'
Co-worker had the solution.. Thanks for your time :)
I wanna insert a region in an edit view and then fold this region.
// fEditView: IOTAEditView;
var
writer: IOTAEditWriter;
begin
writer := fEditView.Buffer.CreateUndoableWriter;
//...
writer.Insert('{$REGION ''Documentation''}'#13#10'{$ENDREGION}');
writer := nil; // Flush the buffer
fEditView.Position.GotoLine(lineNo); // go to the line number of the region
fEditView.Paint;
end;
This code snippet will insert a region in the code editor. But the IDE needs some action to generate such a region in the code editor.
Is there any way to force the IDE do this action and then I can use
(fEditView as IOTAElideActions).ElideNearestBlock;
to fold it?
Found by grubby trial-and-error :-)
(fEditView as IOTAElideActions).EnableElisions;
(fEditView as IOTAElideActions).EnableElisions;
(fEditView as IOTAElideActions).ElideNearestBlock;
EnableElisions() appears to actually toggle the elisions feature.
Turning elisions off and back on again seems to have the desired effect. There is some not very pretty screen updating going on. Whether it is possible to suppress that I don't know.