[cairo-commit] roadster/src map_draw_cairo.c,1.6,1.7
Nathan Fredrickson
commit at pdx.freedesktop.org
Tue Mar 8 23:32:52 PST 2005
Committed by: nrf
Update of /cvs/cairo/roadster/src
In directory gabe:/tmp/cvs-serv15073/src
Modified Files:
map_draw_cairo.c
Log Message:
* src/map_draw_cairo.c: First round of improvements to map_draw_cairo_line_label(). Instead of centering the label on the line, the label is now positioned so that it is as horizontal as possible with the fewest bends.
Index: map_draw_cairo.c
===================================================================
RCS file: /cvs/cairo/roadster/src/map_draw_cairo.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -d -r1.6 -r1.7
--- map_draw_cairo.c 8 Mar 2005 18:40:50 -0000 1.6
+++ map_draw_cairo.c 9 Mar 2005 07:32:49 -0000 1.7
@@ -140,13 +140,26 @@
cairo_restore(pCairo);
}
-// EXPERIMENTAL TEXT RENDERING
+#define ROAD_MAX_SEGMENTS 100
+#define DRAW_LABEL_BUFFER_LEN (200)
+
+typedef struct labelposition {
+ guint m_nSegments;
+ gdouble m_fLength;
+ gdouble m_fRunTotal;
+ gdouble m_fMeanSlope;
+ gdouble m_fMeanAbsSlope;
+ gdouble m_fScore;
+} labelposition_t;
+
static void map_draw_cairo_line_label(map_t* pMap, cairo_t *pCairo, textlabelstyle_t* pLabelStyle, rendermetrics_t* pRenderMetrics, pointstring_t* pPointString, gdouble fLineWidth, const gchar* pszLabel)
{
if(pPointString->m_pPointsArray->len < 2) return;
-#define ROAD_MAX_SEGMENTS 100
- if(pPointString->m_pPointsArray->len > ROAD_MAX_SEGMENTS) { g_warning("not drawing label for road '%s' with > %d segments.\n", pszLabel, ROAD_MAX_SEGMENTS); return; }
+ if(pPointString->m_pPointsArray->len > ROAD_MAX_SEGMENTS) {
+ g_warning("not drawing label for road '%s' with > %d segments.\n", pszLabel, ROAD_MAX_SEGMENTS);
+ return;
+ }
gfloat fFontSize = pLabelStyle->m_afFontSizeAtZoomLevel[pRenderMetrics->m_nZoomLevel-1];
if(fFontSize == 0) return;
@@ -156,36 +169,41 @@
return;
}
+ gchar* pszFontFamily = ROAD_FONT;
+
+ cairo_save(pCairo);
+ cairo_select_font(pCairo, pszFontFamily, CAIRO_FONT_SLANT_NORMAL,
+ pLabelStyle->m_abBoldAtZoomLevel[pRenderMetrics->m_nZoomLevel-1] ?
+ CAIRO_FONT_WEIGHT_BOLD : CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_scale_font(pCairo, fFontSize);
+
+ // get total width of string
+ cairo_text_extents_t extents;
+ cairo_text_extents(pCairo, pszLabel, &extents);
+
+ // now find the ideal location
+
+ // place to store potential label positions
+ labelposition_t aPositions[ROAD_MAX_SEGMENTS];
+ gdouble aSlopes[ROAD_MAX_SEGMENTS];
+
mappoint_t* apPoints[ROAD_MAX_SEGMENTS];
gint nNumPoints = pPointString->m_pPointsArray->len;
- // figure out which way the road goes overall, by looking at the first and last points
- mappoint_t* pMapPoint1 = g_ptr_array_index(pPointString->m_pPointsArray, 0);
- mappoint_t* pMapPoint2 = g_ptr_array_index(pPointString->m_pPointsArray, pPointString->m_pPointsArray->len-1);
+ mappoint_t* pMapPoint1;
+ mappoint_t* pMapPoint2;
- // Does it go left-to-right?
- // NOTE: a better test would be to figure out the total length of roadsegment that goes left-to-right
- // and the total length that goes right-to-left, and swap the whole thing if right-to-left wins
- if(pMapPoint1->m_fLongitude < pMapPoint2->m_fLongitude) {
- // YES-- just copy the array
- gint iRead;
- for(iRead=0 ; iRead<pPointString->m_pPointsArray->len ; iRead++) {
- apPoints[iRead] = g_ptr_array_index(pPointString->m_pPointsArray, iRead);
- }
- }
- else {
- // NO-- (right-to-left) so reverse the array
- gint iRead,iWrite;
- for(iWrite=0, iRead=pPointString->m_pPointsArray->len-1 ; iRead>= 0 ; iWrite++, iRead--) {
- apPoints[iWrite] = g_ptr_array_index(pPointString->m_pPointsArray, iRead);
- }
+ // load point string into an array
+ gint iRead;
+ for(iRead=0 ; iRead<nNumPoints ; iRead++) {
+ apPoints[iRead] = g_ptr_array_index(pPointString->m_pPointsArray, iRead);
}
- //
- // Measure total line length (perhaps this should be passed in)
- //
+ // measure total line length
gdouble fTotalLineLength = 0.0;
- gint iPoint;
+ gint nPositions = 1;
+ gint iPoint, iPosition;
+
for(iPoint=1 ; iPoint<nNumPoints ; iPoint++) {
pMapPoint1 = apPoints[iPoint-1];
pMapPoint2 = apPoints[iPoint];
@@ -198,43 +216,102 @@
// determine slope of the line
gdouble fRise = fY2 - fY1;
gdouble fRun = fX2 - fX1;
+ gdouble fSlope = (fRun==0) ? G_MAXDOUBLE : (fRise/fRun);
gdouble fLineLength = sqrt((fRun*fRun) + (fRise*fRise));
- fTotalLineLength += fLineLength;
- }
+ aSlopes[iPoint] = fSlope;
+ aPositions[iPoint].m_nSegments = 0;
+ aPositions[iPoint].m_fLength = 0.0;
+ aPositions[iPoint].m_fRunTotal = 0.0;
+ aPositions[iPoint].m_fMeanSlope = 0.0;
+ aPositions[iPoint].m_fMeanAbsSlope = 0.0;
- gchar* pszFontFamily = ROAD_FONT;
+ for(iPosition = nPositions ; iPosition <= iPoint ; iPosition++) {
+ aPositions[iPosition].m_fLength += fLineLength;
+ aPositions[iPosition].m_fRunTotal += fRun;
+ aPositions[iPosition].m_nSegments++;
+ aPositions[iPosition].m_fMeanSlope += fSlope;
+ aPositions[iPosition].m_fMeanAbsSlope += (fSlope<0) ? -fSlope : fSlope;
- cairo_save(pCairo);
- cairo_select_font(pCairo, pszFontFamily,
- CAIRO_FONT_SLANT_NORMAL,
- pLabelStyle->m_abBoldAtZoomLevel[pRenderMetrics->m_nZoomLevel-1] ? CAIRO_FONT_WEIGHT_BOLD : CAIRO_FONT_WEIGHT_NORMAL);
- cairo_scale_font(pCairo, fFontSize);
+ if(aPositions[iPosition].m_fLength >= extents.width) nPositions++;
+ }
- // Get total width of string
- cairo_text_extents_t extents;
- cairo_text_extents(pCairo, pszLabel, &extents);
+ fTotalLineLength += fLineLength;
+ }
+
+ // if label is longer than entire line, we're out of luck
if(extents.width > fTotalLineLength) {
cairo_restore(pCairo);
return;
}
+ gdouble fMaxScore = 0.0;
+ gint iBestPosition;
+
+ for(iPosition = 1 ; iPosition < nPositions ; iPosition++) {
+ // finish calculating mean slope
+ aPositions[iPosition].m_fMeanSlope /= aPositions[iPosition].m_nSegments;
+ aPositions[iPosition].m_fMeanAbsSlope /= aPositions[iPosition].m_nSegments;
+
+ // calculating std dev of slope
+ gint iEndPoint = iPosition + aPositions[iPosition].m_nSegments;
+ gdouble fDiffSquaredSum = 0.0;
+
+ for(iPoint = iPosition ; iPoint < iEndPoint ; iPoint++) {
+ gdouble fDiff = aSlopes[iPoint] - aPositions[iPosition].m_fMeanSlope;
+ fDiffSquaredSum += (fDiff*fDiff);
+ }
+
+ gdouble fStdDevSlope = sqrt(fDiffSquaredSum / aPositions[iPosition].m_nSegments);
+
+ // calculate a score between 0 (worst) and 1 (best), we want to minimize both the mean and std dev of slope
+ aPositions[iPosition].m_fScore = 1.0/((aPositions[iPosition].m_fMeanAbsSlope+1.0)*(fStdDevSlope+1.0));
+
+ // find position with highest score
+ if(aPositions[iPosition].m_fScore > fMaxScore) {
+ fMaxScore = aPositions[iPosition].m_fScore;
+ iBestPosition = iPosition;
+ }
+ // TODO: sort postions by score and test each against scene manager until we get go ahead
+
+
+ /*
+ g_print("%s: [%d] segments = %d, slope = %2.2f, stddev = %2.2f, score = %2.2f\n", pszLabel, iPosition,
+ aPositions[iPosition].m_nSegments,
+ aPositions[iPosition].m_fMeanAbsSlope,
+ fStdDevSlope,
+ aPositions[iPosition].m_fScore);
+ */
+ }
+
+
cairo_font_extents_t font_extents;
cairo_current_font_extents(pCairo, &font_extents);
- // CENTER IT on the line ?
- // (padding) |-text-| (padding)
- // ============================
gdouble fFrontPadding = 0.0;
- gdouble fFrontPaddingNext = (fTotalLineLength - extents.width) / 2;
- // NOTE: we only worry about padding at the start, the padding at the end should just happen...
+ gdouble fFrontPaddingNext = (aPositions[iBestPosition].m_fLength - extents.width) / 2;
+ gint iStartPoint;
+
+ iStartPoint = iBestPosition;
+ if(aPositions[iBestPosition].m_fRunTotal > 0) {
+ iStartPoint = iBestPosition;
+ }
+ else {
+ // road runs backwards, reverse everything
+ iStartPoint = nNumPoints - iBestPosition - aPositions[iBestPosition].m_nSegments + 1;
+ // reverse the array
+ gint iRead,iWrite;
+ for(iWrite=0, iRead=nNumPoints-1 ; iRead>= 0 ; iWrite++, iRead--) {
+ apPoints[iWrite] = g_ptr_array_index(pPointString->m_pPointsArray, iRead);
+ }
+ }
+
+ gint iEndPoint = iStartPoint + aPositions[iBestPosition].m_nSegments;
gint nTotalStringLength = strlen(pszLabel);
gint nStringStartIndex = 0;
-// g_print("=== NEW STRING: %s (padding %f)\n", pszLabel, fPaddingRemaining);
-
- for(iPoint=1 ; iPoint<nNumPoints ; iPoint++) {
+ for(iPoint = iStartPoint ; iPoint < iEndPoint ; iPoint++) {
RENDERING_THREAD_YIELD;
if(nTotalStringLength == nStringStartIndex) break; // done
@@ -256,29 +333,23 @@
fFrontPadding = fFrontPaddingNext;
fFrontPaddingNext = 0.0;
+ // this is probably not needed now that we only loop over line segments that will contain some label
if(fFrontPadding > fLineLength) {
fFrontPaddingNext = fFrontPadding - fLineLength;
continue;
}
// do this after the padding calculation to possibly save some CPU cycles
- //~ gdouble fAngleInRadians = atan2(fRise, fRun); // * (M_PI/180.0);
-
- // this way works too:
gdouble fAngleInRadians = atan(fRise / fRun);
if(fRun < 0.0) fAngleInRadians += M_PI;
-// g_print("(fRise(%f) / fRun(%f)) = %f, atan(fRise / fRun) = %f: ", fRise, fRun, fRise / fRun, fAngleInRadians);
-
-// g_print("=== NEW SEGMENT, pixel (deltaY=%f, deltaX=%f), line len=%f, (%f,%f)->(%f,%f)\n",fRise, fRun, fLineLength, pMapPoint1->m_fLatitude,pMapPoint1->m_fLongitude,pMapPoint2->m_fLatitude,pMapPoint2->m_fLongitude);
-// g_print(" has screen coords (%f,%f)->(%f,%f)\n", fX1,fY1,fX2,fY2);
+ //g_print("(fRise(%f) / fRun(%f)) = %f, atan(fRise / fRun) = %f: ", fRise, fRun, fRise / fRun, fAngleInRadians);
+ //g_print("=== NEW SEGMENT, pixel (deltaY=%f, deltaX=%f), line len=%f, (%f,%f)->(%f,%f)\n",fRise, fRun, fLineLength, pMapPoint1->m_fLatitude,pMapPoint1->m_fLongitude,pMapPoint2->m_fLatitude,pMapPoint2->m_fLongitude);
+ //g_print(" has screen coords (%f,%f)->(%f,%f)\n", fX1,fY1,fX2,fY2);
-#define DRAW_LABEL_BUFFER_LEN (200)
gchar azLabelSegment[DRAW_LABEL_BUFFER_LEN];
- //
// Figure out how much of the string we can put in this line segment
- //
gboolean bFoundWorkableStringLength = FALSE;
gint nWorkableStringLength;
if(iPoint == (nNumPoints-1)) {
@@ -293,7 +364,7 @@
g_assert((nTotalStringLength - nStringStartIndex) > 0);
for(nWorkableStringLength = (nTotalStringLength - nStringStartIndex) ; nWorkableStringLength >= 1 ; nWorkableStringLength--) {
-// g_print("trying nWorkableStringLength = %d\n", nWorkableStringLength);
+ //g_print("trying nWorkableStringLength = %d\n", nWorkableStringLength);
if(nWorkableStringLength >= DRAW_LABEL_BUFFER_LEN) break;
@@ -301,20 +372,20 @@
memcpy(azLabelSegment, &pszLabel[nStringStartIndex], nWorkableStringLength);
azLabelSegment[nWorkableStringLength] = '\0';
-// g_print("azLabelSegment = %s\n", azLabelSegment);
+ //g_print("azLabelSegment = %s\n", azLabelSegment);
// measure the label
cairo_text_extents(pCairo, azLabelSegment, &extents);
// if we're skipping ahead some (frontpadding), effective line length is smaller, so subtract padding
if(extents.width <= (fLineLength - fFrontPadding)) {
-// g_print("found length %d for %s\n", nWorkableStringLength, azLabelSegment);
+ //g_print("found length %d for %s\n", nWorkableStringLength, azLabelSegment);
bFoundWorkableStringLength = TRUE;
// if we have 3 pixels, and we use 2, this should be NEGATIVE 1
// TODO: we should only really do this if the next segment doesn't take a huge bend
fFrontPaddingNext = extents.width - (fLineLength - fFrontPadding);
- fFrontPaddingNext /= 2; // no good explanation for this
+ fFrontPaddingNext /= 2; // no good explanation for this
break;
}
}
@@ -326,7 +397,7 @@
// give the next segment some padding if we went over on this segment
fFrontPaddingNext = extents.width - (fLineLength - fFrontPadding);
- // g_print("forcing a character (%s) on small segment, giving next segment front-padding: %f\n", azLabelSegment, fFrontPaddingNext);
+ //g_print("forcing a character (%s) on small segment, giving next segment front-padding: %f\n", azLabelSegment, fFrontPaddingNext);
}
}
@@ -351,44 +422,30 @@
gdouble fPerpendicularNormalizedY = -(fRun / fLineLength);
// we want the normal pointing towards the top of the screen. that's the negative Y direction.
-// if(fPerpendicularNormalizedY > 0) fPerpendicularNormalizedY = -fPerpendicularNormalizedY;
+ //if(fPerpendicularNormalizedY > 0) fPerpendicularNormalizedY = -fPerpendicularNormalizedY;
- // text too big to fit? then move the text "up" above the line
- //~ if(extents.height > (fLineWidth - LABEL_PIXEL_RELIEF_INSIDE_LINE)) {
- //~ // Raise the text "up" (away from center of line) half the width of the line
- //~ // This leaves it resting on the line. Then add a few pixels of relief.
-
- //~ // NOTE: the point started in the dead-center of the line
- //~ fDrawX += (fPerpendicularNormalizedX * ((fLineWidth / 2) + LABEL_PIXELS_ABOVE_LINE));
- //~ fDrawY += (fPerpendicularNormalizedY * ((fLineWidth / 2) + LABEL_PIXELS_ABOVE_LINE));
- //~ }
- //~ else {
- //~ // just nudge it "down" slightly-- the text shows up "ABOVE" and to the "RIGHT" of the point
-// fDrawX -= (fPerpendicularNormalizedX * extents.height/2);
-// fDrawY -= (fPerpendicularNormalizedY * extents.height/2);
- fDrawX -= (fPerpendicularNormalizedX * font_extents.ascent/2);
- fDrawY -= (fPerpendicularNormalizedY * font_extents.ascent/2);
- //~ }
+ fDrawX -= (fPerpendicularNormalizedX * font_extents.ascent/2);
+ fDrawY -= (fPerpendicularNormalizedY * font_extents.ascent/2);
cairo_save(pCairo);
- cairo_move_to(pCairo, fDrawX, fDrawY);
- cairo_set_rgb_color(pCairo, 0.0,0.0,0.0);
- cairo_set_alpha(pCairo, 1.0);
- cairo_rotate(pCairo, fAngleInRadians);
+ cairo_move_to(pCairo, fDrawX, fDrawY);
+ cairo_set_rgb_color(pCairo, 0.0,0.0,0.0);
+ cairo_set_alpha(pCairo, 1.0);
+ cairo_rotate(pCairo, fAngleInRadians);
- gdouble fHaloSize = pLabelStyle->m_afHaloAtZoomLevel[pRenderMetrics->m_nZoomLevel-1];
- if(fHaloSize >= 0) {
- cairo_save(pCairo);
- cairo_text_path(pCairo, azLabelSegment);
- cairo_set_line_width(pCairo, fHaloSize);
- cairo_set_rgb_color(pCairo, 1.0,1.0,1.0);
- cairo_set_line_join(pCairo, CAIRO_LINE_JOIN_BEVEL);
- //cairo_set_miter_limit(pCairo, 0.1);
- cairo_stroke(pCairo);
- cairo_restore(pCairo);
- }
- cairo_show_text(pCairo, azLabelSegment);
- //cairo_fill(pCairo);
+ gdouble fHaloSize = pLabelStyle->m_afHaloAtZoomLevel[pRenderMetrics->m_nZoomLevel-1];
+ if(fHaloSize >= 0) {
+ cairo_save(pCairo);
+ cairo_text_path(pCairo, azLabelSegment);
+ cairo_set_line_width(pCairo, fHaloSize);
+ cairo_set_rgb_color(pCairo, 1.0,1.0,1.0);
+ cairo_set_line_join(pCairo, CAIRO_LINE_JOIN_BEVEL);
+ //cairo_set_miter_limit(pCairo, 0.1);
+ cairo_stroke(pCairo);
+ cairo_restore(pCairo);
+ }
+ cairo_show_text(pCairo, azLabelSegment);
+ //cairo_fill(pCairo);
cairo_restore(pCairo);
// scenemanager_claim_polygon(pMap->m_pSceneManager, GdkPoint *pPoints, gint nNumPoints);
More information about the cairo-commit
mailing list