418 lines
15 KiB
C++
418 lines
15 KiB
C++
#include "StdAfx.h"
|
|
#include "myEPlotSection.h"
|
|
#include "../SLNKDWFImpl/CxImage\CxImage\ximage.h"
|
|
#include "../SLNKDWFImpl/Whip2DCImpl.h"
|
|
#include "SLNKContourImpl.h"
|
|
#include "SLNKSymbolDefImpl.h"
|
|
#include <math.h>
|
|
|
|
// Bij EPlotsection (external DWF)
|
|
CSLNKSymbolDefinition::CSLNKSymbolDefinition(CComQIPtr<IEPlotSection> EPlotSection)
|
|
{
|
|
m_iEPlotSection = EPlotSection;
|
|
|
|
CEPlotSectionImpl *epli;
|
|
m_iEPlotSection->get_EPlotSectionImpl((BYTE **)&epli);
|
|
m_wtFile.set_eplotsection(epli);
|
|
m_AsBitmap = NULL;
|
|
m_BuiltIn = false;
|
|
m_hasBitmap = false;
|
|
|
|
calculateBoundary(m_BoundingContour); // rekent ook BoundingContour.m_Units van het symbool uit
|
|
|
|
m_Origin = CSLNKContourImpl::Centroid(m_BoundingContour);
|
|
};
|
|
|
|
// Define symbol by W2D path name
|
|
CSLNKSymbolDefinition::CSLNKSymbolDefinition(CString W2Dfilepath)
|
|
{
|
|
m_wtFile.set_filename(W2Dfilepath);
|
|
|
|
m_AsBitmap = NULL;
|
|
m_BuiltIn = false;
|
|
m_hasBitmap = false;
|
|
|
|
calculateBoundary(m_BoundingContour); // rekent ook BoundingContour.m_Units van het symbool uit
|
|
|
|
m_Origin = CSLNKContourImpl::Centroid(m_BoundingContour);
|
|
};
|
|
|
|
// Bij Contour list (builtin symbol)
|
|
CSLNKSymbolDefinition::CSLNKSymbolDefinition(int count, WT_Logical_Point const *points, WT_Units units)
|
|
{
|
|
m_BuiltIn = true;
|
|
m_wtFile.set_eplotsection(NULL);
|
|
this->m_dwgScale = 1.0;
|
|
m_AsBitmap = NULL;
|
|
m_hasBitmap = false;
|
|
m_BoundingContour.m_Units = units;
|
|
m_BoundingContour.set(count, points, false);
|
|
m_Origin = CSLNKContourImpl::Centroid(m_BoundingContour);
|
|
};
|
|
|
|
// By bitmap
|
|
CSLNKSymbolDefinition::CSLNKSymbolDefinition(CString const filepath, double const height, WT_Units units)
|
|
{
|
|
m_wtFile.set_eplotsection(NULL);
|
|
m_AsBitmap = NULL;
|
|
m_BuiltIn = false;
|
|
m_BoundingContour.m_Units = units;
|
|
CxImage img;
|
|
if (!img.Load(filepath))
|
|
{ // Rood kruisje?
|
|
img.Create(32, 32, 24);
|
|
img.Clear(255); // Wit
|
|
img.DrawLine(0,31,0,31,RGB(255,0,0));
|
|
img.DrawLine(0,31,31,0,RGB(255,0,0));
|
|
};
|
|
BYTE *m_Buffer=NULL;
|
|
long m_size;
|
|
img.Encode(m_Buffer, m_size, CXIMAGE_FORMAT_PNG);
|
|
|
|
this->m_dwgScale = (double)img.GetHeight()/ height;
|
|
|
|
//myDoTRACE("\nCreates symbol bitmap (%d,%d)->PNG %d byte (W2D was %d)", pixeldx,pixeldy,m_size, epli->size());
|
|
|
|
m_AsBitmap = new
|
|
WT_PNG_Group4_Image(
|
|
(int)img.GetHeight(), /*rows*/
|
|
(int)img.GetWidth(), /*cols*/
|
|
WT_PNG_Group4_Image::PNG,
|
|
3,
|
|
NULL,
|
|
m_size,
|
|
(WT_Byte*)m_Buffer,
|
|
WT_Logical_Point(0,0),
|
|
WT_Logical_Point(img.GetWidth(),img.GetHeight()),
|
|
WD_False); // Geen copy, we verwijderen zelf expliciet
|
|
|
|
WT_Logical_Point pts4[4] = { WT_Logical_Point(0, 0),
|
|
WT_Logical_Point(img.GetWidth(), 0),
|
|
WT_Logical_Point(img.GetWidth(), img.GetHeight()),
|
|
WT_Logical_Point(0, img.GetHeight())};
|
|
m_BoundingContour.set(4, pts4, true);
|
|
m_Origin = CSLNKContourImpl::Centroid(m_BoundingContour);
|
|
};
|
|
|
|
CSLNKSymbolDefinition::~CSLNKSymbolDefinition(void)
|
|
{
|
|
if (m_AsBitmap)
|
|
{
|
|
CxImage xx;xx.FreeMemory((void *)m_AsBitmap->data()); // We hebben een copy gedaan dus dit mag/moet
|
|
delete m_AsBitmap;
|
|
}
|
|
}
|
|
|
|
// Bepaal een bounding contour van ons symbool. Die is eventueel later leuk te gebruiken voor een HitTest
|
|
// v0: bounding box
|
|
// v1: bounding octagon
|
|
// Algoritm: search google for Bounding Octagon
|
|
// http://geometryalgorithms.com/Archive/algorithm_0107/algorithm_0107.htm
|
|
// (v2: bounding convex hull waarschijnlijk overkill)
|
|
//#include <assert.h>
|
|
WT_Result CSLNKSymbolDefinition::calculateBoundary (CSLNKContourImpl &BoundingContour, WT_Transform *tm/*=null*/)
|
|
{
|
|
WT_Result result;
|
|
|
|
if (m_BuiltIn || m_AsBitmap != NULL)
|
|
{
|
|
return WT_Result::Success;
|
|
}
|
|
|
|
m_wtFile.set_file_mode(WT_File::File_Read);
|
|
result = m_wtFile.open();
|
|
if (result != WT_Result::Success)
|
|
throw myCString("ERROR: Unable to open symbol file");
|
|
|
|
if (tm != NULL)
|
|
{
|
|
m_wtFile.heuristics().set_transform(*tm);
|
|
m_wtFile.heuristics().set_apply_transform(true);
|
|
}
|
|
|
|
// Door de enorm grote dwf-coordinaten lopen we een integer overflow risico tijdens
|
|
// onze bepaling van Pmin en Pmax (die min en max van x+y bepalen) en in theorie
|
|
// ook van Qmin en Qmax (die over het verschil x-y gaan) hoewel dat in de praktijk rond 0 zal liggen
|
|
// Daarom rekenen we systematisch met de helft. Dat moet risico's voorkomen en de afrondfout nemen
|
|
// we voor lief
|
|
// Merk op dat de resulterende Octagon kleiner is dan de de bounding box, die zeker binnen de coordinaat
|
|
// ruimte valt dus voor het eindresultaat is er geen risico. Alleen oppassen voor tussenresultaten dus
|
|
// Ging concreet mis met 41610ev_symbols.dwf
|
|
WT_Integer32 Xmin, Xmax, Ymin, Ymax;
|
|
WT_Integer32 Pmin2, Pmax2, Qmin2, Qmax2;
|
|
// Signal invalid
|
|
Xmin = Ymin = Pmin2 = Qmin2 = INT_MAX;
|
|
Xmax = Ymax = Pmax2 = Qmax2 = INT_MIN;
|
|
|
|
BOOL ViewportDone = FALSE; // We doen alleen de eerste viewport.
|
|
// Gaf problemen bij symbolen die vanuit paperspace waren geexporteerd
|
|
// Do the actual reading.
|
|
while ((result = m_wtFile.process_next_object()) == WT_Result::Success)
|
|
{
|
|
//myTRACE("Attribute '%s'\n", wtFile->file_stats()->descriptions());
|
|
if (!m_wtFile.rendition().visibility().visible())
|
|
continue;
|
|
|
|
switch(m_wtFile.current_object()->object_type())
|
|
{
|
|
case WT_Object::Attribute:
|
|
{
|
|
const WT_Attribute *obj = (WT_Attribute *)m_wtFile.current_object();
|
|
if (obj->object_id() == WT_Object::Viewport_ID)
|
|
{
|
|
if (ViewportDone)
|
|
{
|
|
myTRACE("\nSkipping second viewport in symbol");
|
|
break;
|
|
}
|
|
ViewportDone = TRUE;
|
|
WT_Viewport *viewport = (WT_Viewport *)m_wtFile.current_object();
|
|
BoundingContour.m_Units = viewport->viewport_units(); // Die onthouden we
|
|
m_dwgScale = BoundingContour.m_Units.application_to_dwf_transform()(0,0); // Symbol's dScale
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case WT_Object::Drawable:
|
|
{
|
|
WT_Drawable *obj = (WT_Drawable *)m_wtFile.current_object();
|
|
|
|
if (obj->object_id() != WT_Object::Origin_ID) // Belachelijk dat die Drawable is?
|
|
{
|
|
WT_Logical_Box bx = obj->bounds(&m_wtFile);
|
|
Xmin = min(Xmin, bx.minpt().m_x);
|
|
Ymin = min(Ymin, bx.minpt().m_y);
|
|
Xmax = max(Xmax, bx.maxpt().m_x);
|
|
Ymax = max(Ymax, bx.maxpt().m_y);
|
|
|
|
// Voor echte (deel)cirkels gaan we de diagonaal bounding box scherper berekenen
|
|
if (obj->object_id() == WT_Object::Outline_Ellipse_ID || obj->object_id() == WT_Object::Filled_Ellipse_ID)
|
|
{//continue;
|
|
WT_Ellipse *elp = (WT_Ellipse *)obj;
|
|
if (elp->minor() == elp->major()) // Anders te ingewikkeld
|
|
{ // Bepaal het ingeschreven vierkant van de cirkel
|
|
WT_Integer32 dr = myRound(elp->minor() / sqrt(2.0) + 0.5); // Altijd omhoog afronden
|
|
WT_Logical_Box bx2;
|
|
bx2.minpt() = WT_Logical_Point(elp->position().m_x - dr, elp->position().m_y - dr);
|
|
bx2.maxpt() = WT_Logical_Point(elp->position().m_x + dr, elp->position().m_y + dr);
|
|
// Houdt er wel rekening mee dat bij partiele cirkels het origineel scherper kan
|
|
// zijn omdat wij er geen rekening houden dat de cirkel misschien partieel is
|
|
Pmin2 = min(Pmin2, max(bx.minpt().m_x/2 + bx.minpt().m_y/2,bx2.minpt().m_x/2 + bx2.minpt().m_y/2)); // Linksonder
|
|
Qmax2 = max(Qmax2, min(bx.maxpt().m_x/2 - bx.minpt().m_y/2,bx2.maxpt().m_x/2 - bx2.minpt().m_y/2)); // Rechtsonder
|
|
Pmax2 = max(Pmax2, min(bx.maxpt().m_x/2 + bx.maxpt().m_y/2,bx2.maxpt().m_x/2 + bx2.maxpt().m_y/2)); // Rechtsboven
|
|
Qmin2 = min(Qmin2, max(bx.minpt().m_x/2 - bx.maxpt().m_y/2,bx2.minpt().m_x/2 - bx2.maxpt().m_y/2)); // Linksboven
|
|
break;
|
|
}
|
|
// else gewoon 'doorvallen' naar standaard Pmin2 etc.
|
|
}
|
|
|
|
// Voor WT_Pointset gebaseerde objecten kunnen we het ook wel beter
|
|
if (obj->object_id() == WT_Object::Polygon_ID ||
|
|
obj->object_id() == WT_Object::Polyline_ID ||
|
|
obj->object_id() == WT_Object::Polytriangle_ID)
|
|
{
|
|
WT_Point_Set *pts;
|
|
// Rechtstreekse cast naar (WT_Point_Set *) gaf memory violations?
|
|
switch (obj->object_id())
|
|
{
|
|
case WT_Object::Polygon_ID : pts = (WT_Polygon *)m_wtFile.current_object(); break;
|
|
case WT_Object::Polyline_ID : pts = (WT_Polyline *)m_wtFile.current_object(); break;
|
|
case WT_Object::Polytriangle_ID: pts = (WT_Polytriangle *)m_wtFile.current_object(); break;
|
|
}
|
|
|
|
for (int i=0;i<pts->count(); i++)
|
|
{
|
|
WT_Logical_Point &pt = pts->points()[i];
|
|
Pmin2 = min(Pmin2, pt.m_x/2 + pt.m_y/2); // Linksonder
|
|
Qmax2 = max(Qmax2, pt.m_x/2 - pt.m_y/2); // Rechtsonder
|
|
Pmax2 = max(Pmax2, pt.m_x/2 + pt.m_y/2); // Rechtsboven
|
|
Qmin2 = min(Qmin2, pt.m_x/2 - pt.m_y/2); // Linksboven
|
|
}
|
|
break; // we zijn wel klaar
|
|
}
|
|
|
|
Pmin2 = min(Pmin2, bx.minpt().m_x/2 + bx.minpt().m_y/2); // Linksonder
|
|
Qmax2 = max(Qmax2, bx.maxpt().m_x/2 - bx.minpt().m_y/2); // Rechtsonder
|
|
Pmax2 = max(Pmax2, bx.maxpt().m_x/2 + bx.maxpt().m_y/2); // Rechtsboven
|
|
Qmin2 = min(Qmin2, bx.minpt().m_x/2 - bx.maxpt().m_y/2); // Linksboven
|
|
}
|
|
// Ook maar even hasBitmap bepalen
|
|
if (obj->object_id() == WT_Object::Image_ID)
|
|
{
|
|
m_hasBitmap = true;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
//myTRACE("Skipping '%s'\n", wtFile->file_stats()->descriptions());
|
|
break;
|
|
}
|
|
}
|
|
// if (result == WT_Result::End_Of_DWF_Opcode_Found)
|
|
// dwfresult = DwfResult::Success;
|
|
|
|
m_wtFile.heuristics().set_apply_transform(false);
|
|
m_wtFile.close();
|
|
|
|
// Rechthoek
|
|
// WT_Logical_Point pts1[4] = { WT_Logical_Point(Xmin, Ymin),
|
|
// WT_Logical_Point(Xmax, Ymin),
|
|
// WT_Logical_Point(Xmax, Ymax),
|
|
// WT_Logical_Point(Xmin, Ymax)};
|
|
//m_BoundingBox = WT_Logical_Box(Xmin, Ymin, Xmax, Ymax);
|
|
|
|
// Diamant heeft overflow risico
|
|
// WT_Logical_Point pts2[4] = { WT_Logical_Point(Pmin2+Qmin2, Pmin2-Qmin2),
|
|
// WT_Logical_Point(Pmin2+Qmax2, Pmin2-Qmax2),
|
|
// WT_Logical_Point(Pmax2+Qmax2, Pmax2-Qmax2),
|
|
// WT_Logical_Point(Pmax2+Qmin2, Pmax2-Qmin2)};
|
|
|
|
// Octagon
|
|
// Expressievolgorde is telkens belangrijk om overflow te voorkomen
|
|
WT_Logical_Point pts3[8] = { WT_Logical_Point(Pmin2-Ymin+Pmin2, Ymin),
|
|
WT_Logical_Point(Qmax2+Ymin+Qmax2, Ymin),
|
|
WT_Logical_Point(Xmax, Xmax-Qmax2-Qmax2),
|
|
WT_Logical_Point(Xmax, Pmax2-Xmax+Pmax2),
|
|
WT_Logical_Point(Pmax2-Ymax+Pmax2, Ymax),
|
|
WT_Logical_Point(Qmin2+Ymax+Qmin2, Ymax),
|
|
WT_Logical_Point(Xmin, Xmin-Qmin2-Qmin2),
|
|
WT_Logical_Point(Xmin, Pmin2-Xmin+Pmin2)};
|
|
|
|
BoundingContour.set(sizeof(pts3)/sizeof(pts3[0]), pts3, true);
|
|
|
|
return result;
|
|
}
|
|
|
|
WT_Result CSLNKSymbolDefinition::serialize(WT_File & file, WT_Color pColor, BOOL pColorSet, WT_Integer32 pLineweight)
|
|
{
|
|
if (m_AsBitmap)
|
|
{ // Wel even kopietje trekken omdat anders niet opnieuw getransformeerd wordt
|
|
WT_PNG_Group4_Image(
|
|
m_AsBitmap->rows(), /*rows*/
|
|
m_AsBitmap->columns(), /*cols*/
|
|
WT_PNG_Group4_Image::PNG,
|
|
3,
|
|
NULL,
|
|
m_AsBitmap->data_size(),
|
|
(WT_Byte *)m_AsBitmap->data(),
|
|
m_AsBitmap->bounds().minpt(),
|
|
m_AsBitmap->bounds().maxpt(),
|
|
WD_False).serialize(file);
|
|
return WT_Result::Success;
|
|
}
|
|
|
|
if (m_BuiltIn) // Die hebben alleen een bounding contour
|
|
return WT_Result::Success;
|
|
|
|
// Vertrouw de huidige rendition niet! Het eerste lijntje van een symbool kan default wit verwachten (CO206.DWF)
|
|
// Tijdens het serializen van het symbool hebben we niets met (desired)_rendition te maken
|
|
if (pColorSet)
|
|
pColor.serialize(file);
|
|
else
|
|
WT_Color().serialize(file); // Op de default zetten.
|
|
|
|
if (pLineweight < 0)
|
|
file.desired_rendition().line_weight() = WT_Line_Weight(0);
|
|
else
|
|
file.desired_rendition().line_weight() = WT_Line_Weight(pLineweight);
|
|
file.desired_rendition().line_weight().serialize(file);
|
|
|
|
//TODO: Moet eigenlijk voor alle attributen
|
|
file.desired_rendition().font() = WT_Font();
|
|
WT_Font().serialize(file);
|
|
|
|
WT_Result result;
|
|
// Open onze symbool DWF
|
|
m_wtFile.set_file_mode(WT_File::File_Read);
|
|
result = m_wtFile.open();
|
|
if (result != WT_Result::Success)
|
|
return result; // Waarschijnlijk builtin symbol?
|
|
|
|
// Op zich is er minder kans op overflow als we het symbool tijden het inlezen
|
|
// al richting (0,0) zetten. Dan wordt echter niet meer de definitieve transform
|
|
// uitgevoerd omdat m_transformed op WD_True staat.
|
|
// WT_Logical_Point lshift(- m_Origin.m_x,- m_Origin.m_y);
|
|
//
|
|
// m_wtFile.heuristics().set_transform(WT_Transform (lshift, 0, 0, 0));
|
|
// m_wtFile.heuristics().set_apply_transform(true);
|
|
|
|
// Do the actual reading.
|
|
// Een apply_transform is door de aanroeper al op file gezet.
|
|
// Dankzij set_apply_transform gaat de rest miraculeus vanzelf
|
|
while ((result = m_wtFile.process_next_object()) == WT_Result::Success)
|
|
{
|
|
//myTRACE("Attribute '%s'\n", wtFile->file_stats()->descriptions());
|
|
switch(m_wtFile.current_object()->object_type())
|
|
{
|
|
case WT_Object::Drawable:
|
|
#ifdef _DEBUG
|
|
if (m_wtFile.current_object()->object_id() == WT_Object::Origin_ID
|
|
&& !file.heuristics().allow_binary_data()
|
|
)
|
|
{ // Origin is niet toegestaan bij ASCII
|
|
break;
|
|
}
|
|
#endif
|
|
m_wtFile.current_object()->serialize(file);
|
|
break;
|
|
case WT_Object::Attribute:
|
|
{
|
|
const WT_Attribute *obj = (WT_Attribute *)m_wtFile.current_object();
|
|
// myTRACE("Attribute '%s'\n", my_plan_file.file_stats()->descriptions());
|
|
// Hoewel een Attibute gaat hij toch niet via desired_rendition
|
|
// Omdat we weten dat we net uit uit een DWF komen kunnen we best
|
|
// wel rechtstreeks serializen
|
|
// Let wel: desired_rendition is dan niet meer te vertrouwen. Zie @@@
|
|
if (obj->object_id() == WT_Object::View_ID)
|
|
{
|
|
// De view komt VOOR de Viewport dus wordt niet goed vertaald
|
|
// bovendien doen we er toch niets mee
|
|
break;
|
|
}
|
|
if (obj->object_id() == WT_Object::Layer_ID)
|
|
{
|
|
// Zou toch wel erg complex worden. Werkt ook maar matig
|
|
break;
|
|
}
|
|
if (obj->object_id() == WT_Object::Line_Weight_ID)
|
|
{
|
|
if (pLineweight >= 0) // Hoeven we niet steeds weer te zetten
|
|
{
|
|
break;
|
|
}
|
|
// Lineweight wordt door de DWF kit niet getransformeerd
|
|
// daarom doen we dat maar zelf. (Origineel ongemoeid laten)
|
|
double mul = file.heuristics().transform().m_x_scale; // x en y zijn toch gelijk
|
|
WT_Line_Weight wl;
|
|
wl.weight_value() = myRound(((WT_Line_Weight *)obj)->weight_value() * mul);
|
|
wl.serialize(file);
|
|
break;
|
|
}
|
|
if (obj->object_id() == WT_Object::Color_ID)
|
|
{ // Misschien een zwart-wit toggle doorvoeren? Of zelfs highlight?
|
|
// m_iEPlotSection->get_PaperColor()
|
|
if (pColorSet) // Hoeven we niet steeds weer te zetten
|
|
{
|
|
break;
|
|
}
|
|
//else gewoon naar de default doorvallen
|
|
}
|
|
if (obj->object_id() == WT_Object::Viewport_ID)
|
|
{
|
|
break; // Voorkom dat het Viewport object in de DWF komt
|
|
// Dan krijgen we last omdat we er op clippen
|
|
// eigenlijk moeten we de originele viewport
|
|
// terug zetten na afloop
|
|
}
|
|
obj->serialize(file);
|
|
}
|
|
break;
|
|
default:
|
|
//myTRACE("Skipping '%s'\n", wtFile->file_stats()->descriptions());
|
|
break;
|
|
}
|
|
}
|
|
m_wtFile.close();
|
|
return WT_Result::Success;
|
|
}
|