Block preview in a gallery
Posted: 21 May 2013, 16:31
Hello,
I'm trying to create a gallery in my application. This gallery should display thumbnails for each block found into a DXF/DWG File.
First solution I tried :
Create a new TsgCADImage for each thumbnail, copy the block entity to it from the original TsgCADImage (using assignEntity, and also copy all entities of the original block...), create an insert and assign the new TsgCADImage to a TImage.picture.graphic property.
It works quite correctly, although I can't free the original TsgCADImage before the new ones. After having read this forum I think some objects of the original CADImage are linked to my new entities, and should be copied manually if I wanted to solve this problem.
But the main problem of this solution is the amount of memory it takes. For an original CADImage which contains about 70 blocks and takes about 4 Mo into memory, The creation of 70 new CADImages with one block each one takes about 250 Mo ! And the time it takes to be displayed at screen is rather long. So I decided to search for another solution.
Second solution I tried :
Create only one TsgCADImage to load the file, and find a way to display a block onto a canvas. I found a procedure DrawRect in TsgCADImage, so I tried to create another procedure TsgCADImage.drawBlock, with the same principle. In fact I needed to have only one block inserted into my drawing, and then I could call draw like it is done into DrawRect. So this is my code :
With this method, my thumbnails are drawn rather quickly, and it takes a negligible amount of memory.
But when I free the CADImage, I have some access violation in TsgDXFConverter.Clear procedure, into the call to FBlocks.Clear ; it seems like blocks are already released.
Just to see what I do with this procedure, this my calling code :
Third solution :
I think it would be possible to make the same drawing without creating a layout and an insert, but I didn't reach to make something work correctly this way.
This is what I tried :
- create a property FDrawBlock:TsgDXFBlock in TsgCADImage.
- assign this property with the block before calling Draw into the procedure DrawBlock, and clear this property after.
- in the procedure TsgCADImage.Draw, if property FDrawBlock is assigned, iterate through the block intead of iterate through currentLayout.
- in the procedure TsgCADImage.Draw, modify evaluation expression for vRectVisible : vRectVisible := (FDrawBlock<>nil) or IsEntityInRectVisible(vTmpIRect);
It works only if layout and insert are created into the procedure DrawBlock, or else I've a stack overflow into realLayer function.
So please could you help me to make the second or third solution work ? I think I'm near my goal, but now I have no more idea.
I'm trying to create a gallery in my application. This gallery should display thumbnails for each block found into a DXF/DWG File.
First solution I tried :
Create a new TsgCADImage for each thumbnail, copy the block entity to it from the original TsgCADImage (using assignEntity, and also copy all entities of the original block...), create an insert and assign the new TsgCADImage to a TImage.picture.graphic property.
It works quite correctly, although I can't free the original TsgCADImage before the new ones. After having read this forum I think some objects of the original CADImage are linked to my new entities, and should be copied manually if I wanted to solve this problem.
But the main problem of this solution is the amount of memory it takes. For an original CADImage which contains about 70 blocks and takes about 4 Mo into memory, The creation of 70 new CADImages with one block each one takes about 250 Mo ! And the time it takes to be displayed at screen is rather long. So I decided to search for another solution.
Second solution I tried :
Create only one TsgCADImage to load the file, and find a way to display a block onto a canvas. I found a procedure DrawRect in TsgCADImage, so I tried to create another procedure TsgCADImage.drawBlock, with the same principle. In fact I needed to have only one block inserted into my drawing, and then I could call draw like it is done into DrawRect. So this is my code :
Code: Select all
procedure TsgCADImage.DrawBlock(canvas:TCanvas; Block: TsgDXFBlock; DestRect: TRect);
var
vInsert: TsgDXFInsert;
vLayout: TsgDXFLayout;
oldLayout: TsgDXFLayout;
oRatio,dRatio,l:real;
begin
if FCurrentLayout = nil then Exit;
try
oldLayout:=CurrentLayout;
vLayout:=TsgDXFLayout.Create;
Converter.AddLayout(vLayout);
CurrentLayout:=vLayout;
vInsert:=TsgDXFInsert.Create;
if assigned(Converter.OnCreate) then
Converter.OnCreate(vInsert);
vInsert.Block:=Block;
vInsert.Point := MakeFPoint(0, 0, 0);
CurrentLayout.AddEntity(vInsert);
converter.Loads(vInsert);
GetExtents;
oRatio:=(Extents.Right-Extents.Left)/(Extents.Top-Extents.Bottom);
dRatio:=(destRect.Right-destRect.Left)/(destRect.Bottom-destRect.Top);
if dRatio>oRatio then
begin
l:=(destRect.Bottom-destRect.Top)*oRatio;
destRect.Left:=destRect.Left + round((destRect.Right-destRect.Left-l)/2);
destRect.Right:=destRect.Right - round((destRect.Right-destRect.Left-l)/2);
end
else
begin
l:=(destRect.Right-destRect.Left)/oRatio;
destRect.top:=destRect.top + round((destRect.Bottom-destRect.top-l)/2);
destRect.Bottom:=destRect.Bottom - round((destRect.Bottom-destRect.top-l)/2);
end;
Draw(canvas, DestRect);
converter.RemoveEntity(vInsert,true);
CurrentLayout:=oldLayout;
converter.DeleteLayout(vLayout);
finally
end;
end;
But when I free the CADImage, I have some access violation in TsgDXFConverter.Clear procedure, into the call to FBlocks.Clear ; it seems like blocks are already released.
Just to see what I do with this procedure, this my calling code :
Code: Select all
for ioBlock:=0 to sgImg.Converter.Counts[csBlocks]-1 do
begin
oblock:=sgImg.Converter.Blocks[ioBlock];
if (not oBlock.SpecPaperSpace) or (oBlock.name='*[Model]') then
begin
panel:=AdvPanelGroup1.Panels[icBlock]; //the exact number of panels are created earlier in my code
img:=TImage.Create(panel);
img.Parent:=panel;
Img.Align:=alClient;
sgImg.DrawBlock(img.canvas,oBlock,rect(0,0,img.width,img.height));
inc(icBlock);
end;
end;
I think it would be possible to make the same drawing without creating a layout and an insert, but I didn't reach to make something work correctly this way.
This is what I tried :
- create a property FDrawBlock:TsgDXFBlock in TsgCADImage.
- assign this property with the block before calling Draw into the procedure DrawBlock, and clear this property after.
- in the procedure TsgCADImage.Draw, if property FDrawBlock is assigned, iterate through the block intead of iterate through currentLayout.
- in the procedure TsgCADImage.Draw, modify evaluation expression for vRectVisible : vRectVisible := (FDrawBlock<>nil) or IsEntityInRectVisible(vTmpIRect);
It works only if layout and insert are created into the procedure DrawBlock, or else I've a stack overflow into realLayer function.
So please could you help me to make the second or third solution work ? I think I'm near my goal, but now I have no more idea.