Files
Slnkdwf/SlnkDWFCom/WhipFile.cpp
Jos Groot Lipman 6e502e509d V4.30 23-08-2022
- SZDB#82496 plaatjes verkleinen met Resample2 ipv. Resample
  - CIZN#82871 tekeningen met zwarte lijnen forceren (whipfile.forcePlanColor)

svn path=/Slnkdwf/trunk/; revision=65166
2024-06-17 10:32:44 +00:00

1761 lines
59 KiB
C++
Raw Blame History

/******************************************************************************
* File : WhipFile.cpp (c) 2005-2011, SG|Facilitor
*
* Author : J. Groot Lipman
* Project : SLNKDWF
* Version : 2.xx
*
* Function : Implementeert class CWhipFile
* CWhipfile biedt functionaliteit om een bestaande DWF te
* manipuleren en het resultaat op te slaan als een nieuwe DWF
* - Contouren kunnen herkend worden (polygon op goede laag
* met een label erin op de goede laag)
* - Deze contouren kunnen gemanipuleerd worden (kleuren bijv)
* - Deze labels kunnen gemanipuleerd worden (text, positie)
* - Deze contouren worden bij het wegschrijven 'gewrapped' in
* een node objecten. Deze wordt opgeleverd door Whip2DC bij
* aanwijzen
* - Symbolen kunnen worden toegevoegd
*****************************************************************************/
#include "stdafx.h"
#include "myEPlotSection.h"
#include "WhipFile.h"
#include "SLNKContour.h"
#include "SLNKDWFVersion.h"
//
// WHIPFILE.CPP
//
// This file defines class CWhipFile
// which is mostly concerned with detecting closed contours
// and merging a Contour file with a planfile. <<= jun 2007: al lang obsolete
//
// CWhipFile
const WT_Logical_Point CWhipFile::star[] =
{ WT_Logical_Point(-1000, 0),
WT_Logical_Point( -200, 200),
WT_Logical_Point( 0, 1000),
WT_Logical_Point( 200, 200),
WT_Logical_Point( 1000, 0),
WT_Logical_Point( 200, -200),
WT_Logical_Point( 0,-1000),
WT_Logical_Point( -200, -200),
WT_Logical_Point(-1000, 0)
};
const WT_Logical_Point CWhipFile::octa[] =
{ WT_Logical_Point( 1000, 414),
WT_Logical_Point( 414, 1000),
WT_Logical_Point( -414, 1000),
WT_Logical_Point(-1000, 414),
WT_Logical_Point(-1000, -414),
WT_Logical_Point( -414,-1000),
WT_Logical_Point( 414,-1000),
WT_Logical_Point( 1000, -414),
WT_Logical_Point( 1000, 414)
};
const WT_Logical_Point CWhipFile::square[] =
{ WT_Logical_Point( 0, 0),
WT_Logical_Point( 0, 1000),
WT_Logical_Point( 1000, 1000),
WT_Logical_Point( 1000, 0),
WT_Logical_Point( 0, 0)
};
const WT_Logical_Point CWhipFile::empty[] = // Lege contour
{ WT_Logical_Point( INT_MAX, INT_MAX)
};
extern HINSTANCE g_hInstance;
CWhipFile::CWhipFile()
{
m_FontName.set("Arial");
m_FontHeight = 400.0;
m_FontHeightSymbols = 200.0;
m_SymbolFontName.set("Font Awesome 5 Pro Regular");
m_SymbolFontHeight = 1000.0;
m_reContouren.Parse(".*", FALSE);
m_reLabels.Parse(".*", FALSE);
m_reLayers.Parse(".*", FALSE);
m_hintScale = -1.0;
m_minContSize = 0.20e6; // Minimale opp om herkend te worden.
m_minMergeDistance = - 1.0; // Minimale mm afstand om lijnsegmenten te mergen, default uit
m_forFind = TRUE; // backward compatible
m_activeLayerName = "";
m_vdpi = 0;
m_builder_len = 0;
m_flags = 0;
m_forcePlan = FALSE;
// Predefine fixed symbols
// Merk op dat m_contunits hier nog gewoon de default identity matrix is
CSLNKSymbolDefinition * symb = new CSLNKSymbolDefinition(sizeof(star)/sizeof(star[0]), star, m_contunits);
m_SLNKSymbolDefinitions.SetAt("*STAR", symb);
symb = new CSLNKSymbolDefinition(sizeof(octa)/sizeof(octa[0]), octa, m_contunits);
m_SLNKSymbolDefinitions.SetAt("*OCTAGON", symb);
symb = new CSLNKSymbolDefinition(sizeof(square)/sizeof(square[0]), square, m_contunits);
m_SLNKSymbolDefinitions.SetAt("*SQUARE", symb);
symb = new CSLNKSymbolDefinition(sizeof(empty)/sizeof(empty[0]), empty, m_contunits);
m_SLNKSymbolDefinitions.SetAt("*EMPTY", symb);
}
int xxxx;
STDMETHODIMP CWhipFile::InterfaceSupportsErrorInfo(REFIID riid)
{
static const IID* arr[] =
{
&IID_IWhipFile
};
for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
if (InlineIsEqualGUID(*arr[i],riid))
return S_OK;
}
return S_FALSE;
}
// Open de contour file
STDMETHODIMP CWhipFile::Load(BSTR WhipPath)
{
myDoTRACE("\nCWhipFile::Open('%ls')", WhipPath);
m_W2DFile.set_filename(WhipPath);
return ProcessContouren();
}
// Set xc = CreateObject("SLNKDWF.DWFFile")
// xc.Open(dwfFile)
// Set y = CreateObject("SLNKDWF.WhipFile")
// y.LoadStream xc.EplotSections.Item(0)
STDMETHODIMP CWhipFile::LoadStream(VARIANT EPlotStream)
{
myDoTRACE("\nCCWhipFile::LoadStream()");
if (EPlotStream.vt!=VT_ERROR)
{
VARIANT *var2 = &EPlotStream;
if (var2->vt==(VT_VARIANT|VT_BYREF)) // ByRef
var2 = (VARIANT *)var2->pvarVal;
if (var2->vt==VT_DISPATCH)
{
CComQIPtr<IEPlotSection> EPlotSection;
EPlotSection = var2->pdispVal;
if (!EPlotSection)
return E_INVALIDARG;
else
{
m_iEPlotSection = EPlotSection;
CEPlotSectionImpl *epli;
m_iEPlotSection->get_EPlotSectionImpl((BYTE **)&epli);
m_W2DFile.set_eplotsection(epli);
}
}
else
return E_INVALIDARG;
}
return ProcessContouren();
}
void comment(myWT_File &my_file, const char *str)
{
if (!my_file.m_ascii)
return;
WT_Comments cmt;
cmt.set(str);
cmt.serialize(my_file);
}
HRESULT CWhipFile::ProcessContouren()
{
m_W2DFile.set_file_mode(WT_File::File_Read);
// Alle callback functies zijn static. Die kunnen de member-variabelen
// niet benaderen. Daarom maar via set_user_data onzelf doorgeven
// (voor 2.80 deden we dat complexer via een m_State)
m_W2DFile.heuristics().set_user_data((void *)this);
if (m_W2DFile.open() == WT_Result::Success)
{ xxxx=0;
CmyTimer xx("Contouren opzoeken");
m_contLayerActive = true;
m_labelLayerActive = true; // Kan bij de eerste laag anders blijken te zijn
read_for_contours(); // Zoek eerst alle contouren en labels
processLabels(); // En verwerk ze daarna
myDoTRACE("\nFound %d polylines, %d contours and %d labels.", xxxx, m_SLNKContouren.GetCount(), m_SLNKLabels.GetCount());
WT_Point3D dwgPt1 = m_contunits.transform(WT_Logical_Point(INT_MIN, INT_MIN));
WT_Point3D dwgPt2 = m_contunits.transform(WT_Logical_Point(INT_MAX, INT_MAX));
CComQIPtr<IDWGPoint> pt;
m_dwgLimits->get_min(&pt);
pt->put_DwgX(dwgPt1.m_x);
pt->put_DwgY(dwgPt1.m_y);
pt.Release();
CComQIPtr<IDWGPoint> pt2;
m_dwgLimits->get_max(&pt2);
pt2->put_DwgX(dwgPt2.m_x);
pt2->put_DwgY(dwgPt2.m_y);
pt2.Release();
m_W2DFile.close(); // closing Input file.
if (m_iEPlotSection) // is er niet bij empty.w2d
{
double ClipWidth, ClipHeight;
m_iEPlotSection->get_PaperClipWidth(&ClipWidth);
m_iEPlotSection->get_PaperClipHeight(&ClipHeight);
if (ClipWidth > 0 && ClipHeight > 0)
{
double dwfDx = m_view.view().m_max.m_x - m_view.view().m_min.m_x;
double dwfDy = m_view.view().m_max.m_y - m_view.view().m_min.m_y;
m_vdpi = myRound(max(dwfDx / ClipWidth, dwfDy / ClipHeight));
myDoTRACE("\nGuessing vector dpi: %d", m_vdpi);
}
}
}
else
{
return myAtlReportError (GetObjectCLSID(), "ERROR CWhipFile:: Unable to open file/stream:");
}
return S_OK;
}
STDMETHODIMP CWhipFile::SetLayers(BSTR reContouren, BSTR reLabels)
{
myTRACE("\nParsing met contouren %ls", reContouren);
myTRACE("\nParsing met labels %ls", (LPCTSTR)reLabels);
REParseError status = m_reContouren.Parse( CString(reContouren), false );
if (REPARSE_ERROR_OK != status)
return myAtlReportError (GetObjectCLSID(), "ERROR: Unable to parse Whipfile contour regLayers");
status = m_reLabels.Parse( CString(reLabels), false );
if (REPARSE_ERROR_OK != status)
return myAtlReportError (GetObjectCLSID(), "ERROR: Unable to parse Whipfile label regLayers");
return S_OK;
}
// Lagen die over moeten blijven. De rest wordt er uit gestript.
STDMETHODIMP CWhipFile::SetFilterLayers(BSTR reLayers)
{
myTRACE("\nFilter met lagen %ls", reLayers);
REParseError status = m_reLayers.Parse( CString(reLayers), false );
if (REPARSE_ERROR_OK != status)
return myAtlReportError (GetObjectCLSID(), "ERROR: Unable to parse Whipfile SetFilterLayers");
return S_OK;
}
void CWhipFile::read_for_contours()
{
WT_Result result;
m_W2DFile.set_text_action(my_process_text);
m_W2DFile.set_polyline_action(my_process_polyline);
m_W2DFile.set_outline_ellipse_action(my_process_outlineEllipse);
// Shiften tijdens *inlezen* om overflow te voorkomen
// Problemen: hoe achterhaal je op een nette manier zo vroeg hoeveel je moet shiften
// (uit DWFGraphicResoure.transform() voelt niet goed)
// Bovendien werkt de transform niet op de Viewport/Units zodat dwf2dwg conversie niet klopt
// WT_Logical_Point lshift(m_W2DFile.lshift()[0] - 1, m_W2DFile.lshift()[1] - 1);
// m_W2DFile.heuristics().set_transform(WT_Transform(lshift, 1.0, 1.0, 0));
// y:\Library\Dwf770\develop\global\src\dwf\whiptk\heuristics.h
// This is only used when writing to a file. This method is not applicable in a read mode.
// anders zou ik het graag tijdens inlezen al doen om overflow problemen veel vroeger te voorkomen
// Toch lijkt het al te werken?
// m_W2DFile.heuristics().set_apply_transform(true);
// AutoCAD does not generate polygon's for closed polylines
// It only does for filled SOLID hatching
//m_W2DFile.set_polygon_action(my_process_polygon);
//m_W2DFile.set_polytriangle_action(my_process_polytriangle);
m_W2DFile.set_layer_action(my_process_layer);
// Do the actual reading.
do {
result = m_W2DFile.process_next_object();
} while (result == WT_Result::Success);
// Nu zijn ze wel een keer bekend, we hebben ze later weer nodig
// m_contunits=m_W2DFile.rendition().drawing_info().units();werkt niet goed met paperspace
// Deze doet het in de praktijk altijd wel?
m_contunits = myWT_File::fixUnits(m_W2DFile.rendition().viewport().viewport_units(), m_flags);
m_view=m_W2DFile.rendition().rendering_options().view();
// We gaan er later nog een keer door voor de plattegrond. Geen processing dan
m_W2DFile.set_text_action(WT_Text::default_process);
m_W2DFile.set_outline_ellipse_action(WT_Outline_Ellipse::default_process);
m_W2DFile.set_polyline_action(WT_Polyline::default_process);
}
// Result: Area in original drawing coordinates
// Veronderstel dat het laatste punt gelijk is aan het eerste
double PolygonArea(WT_Point_Set * ps, WT_Units units)
{
int i,j;
double area = 0;
for (i=0;i<ps->count();i++) {
j = (i + 1) % ps->count();
WT_Point3D dwfPtA = units.transform(ps->points()[i]);
WT_Point3D dwfPtB = units.transform(ps->points()[j]);
area += dwfPtA.m_x * dwfPtB.m_y;
area -= dwfPtA.m_y * dwfPtB.m_x;
}
area /= 2;
return(area < 0 ? -area : area);
}
// Iterate all labels and determine in which contour they fall
void CWhipFile::processLabels()
{
for (size_t lbl=0; lbl<m_SLNKLabels.GetCount(); lbl++)
{
WT_Text text = m_SLNKLabels[lbl];
bool found = false;
// Find inside which Polygon it falls
// When multiple: choose smallest!
CSLNKContourImpl *prevContour = NULL;
for (size_t i=0; i<m_SLNKContouren.GetCount(); i++)
{
CSLNKContourImpl *contour= (m_SLNKContouren[i]);
if (CSLNKContourImpl::PointInPolygon(text.position(), *contour))
{
myTRACE("\n%s with area %8.2fm2", text.string().ascii(), contour->m_DWGArea/1e6);
if (contour->m_contLabel.length()==0)
{
if (prevContour != NULL)
{
myTRACE(" Hmm, we hadden al eerder succes");
if (prevContour->m_DWGArea > contour->m_DWGArea)
{
myTRACE(" en we hebben een kleinere. Reset prevContour");
prevContour->m_contLabel = "";
prevContour->m_ShowLabel = "";
prevContour->m_Key = "";
prevContour = contour;
}
else // Nieuwe contour is niet beter
{
contour = prevContour;
}
}
else
prevContour = contour;
found = true;
prevContour->m_Key = text.string().ascii();
// Spaties voor of achteraan geven op de lange termijn
// problemen als we via XML communiceren
prevContour->m_Key.Trim();
prevContour->m_contLabel = prevContour->m_Key;
prevContour->m_ShowLabel = prevContour->m_Key;
#ifdef SHAKE_FOR_TEST
if (1||prevContour->m_Label.equals("x706"))
{
srand( (unsigned)time( NULL ) );
int i;
for (i=0;i<prevContour->count();i++) {
WT_Logical_Point *pt = &(prevContour->points()[i]);
int diff = (int)(400); // 0.1% afwijking
double dx = ((double)rand() / RAND_MAX - 0.5);
double dy = ((double)rand() / RAND_MAX - 0.5);
pt->m_x += diff * dx;
pt->m_y += diff * dy;
}
}
#endif
prevContour->m_ptLabel = text.position();
}
else
{
myTRACE("\nLabel %s with area %8.2fm2", text.string().ascii(), contour->m_DWGArea/1e6);
if (!strcmp(text.string().ascii(), contour->m_contLabel.ascii()))
myTRACE(" komt dubbel voor (maar wel dezelfde contour)!");
else
myTRACE(" was al bezet door %s", contour->m_contLabel.ascii());
}
}
}
if (!found)
{
m_UnmatchedLabels.Add(text);
myTRACE("\nLabel %s has no matching contour", text.string().ascii());
}
}
return;
}
WT_Result CWhipFile::my_process_layer (WT_Layer & layer, WT_File & file)
{
file.rendition().layer() = layer;
WT_Layer *ll;
CWhipFile *deze = (CWhipFile *)file.heuristics().user_data();
CString layer_name(layer.layer_name().ascii());
if (layer_name != "") // Anders zittie alleen maar in de weg
{ // Dat doet de default_process 'fout'
// Overbodige lagen negeren we:
if (layer_name == deze->m_activeLayerName)
return WT_Result::Success;
file.layer_list().add_layer(layer);
ll = &layer;
}
else
{
WT_Integer32 layer_num = layer.layer_num();
ll = file.layer_list().find_layer_from_index(layer_num);
if (ll)
layer_name = ll->layer_name().ascii();
else
myTRACE("Vreemd, laag %d niet teruggevonden", layer_num);
}
if (ll)
{
deze->m_activeLayerName = layer_name;
deze->m_labelLayerActive = deze->labelMatch(layer_name);
deze->m_contLayerActive = deze->contMatch(layer_name);
}
return WT_Result::Success;
}
// Kijk of een punt dicht bij het einde van builder ligt
bool CWhipFile::pointnearend(CWhipFile *deze, WT_Units units, WT_Logical_Point & pt2)
{
WT_Logical_Point pt1 = deze->m_builder[deze->m_builder_len - 1];
if (pt1 == pt2) // quick shortcut
return true;
WT_Point3D dwgPt1 = units.transform(pt1);
WT_Point3D dwgPt2 = units.transform(pt2);
double dist = sqrt(pow(dwgPt1.m_x - dwgPt2.m_x, 2) + pow(dwgPt1.m_y - dwgPt2.m_y, 2));
return (dist < deze->m_minMergeDistance); // Minder dan een cm
}
// Kijk of builder nu een gesloten contour vormt.
// Zo ja voeg hem dan toe (mits groot genoeg) en reset builder
void CWhipFile::test_builder_now_complete(CWhipFile *deze, WT_Units &units)
{
if (deze->m_builder_len > 3 && pointnearend(deze, units, deze->m_builder[0])) // nu closed? Dan toevoegen!
{
CSLNKContourImpl *myContour = new CSLNKContourImpl(deze->m_builder_len, deze->m_builder, WD_True, units);
myContour->m_DWGArea = PolygonArea(myContour, units);
WT_Point3D dwgPt = units.transform(myContour->points()[0]);
myTRACE("\nMerge contour with area %8.0f (%8.2fm2) starting DWF(%3d %d,%d) DWG(%.3f,%.3f)",
myContour->m_DWGArea, myContour->m_DWGArea / 1e6,
myContour->count(),
myContour->points()[0].m_x, myContour->points()[0].m_y,
dwgPt.m_x, dwgPt.m_y);
if (myContour->m_DWGArea >= deze->m_minContSize)
deze->m_SLNKContouren.Add(myContour);// Doet uiteindelijk wel een delete op myContour
else
{
myTRACE(" te klein bevonden");
delete myContour;
}
deze->m_builder_len = 0; // En resetten
}
}
// Punt op arc van sr (start radian) naar er (end radian) met factor tussen 0 en 1
WT_Logical_Point radiuspoint(WT_Logical_Point &center, double radius, double sr, double er, double factor)
{
double angle = sr + (er - sr) * factor;
return WT_Logical_Point (center.m_x + myRound(radius * cos(angle)),
center.m_y + myRound(radius * sin(angle)));
}
void arc2poly(WT_Logical_Point builder[], int &builder_len,
WT_Outline_Ellipse & outlineEllipse, bool reverse = false)
{
double sr = outlineEllipse.start_radian();
double er = outlineEllipse.end_radian();
if (reverse)
{
double h = sr;
sr = er;
er = h;
}
double radius = outlineEllipse.major();
int arcsteps = myRound(abs (er - sr) / TWO_PI * 16); // 16 segmenten voor een hele cirkel
if (MAX_BUILDER < builder_len + arcsteps) // overflow
return;
for (int i = 1; i < arcsteps; i++)
builder[builder_len++] = radiuspoint(outlineEllipse.position(), radius, sr, er, ((double) i) / arcsteps);
}
WT_Result CWhipFile::my_process_outlineEllipse(WT_Outline_Ellipse & outlineEllipse, WT_File & file)
{
CWhipFile *deze = (CWhipFile *)file.heuristics().user_data();
if (!deze->m_contLayerActive)
return WT_Result::Success; // Wrong layer
if (deze->m_minMergeDistance < 0)
return WT_Result::Success; // We doen niet aan builden
if (MAX_BUILDER < deze->m_builder_len + 5) // overflow
{
deze->m_builder_len = 0; // reset builder
return WT_Result::Success;
}
WT_Units units = myWT_File::fixUnits(file.rendition().viewport().viewport_units(), deze->m_flags);
WT_Point3D dwgPt = units.transform(outlineEllipse.position());
myTRACE("\nFound Ellipse center DWF(%d,%d) DWG(%.3f,%.3f) with minor %d major %d startangle %d endangle %d",
outlineEllipse.position().m_x, outlineEllipse.position().m_y,
dwgPt.m_x, dwgPt.m_y,
outlineEllipse.major(), outlineEllipse.minor(),
outlineEllipse.start(), outlineEllipse.end());
// veronderstel outlineEllipse.major() == outlineEllipse.minor()
double sr = outlineEllipse.start_radian();
double er = outlineEllipse.end_radian();
double radius = outlineEllipse.major();
WT_Logical_Point start = radiuspoint(outlineEllipse.position(), radius, sr, er, 0.0);
WT_Logical_Point end = radiuspoint(outlineEllipse.position(), radius, sr, er, 1.0);
myTRACE("\n start DWF(%d,%d)\n end DWF(%d,%d)",
start.m_x, start.m_y, end.m_x, end.m_y);
if (deze->m_builder_len == 0) // Het is het allereerste segment, bijv. "100976 - C00 - F2-Model-FMH-ARCS.dwf" A-00..BMC
{
deze->m_builder[deze->m_builder_len++] = start; // TODO: Soms moeten we mogelijk start en end nog omwisselen?
arc2poly(deze->m_builder, deze->m_builder_len, outlineEllipse);
deze->m_builder[deze->m_builder_len++] = end;
}
else // Zijn we aan het builden. Kijk of onze ellipse achteraan aansluit
{
if (pointnearend(deze, units, start))
{
//deze->m_builder[deze->m_builder_len++] = start;
arc2poly(deze->m_builder, deze->m_builder_len, outlineEllipse);
deze->m_builder[deze->m_builder_len++] = end;
test_builder_now_complete(deze, units);
}
else if (pointnearend(deze, units, end))
{
//deze->m_builder[deze->m_builder_len++] = end;
arc2poly(deze->m_builder, deze->m_builder_len, outlineEllipse, true /* reverse */);
deze->m_builder[deze->m_builder_len++] = start;
test_builder_now_complete(deze, units);
}
else // Sluit niet aan, reset builder
{
deze->m_builder_len = 0;
}
}
return WT_Result::Success;
}
// This is what AutoCAD seems to create for a normal closed polyline
// but also for the rendition of the character '0'
WT_Result CWhipFile::my_process_polyline(WT_Polyline & polyline, WT_File & file)
{
CWhipFile *deze = (CWhipFile *)file.heuristics().user_data();
if (!deze->m_contLayerActive)
return WT_Result::Success; // Wrong layer
bool frstIsLast = (polyline.points()[0].m_x == polyline.points()[polyline.count()-1].m_x &&
polyline.points()[0].m_y == polyline.points()[polyline.count()-1].m_y);
// (Vigor) DWF's uit ARKEY hebben de onhebbelijke eigenschap dat de contour begint
// met een aanhaallijntje. Dat ondersteunen we dan toch maar.
bool scndIsLast = false;
if (g_SLNKOptions.m_SkipContLeader && polyline.count() > 2) // Pas op dat je de 'L' van 2 punten niet negeert (voor builden)
{
scndIsLast = (polyline.points()[1].m_x == polyline.points()[polyline.count()-1].m_x &&
polyline.points()[1].m_y == polyline.points()[polyline.count()-1].m_y);
}
// Houthoff tekening 1082ma50-F-B---04-Model.dwf had voor ruimte 04.44 dat
// de contour nog <20><>n lijntje doorliep na sluiten. Dat ondersteunen we hier maar.
bool xscndIsFirst = false; // een-na-laatste
if (polyline.count() > 2) // Pas op dat je de 'L' van 2 punten niet negeert (voor builden)
{
xscndIsFirst = (polyline.points()[0].m_x == polyline.points()[polyline.count()-2].m_x &&
polyline.points()[0].m_y == polyline.points()[polyline.count()-2].m_y);
}
//WT_Units units=file.rendition().drawing_info().units(); werkt niet goed met paperspace
// Waarom zetten we hier m_contunits eigenlijk nog niet gewoon direct?
WT_Units units = myWT_File::fixUnits(file.rendition().viewport().viewport_units(), deze->m_flags);
// if (polyline.count() == 3 && polyline.points()[0].m_x == 2147470970 && polyline.points()[0].m_y == 22603)
// DebugBreak();
// if (polyline.count() == 2)
// DebugBreak();
if (!frstIsLast && !scndIsLast && !xscndIsFirst) // Niet gesloten. Misschien met builder aan de slag?
{
if (deze->m_minMergeDistance < 0)
return WT_Result::Success; // We doen niet aan builden
if (!pointnearend(deze, units, polyline.points()[0])) //
deze->m_builder_len = 0;
if (MAX_BUILDER < deze->m_builder_len + polyline.count()) // overflow
{
deze->m_builder_len = 0; // reset builder
}
if (deze->m_builder_len == 0 || pointnearend(deze, units, polyline.points()[0]))
{
for (int i = deze->m_builder_len == 0 ? 0 : 1; i < polyline.count(); i++) // eerste eventueel overslaan, die matchte tenslotte
{
deze->m_builder[deze->m_builder_len++] = polyline.points()[i];
}
test_builder_now_complete(deze, units);
}
else
{
deze->m_builder_len = 0; // Anders resetten
}
return WT_Result::Success;
}
xxxx++;
// WD_True as we are going to mess
CSLNKContourImpl *myContour;
if (scndIsLast) // eerste lijntje droppen
myContour = new CSLNKContourImpl(polyline.count()-1,polyline.points()+1, WD_True, units);
else
myContour = new CSLNKContourImpl(polyline.count(),polyline.points(), WD_True, units);
// A major problem: Sometimes AutoCAD's DWFOUT merges two adjacent polylines
// into one WT_Polyline. We hate that so we start splitting them again here
// Note this also happens on rendering of some texts. We don't care about
// that but cannot detect the difference, so split them as well.
// TODO: Als een afgesplitste contour uiteindelijk binnen de start contour
// valt dan hebben we te maken met een eiland dat door een tekenaar
// gemaakt is. Dan moeten we niet splitsen. Zucht..
for (int i=1; i<myContour->count()-2; i++) // -2: Laat normale eindpunt ongemoeid
{
if ((myContour->points()[i]).m_x == (myContour->points()[0]).m_x &&
myContour->points()[i].m_y == myContour->points()[0].m_y
)
{ // Van 0..i is al een loop. Trek die er alvast uit
WT_Point3D dwgPt = units.transform(myContour->points()[0]);
myTRACE("\nSplit case %d-%d starting DWF(%d,%d) DWG(%.3f,%.3f)",i,myContour->count(),
myContour->points()[0].m_x, myContour->points()[0].m_y,
dwgPt.m_x, dwgPt.m_y);
// Create a copy of the looping section
// Sample: i=6, j=2, diff=4 pt
CSLNKContourImpl *myContour2 = new CSLNKContourImpl(i, myContour->points(), WD_True, units);
// Als alle volgende punten binnen myContour2 vallen hebben we met een (komend) eiland te maken.
// Dan hebben we spijt en gaan toch niet splitsen
bool allInside = true;
for (int i2=i+1; allInside && i2 < myContour->count(); i2++)
{
allInside = allInside && CSLNKContourImpl::PointInPolygon(myContour->points()[i2], *myContour2);
}
if(allInside)
{
delete myContour2; // toch maar niet
continue;
}
// Create a copy of the end section
CSLNKContourImpl *myContour3 = new CSLNKContourImpl(myContour->count()-i, myContour->points()+i, WD_True, units);
// Omgekeerd: Als alle myContour2 punten binnen myContour3 vallen
// is het *eerste* stuk het eiland.
// Dan hebben we ook spijt en gaan toch niet splitsen
allInside = true;
for (int i2=0; allInside && i2 < myContour2->count(); i2++)
{
allInside = allInside && CSLNKContourImpl::PointInPolygon(myContour2->points()[i2], *myContour3);
}
if(allInside)
{
delete myContour2; // toch maar niet
delete myContour3;
continue;
}
myContour2->m_DWGArea = PolygonArea(myContour2, units);
deze->m_SLNKContouren.Add(myContour2);
// Delete the old one (seem all together a little overkill)
delete myContour;
myContour = myContour3;
i=0; // vanaf daar verder kijken
}
}
// Bij akzo 4310 kwam (Alleen AutoCAD 2004) het volgende geval voor.
// Dat onderkennen we door te testen met het oude algoritme en een twee-punts overlap
// 0,8--->1
// | |
// | v
// 3,7<--2,6
// | ^
// v |
// 4----->5
for (int i=1; i<myContour->count()-1; i++)
{
for (int j=0; j<i; j++)
if (myContour->points()[i] == myContour->points()[j])
{
myTRACE("\nSplitsen?");
if (myContour->points()[i+1] == myContour->points()[j+1])
{
myTRACE(" YES");
// Van j..i is een loop. Trek die er uit
// myTRACE("\nSplit case %d-%d",i,myContour->count());
// Create a copy of the looping section
// Sample: i=6, j=2, diff=4 pt
CSLNKContourImpl *myContour2 = new CSLNKContourImpl(i-j+1, myContour->points()+j, WD_True, units);
myContour2->m_DWGArea = PolygonArea(myContour2, units);
deze->m_SLNKContouren.Add(myContour2);
// Close the hole in myContour
for (int k=j+1; k+i-j<myContour->count(); k++)
myContour->points()[k]=myContour->points()[k+i-j];
// Create a copy of the start section
CSLNKContourImpl *myContour3 = new CSLNKContourImpl(myContour->count()-(i-j), myContour->points(), WD_True, units);
// Delete the old one (seem all together a little overkill)
delete myContour;
myContour = myContour3;
i=j; // vanaf daar verder kijken
}
}
}
// Last (or only) section
myContour->m_DWGArea = PolygonArea(myContour, units);
WT_Point3D dwgPt = units.transform(myContour->points()[0]);
myTRACE("\nFound contour with area %8.0f (%8.2fm2) starting DWF(%3d %d,%d) DWG(%.3f,%.3f)",
myContour->m_DWGArea, myContour->m_DWGArea/1e6,
myContour->count(),
myContour->points()[0].m_x, myContour->points()[0].m_y,
dwgPt.m_x, dwgPt.m_y);
if (myContour->m_DWGArea >= deze->m_minContSize)
deze->m_SLNKContouren.Add(myContour);// Doet uiteindelijk wel een delete op myContour
else
{
myTRACE(" te klein bevonden");
delete myContour;
}
deze->m_builder_len = 0; // In ieder geval resetten
return WT_Result::Success;
}
// Note: For SHX-fonts AutoCAD 2002+ creates an invisible (Text) object
// which will get used here
WT_Result CWhipFile::my_process_text (WT_Text & text, WT_File & file)
{
CWhipFile *deze = (CWhipFile *)file.heuristics().user_data();
if (!deze->m_labelLayerActive)
return WT_Result::Success; // Wrong layer
WT_Text roomnr(text);
roomnr.string().set(text.string().length(), text.string()); // maak kopie, indien mogelijk wordt dat vanzelf ascii
if (roomnr.string().is_ascii() && strlen(roomnr.string().ascii()) > 0)
deze->m_SLNKLabels.Add(roomnr);
return WT_Result::Success;
}
// 'Herschrijf' de plan DWF naar de output
// Doe eerst de 'header' van de DWF (viewport en zo) en zodra
// we de eerste drawable tegenkomen doen we de contouren tussendoor
// zodat die (als ze ingekleurd worden) altijd onderaan liggen
HRESULT CWhipFile::SerializePlan(WT_File & my_plan_file, myWT_File & my_file, double scale)
{
CmyTimer tm("Serializing plan");
WT_Result result;
BOOL firstDrawable = true;
BOOL CurrentLayerOn = TRUE; // Skip contourlagen
// Deze drie waren niet geclear'd door de close na het lezen van de contouren
my_plan_file.layer_list().remove_all();
my_plan_file.object_node_list().remove_all();
my_plan_file.dash_pattern_list().remove_all();
// TODO Is dit 2.70 nog wel nodig? We doen verderop ook een en ander
// Zet wel m_activeLayerName dus nog laten zo
my_plan_file.set_layer_action(my_process_layer); // Override default processing
// Do the actual reading.
CString last_layer;
m_activeLayerName = ""; // reset
WT_Color planclr(m_forcePlanColor >> 16, (m_forcePlanColor & 0xff00) >> 8, m_forcePlanColor & 0xff);
if (m_forcePlan) // plattegrond grijzig maken
{
my_file.desired_rendition().color() = my_file.rendition().color() = planclr;
planclr.serialize(my_file);
}
while ((result = my_plan_file.process_next_object()) == WT_Result::Success)
{
switch(my_plan_file.current_object()->object_type())
{
case WT_Object::Drawable:
if (firstDrawable)
{
comment(my_file, "== Contouren start (solid=true)");
GenerateContouren(my_plan_file, my_file, scale, true); // Alle 'solid' kleuren
comment(my_file, "== Contouren end (solid=true)");
firstDrawable = false;
if (m_forcePlan) // plattegrond (opnieuw) grijzig maken
{
my_file.desired_rendition().color() = my_file.rendition().color() = planclr;
planclr.serialize(my_file);
}
}
if (my_plan_file.current_object()->object_id() == WT_Object::Origin_ID
&& !my_file.heuristics().allow_binary_data()
)
{ // I do not understand why but it will assert otherwise
break;
}
if (CurrentLayerOn)
my_plan_file.current_object()->serialize(my_file);
break;
case WT_Object::Attribute:
{ // TODO: Als !CurrentLayerOn dan zouden we ook nog fors kunnen schrappen in de WT_Attributes
// die vaak niet meer van toepassing zijn. Dan moeten we echter met desireded rendition gaan werken
// en dat is me nog even te veel werk.
// 2.80: WT_Color en WT_Font alvast wel via desired, scheelt best in de DWF-grootte
const WT_Attribute *obj = (WT_Attribute *)my_plan_file.current_object();
switch(obj->object_id())
{
case WT_Object::URL_ID: // Strippen omdat ze in de weg kunnen zitten met onze eigen URL's
case WT_Object::Object_Node_ID: // kunnen we ook niets mee. Had RWSN#20095 in 1101EM0108_01.dwf
break;
case WT_Object::Layer_ID:
{ // Sla de oorspronkelijke contour lagen over
// En ook de lagen die we niet willen
CurrentLayerOn = TRUE;
WT_Layer *layer = (WT_Layer *)obj;
WT_Integer32 layer_num = layer->layer_num();
if (layer_num==0)
{ // ARKEY heeft lagen met nummer 0. Dat mag niet volgens de
// Whip-spec en gaat ook mis (alleen) bij binaire DWF's
if (my_file.heuristics().allow_binary_data())
{
myDoTRACE("\nSkipping layer with invalid number 0");
break;
}
}
// Noot: gooi de originele contour-lagen er altijd uit
if ((labelMatch(m_activeLayerName) ||
contMatch(m_activeLayerName) ||
!layerMatch(m_activeLayerName))
)
CurrentLayerOn = FALSE; // Layer object hoeft ook niet meer geserialized
else
{
if (m_activeLayerName != last_layer) // cleaning, scheelt fors bij RIJSWIJKHB-02.dwf
{
layer->serialize(my_file);
last_layer = m_activeLayerName;
}
}
break;
}
case WT_Object::Color_ID:
{
if (!m_forcePlan) // gewoon originele kleur
my_file.desired_rendition().color() = *((WT_Color *)obj);
break;
}
case WT_Object::Font_ID:
{
my_file.desired_rendition().font() = *((WT_Font *)obj);
break;
}
default:
{
#ifdef _DEBUG
CString s(my_plan_file.file_stats()->descriptions());
// myTRACE("Attribute '%s'\n", s);
#endif
// 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 @@@
obj->serialize(my_file);
}
}
}
break;
case WT_Object::Informational:
my_plan_file.current_object()->serialize(my_file);
break;
case WT_Object::Wrapper:
// my_plan_file.current_object()->serialize(my_file); // Geeft dubbele (W2D V06.00)
break;
case WT_Object::Definition:
my_plan_file.current_object()->serialize(my_file);
break;
default:
#ifdef _DEBUG
CString s(my_plan_file.file_stats()->descriptions());
myTRACE("Skipping '%s'\n", s);
#endif
break;
}
}
myDoTRACE("\nWritten %d/%d layers", my_file.layer_list().count(), my_plan_file.layer_list().count());
// if (result == WT_Result::End_Of_DWF_Opcode_Found)
// dwfresult = DwfResult::Success;
// TODO: desired_rendition klopt nu niet meer met de daadwerkelijke rendition?
return S_OK;
}
// Extra saveas wrapper om met __try een eventuele Win32 exception ook op te vangen
STDMETHODIMP CWhipFile::SaveAs(BSTR WhipPath, VARIANT_BOOL ascii)
{
myTRACE("\nAbout to save into %ls", (LPCTSTR)WhipPath);
__try {
return SaveAs2(WhipPath, ascii);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
DWORD exCode = GetExceptionCode();
myDoTRACE("\nUnhandled Win32 exception 0x%x in CWhipFile::SaveAs", exCode);
return myAtlReportError (GetObjectCLSID(), "\nCWhipFile::SaveAs('%ls')\nUnhandled Win32 exception 0x%x", (LPCSTR)WhipPath, exCode);
}
}
STDMETHODIMP CWhipFile::SaveAs2(BSTR WhipPath, VARIANT_BOOL ascii)
{
try {
myWT_File my_file;
my_file.set_filename(WhipPath);
my_file.m_ascii = ascii;
return Generate(my_file);
}
catch (WT_Result::Enum e)
{
CString err;
err.Format("\nInternal error WT_Result::Enum %d",e);
return myAtlReportError (GetObjectCLSID(), "\nCWhipFile::SaveAs('%ls')\n%s", (LPCSTR)WhipPath, err);
}
catch (CString &err)
{
return myAtlReportError (GetObjectCLSID(), err);
}
}
// Altijd hoogte 1000, echte fontheight gaf overflow bij SDU
class tempFontDC
{
public:
tempFontDC(WT_String m_FontName)
{
HDC tempDC = CreateDC("DISPLAY",NULL,NULL,NULL);
m_DC = CreateCompatibleDC ( tempDC );
DeleteDC(tempDC);
LOGFONT lf;
memset(&lf, 0, sizeof(LOGFONT)); // clear out structure.
strcpy_s(lf.lfFaceName, LF_FACESIZE, m_FontName.ascii());
lf.lfHeight = -FONT_SIZER;
fnt = CreateFontIndirect(&lf);
HGDIOBJ oldfont = SelectObject(m_DC, fnt);
}
~tempFontDC()
{
// Opruimen
DeleteObject(fnt);
DeleteDC(m_DC);
}
inline operator const HDC()
{ return m_DC; }
private:
HDC m_DC;
HFONT fnt;
};
bool CWhipFile::GenerateSymbols(myWT_File &my_file)
{
double scale = m_contunits.application_to_dwf_transform()(0,0);
// TODO: Echt uniek layernum (en URL num) bepalen
my_file.desired_rendition().layer() = WT_Layer(my_file, 65530, "SLNK Symbols");
for (size_t i=0; i<m_SLNKSymbols.GetCount(); i++)
{
CSLNKSymbolImpl *symbol= m_SLNKSymbols[i];
if (symbol->m_unicode.length()) // die doen we in de tweede slag allemaal tegelijk
continue;
if (symbol->m_symbolName == "" || !m_SLNKSymbolDefinitions.Lookup(symbol->m_symbolName))
symbol->serialize(my_file, m_contunits, NULL,
m_next_node_num,
m_hintScale, m_forFind, scale);
else
{
CString s; s.Format("About to insert symbol %d %s (%s)", m_next_node_num, symbol->m_SLNKContour.m_Key, symbol->m_symbolName);
comment(my_file, s);
symbol->serialize(my_file, m_contunits, m_SLNKSymbolDefinitions[symbol->m_symbolName],
m_next_node_num,
m_hintScale, m_forFind, scale);
}
}
//
// nu de unicode symbolen
//
tempFontDC myDC(m_SymbolFontName);
//TEXTMETRIC tm;
int ttfdescent = 0; // zo ver moet het karakter nog omhoog om het precies in het midden aan te grijpen
//if (GetTextMetrics(myDC, &tm) != 0)
//{
// ttfdescent = MulDiv(tm.tmDescent, FONT_SIZER, tm.tmHeight + 250); // waarom is die 250 nodig om 125 te krijgen? Nu nog hardcoded in SLNKSymbolImpl.cpp
//}
int size = ::GetOutlineTextMetricsW(myDC, 0, NULL); // alleen voor TrueType!
if (size != 0)
{
OUTLINETEXTMETRICW *otm = (OUTLINETEXTMETRICW *)new char[size];
WD_Assert(otm != NULL);
UINT status = GetOutlineTextMetricsW(myDC, size, otm);
WD_Assert(status != 0);
ttfdescent = otm->otmDescent;
delete[] otm;
}
WT_Font myfont;
myfont.font_name().set(m_SymbolFontName);
myfont.height() = myRound(m_SymbolFontHeight); // 1000 sluit aan bij onze ingebouwde symbolen
myfont.rotation() = 0;
myfont.width_scale() = 0;
myfont.flags() = 0;
my_file.desired_rendition().font() = myfont;
myfont.serialize(my_file);
for (size_t i = 0; i<m_SLNKSymbols.GetCount(); i++)
{
CSLNKSymbolImpl *symbol = m_SLNKSymbols[i];
if (symbol->m_unicode.length()) // die doen we nu
{
CString s; s.Format("About to insert symbol %d %s (%s)", m_next_node_num, symbol->m_SLNKContour.m_Key, symbol->m_symbolName);
comment(my_file, s);
symbol->serialize(my_file, m_contunits, m_SLNKSymbolDefinitions[symbol->m_symbolName],
m_next_node_num,
m_hintScale, m_forFind, scale, ttfdescent);
}
}
// Onze labels kunnen later door een achtergrondkleur een polygon hebben
// Zorg dat die niet binnen een node valt
WT_Object_Node my_node(my_file, 0);
my_file.desired_rendition().object_node() = my_node;
return true;
}
bool CWhipFile::GenerateSymbolLabels(myWT_File &my_file)
{
double scale = m_contunits.application_to_dwf_transform()(0,0);
// Zet een DC op om bij 'Center' de afmetingen van de tekst te kunnen bepalen
int fontheight= myRound(m_FontHeightSymbols * scale);
tempFontDC myDC(m_FontName);
WT_Font myfont;
myfont.font_name() = m_FontName;
myfont.height() = fontheight;
myfont.rotation() = 0;
myfont.width_scale() = 0;
my_file.desired_rendition().font() = my_file.rendition().font() = myfont;
myfont.serialize(my_file); // Vertrouw niets. Expliciete serialize. Zie @@@
WT_Color clr(255,255,255,0); //Teksten wit
my_file.desired_rendition().color() = my_file.rendition().color() = clr;
clr.serialize(my_file); // Vertrouw niets. Expliciete serialize. Zie @@@
// TODO: Echt uniek layernum (en URL num) bepalen
my_file.desired_rendition().layer() = WT_Layer(my_file, 65531, "SLNK Symbol Labels");
for (size_t i=0; i<m_SLNKSymbols.GetCount(); i++)
{
CSLNKSymbolImpl *symbol= m_SLNKSymbols[i];
WT_Transform wasTransform = my_file.heuristics().transform();
WT_Boolean wasApplyTransform = my_file.heuristics().apply_transform();
my_file.heuristics().set_apply_transform(WD_False); // Hebben we al rechtstreeks op de contour gedaan
symbol->m_SLNKContour.SerializeLabel(my_file, scale, myDC);
// En transform weer terug
my_file.heuristics().set_transform(wasTransform);
my_file.heuristics().set_apply_transform(wasApplyTransform);
}
return true;
}
// Schrijf alle uitgerekende contouren naar het DWF bestand
// Deze functie wordt twee keer aangeroepen
// Heel vroeg (voor de plattegrond) worden alle contouren met een solid kleur
// wegeschreven. Die komen dan helemaal onderop. Ze zouden anders afdekken
// In de tweede slag komen alle transparante contouren. Die komen na de
// plattegrond zodat de plattegrond doorschijnt.
// TODO: Sorteren op dunste contouren eerst, die dikke moeten bovenop?
bool CWhipFile::GenerateContouren(WT_File &my_planfile, myWT_File &my_file,
double scale, BOOL solidOnly)
{
// Zorg dat we zelf neutraal blijven
WT_Layer keepLayer = my_planfile.rendition().layer();
WT_Color keepColor = my_planfile.rendition().color();
WT_Line_Style keepStyle = my_planfile.rendition().line_style();
WT_Font keepFont = my_planfile.rendition().font();
// Vertrouw de huidige rendition niet!
WT_Color().serialize(my_file);
WT_Line_Style style;
style.line_join() = WT_Line_Style::Diamond_Join;
my_file.desired_rendition().line_weight() = myRound(10 * scale);
style.serialize(my_file);
//Store the current object node hoewel die echt null zal zijn....
WT_Object_Node current_node = my_file.desired_rendition().object_node();
// TODO: Echt uniek layernum (en URL num) bepalen
my_file.desired_rendition().layer() = WT_Layer(my_file, 65532, "SLNK Contours");
// Beide 'slagen' forceren (FSN#14349)
my_file.desired_rendition().layer().serialize(my_file);
// Sorteren: die met m_onTop zetten we achteraan/ bovenop
size_t last = m_SLNKContouren.GetCount() - 1; // alles na last heeft onTop
for (size_t i=0; i<last && i<m_SLNKContouren.GetCount(); i++)
{
CSLNKContourImpl *contour= m_SLNKContouren[i];
if (contour->m_onTop)
{
while (last > i && last > 0 && m_SLNKContouren[last]->m_onTop)
last --;
if (last > i && last > 0)
{
m_SLNKContouren[i] = m_SLNKContouren[last];
m_SLNKContouren[last] = contour;
last--;
}
}
}
for (size_t i=0; i<m_SLNKContouren.GetCount(); i++)
{
CSLNKContourImpl *contour= m_SLNKContouren[i];
// i ook gebruiken als node_num, eigenlijk initialiseren op laatste van planfile
contour->serialize(my_file, m_next_node_num, solidOnly, m_forFind, scale);
}
my_file.desired_rendition().object_node() = current_node;
my_file.desired_rendition().line_weight() = 0; // Voor als er later bijvoorbeeld nog symbolen komen
my_file.desired_rendition().layer() = my_file.rendition().layer() = keepLayer;
if (keepLayer.layer_num()) keepLayer.serialize(my_file); // Zeker terugzetten
my_file.desired_rendition().color() = my_file.rendition().color() = keepColor;
keepColor.serialize(my_file); // Zeker terugzetten
my_file.desired_rendition().line_style() = my_file.rendition().line_style() = keepStyle;
keepStyle.serialize(my_file); //
my_file.desired_rendition().font() = my_file.rendition().font() = keepFont;
keepFont.serialize(my_file); //
// Onze labels kunnen later door een achtergrondkleur een polygon hebben
// Zorg dat die niet binnen een node valt
WT_Object_Node my_node(my_file, 0);
my_file.desired_rendition().object_node() = my_node;
return true;
}
bool CWhipFile::GenerateLabels(WT_File &my_planfile, myWT_File &my_file, double scale)
{
// Zorg dat we zelf neutraal blijven
WT_Layer keepLayer = my_planfile.rendition().layer();
WT_Color keepColor = my_planfile.rendition().color();
// TODO: Echt uniek layernum (en URL num) bepalen
my_file.desired_rendition().layer() = WT_Layer(my_file, 65533, "SLNK Labels");
WT_Font f;
f.width_scale() = 0; // Triggert FONT_WIDTH_SCALE_BIT bitje zetten
f.rotation() = 0;
f.serialize(my_file); // Op de default zetten.
my_file.desired_rendition().font() = my_file.rendition().font() = f;
// Zet een DC op om bij 'Center' de afmetingen van de tekst te kunnen bepalen
int fontheight= myRound(m_FontHeight * scale);
tempFontDC myDC(m_FontName);
WT_Font myfont;
myfont.font_name() = m_FontName;
myfont.height() = fontheight;
myfont.width_scale() = DEFAULT_WIDTH_SCALE; // Triggert FONT_WIDTH_SCALE_BIT bitje zetten
myfont.rotation() = 0;
my_file.desired_rendition().font() = my_file.rendition().font() = myfont;
myfont.serialize(my_file); // Vertrouw niets. Expliciete serialize. Zie @@@
// En nu nog een keer alle teksten
for (size_t i=0; i<m_SLNKContouren.GetCount(); i++)
{
CSLNKContourImpl *contour= m_SLNKContouren[i];
// Een polyline hebben we nu niet nodig
if (contour->m_ShowLabel != "")
{ // We have got a proper label/contour, origineel of gezet door de applicatie
contour->SerializeLabel(my_file, scale, myDC);
}
}
my_file.desired_rendition().layer() = my_file.rendition().layer() = keepLayer;
if (keepLayer.layer_num()) keepLayer.serialize(my_file); // Zeker terugzetten
my_file.desired_rendition().color() = my_file.rendition().color() = keepColor;
keepColor.serialize(my_file); // Zeker terugzetten
return true;
}
// We hebben de contouren bepaald. Genereer daarvoor nu nieuwe DWF-primitieven
// In het bijzonder kennen we er een kleurtje aan toe en plaatsen wel de labels (opnieuw)
STDMETHODIMP CWhipFile::Generate(myWT_File &my_file)
{
// Bepaal de te gebruiken hoogte in Logical Points
double scale = m_contunits.application_to_dwf_transform()(0,0);
myDoTRACE("\nSchaal: %.2f", scale);
m_next_node_num = 0;
WT_Result result;
my_file.set_file_mode(WT_File::File_Write);
WT_Boolean binary = !my_file.m_ascii;
my_file.heuristics().set_allow_binary_data(binary);
// Zal niet zo snel meer gebeuren maar we willen het zeker niet
my_file.heuristics().set_allow_drawable_merging(WD_False);
// Let op: De viewer kan 60-file niet zo maar lezen, dat moet naar packed-DWF
// Later: viewer 6.5 kan gewoon een W2D file tonen
// Let op: bij versie 55 worden Node's niet weggeschreven
// my_file.heuristics().set_target_version(55);
my_file.heuristics().set_target_version(600); // Zodat DWF Viewer 6.0 niet moeilijk doet
if (my_file.open() != WT_Result::Success)
{
return myAtlReportError (GetObjectCLSID(), "ERROR: Unable to open file for writing: %s", my_file.filename().ascii());
ATLASSERT(false);
}
char module[_MAX_PATH]; // HMODULE
GetModuleFileName(g_hInstance, module, sizeof(module));
CString vers;
vers.Format("== Generated by SLNKDWF %s %s", module, SLNK_BUILDVERSION);
comment(my_file, vers);
// Symbolen worden mogelijk geplaatst net buiten onze coordinatenruimte (met name X)
// Dat gaf clipping problemen.
// Daarom verplaatsen we alles in onze coordinatenruimte naar links/onder (0,0)
// Merk op dat dit alleen helpt later in de de 'viewer', liefst zou ik het
// al tijdens het *inlezen* doen maar dat had haken en ogen (zie read_for_contours())
WT_Logical_Point lshift(-(max(0,this->m_view.view().m_min.m_x)),
-(max(0,this->m_view.view().m_min.m_y))); // Iets minder clipping van INT_MAX
// Contouren hebben we ondertussen al wel/
// Nu nog een keer door de DWF om de rest te kopieren naar de output
m_W2DFile.set_file_mode(WT_File::File_Read);
my_file.heuristics().set_apply_transform(WD_False);
myTRACE("\nAbout to open plan");
if (m_W2DFile.open() == WT_Result::Success)
{
comment(my_file, "== Original file start");
// TODO: Dit moet uit de originele PlanDWF z'n Manifest/Paper komen
int keep=my_file.heuristics().target_version();
// Even background ondersteunen
my_file.heuristics().set_target_version(55);
WT_Background bg(WT_Color(0,0,0));
bg.serialize(my_file);
my_file.heuristics().set_target_version(keep);
my_file.heuristics().set_transform(WT_Transform (lshift, 1.0, 1.0, 0));
my_file.heuristics().set_apply_transform(true);
SerializePlan(m_W2DFile, my_file, scale); // Heeft eventueel een GenerateContouren in zich!
comment(my_file, "== Original file end");
m_W2DFile.close();
}
else
{
myTRACE("\nKon plan niet openen");
return myAtlReportError (GetObjectCLSID(), "ERROR: Unable to open plan file");
}
comment(my_file, "== Contouren start (solid=false)");
// Twee loops. Eerst alle contouren (en vlakvullingen), daarna pas
// de teksten. Zodoende zijn de teksten altijd leesbaar
GenerateContouren(m_W2DFile, my_file, scale, false); // Alle transparante kleuren
comment(my_file, "== Contouren end (solid=false)");
//merging geeft vreemd effect op de contouren die we om symbolen tekenen in _DEBUG mode
// dan worden die contouren niet meer meegeschaald? TODO: Uitzoeken
//my_file.heuristics().set_allow_drawable_merging(WD_True); // Voor symbolen kan het (na verschalen) wel schelen
comment(my_file, "== Symbols start");
GenerateSymbols(my_file); // Ook alle contouren van symbolen
comment(my_file, "== Symbol labels start");
GenerateSymbolLabels(my_file);
comment(my_file, "== Contour labels start");
GenerateLabels(m_W2DFile, my_file, scale);
myTRACE("\nAbout to close");
//Restore the saved object node.
my_file.desired_rendition().fill() = WD_False;
my_file.close();
myTRACE("\nDone saving");
return S_OK;
}
// Highlight unrecognized contouren
STDMETHODIMP CWhipFile::HighlightUnrecognized()
{
try {
for (size_t i=0; i<m_SLNKContouren.GetCount(); i++)
{
CSLNKContourImpl *contour= m_SLNKContouren[i];
if (contour->m_contLabel.length()==0)
{
contour->m_Color.set(255, 0, 0);
}
}
return S_OK;
}
catch (WT_Result::Enum e)
{
CString err;
err.Format("\nInternal error WT_Result::Enum %d",e);
return myAtlReportError (GetObjectCLSID(), "\nCWhipFile::HighlightUnrecognized()\n%s", err);
}
}
STDMETHODIMP CWhipFile::SetLabelFont(BSTR FontName, DOUBLE FontHeight, DOUBLE FontHeightSymbols)
{
m_FontName.set(FontName);
m_FontHeight = FontHeight;
m_FontHeightSymbols = (FontHeightSymbols>0)?FontHeightSymbols:FontHeight;
for (size_t i=0; i<m_SLNKContouren.GetCount(); i++)
{
CSLNKContourImpl *contour= m_SLNKContouren[i];
contour->m_Fontheight = m_FontHeight;
}
return S_OK;
}
STDMETHODIMP CWhipFile::SetLabelPosition(BYTE p_LabelPos)
{
CSLNKContourImpl::LABELPOS LabelPos;
switch (p_LabelPos)
{
case 1: LabelPos = CSLNKContourImpl::LABEL_DEFAULT; break;
case 2: LabelPos = CSLNKContourImpl::LABEL_CENTROID; break;
case 3: LabelPos = CSLNKContourImpl::LABEL_TOPLEFT; break;
case 4: LabelPos = CSLNKContourImpl::LABEL_OUTSIDERIGHT; break;
case 5: LabelPos = CSLNKContourImpl::LABEL_OUTSIDEBOTTOM; break;
default: LabelPos = CSLNKContourImpl::LABEL_DEFAULT;
}
for (size_t i=0; i<m_SLNKContouren.GetCount(); i++)
{
CSLNKContourImpl *contour= m_SLNKContouren[i];
contour->m_Labelpos = LabelPos;
}
return S_OK;
}
STDMETHODIMP CWhipFile::SetSymbolFont(BSTR FontName, DOUBLE FontHeight /* = 1000 */)
{
m_SymbolFontName.set(FontName);
m_SymbolFontHeight = FontHeight;
return S_OK;
}
STDMETHODIMP CWhipFile::get_AddSymbol(DOUBLE dwgX, DOUBLE dwgY, BSTR symbolName, ISLNKSymbol** pVal)
{
CString name(symbolName);
myTRACE("\nAdding symbol %s at %.2f,%.2f", name, dwgX, dwgY);
CSLNKSymbolImpl *mySymbol = new CSLNKSymbolImpl(dwgX, dwgY, m_contunits);
mySymbol->m_SLNKContour.m_Fontheight = m_FontHeightSymbols;
m_SLNKSymbols.Add(mySymbol);
// Als resultaat leveren we een COM object op waar je eventueel de rest van
// de properties op kunt zetten
CComObject<CSLNKSymbol> *theSymbol;
HRESULT hr = CComObject<CSLNKSymbol>::CreateInstance(&theSymbol);
if(FAILED(hr)) return hr;
theSymbol->AddRef();
hr = theSymbol->QueryInterface(IID_ISLNKSymbol, (void **)pVal);
theSymbol->SetParent(this); // Zodat scope blijft
theSymbol->Release();
if(FAILED(hr)) return hr;
theSymbol->SetImpl(mySymbol); // Heel belangrijk: zet de implementatie waar we een interface op bieden
if (name[0] == '@') // unicode karakter @0000F134 voor fa-fire-extinguisher
{
long bytes = strtol(name.Mid(1), NULL, 16);
mySymbol->m_unicode.set(4, (WT_Unsigned_Integer16 *) &bytes);
name = "*SQUARE"; // voor de extents
}
else if (!m_SLNKSymbolDefinitions.Lookup(name))
name = "*STAR"; // Die vinden we altijd ja
ATLASSERT(m_SLNKSymbolDefinitions.Lookup(name));
mySymbol->m_symbolName = name;
mySymbol->m_SLNKContour.set(m_SLNKSymbolDefinitions[name]->m_BoundingContour.count(),
m_SLNKSymbolDefinitions[name]->m_BoundingContour.points(), true); // TODO: Is copy wel nodig?==>Ja, we gaan ze later transformeren!
// De voorgedefinieerde symbolen hebben alleen maar een contour. Die activeren we hier
if (mySymbol->m_unicode.length())
{
mySymbol->m_SLNKContour.m_Color = WT_Color(0, 0, 0, 0); // de square zelf willen we niet zien
#ifdef _DEBUG
mySymbol->m_SLNKContour.m_outlineColor = WT_Color(255, 0, 0, 64); // Even met outline
#endif
}
else if (name[0] == '*')
{
// Default rood
mySymbol->m_SLNKContour.m_Color = WT_Color(255,0,0,255);
mySymbol->m_SLNKContour.m_outlineColor = WT_Color(255,0,0,255); // Anders wordtie helemaal niet getekend?
}
return S_OK;
}
STDMETHODIMP CWhipFile::DefineSymbol(BSTR symbolName, VARIANT EPlotStream, ISLNKContour** pContour)
{
myTRACE("\nDefining symbol %ls", symbolName);
if (EPlotStream.vt == VT_ERROR)
return E_INVALIDARG;
VARIANT *var2 = &EPlotStream;
if (var2->vt==(VT_VARIANT|VT_BYREF)) // ByRef
var2 = (VARIANT *)var2->pvarVal;
if (var2->vt!=VT_DISPATCH)
return E_INVALIDARG;
CComQIPtr<IEPlotSection> EPlotSection;
EPlotSection = var2->pdispVal;
if (!EPlotSection)
return E_INVALIDARG;
CSLNKSymbolDefinition * symb = new CSLNKSymbolDefinition(EPlotSection);
CString name(symbolName);
m_SLNKSymbolDefinitions.SetAt(name, symb);
return return_ContourItem(&symb->m_BoundingContour, pContour); // Geef de contour terug in pcontour
}
// Define symbol by W2D filename
STDMETHODIMP CWhipFile::DefineW2DSymbol(BSTR symbolName, BSTR WhipPath, ISLNKContour** pContour)
{
myTRACE("\nDefining symbol %ls by W2D file %ls", symbolName, WhipPath);
CSLNKSymbolDefinition * symb;
try
{
symb = new CSLNKSymbolDefinition(CString(WhipPath));
}
catch (WT_Result::Enum e)
{
CString err;
err.Format("\nInternal error WT_Result::Enum %d", e);
return myAtlReportError(GetObjectCLSID(), "\nCWhipFile::DefineW2DSymbol('%ls')\n%s", (LPCSTR)WhipPath, err);
}
catch (CString &err)
{
return myAtlReportError (GetObjectCLSID(), "Error defining symbol %ls file %ls:%s\n", symbolName, WhipPath, err);
}
m_SLNKSymbolDefinitions.SetAt(CString(symbolName), symb);
return return_ContourItem(&symb->m_BoundingContour, pContour); // Geef de contour terug in pcontour
}
STDMETHODIMP CWhipFile::DefineBitmapSymbol(BSTR symbolName, BSTR symbolPath, double height)
{
myTRACE("\nDefining bitmap symbol %ls", symbolName);
CSLNKSymbolDefinition * symb = new CSLNKSymbolDefinition(CString(symbolPath), height, m_contunits);
CString name(symbolName);
m_SLNKSymbolDefinitions.SetAt(name, symb);
return S_OK;
}
STDMETHODIMP CWhipFile::SymbolOrigin(BSTR symbolName, DOUBLE dwgX, DOUBLE dwgY)
{
CString name(symbolName);
myTRACE("\nMoving symbol %s to %.2f,%.2f", name, dwgX, dwgY);
if (!m_SLNKSymbolDefinitions.Lookup(name))
return E_INVALIDARG;
CSLNKSymbolDefinition * symb = m_SLNKSymbolDefinitions[name];
myTRACE("\nOld origin %d, %d", symb->m_Origin.m_x, symb->m_Origin.m_y);
symb->m_Origin = symb->m_BoundingContour.m_Units.transform(WT_Point3D(dwgX, dwgY));
myTRACE("\nNew origin %d, %d", symb->m_Origin.m_x, symb->m_Origin.m_y);
return S_OK;
}
// Merk op: als iemand twee keer dezelfde contour opvraagt maken we twee onafhankelijke
// SLNKContour objecten aan die beide naar dezelfde CSLNKContourImpl wijzen
// Het maakt verder weinig uit en is wel gemakkelijker (toch?)
STDMETHODIMP CWhipFile::get_Contour(BSTR IdentLabel, ISLNKContour** pVal)
{
try {
for (size_t i=0; i<m_SLNKContouren.GetCount(); i++)
{
CSLNKContourImpl *contour = m_SLNKContouren[i];
if (contour->m_Key == CString(IdentLabel))
{
return get_ContourItem((ULONG) i, pVal);
}
}
return S_OK; // Niet gevonden. Automatisch NULL?
}
catch (WT_Result::Enum e)
{
CString err;
err.Format("\nInternal error WT_Result::Enum %d",e);
return myAtlReportError (GetObjectCLSID(), "\nCWhipFile::get_Contour('%ls')\n%s", (LPCSTR)IdentLabel, err);
}
return S_OK;
}
STDMETHODIMP CWhipFile::get_hintScale(DOUBLE* pVal)
{
(*pVal) = m_hintScale;
return S_OK;
}
STDMETHODIMP CWhipFile::put_hintScale(DOUBLE newVal)
{
m_hintScale = newVal;
myTRACE("\nhintScale set to %.6f", newVal);
return S_OK;
}
STDMETHODIMP CWhipFile::put_forFind(VARIANT_BOOL newVal)
{
m_forFind = newVal;
return S_OK;
}
// Levert aan de hand van een coordinaat op in welke ruimte deze valt
// Gebruikt door Facilitor Graphics om bijvoorbeeld te controleren of
// de XY van een symbool (nog) wel binnen een ruimte valt.
// mei 2010: Had dit niet ook opgelost kunnen worden met Whip2PNG.Find
// -->Neen, die hebben we daar in de code nog niet?
STDMETHODIMP CWhipFile::get_FindInContour(DOUBLE dwgX, DOUBLE dwgY, BSTR* pVal)
{
CString result;
WT_Point3D insertion(dwgX, dwgY);
WT_Logical_Point LPInsertion = m_contunits.transform(WT_Point3D(dwgX, dwgY));
for (size_t i=0; i<m_SLNKContouren.GetCount(); i++)
{
CSLNKContourImpl *contour= m_SLNKContouren[i];
if (contour->m_Key != "")
if (CSLNKContourImpl::PointInPolygon(LPInsertion, *contour))
{
result = contour->m_Key;
(*pVal) = result.AllocSysString();
}
}
return S_OK;
}
STDMETHODIMP CWhipFile::get_ContourCount(LONG* pVal)
{
*pVal = (LONG)m_SLNKContouren.GetCount();
return S_OK;
}
STDMETHODIMP CWhipFile::return_ContourItem(CSLNKContourImpl *pContour, ISLNKContour** pVal)
{
CComObject<CSLNKContour> *theContour;
HRESULT hr = CComObject<CSLNKContour>::CreateInstance(&theContour);
if(FAILED(hr)) return hr;
theContour->AddRef();
hr = theContour->QueryInterface(IID_ISLNKContour, (void **)pVal);
theContour->SetParent(this);
theContour->Release();
if(FAILED(hr)) return hr;
theContour->SetImpl(pContour); // Heel belangrijk: zet de implementatie waar we een interface op bieden
return S_OK; // Wel gevonden
}
STDMETHODIMP CWhipFile::get_ContourItem(ULONG i, ISLNKContour** pVal)
{
if (i < 0 || i >= m_SLNKContouren.GetCount())
return E_INVALIDARG;
return return_ContourItem(m_SLNKContouren[i], pVal);
}
STDMETHODIMP CWhipFile::get_minContSize(DOUBLE* pVal)
{
(*pVal) = m_minContSize;
return S_OK;
}
STDMETHODIMP CWhipFile::put_minContSize(DOUBLE newVal)
{
m_minContSize = newVal;
return S_OK;
}
STDMETHODIMP CWhipFile::get_minMergeDistance(DOUBLE* pVal)
{
(*pVal) = m_minMergeDistance;
return S_OK;
}
STDMETHODIMP CWhipFile::put_minMergeDistance(DOUBLE newVal)
{
m_minMergeDistance = newVal;
return S_OK;
}
STDMETHODIMP CWhipFile::get_flags(LONG* pVal)
{
(*pVal) = m_flags;
return S_OK;
}
STDMETHODIMP CWhipFile::put_flags(LONG newVal)
{
m_flags = newVal;
return S_OK;
}
STDMETHODIMP CWhipFile::get_AddContour(ISLNKContour** pVal)
{
// Je mag eigenlijk geen echt lege contour doorgeven, daar assert de pointset.cpp op?
CSLNKContourImpl *myContour = new CSLNKContourImpl(sizeof(empty)/sizeof(empty[0]), empty, WD_True, m_contunits);
myContour->m_contLabel = "Dynamic"; // Anders wordt hij niet getekend uiteindelijk
myContour->m_isDynamic = false; // Klinkt tegenstrijdig maar zorgt dat eerste AddPoint de boel reset
this->m_SLNKContouren.Add(myContour);
return get_ContourItem((ULONG)m_SLNKContouren.GetCount() - 1, pVal);
};
STDMETHODIMP CWhipFile::get_vectorDpi(LONG* pVal)
{
*pVal = m_vdpi;
return S_OK;
}
STDMETHODIMP CWhipFile::get_DwgLimits(IBoundingBox** pVal)
{
return m_dwgLimits->QueryInterface(IID_IBoundingBox, (void**)pVal);
}
STDMETHODIMP CWhipFile::get_UnmatchedLabels(BSTR* pUnmatched)
{
CString result = "";
for (size_t lbl = 0; lbl < m_UnmatchedLabels.GetCount(); lbl++)
{
WT_Text text = m_UnmatchedLabels[lbl];
if (result != "")
result += "\n";
result += text.string().ascii();
}
(*pUnmatched) = result.AllocSysString();
return S_OK;
}
STDMETHODIMP CWhipFile::get_forcePlanColor(LONG* pVal)
{
(*pVal) = m_forcePlanColor;
return S_OK;
}
STDMETHODIMP CWhipFile::put_forcePlanColor(LONG newVal)
{
m_forcePlanColor = newVal;
m_forcePlan = TRUE;
return S_OK;
}