#include "StdAfx.h" #include "slnkcontourImpl.h" #include #ifndef PI #define PI 3.14159265358979323846 #endif #define PALETTE_RED 1 // Color in the default colormap is red. /*static*/ WT_Integer32 CSLNKContourImpl::m_next_node_num = 0; // Eigenlijk initialiseren op laatste van planfile CSLNKContourImpl::CSLNKContourImpl(void) : m_DWGArea(-1), m_outlineAlpha(255) { m_Color = WT_RGBA32(0,0,0,0); // alpha==0, 100% transparant } CSLNKContourImpl::~CSLNKContourImpl(void) { } // Algoritm: search google for Randolph Franklin polygon BOOL CSLNKContourImpl::PointInPolygon(const WT_Logical_Point pt, const WT_Point_Set &ps) { double ptx = pt.m_x; double pty = pt.m_y; int i, j, c = 0; for (i = 0, j = ps.count()-1; i < ps.count(); j = i++) { WT_Logical_Point pti = ps.points()[i]; if (pti.m_x==ptx&&pti.m_y==pty) // Op een hoekpunt is altijd goed! { return TRUE; } WT_Logical_Point ptj = ps.points()[j]; if ((((pti.m_y <= pty) && (pty < ptj.m_y)) || ((ptj.m_y <= pty) && (pty < pti.m_y))) && (ptx < (ptj.m_x - pti.m_x) * (pty - pti.m_y) / (ptj.m_y - pti.m_y) + pti.m_x)) c = !c; } return c; } BOOL CSLNKContourImpl::PointInPolygon(const CPoint pt, const CPoint *ps, int size) { double ptx = pt.x; double pty = pt.y; int i, j, c = 0; for (i = 0, j = size-1; i < size; j = i++) { CPoint pti = ps[i]; if (pti.x==ptx&&pti.y==pty) // Op een hoekpunt is altijd goed! { return TRUE; } CPoint ptj = ps[j]; if ((((pti.y <= pty) && (pty < ptj.y)) || ((ptj.y <= pty) && (pty < pti.y))) && (ptx < (ptj.x - pti.x) * (pty - pti.y) / (ptj.y - pti.y) + pti.x)) c = !c; } return c; } // http://www.codeguru.com/forum/printthread.php?t=194400 void DistanceFromLine(double cx, double cy, double ax, double ay , double bx, double by, double &distanceSegment, double &distanceLine) { // // find the distance from the point (cx,cy) to the line // determined by the points (ax,ay) and (bx,by) // // distanceSegment = distance from the point to the line segment // distanceLine = distance from the point to the line (assuming // infinite extent in both directions // /* Subject 1.02: How do I find the distance from a point to a line? Let the point be C (Cx,Cy) and the line be AB (Ax,Ay) to (Bx,By). Let P be the point of perpendicular projection of C on AB. The parameter r, which indicates P's position along AB, is computed by the dot product of AC and AB divided by the square of the length of AB: (1) AC dot AB r = --------- ||AB||^2 r has the following meaning: r=0 P = A r=1 P = B r<0 P is on the backward extension of AB r>1 P is on the forward extension of AB 00 C is right of AB s=0 C is on AB Compute s as follows: (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay) s = ----------------------------- L^2 Then the distance from C to P = |s|*L. */ double r_numerator = (cx-ax)*(bx-ax) + (cy-ay)*(by-ay); double r_denomenator = (bx-ax)*(bx-ax) + (by-ay)*(by-ay); double r = r_numerator / r_denomenator; // double px = ax + r*(bx-ax); double py = ay + r*(by-ay); // double s = ((ay-cy)*(bx-ax)-(ax-cx)*(by-ay) ) / r_denomenator; distanceLine = fabs(s)*sqrt(r_denomenator); // // (xx,yy) is the point on the lineSegment closest to (cx,cy) // double xx = px; double yy = py; if ( (r >= 0) && (r <= 1) ) { distanceSegment = distanceLine; } else { double dist1 = (cx-ax)*(cx-ax) + (cy-ay)*(cy-ay); double dist2 = (cx-bx)*(cx-bx) + (cy-by)*(cy-by); if (dist1 < dist2) { xx = ax; yy = ay; distanceSegment = sqrt(dist1); } else { xx = bx ; yy = by; distanceSegment = sqrt(dist2); } } return; } // Vind de hoek van de edge van ps die het dichts bij pt ligt // (het maakt ons niet uit of pt er binnen of buiten ligt) // A = P2Y - P1Y // B = P1X - P2X // C = P2X * P1Y - P2Y * P1X // DistToLine = Abs((A * PX + B * PY + C) / Sqr(A * A + B * B)) /*static*/ void CSLNKContourImpl::EdgeAngle(const WT_Logical_Point pt, const WT_Point_Set &ps, double &EdgeAngle, double &EdgeDistance) { double ptx = pt.m_x; double pty = pt.m_y; int i, j, c = 0; double minDistSeg = 0x7FFFFFFF; // Max WT_Integer32 double minDistLine; double angle = 0.0; myTRACE("\npt = %d,%d", pt.m_x, pt.m_y); for (i = 0, j = ps.count()-1; i < ps.count(); j = i++) { WT_Logical_Point pt1 = ps.points()[i]; WT_Logical_Point pt2 = ps.points()[j]; double DistSeg, DistLine; DistanceFromLine(pt.m_x, pt.m_y, pt1.m_x, pt1.m_y, pt2.m_x, pt2.m_y, DistSeg, DistLine); // Bij een 'ingedeukte' ruimte kan DistSeg == minDistSeg zijn. Dan wil // je juist een zo'n groot mogelijke afstand tot de virtuele projectie if (DistSeg < minDistSeg || (DistSeg == minDistSeg && DistLine > minDistLine)) { long dy = pt2.m_y - pt1.m_y; long dx = pt2.m_x - pt1.m_x; //ASSERT(!(dx == 0 && dy == 0)); angle = atan2((double)dy, (double)dx) * (180 / PI); // Tussen -180 en +180 if (dy==0) if (dx<0) angle=-180; else angle=0; myTRACE("\nDist %.1f angle set to: %.3f", DistSeg, angle); minDistSeg = DistSeg; minDistLine = DistLine; } } if (DWFArea(ps) < 0) EdgeAngle = angle; else EdgeAngle = 180+angle; if (EdgeAngle < 0) EdgeAngle += 360; EdgeDistance = minDistLine; //We rekenen met Seg maar projecteren uiteindelijk op Line } // Signed Area needed for centroid! // Negative is clockwise, Positve counterclockwise /*static*/double CSLNKContourImpl::DWFArea(const WT_Point_Set &pg) { int i; double area = 0; for (i=0;i points()[i].m_x - pt.m_x) pt = points()[i]; } return pt; } case LABEL_OUTSIDERIGHT: { WT_Logical_Box bx = bounds(); return WT_Logical_Point(bx.maxpt().m_x, bx.minpt().m_y/2+bx.maxpt().m_y/2); // Pas op voor overflow } case LABEL_OUTSIDEBOTTOM: { WT_Logical_Box bx = bounds(); return WT_Logical_Point(bx.minpt().m_x/2+bx.maxpt().m_x/2, bx.minpt().m_y); // Pas op voor overflow } default: return WT_Logical_Point(m_ptLabel.m_x, m_ptLabel.m_y); } } /*static*/WT_Logical_Point CSLNKContourImpl::Centroid(const WT_Point_Set &pg) { double ptx = 0; double pty = 0; int i, j; for (i = 0; i < pg.count(); i++) { j = (i + 1) % pg.count(); double Xi=pg.points()[i].m_x, // Alles naar double anders krijgen wel overflows Yi=pg.points()[i].m_y, Xj=pg.points()[j].m_x, Yj=pg.points()[j].m_y; double dArea = Xi*Yj - Xj*Yi; ptx += (Xi+Xj) * dArea; pty += (Yi+Yj) * dArea; } double dDWFArea6 = 6*DWFArea(pg); return WT_Logical_Point((WT_Integer32)(ptx / dDWFArea6), (WT_Integer32)(pty / dDWFArea6)); } void CSLNKContourImpl::SerializeLabel(WT_File &my_file, LABELPOS pos, int fontheight, double scale, HDC myDC) { if (m_Color.rgba().m_rgb.a==255) { // Volle achtergrond kleur COLORREF DCclr = RGB(m_Color.rgba().m_rgb.r, m_Color.rgba().m_rgb.g, m_Color.rgba().m_rgb.b); // Net niet 100% kleuren om te zorgen dat bij achtergrond // zwart/wit toggle deze kleuren niet mee togglen if (isDarkRGB(DCclr)) my_file.desired_rendition().color() = WT_Color(254,254,254,0); else my_file.desired_rendition().color() = WT_Color(1,1,1,0); } else my_file.desired_rendition().color() = WT_Color(255,255,255,0); //Teksten wit CString tok(m_ExtraLabel); // strtok seems to modify tok.Replace("~", "\n"); // We ondersteunen ook ~ als newline tok.Replace("[br]", "\n"); // We ondersteunen ook [br] als newline tok.Replace("[BR]", "\n"); // We ondersteunen ook [BR] als newline WT_Logical_Point ptTxt = LabelPosition(pos); // De wiskundige label-positie is bepaald. Nu iets corrigeren om // (afhankelijk van het font) mooier te zijn // Ook eventueel hori/vert centreren int width = -1; // Voor UBB code [c] van centreren switch (pos) { case CSLNKContourImpl::LABEL_DEFAULT: break; case CSLNKContourImpl::LABEL_TOPLEFT: // Iets naar rechtstonder moven ptTxt.m_x += fontheight/4; ptTxt.m_y -= fontheight; break; case CSLNKContourImpl::LABEL_OUTSIDERIGHT: // Iets naar rechts en verticaal centreren { // Eerst een keer simuleren om grootte te bepalen ATLASSERT(myDC!=NULL); WT_Logical_Point ptBR = DrawOneLabel(my_file, ptTxt, tok, fontheight, scale, myDC); width = (ptBR.m_x - ptTxt.m_x); ptTxt.m_y -= (ptBR.m_y - ptTxt.m_y)/2 + fontheight*9/10; ptTxt.m_x += fontheight/4; break; } case CSLNKContourImpl::LABEL_OUTSIDEBOTTOM: // Iets naar beneden en horizontaal centreren { // Eerst een keer simuleren om grootte te bepalen ATLASSERT(myDC!=NULL); WT_Logical_Point ptBR = DrawOneLabel(my_file, ptTxt, tok, fontheight, scale, myDC); width = (ptBR.m_x - ptTxt.m_x); ptTxt.m_y -= fontheight; ptTxt.m_x -= (ptBR.m_x - ptTxt.m_x)/2; break; } case CSLNKContourImpl::LABEL_CENTROID: { ATLASSERT(myDC!=NULL); // Als niet binnen contour dan terugvallen op DEFAULT if (!CSLNKContourImpl::PointInPolygon(ptTxt, *this)) ptTxt = LabelPosition(CSLNKContourImpl::LABEL_DEFAULT); // Eerst een keer simuleren om grootte te bepalen WT_Logical_Point ptBR = DrawOneLabel(my_file, ptTxt, tok, fontheight, scale, myDC); width = (ptBR.m_x - ptTxt.m_x); ptTxt.m_x -= (ptBR.m_x - ptTxt.m_x)/2; ptTxt.m_y -= (ptBR.m_y - ptTxt.m_y)/2 + fontheight*9/10; // Nooit verder naar links/boven dan TOPLEFT zou doen // JGL: jul 2007: Waarom niet? Bij heel kleine ruimtes mag bij centreren de boel er best wel iets buiten // WT_Logical_Point ptTL = LabelPosition(CSLNKContourImpl::LABEL_TOPLEFT); //ptTxt.m_x = max(ptTxt.m_x, ptTL.m_x+fontheight/4); //ptTxt.m_y = min(ptTxt.m_y, ptTL.m_y-fontheight); #ifdef _DEBUG WT_Polymarker pm(1, &ptTxt, true); pm.serialize(my_file); #endif } } // Nu echt tekenen met width<>-2 DrawOneLabel(my_file, ptTxt, tok, fontheight, scale, myDC, width); } // Draw one (possibly multline) label // When width==-2 the drawing is simulated and the result // is the calculated bottomright of the full text // Als width==-1 is centreren niet mogelijk/nodig // Als width>0 is centreren mogelijk via [c] WT_Logical_Point CSLNKContourImpl::DrawOneLabel(WT_File &my_file, WT_Logical_Point ptTxt, CString tok, int fontheight, double scale, HDC myDC, int width/*=-2*/)// Voor centreren. Moet aanroeper al een keer bepaald hebben { long max_width=0; // Bepaal hiermee 'rechtsonder' van de tekst als myDC //TODO: UBB sluitcodes ondersteunen int curpos = 0; CString token = tok.Tokenize("\n", curpos); int line = 0; bool centering=false; while( token != "" ) { // invariant (ook bij binnenkomst): ptTxt.m_y bevat het linksonderpunt van de huidige regel // uitgaande van fontheight. Bij [s] gebruik moeten we dus corrigeren //CString f(token); int skipextra = 0; // Extra skip bij underline long size=100; int thisLineHeight=fontheight; // mooie default // Supported UBB-like codes: // [[This is literal text in square brackets] // [i]This text is italic // [b]This text is bold // [u]This text is underline // [cFF00FF]This text is colored // [s50]This text is 50% sized van standaard labelfont // [S800]This text is 800 height sized // Bovenstaand alleen voor de huidige regel // [c] Centreer alle(!) volgende regels binnen bounding box (anders links gealigned) while (token[0] == '[') { token.Delete(0); if (token[0]=='[') break; // [[ALF-TB] to escape parsing switch (tolower(token[0])) { case 'i': // Italic if (width != -2) my_file.desired_rendition().font().style().set_italic(true); token.Delete(0); break; case 'b': // Bold if (width != -2) my_file.desired_rendition().font().style().set_bold(true); token.Delete(0); break; case 'u': // Underline if (width != -2) my_file.desired_rendition().font().style().set_underlined(true); token.Delete(0); skipextra = fontheight / 5; break; case 'c': // Color or Center { int end = token.Find(']'); if (end==1) { token.Delete(0); centering = true; break; } if (end >= 0) { long clr; if (sscanf(token, "c%lx]", &clr)) { WT_Color x(clr>>16,(clr&0xff00)>>8,clr&0xff); if (width != -2) my_file.desired_rendition().color() = x; } token.Delete(0, end); } break; } case 's': // Size { BOOL bigS = (token[0]=='S'); int end = token.Find(']'); if (end >= 0) { token.Delete(0); if (sscanf(token, "%d]", &size)) { if (bigS) thisLineHeight = myRound(size * scale); else thisLineHeight = myRound(double(size)*fontheight/100); ptTxt.m_y -= (thisLineHeight - fontheight); // Correctie op de invariant voor huidige regel if (width != -2) my_file.desired_rendition().font().height() = thisLineHeight; } token.Delete(0, end); } break; } } if (token[0]==']') token.Delete(0); // Skip closing ] } if (width != -2) // Echt tekenen (lees: wegschrijven naar DWF) { int horoffset=0; if (centering) { RECT rc = { 0, 0, 0, 0}; DrawText(myDC, token, token.GetLength(), &rc, DT_CALCRECT); // Grootte bepalen // Tekst horizontaal centreren horoffset = width/2 - MulDiv(rc.right,thisLineHeight,FONT_SIZER)/2; } WT_String txt; if (WT_String::is_ascii(token.GetLength(), token)) // txt.set(f.GetLength(), f); txt.set(token.GetLength(), token); else { // Use UNICODE for example for Michaël CStringW s(token); txt.set(s.GetLength(), s); } WT_Text my_text(WT_Logical_Point(ptTxt.m_x+horoffset, ptTxt.m_y), txt); my_text.serialize(my_file); // Altijd terug voor volgende regel my_file.desired_rendition().font().style().set_italic(false); my_file.desired_rendition().font().style().set_bold(false); my_file.desired_rendition().font().style().set_underlined(false); my_file.desired_rendition().color() = WT_Color(255,255,255,0); //Teksten wit TODO: (contrasteren met background) my_file.desired_rendition().font().height() = fontheight; } else { // We willen de breedte en hoogte weten van de volledige tekst RECT rc = { 0, 0, 0, 0}; // Volgens de documentatie werkt DT_CALCRECT niet voor de breedte bij // multiline teksten maar in de praktijk wel? // ==>JGL: Onze token is toch maar één regel DrawText(myDC, token, token.GetLength(), &rc, DT_CALCRECT); // Grootte bepalen // Tekst horizontaal en verticaal centreren // ptRes.m_y += rc.bottom; halen we wel uit ptY max_width = max(max_width, MulDiv(rc.right,thisLineHeight,fontheight)); } line ++; /* Get next token: */ token = tok.Tokenize("\n", curpos); ptTxt.m_y -= fontheight + skipextra; } return WT_Logical_Point(ptTxt.m_x+MulDiv(max_width,fontheight,FONT_SIZER), ptTxt.m_y); } /**************************************************************************** Serialize een contour. Varianten: - (!fromSymbol)Gesloten polyline op contourlaag Hiermee komen we twee keer langs, met en zonder solidOnly - waar uiteindelijk geen label in gevonden (m_contLabel is leeg) - waar uiteindelijk wel label in gevonden (m_Key zal waarschijnlijk contourlabel zijn maar kan zijn veranderd) - Geen kleur gezet - Met solid kleur gezet - Met transparante kleur gezet - (m_fromSymbol) Symbool (al dan niet met m_Key gedefinieerd) - Ingebouwd (star) - Gedefinieerd (bounding octagon) Als solidOnly dan alleen als alpha==255 en een label (wel herkend) ****************************************************************************/ WT_Result CSLNKContourImpl::serialize(WT_File & file, BOOL solidOnly) { if (!m_fromSymbol && m_contLabel == "" && !solidOnly) // Alleen bij tweede slag { // May very well be a textobject itself, just emit it #ifdef _DEBUG // niet meer in release-mode file.desired_rendition().color() = WT_Color(PALETTE_RED, file.desired_rendition().color_map()); file.desired_rendition().line_weight() = 0; WT_Polyline my_line( count(), points(), WD_False); my_line.serialize(file); #endif } else { // We have got a proper label/contour if (m_fromSymbol || (m_Color.rgba().m_rgb.a==255 && solidOnly) || (m_Color.rgba().m_rgb.a<255 && !solidOnly)) { // Start a node for 'everything'. Dit is wat we concreet teruggeven bij klikken in de tekening WT_Object_Node my_node(file,m_next_node_num++,m_Key); file.desired_rendition().object_node() = my_node; //char s[256]; //sprintf(s, "Ruimte %s (%.2fm2)", m_Label.ascii(), m_DWGArea/1e6); // Van de URL gebruiken we met name de friendlyName voor de poly2Map if (m_Url.index() !=0 ) { file.desired_rendition().url().add_url_optimized(m_Url, file); } // // Add the filled polygon for the room // // Ook als geheel doorzichtig // - poly2Map heeft namelijk een poly nodig // - voor als we ruimte willen aanwijzen want de point-in werkt later op polygons file.desired_rendition().color() = m_Color; // Set the color for the polygon WT_Polygon my_poly( count(), points(), WD_False); my_poly.serialize(file); #if 0 WT_RGBA32 rgba[3]; WT_RGBA32 clr(this.m_Color.rgba()); rgba[0] = clr; rgba[1] = WT_RGBA32 (clr.m_rgb.r+64>255?255:clr.m_rgb.r+64, clr.m_rgb.g+64>255?255:clr.m_rgb.g+64, clr.m_rgb.b+64>255?255:clr.m_rgb.b+64, clr.m_rgb.a); rgba[1] = WT_RGBA32 (clr.m_rgb.r-64<0?0:clr.m_rgb.r-64, clr.m_rgb.g-64<0?0:clr.m_rgb.g-64, clr.m_rgb.b-64<0?0:clr.m_rgb.b-64, clr.m_rgb.a); rgba[2] = rgba[1]; WT_Logical_Point vP[3]; vP[0] = this.LabelPosition(CSLNKContourImpl::LABEL_CENTROID); if (!CSLNKContourImpl::PointInPolygon(vP[0], *contour)) vP[0] = this.LabelPosition(CSLNKContourImpl::LABEL_DEFAULT); for (int i = 0; i < count()-1; i++) { vP[1] = points()[i]; vP[2] = points()[i+1]; WT_Gouraud_Polytriangle gpt (3, vP, rgba, WD_True); gpt.serialize (my_file); } #endif if (m_Pattern.pattern_id() != WT_Fill_Pattern::Solid) { // Nog een keer gearceerd WT_Fill_Pattern pOld = file.desired_rendition().fill_pattern(); file.desired_rendition().fill_pattern() = m_Pattern; WT_RGBA32 clr(m_Color.rgba()); //WT_Color xor((128+clr.m_rgb.r)%256, (128+clr.m_rgb.g)%256,(128+clr.m_rgb.b)%256); WT_Color xor(255, (128+clr.m_rgb.g)%256,(128+clr.m_rgb.b)%256); file.desired_rendition().color() = xor; // Inverse WT_Polygon my_poly2( count(), points(), WD_False); my_poly2.serialize(file); file.desired_rendition().fill_pattern() = pOld; } // // Nu nogmaals om de rand van de ruimte te tekenen // Dikte is door de aanroeper al gezet // // TODO: Altijd contrasteren met achtergrond if (m_outlineAlpha>0) // Voor symbolen niet nodig { file.desired_rendition().color() = WT_Color(128,128,128, m_outlineAlpha); // Set the color for the polyline //my_file.desired_rendition().color() = WT_Color(0,0,0); // Zwart WT_Polyline my_line( count(), points(), WD_False); my_line.serialize(file); } file.desired_rendition().url().clear(); } } return WT_Result::Success; }; #ifndef DWFTK_READ_ONLY void CSLNKContourImpl::serializeXML( DWFToolkit::DWFXMLSerializer& rSerializer, unsigned int nFlags ) throw( DWFException ) { rSerializer.startElement( L"Contour ", L"SLNKDWF" ); rSerializer.addAttribute( L"Label", DWFString(m_contLabel) ); wchar_t zTempBuffer[32]; _DWFCORE_SWPRINTF( zTempBuffer, 32, L"%.2f", m_DWGArea ); rSerializer.addAttribute( "Area", zTempBuffer ); //rSerializer.addAttribute( DWFXML::kzAttribute_Category, _zCategory ); //rSerializer.addAttribute( DWFXML::kzAttribute_Type, _zType ); //rSerializer.addAttribute( DWFXML::kzAttribute_Units, _zUnits ); rSerializer.endElement(); } #endif