[mapguide-commits] r5089 - trunk/MgDev/Common/Stylization
svn_mapguide at osgeo.org
svn_mapguide at osgeo.org
Thu Aug 12 13:38:29 EDT 2010
Author: waltweltonlair
Date: 2010-08-12 17:38:29 +0000 (Thu, 12 Aug 2010)
New Revision: 5089
Removed:
trunk/MgDev/Common/Stylization/SE_LineRenderer.h
Modified:
trunk/MgDev/Common/Stylization/Makefile.am
trunk/MgDev/Common/Stylization/SE_LineRenderer.cpp
trunk/MgDev/Common/Stylization/SE_Renderer.cpp
trunk/MgDev/Common/Stylization/SE_Renderer.h
trunk/MgDev/Common/Stylization/Stylization.vcproj
Log:
Small refactoring...
I moved the implementation of all line style related methods into SE_LineRenderer.cpp.
I got rid of the actual SE_LineRenderer class - it was just a bunch of static methods.
All the methods are now instance methods declared on the SE_Renderer class. This has
the advantage that we no longer need to pass SE_Renderers into the static methods.
So now the implementation of SE_Renderer is simply split between SE_Renderer.cpp and
SE_LineRenderer.cpp.
Modified: trunk/MgDev/Common/Stylization/Makefile.am
===================================================================
--- trunk/MgDev/Common/Stylization/Makefile.am 2010-08-12 16:42:37 UTC (rev 5088)
+++ trunk/MgDev/Common/Stylization/Makefile.am 2010-08-12 17:38:29 UTC (rev 5089)
@@ -179,7 +179,6 @@
SE_BufferPool.h \
SE_ExpressionBase.h \
SE_LineBuffer.h \
- SE_LineRenderer.h \
SE_Matrix.h \
SE_PositioningAlgorithms.h \
SE_Renderer.h \
Modified: trunk/MgDev/Common/Stylization/SE_LineRenderer.cpp
===================================================================
--- trunk/MgDev/Common/Stylization/SE_LineRenderer.cpp 2010-08-12 16:42:37 UTC (rev 5088)
+++ trunk/MgDev/Common/Stylization/SE_LineRenderer.cpp 2010-08-12 17:38:29 UTC (rev 5089)
@@ -16,7 +16,7 @@
//
#include "stdafx.h"
-#include "SE_LineRenderer.h"
+#include "SE_Renderer.h"
#include "SE_BufferPool.h"
#include "RS_FontEngine.h"
@@ -210,17 +210,17 @@
//
// ============================================================================
-void SE_LineRenderer::ProcessLineOverlapWrap(SE_Renderer* renderer, LineBuffer* geometry, SE_RenderLineStyle* style)
+void SE_Renderer::ProcessLineOverlapWrap(LineBuffer* geometry, SE_RenderLineStyle* style)
{
_ASSERT(style->repeat > 0.0);
- SE_BufferPool* lbp = renderer->GetBufferPool();
+ SE_BufferPool* lbp = GetBufferPool();
SE_RenderPrimitiveList& rs = style->symbol;
- RS_FontEngine* fe = renderer->GetRSFontEngine();
+ RS_FontEngine* fe = GetRSFontEngine();
- double px2su = renderer->GetScreenUnitsPerPixel();
+ double px2su = GetScreenUnitsPerPixel();
RS_Bounds styleBounds(DBL_MAX, DBL_MAX, -DBL_MAX, -DBL_MAX);
styleBounds.add_point(style->bounds[0]);
@@ -267,20 +267,20 @@
// create the chopped up LineBuffer for this primitive
choppedBuffers[cur_prim] = LineBufferPool::NewLineBuffer(lbp, 128, FdoDimensionality_XY, true);
- SE_LineRenderer::ChopLineBuffer(renderer, pl->geometry->xf_buffer(), choppedBuffers[cur_prim]);
+ ChopLineBuffer(pl->geometry->xf_buffer(), choppedBuffers[cur_prim]);
// update rendering attributes to account for the selection mode - it's
// ok to update these attributes and not revert them
- if (renderer->m_bSelectionMode)
+ if (m_bSelectionMode)
{
if (primitive->type == SE_RenderPrimitive_Polygon)
{
SE_RenderPolygon* pl = (SE_RenderPolygon*)primitive;
- pl->fill = renderer->m_selFillColor;
+ pl->fill = m_selFillColor;
}
- pl->lineStroke.color = renderer->m_selLineStroke.color;
- pl->lineStroke.weight = renderer->m_selLineStroke.weight;
+ pl->lineStroke.color = m_selLineStroke.color;
+ pl->lineStroke.weight = m_selLineStroke.weight;
}
}
else if (primitive->type == SE_RenderPrimitive_Text)
@@ -293,12 +293,12 @@
// update rendering attributes to account for the selection mode - it's
// ok to update these attributes and not revert them
- if (renderer->m_bSelectionMode)
+ if (m_bSelectionMode)
{
- tp->tdef.textcolor() = renderer->m_textForeColor;
- tp->tdef.ghostcolor() = renderer->m_textBackColor;
-// tp->tdef.framecolor() = renderer->m_textBackColor;
-// tp->tdef.opaquecolor() = renderer->m_textBackColor;
+ tp->tdef.textcolor() = m_textForeColor;
+ tp->tdef.ghostcolor() = m_textBackColor;
+// tp->tdef.framecolor() = m_textBackColor;
+// tp->tdef.opaquecolor() = m_textBackColor;
}
}
else if (primitive->type == SE_RenderPrimitive_Raster)
@@ -332,7 +332,7 @@
// make a list of hotspots where we go from one join to another
// or from join to straight line or from straight line to join
- int ptCount = SE_LineRenderer::ConfigureHotSpots(renderer, geometry, cur_contour, style, styleBounds, hotspots);
+ int ptCount = ConfigureHotSpots(geometry, cur_contour, style, styleBounds, hotspots);
double total_length = hotspots[ptCount-1].mid;
// get the distribution for the current contour
@@ -434,7 +434,7 @@
{
// We're not yet in danger of a corner (or the start/end of the distribution).
// Just put the pedal to the metal and draw an unwarped symbol.
- renderer->DrawSymbol(rs, xformStart, next_hotspot->angle_start);
+ DrawSymbol(rs, xformStart, next_hotspot->angle_start);
drawpos += repeat;
sym_minx = drawpos + styleBounds.minx;
@@ -866,14 +866,14 @@
geomToDraw = NULL;
else
{
- geomToDraw = SE_LineRenderer::ClipPolygon(lbp, geom, startpos, endpos);
+ geomToDraw = ClipPolygon(lbp, geom, startpos, endpos);
spLB.reset(geomToDraw);
}
}
if (geomToDraw)
{
- renderer->DrawScreenPolygon(geomToDraw, NULL, pl->fill);
+ DrawScreenPolygon(geomToDraw, NULL, pl->fill);
if (spLB.get())
LineBufferPool::FreeLineBuffer(lbp, spLB.release());
}
@@ -900,14 +900,14 @@
geomToDraw = NULL;
else
{
- geomToDraw = SE_LineRenderer::ClipPolyline(lbp, geom, startpos, endpos);
+ geomToDraw = ClipPolyline(lbp, geom, startpos, endpos);
spLB.reset(geomToDraw);
}
}
if (geomToDraw)
{
- renderer->DrawScreenPolyline(geomToDraw, NULL, pl->lineStroke);
+ DrawScreenPolyline(geomToDraw, NULL, pl->lineStroke);
if (spLB.get())
LineBufferPool::FreeLineBuffer(lbp, spLB.release());
}
@@ -934,7 +934,7 @@
// We must recalculate the text metrics with the new tdef before we can call DrawScreenText.
RS_TextMetrics tm;
if (fe->GetTextMetrics(tp->content, tdef, tm, false))
- renderer->DrawScreenText(tm, tdef, x, y, NULL, 0, 0.0);
+ DrawScreenText(tm, tdef, x, y, NULL, 0, 0.0);
}
}
break;
@@ -956,7 +956,7 @@
geom.get_point(0, x, y);
double angleDeg = (rp->angleRad + last_angleRad) * M_180PI;
- renderer->DrawScreenRaster(imgData.data, imgData.size, imgData.format, imgData.width, imgData.height, x, y, rp->extent[0], rp->extent[1], angleDeg);
+ DrawScreenRaster(imgData.data, imgData.size, imgData.format, imgData.width, imgData.height, x, y, rp->extent[0], rp->extent[1], angleDeg);
}
}
}
@@ -999,8 +999,8 @@
// This method configures hotspots for the supplied feature geometry.
// Hotspots contain all the information needed to render the warped
// symbol distribution for the polyline.
-int SE_LineRenderer::ConfigureHotSpots(SE_Renderer* renderer, LineBuffer* geometry, int cur_contour,
- SE_RenderLineStyle* style, RS_Bounds& styleBounds, HotSpot* hotspots)
+int SE_Renderer::ConfigureHotSpots(LineBuffer* geometry, int cur_contour, SE_RenderLineStyle* style,
+ RS_Bounds& styleBounds, HotSpot* hotspots)
{
// This value is used when computing the miter warping. The maximum
// warp occurs at + or - affected_height from the centerline and all
@@ -1010,7 +1010,7 @@
bool is_closed = geometry->contour_closed(cur_contour);
// initialize hotspot vertices to the set of reduced points
- int ptCount = SE_LineRenderer::ComputePoints(renderer, geometry, cur_contour, hotspots);
+ int ptCount = ComputePoints(geometry, cur_contour, hotspots);
_ASSERT(ptCount >= 2);
// compute the parametric positions and entry/exit angles for the hotspots
@@ -1439,7 +1439,7 @@
// Flip the angles for renderers with Y pointing down, since they were computed
// from screen points. The sin/cos values for the angles must stay in screen
// space, however.
- if (!renderer->YPointsUp())
+ if (!YPointsUp())
{
for (int i=0; i<ptCount; ++i)
{
@@ -1458,9 +1458,9 @@
// the hotspot vertices to the reduced set of points.
// TODO: WCW - enhance the point reduction to replace each blob of
// closely spaced points with its average position
-int SE_LineRenderer::ComputePoints(SE_Renderer* renderer, LineBuffer* geometry, int cur_contour, HotSpot* hotspots)
+int SE_Renderer::ComputePoints(LineBuffer* geometry, int cur_contour, HotSpot* hotspots)
{
- double ds = renderer->GetDrawingScale();
+ double ds = GetDrawingScale();
double d2min = ds*ds*OPTIMIZE_DISTANCE_SQ;
// start index and number of points in the point array for current contour
@@ -1490,7 +1490,7 @@
skipped = false;
- renderer->WorldToScreenPoint(x, y, hotspots[ptCount].x, hotspots[ptCount].y);
+ WorldToScreenPoint(x, y, hotspots[ptCount].x, hotspots[ptCount].y);
++ptCount;
lastx = x;
@@ -1508,7 +1508,7 @@
if (x == lastx && y == lasty)
{
// use an offset of 0.1 pixels
- double offset = 0.1*renderer->GetScreenUnitsPerPixel();
+ double offset = 0.1*GetScreenUnitsPerPixel();
// they're identical - add a second hotspot and adjust both so we
// have a short (yet valid) segment centered about the original point
@@ -1537,7 +1537,7 @@
if (skipped)
{
// the last point is still stored in x,y
- renderer->WorldToScreenPoint(x, y, hotspots[ptCount-1].x, hotspots[ptCount-1].y);
+ WorldToScreenPoint(x, y, hotspots[ptCount-1].x, hotspots[ptCount-1].y);
}
return ptCount;
@@ -1545,9 +1545,9 @@
///////////////////////////////////////////////////////////////////////////////
-void SE_LineRenderer::ChopLineBuffer(SE_Renderer* renderer, LineBuffer* inBuffer, LineBuffer* outBuffer)
+void SE_Renderer::ChopLineBuffer(LineBuffer* inBuffer, LineBuffer* outBuffer)
{
- double pixelsPerScreenUnit = 1.0 / renderer->GetScreenUnitsPerPixel();
+ double pixelsPerScreenUnit = 1.0 / GetScreenUnitsPerPixel();
for (int j=0; j<inBuffer->cntr_count(); ++j)
{
@@ -1630,7 +1630,7 @@
// Clips a polyline against the clip region. A new contour is created
// each time the polyline passes a clip boundary. The algorithm is just
// Cohen-Sutherland modified for 1D clipping (using the parameter Z).
-LineBuffer* SE_LineRenderer::ClipPolyline(LineBufferPool* lbp, LineBuffer& geometry, double zMin, double zMax)
+LineBuffer* SE_Renderer::ClipPolyline(LineBufferPool* lbp, LineBuffer& geometry, double zMin, double zMax)
{
// need at least two points
if (geometry.point_count() < 2)
@@ -1663,7 +1663,7 @@
{
geometry.get_point(i-1, aline[0], aline[1], aline[4]);
geometry.get_point(i , aline[2], aline[3], aline[5]);
- int res = SE_LineRenderer::ClipLine(zMin, zMax, aline, bline);
+ int res = ClipLine(zMin, zMax, aline, bline);
if (res == 0)
{
@@ -1700,7 +1700,7 @@
///////////////////////////////////////////////////////////////////////////////
// Clips a polygon against the clip region. The algorithm is just
// Sutherland-Hodgman modified for 1D clipping (using the parameter Z).
-LineBuffer* SE_LineRenderer::ClipPolygon(LineBufferPool* lbp, LineBuffer& geometry, double zMin, double zMax)
+LineBuffer* SE_Renderer::ClipPolygon(LineBufferPool* lbp, LineBuffer& geometry, double zMin, double zMax)
{
// need at least two points
if (geometry.point_count() < 3)
@@ -1740,7 +1740,7 @@
{
geometry.get_point(i-1, aline[0], aline[1], aline[4]);
geometry.get_point(i , aline[2], aline[3], aline[5]);
- int res = SE_LineRenderer::ClipLine(zMin, zMax, aline, bline);
+ int res = ClipLine(zMin, zMax, aline, bline);
if (res == 0)
{
@@ -1789,7 +1789,7 @@
#define LEFT 0x01
#define RIGHT 0x02
-int SE_LineRenderer::ClipLine(double zMin, double zMax, double* line, double* ret)
+int SE_Renderer::ClipLine(double zMin, double zMax, double* line, double* ret)
{
ret[0] = line[0];
ret[1] = line[1];
@@ -1797,8 +1797,8 @@
ret[3] = line[3];
// compute the initial clip codes for the endpoints
- int clipCode1 = SE_LineRenderer::ClipCode(zMin, zMax, line[4]);
- int clipCode2 = SE_LineRenderer::ClipCode(zMin, zMax, line[5]);
+ int clipCode1 = ClipCode(zMin, zMax, line[4]);
+ int clipCode2 = ClipCode(zMin, zMax, line[5]);
// trivially reject or accept the line segment
if ((clipCode1 & clipCode2) != 0) // both points left or both points right
@@ -1845,7 +1845,7 @@
///////////////////////////////////////////////////////////////////////////////
// Returns whether a point is inside the clip region, or to the left or right.
-int SE_LineRenderer::ClipCode(double zMin, double zMax, double z)
+int SE_Renderer::ClipCode(double zMin, double zMax, double z)
{
if (z < zMin)
return LEFT;
@@ -1855,3 +1855,1057 @@
return INSIDE;
}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Distributes symbols along a polyline using the OverlapNone vertex control option.
+void SE_Renderer::ProcessLineOverlapNone(LineBuffer* geometry, SE_RenderLineStyle* style)
+{
+ _ASSERT(style->repeat > 0.0);
+
+ SE_Matrix symxf;
+ bool yUp = YPointsUp();
+ double px2su = GetScreenUnitsPerPixel();
+
+ double baseAngleRad = style->angleRad;
+
+ // precompute these - these are in renderer space, hence the check for yUp with the sine
+ double baseAngleCos = cos(baseAngleRad);
+ double baseAngleSin = sin(yUp? baseAngleRad : -baseAngleRad);
+
+ // account for any viewport rotation
+ double w2sAngleRad = GetWorldToScreenRotation();
+
+ double angleRad, angleCos, angleSin;
+ if (w2sAngleRad == 0.0)
+ {
+ angleRad = baseAngleRad;
+ angleCos = baseAngleCos;
+ angleSin = baseAngleSin;
+ }
+ else
+ {
+ angleRad = baseAngleRad + w2sAngleRad;
+ angleCos = cos(angleRad);
+ angleSin = sin(yUp? angleRad : -angleRad);
+ }
+
+ // screen coordinates of current line segment
+ double segX0, segY0, segX1, segY1;
+
+ // this is the same for all contours / groups
+ double leftEdge = style->bounds[0].x;
+ double rightEdge = style->bounds[1].x;
+
+ // get segment lengths
+ double* segLens = (double*)alloca(sizeof(double)*geometry->point_count());
+ ComputeSegmentLengths(geometry, segLens);
+
+ // used for segment group calculations
+ int* segGroups = (int*)alloca(2*sizeof(int)*geometry->point_count());
+ double* groupLens = (double*)alloca(sizeof(double)*geometry->point_count());
+
+ // configure the default path line stroke to use
+ SE_LineStroke dpLineStroke = style->dpLineStroke;
+ if (m_bSelectionMode)
+ {
+ dpLineStroke.color = m_selLineStroke.color;
+ dpLineStroke.weight = m_selLineStroke.weight;
+ }
+
+ // Used for drawing the centerline paths at vertices. In the case of
+ // a single contour the start/end points will need a MoveTo/LineTo,
+ // while the interior points will need a MoveTo/LineTo/LineTo.
+ LineBuffer vertexLines(3*geometry->point_count()-2);
+
+ // iterate over the contours
+ for (int j=0; j<geometry->cntr_count(); ++j)
+ {
+ // get starting segment for current contour
+ int start_seg_contour = geometry->contour_start_point(j);
+
+ // skip zero-length contours
+ if (segLens[start_seg_contour] == 0.0)
+ continue;
+
+ // get the distribution for the current contour
+ double repeat = style->repeat;
+ double startOffset = style->startOffset;
+ double endOffset = style->endOffset;
+ if (style->unitsControl == SE_UnitsControl_Parametric)
+ {
+ repeat *= segLens[start_seg_contour];
+ startOffset *= segLens[start_seg_contour];
+ endOffset *= segLens[start_seg_contour];
+
+ // It makes no sense to distribute symbols using a repeat value
+ // which is much less than one pixel. We'll scale up any value
+ // less than 0.25 to 0.5.
+ if (repeat > 0.0 && repeat < 0.25*px2su)
+ {
+ // just increase it by an integer multiple so the overall
+ // distribution isn't affected
+ int factor = (int)(0.5*px2su / repeat);
+ repeat *= factor;
+ }
+ }
+
+ // check if:
+ // - the start offset goes beyond the end of the contour
+ // - the end offset goes beyond the beginning of the contour
+ // - the start offset goes beyond the end offset
+ double offsetSum = rs_max(startOffset, 0.0) + rs_max(endOffset, 0.0);
+ if (offsetSum > segLens[start_seg_contour])
+ continue;
+
+ // compute the segment groups for this contour based on the vertex angle limit
+ int numGroups = ComputeSegmentGroups(geometry, j, style->vertexAngleLimit, segLens, segGroups);
+ if (numGroups == 0)
+ continue;
+
+ // compute the group lengths
+ ComputeGroupLengths(segLens, numGroups, segGroups, groupLens);
+
+ // for this vertex control option we set the offsets to zero if they're unspecified
+ startOffset = rs_max(startOffset, 0.0);
+ endOffset = rs_max(endOffset, 0.0);
+
+ // compute the starting group based on the style's start offset
+ int start_group = 0;
+ if (startOffset > 0.0)
+ {
+ for (int k=0; k<numGroups; ++k)
+ {
+ if (startOffset < groupLens[k])
+ {
+ start_group = k;
+ break;
+ }
+
+ // adjust the start offset so it's relative to the starting group
+ startOffset -= groupLens[k];
+ }
+ }
+
+ // compute the ending group based on the style's end offset
+ int end_group = numGroups-1;
+ if (endOffset > 0.0)
+ {
+ for (int k=numGroups-1; k>=0; --k)
+ {
+ if (endOffset < groupLens[k])
+ {
+ end_group = k;
+ break;
+ }
+
+ // adjust the end offset so it's relative to the ending group
+ endOffset -= groupLens[k];
+ }
+ }
+
+ // iterate over the relevant groups
+ for (int k=start_group; k<=end_group; ++k)
+ {
+ // get segment range for current group
+ int start_seg = segGroups[2*k];
+ int end_seg = segGroups[2*k+1];
+ int cur_seg = start_seg;
+
+ // get the actual start / end offsets for the current group
+ // - for the first group its start offset is the specified value, while
+ // for subsequent groups it's zero (we draw a symbol directly at the
+ // start of the group)
+ // - for the last group the end offset is the specified value, while for
+ // prior groups it's zero (we draw a symbol directly at the end of the
+ // group)
+ double startOffsetGroup = (k == start_group)? startOffset : 0.0;
+ double endOffsetGroup = (k == end_group)? endOffset : 0.0;
+
+ // Compute the symbol distribution for the group. The drawpos variable
+ // is the position of the first symbol. If gap is > 0 then the next symbol
+ // is offset by that amount. Subsequent symbols are then offset by the
+ // repeat, except for the last one which is again offset by the gap.
+ //
+ // Here's a graphical depiction:
+ //
+ // |-----+-----+---+---+---+-----+--------|
+ // s d | \__|__/ | e
+ // t r g r g n
+ // a a a e a d
+ // r w p p p
+ // t p e
+ // o a
+ // s t
+ int numSymbols = 0;
+ double drawpos = startOffsetGroup;
+ double gap = 0.0;
+ ComputeGroupDistribution(groupLens[k], startOffsetGroup, endOffsetGroup, repeat,
+ rightEdge - leftEdge, drawpos, gap, numSymbols);
+ if (numSymbols == 0)
+ continue;
+
+ //-------------------------------------------------------
+ // draw symbols along the group
+ //-------------------------------------------------------
+
+ int numDrawn = 0;
+ double increment;
+
+ // get start point of first segment in screen space
+ WorldToScreenPoint(geometry->x_coord(cur_seg), geometry->y_coord(cur_seg), segX0, segY0);
+
+ while (cur_seg < end_seg)
+ {
+ ++cur_seg;
+
+ // skip zero-length segments - no need to update the start/end points
+ double len = segLens[cur_seg];
+ if (len == 0.0)
+ continue;
+
+ // get end point of current segment in screen space
+ WorldToScreenPoint(geometry->x_coord(cur_seg), geometry->y_coord(cur_seg), segX1, segY1);
+
+ // if our draw position falls within this segment then process
+ if (drawpos <= len)
+ {
+ // compute linear deltas for x and y directions - we will use these
+ // to quickly move along the line without having to do too much math
+ double invlen = 1.0 / len;
+ double dx_incr = (segX1 - segX0) * invlen;
+ double dy_incr = (segY1 - segY0) * invlen;
+
+ if (style->angleControl == SE_AngleControl_FromGeometry)
+ {
+ angleCos = dx_incr*baseAngleCos - dy_incr*baseAngleSin;
+ angleSin = dy_incr*baseAngleCos + dx_incr*baseAngleSin;
+ angleRad = atan2(dy_incr, dx_incr);
+
+ // since dy_incr and dx_incr are in renderer space we need to
+ // negate the angle if y points down
+ if (!yUp)
+ angleRad = -angleRad;
+
+ angleRad += baseAngleRad;
+ }
+ double tx = segX0 + dx_incr * drawpos;
+ double ty = segY0 + dy_incr * drawpos;
+
+ symxf.setIdentity();
+ symxf.rotate(angleSin, angleCos);
+ symxf.translate(tx, ty);
+
+ // loop-draw the symbol along the current segment, incrementing
+ // the draw position by the appropriate amount
+ while (drawpos <= len && numDrawn < numSymbols)
+ {
+ // in the case of labels we only draw them at the interior points
+ if (style->drawLast)
+ {
+ if (numDrawn > 0 && numDrawn < numSymbols-1)
+ AddLabel(geometry, style, symxf, angleRad);
+ }
+ else
+ {
+ // handle the centerline path at the group's start
+ if (numDrawn == 0)
+ {
+ // This is the first time drawing anything for this group. If
+ // this is the starting group, then initialize the centerline
+ // path at the group's start. If it's not the starting group
+ // then we'll add LineTo segments (see further down) to the
+ // existing centerline path at the previous group's end.
+ if (k == start_group)
+ vertexLines.MoveTo(symxf.x2, symxf.y2);
+ }
+ else if (numDrawn == 1 && numSymbols > 2)
+ {
+ // finish and draw the centerline path at the group's start,
+ // aligning it with the left edge of the symbol
+ // TODO: account for symbol rotation
+ vertexLines.LineTo(symxf.x2 + dx_incr*leftEdge, symxf.y2 + dy_incr*leftEdge);
+ DrawScreenPolyline(&vertexLines, NULL, dpLineStroke);
+ vertexLines.Reset();
+ }
+
+ // only draw symbols at the interior points
+ if (numDrawn > 0 && numDrawn < numSymbols-1)
+ DrawSymbol(style->symbol, symxf, angleRad, style->addToExclusionRegion);
+
+ // handle the centerline path at the group's end - only
+ // need to do this if we have at least one interior symbol
+ if (numDrawn == numSymbols-2 && numSymbols > 2)
+ {
+ // initialize the centerline path at the group's end,
+ // aligning it with the right edge of the symbol
+ // TODO: account for symbol rotation
+ vertexLines.MoveTo(symxf.x2 + dx_incr*rightEdge, symxf.y2 + dy_incr*rightEdge);
+ }
+ else if (numDrawn == numSymbols-1)
+ {
+ // This is the last time drawing anything for this group, so
+ // finish the centerline path at the group's end. If this is
+ // the ending group, then also draw it. If it's not the ending
+ // group then we'll draw it up above when we finish the centerline
+ // path at the next group's start.
+ vertexLines.LineTo(symxf.x2, symxf.y2);
+ if (k == end_group)
+ {
+ DrawScreenPolyline(&vertexLines, NULL, dpLineStroke);
+ vertexLines.Reset();
+ }
+ }
+ }
+
+ ++numDrawn;
+
+ // move forward
+ increment = repeat;
+ if (gap != 0.0)
+ {
+ // if we have a gap, then use it after drawing the first
+ // symbol and before drawing the last symbol
+ if (numSymbols == 2)
+ {
+ // in this case the gap takes us until the end
+ increment = gap;
+ }
+ else
+ {
+ if (numDrawn == 1)
+ increment = gap - leftEdge;
+ else if (numDrawn == numSymbols - 1)
+ increment = gap + rightEdge;
+ }
+ }
+
+ symxf.translate(dx_incr*increment, dy_incr*increment);
+ drawpos += increment;
+ }
+ }
+
+ drawpos -= len;
+
+ // start point for next segment is current end point
+ segX0 = segX1;
+ segY0 = segY1;
+ }
+ }
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Distributes symbols along a polyline using the OverlapDirect vertex control option.
+// This setting is intended to only be used in the context of point symbols. These
+// symbols are simply drawn without any wrapping, truncation, or other modification.
+//
+// The following rules apply to the StartOffset and EndOffset parameters:
+// - if StartOffset is specified (>=0) then a symbol is drawn at the start
+// offset location
+// - if EndOffset is specified (>=0) then a symbol is drawn at the end
+// offset location
+// - if StartOffset and EndOffset are both specified but their sum is greater
+// than the contour length (EndOffset offset comes before StartOffset)
+// then no symbols are drawn
+// - if StartOffset and EndOffset are both unspecified (< 0) then no symbols
+// are drawn
+void SE_Renderer::ProcessLineOverlapDirect(LineBuffer* geometry, SE_RenderLineStyle* style)
+{
+ _ASSERT(style->repeat > 0.0);
+
+ SE_Matrix symxf;
+ bool yUp = YPointsUp();
+ double px2su = GetScreenUnitsPerPixel();
+
+ double baseAngleRad = style->angleRad;
+
+ // precompute these - these are in renderer space, hence the check for yUp with the sine
+ double baseAngleCos = cos(baseAngleRad);
+ double baseAngleSin = sin(yUp? baseAngleRad : -baseAngleRad);
+
+ // account for any viewport rotation
+ double w2sAngleRad = GetWorldToScreenRotation();
+
+ double angleRad, angleCos, angleSin;
+ if (w2sAngleRad == 0.0)
+ {
+ angleRad = baseAngleRad;
+ angleCos = baseAngleCos;
+ angleSin = baseAngleSin;
+ }
+ else
+ {
+ angleRad = baseAngleRad + w2sAngleRad;
+ angleCos = cos(angleRad);
+ angleSin = sin(yUp? angleRad : -angleRad);
+ }
+
+ // screen coordinates of current line segment
+ double segX0, segY0, segX1, segY1;
+
+ // get segment lengths
+ double* segLens = (double*)alloca(sizeof(double)*geometry->point_count());
+ ComputeSegmentLengths(geometry, segLens);
+
+ // used for segment group calculations
+ int* segGroups = (int*)alloca(2*sizeof(int)*geometry->point_count());
+ double* groupLens = (double*)alloca(sizeof(double)*geometry->point_count());
+
+ // iterate over the contours
+ for (int j=0; j<geometry->cntr_count(); ++j)
+ {
+ // get starting segment for current contour
+ int start_seg_contour = geometry->contour_start_point(j);
+
+ // skip zero-length contours
+ if (segLens[start_seg_contour] == 0.0)
+ continue;
+
+ // get the distribution for the current contour
+ double repeat = style->repeat;
+ double startOffset = style->startOffset;
+ double endOffset = style->endOffset;
+ if (style->unitsControl == SE_UnitsControl_Parametric)
+ {
+ repeat *= segLens[start_seg_contour];
+ startOffset *= segLens[start_seg_contour];
+ endOffset *= segLens[start_seg_contour];
+
+ // It makes no sense to distribute symbols using a repeat value
+ // which is much less than one pixel. We'll scale up any value
+ // less than 0.25 to 0.5.
+ if (repeat > 0.0 && repeat < 0.25*px2su)
+ {
+ // just increase it by an integer multiple so the overall
+ // distribution isn't affected
+ int factor = (int)(0.5*px2su / repeat);
+ repeat *= factor;
+ }
+ }
+
+ // check if:
+ // - the start offset goes beyond the end of the contour
+ // - the end offset goes beyond the beginning of the contour
+ // - the start offset goes beyond the end offset
+ double offsetSum = rs_max(startOffset, 0.0) + rs_max(endOffset, 0.0);
+ if (offsetSum > segLens[start_seg_contour])
+ continue;
+
+ // compute the segment groups for this contour based on the vertex angle limit
+ int numGroups = ComputeSegmentGroups(geometry, j, style->vertexAngleLimit, segLens, segGroups);
+ if (numGroups == 0)
+ continue;
+
+ // compute the group lengths
+ ComputeGroupLengths(segLens, numGroups, segGroups, groupLens);
+
+ // compute the starting group based on the style's start offset
+ int start_group = 0;
+ if (startOffset > 0.0)
+ {
+ for (int k=0; k<numGroups; ++k)
+ {
+ if (startOffset < groupLens[k])
+ {
+ start_group = k;
+ break;
+ }
+
+ // adjust the start offset so it's relative to the starting group
+ startOffset -= groupLens[k];
+ }
+ }
+
+ // compute the ending group based on the style's end offset
+ int end_group = numGroups-1;
+ if (endOffset > 0.0)
+ {
+ for (int k=numGroups-1; k>=0; --k)
+ {
+ if (endOffset < groupLens[k])
+ {
+ end_group = k;
+ break;
+ }
+
+ // adjust the end offset so it's relative to the ending group
+ endOffset -= groupLens[k];
+ }
+ }
+
+ // iterate over the relevant groups
+ for (int k=start_group; k<=end_group; ++k)
+ {
+ // get segment range for current group
+ int start_seg = segGroups[2*k];
+ int end_seg = segGroups[2*k+1];
+ int cur_seg = start_seg;
+
+ // get the actual start / end offsets for the current group
+ // - for the first group its start offset is the specified value, while
+ // for subsequent groups it's zero (we draw a symbol directly at the
+ // start of the group)
+ // - for the last group the end offset is the specified value, while for
+ // prior groups it's zero (we draw a symbol directly at the end of the
+ // group)
+ double startOffsetGroup = (k == start_group)? startOffset : 0.0;
+ double endOffsetGroup = (k == end_group)? endOffset : 0.0;
+
+ // Compute the symbol distribution for the group. The drawpos variable
+ // is the position of the first symbol. If gap is > 0 then the next symbol
+ // is offset by that amount. Subsequent symbols are then offset by the
+ // repeat, except for the last one which is again offset by the gap.
+ //
+ // Here's a graphical depiction:
+ //
+ // |-----+-----+---+---+---+-----+--------|
+ // s d | \__|__/ | e
+ // t r g r g n
+ // a a a e a d
+ // r w p p p
+ // t p e
+ // o a
+ // s t
+ int numSymbols = 0;
+ double drawpos = startOffsetGroup;
+ double gap = 0.0;
+ ComputeGroupDistribution(groupLens[k], startOffsetGroup, endOffsetGroup, repeat, 0.0,
+ drawpos, gap, numSymbols);
+ if (numSymbols == 0)
+ continue;
+
+ //-------------------------------------------------------
+ // draw symbols along the group
+ //-------------------------------------------------------
+
+ int numDrawn = 0;
+ double increment;
+
+ // get start point of first segment in screen space
+ WorldToScreenPoint(geometry->x_coord(cur_seg), geometry->y_coord(cur_seg), segX0, segY0);
+
+ while (cur_seg < end_seg)
+ {
+ ++cur_seg;
+
+ // skip zero-length segments - no need to update the start/end points
+ double len = segLens[cur_seg];
+ if (len == 0.0)
+ continue;
+
+ // get end point of current segment in screen space
+ WorldToScreenPoint(geometry->x_coord(cur_seg), geometry->y_coord(cur_seg), segX1, segY1);
+
+ // if our draw position falls within this segment then process
+ if (drawpos <= len)
+ {
+ // compute linear deltas for x and y directions - we will use these
+ // to quickly move along the line without having to do too much math
+ double invlen = 1.0 / len;
+ double dx_incr = (segX1 - segX0) * invlen;
+ double dy_incr = (segY1 - segY0) * invlen;
+
+ if (style->angleControl == SE_AngleControl_FromGeometry)
+ {
+ angleCos = dx_incr*baseAngleCos - dy_incr*baseAngleSin;
+ angleSin = dy_incr*baseAngleCos + dx_incr*baseAngleSin;
+ angleRad = atan2(dy_incr, dx_incr);
+
+ // since dy_incr and dx_incr are in renderer space we need to
+ // negate the angle if y points down
+ if (!yUp)
+ angleRad = -angleRad;
+
+ angleRad += baseAngleRad;
+ }
+ double tx = segX0 + dx_incr * drawpos;
+ double ty = segY0 + dy_incr * drawpos;
+
+ symxf.setIdentity();
+ symxf.rotate(angleSin, angleCos);
+ symxf.translate(tx, ty);
+
+ // loop-draw the symbol along the current segment,
+ // incrementing the draw position by the appropriate amount
+ while (drawpos <= len && numDrawn < numSymbols)
+ {
+ // don't draw the same symbol once at the end of a group
+ // and again at the start of the next group
+ if (k == start_group || numDrawn > 0)
+ {
+ // draw the symbol at the current position
+ if (style->drawLast)
+ AddLabel(geometry, style, symxf, angleRad);
+ else
+ DrawSymbol(style->symbol, symxf, angleRad, style->addToExclusionRegion);
+ }
+
+ ++numDrawn;
+
+ // move forward
+ increment = repeat;
+ if (gap != 0.0)
+ {
+ // if we have a gap, then use it after drawing the first
+ // symbol and before drawing the last symbol
+ if (numDrawn == 1 || numDrawn == numSymbols - 1)
+ increment = gap;
+ }
+
+ symxf.translate(dx_incr*increment, dy_incr*increment);
+ drawpos += increment;
+ }
+ }
+
+ drawpos -= len;
+
+ // start point for next segment is current end point
+ segX0 = segX1;
+ segY0 = segY1;
+ }
+ }
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Distributes feature labels along a polyline.
+void SE_Renderer::ProcessLineLabels(LineBuffer* geometry, SE_RenderLineStyle* style)
+{
+ SE_Matrix symxf;
+ bool yUp = YPointsUp();
+
+ double baseAngleRad = style->angleRad;
+
+ // precompute these - these are in renderer space, hence the check for yUp with the sine
+ double baseAngleCos = cos(baseAngleRad);
+ double baseAngleSin = sin(yUp? baseAngleRad : -baseAngleRad);
+
+ // account for any viewport rotation
+ double w2sAngleRad = GetWorldToScreenRotation();
+
+ double angleRad, angleCos, angleSin;
+ if (w2sAngleRad == 0.0)
+ {
+ angleRad = baseAngleRad;
+ angleCos = baseAngleCos;
+ angleSin = baseAngleSin;
+ }
+ else
+ {
+ angleRad = baseAngleRad + w2sAngleRad;
+ angleCos = cos(angleRad);
+ angleSin = sin(yUp? angleRad : -angleRad);
+ }
+
+ // screen coordinates of current line segment
+ double segX0, segY0, segX1, segY1;
+
+ // this is the same for all contours
+ double repeat = PATH_LABEL_SEPARATION_INCHES * MILLIMETERS_PER_INCH * GetScreenUnitsPerMillimeterDevice();
+ double leftEdge = style->bounds[0].x;
+ double rightEdge = style->bounds[1].x;
+ double symWidth = rightEdge - leftEdge;
+
+ // repeat needs to be the separation (end of one label to start of the
+ // next) plus the symbol width
+ repeat += symWidth;
+
+ // get the segment lengths
+ double* segLens = (double*)alloca(sizeof(double)*geometry->point_count());
+ ComputeSegmentLengths(geometry, segLens);
+
+ // iterate over the contours
+ for (int j=0; j<geometry->cntr_count(); ++j)
+ {
+ // get segment range for current contour
+ int start_seg = geometry->contour_start_point(j);
+ int end_seg = geometry->contour_end_point(j);
+
+ // skip contours shorter than the symbol width
+ double contourLen = segLens[start_seg];
+ if (contourLen <= symWidth)
+ continue;
+
+ // how many times should we repeat the symbol along the path?
+ // TODO: fine tune this formula
+ int numSymbols = 1 + (int)((contourLen - symWidth) / repeat);
+ double startOffset = 0.5*(contourLen - (numSymbols - 1) * repeat);
+
+ // account for the symbol's extent to properly center it
+ startOffset -= 0.5*(leftEdge + rightEdge);
+
+ //-------------------------------------------------------
+ // draw symbols along the contour
+ //-------------------------------------------------------
+
+ int numDrawn = 0;
+ int cur_seg = start_seg;
+ double drawpos = startOffset;
+
+ // get start point of first segment in screen space
+ WorldToScreenPoint(geometry->x_coord(cur_seg), geometry->y_coord(cur_seg), segX0, segY0);
+
+ while (cur_seg < end_seg)
+ {
+ ++cur_seg;
+
+ // skip zero-length segments - no need to update the start/end points
+ double len = segLens[cur_seg];
+ if (len == 0.0)
+ continue;
+
+ // get end point of current segment in screen space
+ WorldToScreenPoint(geometry->x_coord(cur_seg), geometry->y_coord(cur_seg), segX1, segY1);
+
+ // if our draw position falls within this segment then process
+ if (drawpos <= len)
+ {
+ // compute linear deltas for x and y directions - we will use these
+ // to quickly move along the line without having to do too much math
+ double invlen = 1.0 / len;
+ double dx_incr = (segX1 - segX0) * invlen;
+ double dy_incr = (segY1 - segY0) * invlen;
+
+ if (style->angleControl == SE_AngleControl_FromGeometry)
+ {
+ angleCos = dx_incr*baseAngleCos - dy_incr*baseAngleSin;
+ angleSin = dy_incr*baseAngleCos + dx_incr*baseAngleSin;
+ angleRad = atan2(dy_incr, dx_incr);
+
+ // since dy_incr and dx_incr are in renderer space we need to
+ // negate the angle if y points down
+ if (!yUp)
+ angleRad = -angleRad;
+
+ angleRad += baseAngleRad;
+ }
+ double tx = segX0 + dx_incr * drawpos;
+ double ty = segY0 + dy_incr * drawpos;
+
+ symxf.setIdentity();
+ symxf.rotate(angleSin, angleCos);
+ symxf.translate(tx, ty);
+
+ // loop-draw the symbol along the current segment, incrementing
+ // the draw position by the appropriate amount
+ while (drawpos <= len && numDrawn < numSymbols)
+ {
+ AddLabel(geometry, style, symxf, angleRad);
+ ++numDrawn;
+
+ // move forward
+ symxf.translate(dx_incr*repeat, dy_incr*repeat);
+ drawpos += repeat;
+ }
+ }
+
+ drawpos -= len;
+
+ // start point for next segment is current end point
+ segX0 = segX1;
+ segY0 = segY1;
+ }
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// This method computes the segment lengths for the geometry. For a given
+// contour with N points starting at index M, the length for the entire
+// contour is stored at location M, while the segment lengths are stored at
+// locations M+n, n=[1, N-1].
+void SE_Renderer::ComputeSegmentLengths(LineBuffer* geometry, double* segLens)
+{
+ // screen coordinates of current line segment
+ double segX0, segY0, segX1, segY1;
+
+ // iterate over the contours
+ for (int j=0; j<geometry->cntr_count(); ++j)
+ {
+ // get segment range for current contour
+ int start_seg = geometry->contour_start_point(j);
+ int end_seg = geometry->contour_end_point(j);
+ int cur_seg = start_seg;
+
+ // compute lengths for the contour and all its segments
+ segLens[start_seg] = 0.0;
+
+ // get start point of first segment in screen space
+ WorldToScreenPoint(geometry->x_coord(cur_seg), geometry->y_coord(cur_seg), segX0, segY0);
+
+ while (cur_seg < end_seg)
+ {
+ ++cur_seg;
+
+ // get end point of current segment in screen space
+ WorldToScreenPoint(geometry->x_coord(cur_seg), geometry->y_coord(cur_seg), segX1, segY1);
+
+ // get segment length
+ double dx = segX1 - segX0;
+ double dy = segY1 - segY0;
+ double len = sqrt(dx*dx + dy*dy);
+
+ segLens[cur_seg] = len;
+ segLens[start_seg] += len;
+
+ // start point for next segment is current end point
+ segX0 = segX1;
+ segY0 = segY1;
+ }
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// This method computes group lengths. The segGroups array is the one
+// obtained from ComputeSegmentGroups.
+void SE_Renderer::ComputeGroupLengths(double* segLens, int numGroups, int* segGroups, double* groupLens)
+{
+ for (int j=0; j<numGroups; ++j)
+ {
+ // get segment range for group
+ int seg0 = segGroups[2*j];
+ int seg1 = segGroups[2*j+1];
+
+ // get the length of the group
+ groupLens[j] = 0.0;
+ for (int i=seg0+1; i<=seg1; ++i)
+ groupLens[j] += segLens[i];
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// This method groups together segments for the specified contour based on
+// the supplied vertex angle limit. Any pair of segments are part of the
+// same group if their relative angle is less then the vertex angle limit.
+// The integer array will contain the indices delineating the segments, i.e.
+// group 0 goes from index segGroups[0] to segGroups[1], group 1 goes from
+// segGroups[2] to segGroups[3], etc. Each group is guranteed to start and
+// end with a non-generate segment. The method returns the number of groups.
+int SE_Renderer::ComputeSegmentGroups(LineBuffer* geometry, int contour, double vertexAngleLimit, double* segLens, int* segGroups)
+{
+ // get segment range for specified contour
+ int start_seg = geometry->contour_start_point(contour);
+ int end_seg = geometry->contour_end_point(contour);
+
+ // skip zero-length contours
+ if (segLens[start_seg] == 0.0)
+ return 0;
+
+ // we have a non-degenerate contour - we'll get at least one group
+
+ // make sure vertex angle limit is positive and in the range [0, 180]
+ vertexAngleLimit = fabs(vertexAngleLimit);
+ vertexAngleLimit = rs_min(vertexAngleLimit, M_PI);
+ double cosLimit = cos(vertexAngleLimit);
+
+ // screen coordinates of current line segment
+ double segX0, segY0, segX1, segY1;
+
+ // keep track of number of groups
+ int numGroups = 0;
+
+ // find the initial group's starting segment (the first non-degenerate segment)
+ int cur_seg = start_seg + 1;
+ while (cur_seg <= end_seg && segLens[cur_seg] == 0.0)
+ ++cur_seg;
+
+ int group_min = cur_seg - 1;
+ int group_max = cur_seg;
+
+ // get the normalized vector for the segment
+ WorldToScreenPoint(geometry->x_coord(cur_seg-1), geometry->y_coord(cur_seg-1), segX0, segY0);
+ WorldToScreenPoint(geometry->x_coord(cur_seg ), geometry->y_coord(cur_seg ), segX1, segY1);
+ double dx0 = (segX1 - segX0) / segLens[cur_seg];
+ double dy0 = (segY1 - segY0) / segLens[cur_seg];
+
+ // iterate over the rest of the contour, adding groups as we find them
+ while (cur_seg < end_seg)
+ {
+ ++cur_seg;
+
+ // find next non-degenerate segment
+ while (cur_seg <= end_seg && segLens[cur_seg] == 0.0)
+ ++cur_seg;
+
+ // no more non-degenerate segments left - done processing the contour
+ if (cur_seg > end_seg)
+ break;
+
+ // get the normalized vector for the segment
+ WorldToScreenPoint(geometry->x_coord(cur_seg-1), geometry->y_coord(cur_seg-1), segX0, segY0);
+ WorldToScreenPoint(geometry->x_coord(cur_seg ), geometry->y_coord(cur_seg ), segX1, segY1);
+ double dx1 = (segX1 - segX0) / segLens[cur_seg];
+ double dy1 = (segY1 - segY0) / segLens[cur_seg];
+
+ // compare relative angles between current and previous segments
+ double cosAngle = dx0*dx1 + dy0*dy1;
+ if (cosAngle < cosLimit)
+ {
+ // vertex limit exceeded - record the existing group
+ segGroups[2*numGroups ] = group_min;
+ segGroups[2*numGroups+1] = group_max;
+ ++numGroups;
+
+ // initialize the next group
+ group_min = cur_seg - 1;
+ group_max = cur_seg;
+ }
+ else
+ {
+ // vertex limit not exceeded - extend current group to this segment
+ group_max = cur_seg;
+ }
+
+ // current normalized vector becomes the old one
+ dx0 = dx1;
+ dy0 = dy1;
+ }
+
+ // record the final group
+ segGroups[2*numGroups ] = group_min;
+ segGroups[2*numGroups+1] = group_max;
+ ++numGroups;
+
+ return numGroups;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Computes the point distribution for a group, given its start offset, end
+// end offset, and repeat.
+//
+// The following rules apply:
+// - If only StartOffset is specified (>=0) then the first point is at the
+// start offset location, and the distribution repeats until the end of
+// the group.
+// - If only EndOffset is specified (>=0) then the last point is at the end
+// offset location, and the distribution repeats until the start of the
+// group.
+// - If StartOffset and EndOffset are both specified then the first and last
+// points are at the start and end offset locations. Points are then
+// distributed at the repeat value within the group. The interior dist-
+// ribution is centered within the start / end offset locations, leaving a
+// gap on either side. The number of interior points is chosen so that
+// repeat/2 < gap < repeat. The symbol width is also taken into account
+// when computing the interior distribution.
+// - If StartOffset and EndOffset are both unspecified (< 0) then points
+// are not included at the start and end offset locations, but the interior
+// distribution is the same as in the previous case assuming zero offsets.
+void SE_Renderer::ComputeGroupDistribution(double groupLen, double startOffset, double endOffset, double repeat,
+ double symWidth, double& startPos, double& gap, int& numSymbols)
+{
+ _ASSERT(repeat > 0.0);
+ _ASSERT(startOffset <= groupLen);
+ _ASSERT(endOffset <= groupLen);
+
+ // Use a slightly smaller group length to avoid round-off issues when
+ // computing the distribution. See comments below.
+ groupLen *= 0.999999999999;
+
+ if (startOffset >= 0.0)
+ {
+ if (endOffset < 0.0)
+ {
+ // only the start offset is specified
+
+ // starting symbol
+ startPos = startOffset;
+
+ // no gap in this case
+ gap = 0.0;
+
+ // interior symbols
+ double remainder = groupLen - startOffset;
+ int numInterior = (int)(remainder / repeat);
+
+ // if the interior distribution goes exactly until the end then reduce
+ // the interior count by one so we don't draw a symbol at the end
+ // NOTE: using a slightly smaller group length addresses this
+// if (remainder <= numInterior*repeat && numInterior > 0)
+// --numInterior;
+
+ numSymbols = 1 + numInterior;
+ }
+ else
+ {
+ // both start and end offsets are specified
+
+ // starting symbol
+ startPos = startOffset;
+
+ // interior symbols - in the case where the repeat is set to the symbol
+ // width, the minimum gap is 0.5*factor*repeat
+ double factor = 0.15;
+ double remainder = rs_max(groupLen - startOffset - endOffset - factor*symWidth, 0.0);
+ int numInterior = (int)(remainder / repeat);
+
+ // if the interior distribution fits exactly then reduce the
+ // interior count by one so we don't draw a symbol at the end
+ // NOTE: using a slightly smaller group length addresses this
+// if (remainder <= numInterior*repeat && numInterior > 0)
+// --numInterior;
+
+ if (numInterior == 0)
+ gap = groupLen - startOffset - endOffset; // no room for any internal symbols
+ else
+ gap = 0.5*(remainder - (numInterior - 1)*repeat - (1.0-factor)*symWidth);
+
+ numSymbols = 2 + numInterior;
+ }
+ }
+ else
+ {
+ if (endOffset < 0.0)
+ {
+ // both start and end offsets are unspecified
+
+ // no gap in this case
+ gap = 0.0;
+
+ // interior symbols
+ double remainder = groupLen;
+ int numInterior = (int)(remainder / repeat);
+
+ // if the interior distribution fits exactly then reduce the
+ // interior count by one so we don't draw a symbol at the end
+ // NOTE: using a slightly smaller group length addresses this
+// if (remainder <= numInterior*repeat && numInterior > 0)
+// --numInterior;
+
+ // starting symbol
+ if (numInterior == 0)
+ startPos = remainder; // no room for any internal symbols
+ else
+ startPos = 0.5*(remainder - (numInterior - 1)*repeat);
+
+ numSymbols = numInterior;
+ }
+ else
+ {
+ // only the end offset is specified
+
+ // no gap in this case
+ gap = 0.0;
+
+ // interior symbols
+ double remainder = groupLen - endOffset;
+ int numInterior = (int)(remainder / repeat);
+
+ // if the interior distribution goes exactly until the start then reduce
+ // the interior count by one so we don't draw a symbol at the start
+ // NOTE: using a slightly smaller group length addresses this
+// if (remainder <= numInterior*repeat && numInterior > 0)
+// --numInterior;
+
+ // starting symbol
+ startPos = groupLen - endOffset - numInterior*repeat;
+
+ numSymbols = 1 + numInterior;
+ }
+ }
+}
Deleted: trunk/MgDev/Common/Stylization/SE_LineRenderer.h
===================================================================
--- trunk/MgDev/Common/Stylization/SE_LineRenderer.h 2010-08-12 16:42:37 UTC (rev 5088)
+++ trunk/MgDev/Common/Stylization/SE_LineRenderer.h 2010-08-12 17:38:29 UTC (rev 5089)
@@ -1,44 +0,0 @@
-//
-// Copyright (C) 2007-2010 by Autodesk, Inc.
-//
-// This library is free software; you can redistribute it and/or
-// modify it under the terms of version 2.1 of the GNU Lesser
-// General Public License as published by the Free Software Foundation.
-//
-// This library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public
-// License along with this library; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-//
-
-#ifndef SE_LINERENDERER_H_
-#define SE_LINERENDERER_H_
-
-#include "SE_Renderer.h"
-
-
-// forward declare
-struct HotSpot;
-
-
-///////////////////////////////////////////////////////////////////////////////
-class SE_LineRenderer
-{
-public:
- static void ProcessLineOverlapWrap(SE_Renderer* renderer, LineBuffer* geometry, SE_RenderLineStyle* style);
-
-private:
- static int ConfigureHotSpots(SE_Renderer* renderer, LineBuffer* geometry, int cur_contour, SE_RenderLineStyle* style, RS_Bounds& styleBounds, HotSpot* hotspots);
- static int ComputePoints(SE_Renderer* renderer, LineBuffer* geometry, int cur_contour, HotSpot* hotspots);
- static void ChopLineBuffer(SE_Renderer* renderer, LineBuffer* inBuffer, LineBuffer* outBuffer);
- static LineBuffer* ClipPolyline(LineBufferPool* lbp, LineBuffer& geometry, double zMin, double zMax);
- static LineBuffer* ClipPolygon(LineBufferPool* lbp, LineBuffer& geometry, double zMin, double zMax);
- static int ClipLine(double zMin, double zMax, double* line, double* ret);
- static int ClipCode(double zMin, double zMax, double z);
-};
-
-#endif
Modified: trunk/MgDev/Common/Stylization/SE_Renderer.cpp
===================================================================
--- trunk/MgDev/Common/Stylization/SE_Renderer.cpp 2010-08-12 16:42:37 UTC (rev 5088)
+++ trunk/MgDev/Common/Stylization/SE_Renderer.cpp 2010-08-12 17:38:29 UTC (rev 5089)
@@ -17,7 +17,6 @@
#include "stdafx.h"
#include "SE_Renderer.h"
-#include "SE_LineRenderer.h"
#include "SE_AreaPositioning.h"
#include "RS_FontEngine.h"
@@ -301,7 +300,7 @@
else if (style->vertexControl == SE_VertexControl_OverlapDirect)
ProcessLineOverlapDirect(featGeom, style);
else
- SE_LineRenderer::ProcessLineOverlapWrap(this, featGeom, style);
+ ProcessLineOverlapWrap(featGeom, style);
}
@@ -652,1060 +651,6 @@
}
-///////////////////////////////////////////////////////////////////////////////
-// This method computes the segment lengths for the geometry. For a given
-// contour with N points starting at index M, the length for the entire
-// contour is stored at location M, while the segment lengths are stored at
-// locations M+n, n=[1, N-1].
-void SE_Renderer::ComputeSegmentLengths(LineBuffer* geometry, double* segLens)
-{
- // screen coordinates of current line segment
- double segX0, segY0, segX1, segY1;
-
- // iterate over the contours
- for (int j=0; j<geometry->cntr_count(); ++j)
- {
- // get segment range for current contour
- int start_seg = geometry->contour_start_point(j);
- int end_seg = geometry->contour_end_point(j);
- int cur_seg = start_seg;
-
- // compute lengths for the contour and all its segments
- segLens[start_seg] = 0.0;
-
- // get start point of first segment in screen space
- WorldToScreenPoint(geometry->x_coord(cur_seg), geometry->y_coord(cur_seg), segX0, segY0);
-
- while (cur_seg < end_seg)
- {
- ++cur_seg;
-
- // get end point of current segment in screen space
- WorldToScreenPoint(geometry->x_coord(cur_seg), geometry->y_coord(cur_seg), segX1, segY1);
-
- // get segment length
- double dx = segX1 - segX0;
- double dy = segY1 - segY0;
- double len = sqrt(dx*dx + dy*dy);
-
- segLens[cur_seg] = len;
- segLens[start_seg] += len;
-
- // start point for next segment is current end point
- segX0 = segX1;
- segY0 = segY1;
- }
- }
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-// This method computes group lengths. The segGroups array is the one
-// obtained from ComputeSegmentGroups.
-void SE_Renderer::ComputeGroupLengths(double* segLens, int numGroups, int* segGroups, double* groupLens)
-{
- for (int j=0; j<numGroups; ++j)
- {
- // get segment range for group
- int seg0 = segGroups[2*j];
- int seg1 = segGroups[2*j+1];
-
- // get the length of the group
- groupLens[j] = 0.0;
- for (int i=seg0+1; i<=seg1; ++i)
- groupLens[j] += segLens[i];
- }
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-// This method groups together segments for the specified contour based on
-// the supplied vertex angle limit. Any pair of segments are part of the
-// same group if their relative angle is less then the vertex angle limit.
-// The integer array will contain the indices delineating the segments, i.e.
-// group 0 goes from index segGroups[0] to segGroups[1], group 1 goes from
-// segGroups[2] to segGroups[3], etc. Each group is guranteed to start and
-// end with a non-generate segment. The method returns the number of groups.
-int SE_Renderer::ComputeSegmentGroups(LineBuffer* geometry, int contour, double vertexAngleLimit, double* segLens, int* segGroups)
-{
- // get segment range for specified contour
- int start_seg = geometry->contour_start_point(contour);
- int end_seg = geometry->contour_end_point(contour);
-
- // skip zero-length contours
- if (segLens[start_seg] == 0.0)
- return 0;
-
- // we have a non-degenerate contour - we'll get at least one group
-
- // make sure vertex angle limit is positive and in the range [0, 180]
- vertexAngleLimit = fabs(vertexAngleLimit);
- vertexAngleLimit = rs_min(vertexAngleLimit, M_PI);
- double cosLimit = cos(vertexAngleLimit);
-
- // screen coordinates of current line segment
- double segX0, segY0, segX1, segY1;
-
- // keep track of number of groups
- int numGroups = 0;
-
- // find the initial group's starting segment (the first non-degenerate segment)
- int cur_seg = start_seg + 1;
- while (cur_seg <= end_seg && segLens[cur_seg] == 0.0)
- ++cur_seg;
-
- int group_min = cur_seg - 1;
- int group_max = cur_seg;
-
- // get the normalized vector for the segment
- WorldToScreenPoint(geometry->x_coord(cur_seg-1), geometry->y_coord(cur_seg-1), segX0, segY0);
- WorldToScreenPoint(geometry->x_coord(cur_seg ), geometry->y_coord(cur_seg ), segX1, segY1);
- double dx0 = (segX1 - segX0) / segLens[cur_seg];
- double dy0 = (segY1 - segY0) / segLens[cur_seg];
-
- // iterate over the rest of the contour, adding groups as we find them
- while (cur_seg < end_seg)
- {
- ++cur_seg;
-
- // find next non-degenerate segment
- while (cur_seg <= end_seg && segLens[cur_seg] == 0.0)
- ++cur_seg;
-
- // no more non-degenerate segments left - done processing the contour
- if (cur_seg > end_seg)
- break;
-
- // get the normalized vector for the segment
- WorldToScreenPoint(geometry->x_coord(cur_seg-1), geometry->y_coord(cur_seg-1), segX0, segY0);
- WorldToScreenPoint(geometry->x_coord(cur_seg ), geometry->y_coord(cur_seg ), segX1, segY1);
- double dx1 = (segX1 - segX0) / segLens[cur_seg];
- double dy1 = (segY1 - segY0) / segLens[cur_seg];
-
- // compare relative angles between current and previous segments
- double cosAngle = dx0*dx1 + dy0*dy1;
- if (cosAngle < cosLimit)
- {
- // vertex limit exceeded - record the existing group
- segGroups[2*numGroups ] = group_min;
- segGroups[2*numGroups+1] = group_max;
- ++numGroups;
-
- // initialize the next group
- group_min = cur_seg - 1;
- group_max = cur_seg;
- }
- else
- {
- // vertex limit not exceeded - extend current group to this segment
- group_max = cur_seg;
- }
-
- // current normalized vector becomes the old one
- dx0 = dx1;
- dy0 = dy1;
- }
-
- // record the final group
- segGroups[2*numGroups ] = group_min;
- segGroups[2*numGroups+1] = group_max;
- ++numGroups;
-
- return numGroups;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-// Computes the point distribution for a group, given its start offset, end
-// end offset, and repeat.
-//
-// The following rules apply:
-// - If only StartOffset is specified (>=0) then the first point is at the
-// start offset location, and the distribution repeats until the end of
-// the group.
-// - If only EndOffset is specified (>=0) then the last point is at the end
-// offset location, and the distribution repeats until the start of the
-// group.
-// - If StartOffset and EndOffset are both specified then the first and last
-// points are at the start and end offset locations. Points are then
-// distributed at the repeat value within the group. The interior dist-
-// ribution is centered within the start / end offset locations, leaving a
-// gap on either side. The number of interior points is chosen so that
-// repeat/2 < gap < repeat. The symbol width is also taken into account
-// when computing the interior distribution.
-// - If StartOffset and EndOffset are both unspecified (< 0) then points
-// are not included at the start and end offset locations, but the interior
-// distribution is the same as in the previous case assuming zero offsets.
-void SE_Renderer::ComputeGroupDistribution(double groupLen, double startOffset, double endOffset, double repeat,
- double symWidth, double& startPos, double& gap, int& numSymbols)
-{
- _ASSERT(repeat > 0.0);
- _ASSERT(startOffset <= groupLen);
- _ASSERT(endOffset <= groupLen);
-
- // Use a slightly smaller group length to avoid round-off issues when
- // computing the distribution. See comments below.
- groupLen *= 0.999999999999;
-
- if (startOffset >= 0.0)
- {
- if (endOffset < 0.0)
- {
- // only the start offset is specified
-
- // starting symbol
- startPos = startOffset;
-
- // no gap in this case
- gap = 0.0;
-
- // interior symbols
- double remainder = groupLen - startOffset;
- int numInterior = (int)(remainder / repeat);
-
- // if the interior distribution goes exactly until the end then reduce
- // the interior count by one so we don't draw a symbol at the end
- // NOTE: using a slightly smaller group length addresses this
-// if (remainder <= numInterior*repeat && numInterior > 0)
-// --numInterior;
-
- numSymbols = 1 + numInterior;
- }
- else
- {
- // both start and end offsets are specified
-
- // starting symbol
- startPos = startOffset;
-
- // interior symbols - in the case where the repeat is set to the symbol
- // width, the minimum gap is 0.5*factor*repeat
- double factor = 0.15;
- double remainder = rs_max(groupLen - startOffset - endOffset - factor*symWidth, 0.0);
- int numInterior = (int)(remainder / repeat);
-
- // if the interior distribution fits exactly then reduce the
- // interior count by one so we don't draw a symbol at the end
- // NOTE: using a slightly smaller group length addresses this
-// if (remainder <= numInterior*repeat && numInterior > 0)
-// --numInterior;
-
- if (numInterior == 0)
- gap = groupLen - startOffset - endOffset; // no room for any internal symbols
- else
- gap = 0.5*(remainder - (numInterior - 1)*repeat - (1.0-factor)*symWidth);
-
- numSymbols = 2 + numInterior;
- }
- }
- else
- {
- if (endOffset < 0.0)
- {
- // both start and end offsets are unspecified
-
- // no gap in this case
- gap = 0.0;
-
- // interior symbols
- double remainder = groupLen;
- int numInterior = (int)(remainder / repeat);
-
- // if the interior distribution fits exactly then reduce the
- // interior count by one so we don't draw a symbol at the end
- // NOTE: using a slightly smaller group length addresses this
-// if (remainder <= numInterior*repeat && numInterior > 0)
-// --numInterior;
-
- // starting symbol
- if (numInterior == 0)
- startPos = remainder; // no room for any internal symbols
- else
- startPos = 0.5*(remainder - (numInterior - 1)*repeat);
-
- numSymbols = numInterior;
- }
- else
- {
- // only the end offset is specified
-
- // no gap in this case
- gap = 0.0;
-
- // interior symbols
- double remainder = groupLen - endOffset;
- int numInterior = (int)(remainder / repeat);
-
- // if the interior distribution goes exactly until the start then reduce
- // the interior count by one so we don't draw a symbol at the start
- // NOTE: using a slightly smaller group length addresses this
-// if (remainder <= numInterior*repeat && numInterior > 0)
-// --numInterior;
-
- // starting symbol
- startPos = groupLen - endOffset - numInterior*repeat;
-
- numSymbols = 1 + numInterior;
- }
- }
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-// Distributes symbols along a polyline using the OverlapNone vertex control option.
-void SE_Renderer::ProcessLineOverlapNone(LineBuffer* geometry, SE_RenderLineStyle* style)
-{
- _ASSERT(style->repeat > 0.0);
-
- SE_Matrix symxf;
- bool yUp = YPointsUp();
- double px2su = GetScreenUnitsPerPixel();
-
- double baseAngleRad = style->angleRad;
-
- // precompute these - these are in renderer space, hence the check for yUp with the sine
- double baseAngleCos = cos(baseAngleRad);
- double baseAngleSin = sin(yUp? baseAngleRad : -baseAngleRad);
-
- // account for any viewport rotation
- double w2sAngleRad = GetWorldToScreenRotation();
-
- double angleRad, angleCos, angleSin;
- if (w2sAngleRad == 0.0)
- {
- angleRad = baseAngleRad;
- angleCos = baseAngleCos;
- angleSin = baseAngleSin;
- }
- else
- {
- angleRad = baseAngleRad + w2sAngleRad;
- angleCos = cos(angleRad);
- angleSin = sin(yUp? angleRad : -angleRad);
- }
-
- // screen coordinates of current line segment
- double segX0, segY0, segX1, segY1;
-
- // this is the same for all contours / groups
- double leftEdge = style->bounds[0].x;
- double rightEdge = style->bounds[1].x;
-
- // get segment lengths
- double* segLens = (double*)alloca(sizeof(double)*geometry->point_count());
- ComputeSegmentLengths(geometry, segLens);
-
- // used for segment group calculations
- int* segGroups = (int*)alloca(2*sizeof(int)*geometry->point_count());
- double* groupLens = (double*)alloca(sizeof(double)*geometry->point_count());
-
- // configure the default path line stroke to use
- SE_LineStroke dpLineStroke = style->dpLineStroke;
- if (m_bSelectionMode)
- {
- dpLineStroke.color = m_selLineStroke.color;
- dpLineStroke.weight = m_selLineStroke.weight;
- }
-
- // Used for drawing the centerline paths at vertices. In the case of
- // a single contour the start/end points will need a MoveTo/LineTo,
- // while the interior points will need a MoveTo/LineTo/LineTo.
- LineBuffer vertexLines(3*geometry->point_count()-2);
-
- // iterate over the contours
- for (int j=0; j<geometry->cntr_count(); ++j)
- {
- // get starting segment for current contour
- int start_seg_contour = geometry->contour_start_point(j);
-
- // skip zero-length contours
- if (segLens[start_seg_contour] == 0.0)
- continue;
-
- // get the distribution for the current contour
- double repeat = style->repeat;
- double startOffset = style->startOffset;
- double endOffset = style->endOffset;
- if (style->unitsControl == SE_UnitsControl_Parametric)
- {
- repeat *= segLens[start_seg_contour];
- startOffset *= segLens[start_seg_contour];
- endOffset *= segLens[start_seg_contour];
-
- // It makes no sense to distribute symbols using a repeat value
- // which is much less than one pixel. We'll scale up any value
- // less than 0.25 to 0.5.
- if (repeat > 0.0 && repeat < 0.25*px2su)
- {
- // just increase it by an integer multiple so the overall
- // distribution isn't affected
- int factor = (int)(0.5*px2su / repeat);
- repeat *= factor;
- }
- }
-
- // check if:
- // - the start offset goes beyond the end of the contour
- // - the end offset goes beyond the beginning of the contour
- // - the start offset goes beyond the end offset
- double offsetSum = rs_max(startOffset, 0.0) + rs_max(endOffset, 0.0);
- if (offsetSum > segLens[start_seg_contour])
- continue;
-
- // compute the segment groups for this contour based on the vertex angle limit
- int numGroups = ComputeSegmentGroups(geometry, j, style->vertexAngleLimit, segLens, segGroups);
- if (numGroups == 0)
- continue;
-
- // compute the group lengths
- ComputeGroupLengths(segLens, numGroups, segGroups, groupLens);
-
- // for this vertex control option we set the offsets to zero if they're unspecified
- startOffset = rs_max(startOffset, 0.0);
- endOffset = rs_max(endOffset, 0.0);
-
- // compute the starting group based on the style's start offset
- int start_group = 0;
- if (startOffset > 0.0)
- {
- for (int k=0; k<numGroups; ++k)
- {
- if (startOffset < groupLens[k])
- {
- start_group = k;
- break;
- }
-
- // adjust the start offset so it's relative to the starting group
- startOffset -= groupLens[k];
- }
- }
-
- // compute the ending group based on the style's end offset
- int end_group = numGroups-1;
- if (endOffset > 0.0)
- {
- for (int k=numGroups-1; k>=0; --k)
- {
- if (endOffset < groupLens[k])
- {
- end_group = k;
- break;
- }
-
- // adjust the end offset so it's relative to the ending group
- endOffset -= groupLens[k];
- }
- }
-
- // iterate over the relevant groups
- for (int k=start_group; k<=end_group; ++k)
- {
- // get segment range for current group
- int start_seg = segGroups[2*k];
- int end_seg = segGroups[2*k+1];
- int cur_seg = start_seg;
-
- // get the actual start / end offsets for the current group
- // - for the first group its start offset is the specified value, while
- // for subsequent groups it's zero (we draw a symbol directly at the
- // start of the group)
- // - for the last group the end offset is the specified value, while for
- // prior groups it's zero (we draw a symbol directly at the end of the
- // group)
- double startOffsetGroup = (k == start_group)? startOffset : 0.0;
- double endOffsetGroup = (k == end_group)? endOffset : 0.0;
-
- // Compute the symbol distribution for the group. The drawpos variable
- // is the position of the first symbol. If gap is > 0 then the next symbol
- // is offset by that amount. Subsequent symbols are then offset by the
- // repeat, except for the last one which is again offset by the gap.
- //
- // Here's a graphical depiction:
- //
- // |-----+-----+---+---+---+-----+--------|
- // s d | \__|__/ | e
- // t r g r g n
- // a a a e a d
- // r w p p p
- // t p e
- // o a
- // s t
- int numSymbols = 0;
- double drawpos = startOffsetGroup;
- double gap = 0.0;
- ComputeGroupDistribution(groupLens[k], startOffsetGroup, endOffsetGroup, repeat,
- rightEdge - leftEdge, drawpos, gap, numSymbols);
- if (numSymbols == 0)
- continue;
-
- //-------------------------------------------------------
- // draw symbols along the group
- //-------------------------------------------------------
-
- int numDrawn = 0;
- double increment;
-
- // get start point of first segment in screen space
- WorldToScreenPoint(geometry->x_coord(cur_seg), geometry->y_coord(cur_seg), segX0, segY0);
-
- while (cur_seg < end_seg)
- {
- ++cur_seg;
-
- // skip zero-length segments - no need to update the start/end points
- double len = segLens[cur_seg];
- if (len == 0.0)
- continue;
-
- // get end point of current segment in screen space
- WorldToScreenPoint(geometry->x_coord(cur_seg), geometry->y_coord(cur_seg), segX1, segY1);
-
- // if our draw position falls within this segment then process
- if (drawpos <= len)
- {
- // compute linear deltas for x and y directions - we will use these
- // to quickly move along the line without having to do too much math
- double invlen = 1.0 / len;
- double dx_incr = (segX1 - segX0) * invlen;
- double dy_incr = (segY1 - segY0) * invlen;
-
- if (style->angleControl == SE_AngleControl_FromGeometry)
- {
- angleCos = dx_incr*baseAngleCos - dy_incr*baseAngleSin;
- angleSin = dy_incr*baseAngleCos + dx_incr*baseAngleSin;
- angleRad = atan2(dy_incr, dx_incr);
-
- // since dy_incr and dx_incr are in renderer space we need to
- // negate the angle if y points down
- if (!yUp)
- angleRad = -angleRad;
-
- angleRad += baseAngleRad;
- }
- double tx = segX0 + dx_incr * drawpos;
- double ty = segY0 + dy_incr * drawpos;
-
- symxf.setIdentity();
- symxf.rotate(angleSin, angleCos);
- symxf.translate(tx, ty);
-
- // loop-draw the symbol along the current segment, incrementing
- // the draw position by the appropriate amount
- while (drawpos <= len && numDrawn < numSymbols)
- {
- // in the case of labels we only draw them at the interior points
- if (style->drawLast)
- {
- if (numDrawn > 0 && numDrawn < numSymbols-1)
- AddLabel(geometry, style, symxf, angleRad);
- }
- else
- {
- // handle the centerline path at the group's start
- if (numDrawn == 0)
- {
- // This is the first time drawing anything for this group. If
- // this is the starting group, then initialize the centerline
- // path at the group's start. If it's not the starting group
- // then we'll add LineTo segments (see further down) to the
- // existing centerline path at the previous group's end.
- if (k == start_group)
- vertexLines.MoveTo(symxf.x2, symxf.y2);
- }
- else if (numDrawn == 1 && numSymbols > 2)
- {
- // finish and draw the centerline path at the group's start,
- // aligning it with the left edge of the symbol
- // TODO: account for symbol rotation
- vertexLines.LineTo(symxf.x2 + dx_incr*leftEdge, symxf.y2 + dy_incr*leftEdge);
- DrawScreenPolyline(&vertexLines, NULL, dpLineStroke);
- vertexLines.Reset();
- }
-
- // only draw symbols at the interior points
- if (numDrawn > 0 && numDrawn < numSymbols-1)
- DrawSymbol(style->symbol, symxf, angleRad, style->addToExclusionRegion);
-
- // handle the centerline path at the group's end - only
- // need to do this if we have at least one interior symbol
- if (numDrawn == numSymbols-2 && numSymbols > 2)
- {
- // initialize the centerline path at the group's end,
- // aligning it with the right edge of the symbol
- // TODO: account for symbol rotation
- vertexLines.MoveTo(symxf.x2 + dx_incr*rightEdge, symxf.y2 + dy_incr*rightEdge);
- }
- else if (numDrawn == numSymbols-1)
- {
- // This is the last time drawing anything for this group, so
- // finish the centerline path at the group's end. If this is
- // the ending group, then also draw it. If it's not the ending
- // group then we'll draw it up above when we finish the centerline
- // path at the next group's start.
- vertexLines.LineTo(symxf.x2, symxf.y2);
- if (k == end_group)
- {
- DrawScreenPolyline(&vertexLines, NULL, dpLineStroke);
- vertexLines.Reset();
- }
- }
- }
-
- ++numDrawn;
-
- // move forward
- increment = repeat;
- if (gap != 0.0)
- {
- // if we have a gap, then use it after drawing the first
- // symbol and before drawing the last symbol
- if (numSymbols == 2)
- {
- // in this case the gap takes us until the end
- increment = gap;
- }
- else
- {
- if (numDrawn == 1)
- increment = gap - leftEdge;
- else if (numDrawn == numSymbols - 1)
- increment = gap + rightEdge;
- }
- }
-
- symxf.translate(dx_incr*increment, dy_incr*increment);
- drawpos += increment;
- }
- }
-
- drawpos -= len;
-
- // start point for next segment is current end point
- segX0 = segX1;
- segY0 = segY1;
- }
- }
- }
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-// Distributes feature labels along a polyline.
-void SE_Renderer::ProcessLineLabels(LineBuffer* geometry, SE_RenderLineStyle* style)
-{
- SE_Matrix symxf;
- bool yUp = YPointsUp();
-
- double baseAngleRad = style->angleRad;
-
- // precompute these - these are in renderer space, hence the check for yUp with the sine
- double baseAngleCos = cos(baseAngleRad);
- double baseAngleSin = sin(yUp? baseAngleRad : -baseAngleRad);
-
- // account for any viewport rotation
- double w2sAngleRad = GetWorldToScreenRotation();
-
- double angleRad, angleCos, angleSin;
- if (w2sAngleRad == 0.0)
- {
- angleRad = baseAngleRad;
- angleCos = baseAngleCos;
- angleSin = baseAngleSin;
- }
- else
- {
- angleRad = baseAngleRad + w2sAngleRad;
- angleCos = cos(angleRad);
- angleSin = sin(yUp? angleRad : -angleRad);
- }
-
- // screen coordinates of current line segment
- double segX0, segY0, segX1, segY1;
-
- // this is the same for all contours
- double repeat = PATH_LABEL_SEPARATION_INCHES * MILLIMETERS_PER_INCH * GetScreenUnitsPerMillimeterDevice();
- double leftEdge = style->bounds[0].x;
- double rightEdge = style->bounds[1].x;
- double symWidth = rightEdge - leftEdge;
-
- // repeat needs to be the separation (end of one label to start of the
- // next) plus the symbol width
- repeat += symWidth;
-
- // get the segment lengths
- double* segLens = (double*)alloca(sizeof(double)*geometry->point_count());
- ComputeSegmentLengths(geometry, segLens);
-
- // iterate over the contours
- for (int j=0; j<geometry->cntr_count(); ++j)
- {
- // get segment range for current contour
- int start_seg = geometry->contour_start_point(j);
- int end_seg = geometry->contour_end_point(j);
-
- // skip contours shorter than the symbol width
- double contourLen = segLens[start_seg];
- if (contourLen <= symWidth)
- continue;
-
- // how many times should we repeat the symbol along the path?
- // TODO: fine tune this formula
- int numSymbols = 1 + (int)((contourLen - symWidth) / repeat);
- double startOffset = 0.5*(contourLen - (numSymbols - 1) * repeat);
-
- // account for the symbol's extent to properly center it
- startOffset -= 0.5*(leftEdge + rightEdge);
-
- //-------------------------------------------------------
- // draw symbols along the contour
- //-------------------------------------------------------
-
- int numDrawn = 0;
- int cur_seg = start_seg;
- double drawpos = startOffset;
-
- // get start point of first segment in screen space
- WorldToScreenPoint(geometry->x_coord(cur_seg), geometry->y_coord(cur_seg), segX0, segY0);
-
- while (cur_seg < end_seg)
- {
- ++cur_seg;
-
- // skip zero-length segments - no need to update the start/end points
- double len = segLens[cur_seg];
- if (len == 0.0)
- continue;
-
- // get end point of current segment in screen space
- WorldToScreenPoint(geometry->x_coord(cur_seg), geometry->y_coord(cur_seg), segX1, segY1);
-
- // if our draw position falls within this segment then process
- if (drawpos <= len)
- {
- // compute linear deltas for x and y directions - we will use these
- // to quickly move along the line without having to do too much math
- double invlen = 1.0 / len;
- double dx_incr = (segX1 - segX0) * invlen;
- double dy_incr = (segY1 - segY0) * invlen;
-
- if (style->angleControl == SE_AngleControl_FromGeometry)
- {
- angleCos = dx_incr*baseAngleCos - dy_incr*baseAngleSin;
- angleSin = dy_incr*baseAngleCos + dx_incr*baseAngleSin;
- angleRad = atan2(dy_incr, dx_incr);
-
- // since dy_incr and dx_incr are in renderer space we need to
- // negate the angle if y points down
- if (!yUp)
- angleRad = -angleRad;
-
- angleRad += baseAngleRad;
- }
- double tx = segX0 + dx_incr * drawpos;
- double ty = segY0 + dy_incr * drawpos;
-
- symxf.setIdentity();
- symxf.rotate(angleSin, angleCos);
- symxf.translate(tx, ty);
-
- // loop-draw the symbol along the current segment, incrementing
- // the draw position by the appropriate amount
- while (drawpos <= len && numDrawn < numSymbols)
- {
- AddLabel(geometry, style, symxf, angleRad);
- ++numDrawn;
-
- // move forward
- symxf.translate(dx_incr*repeat, dy_incr*repeat);
- drawpos += repeat;
- }
- }
-
- drawpos -= len;
-
- // start point for next segment is current end point
- segX0 = segX1;
- segY0 = segY1;
- }
- }
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-// Distributes symbols along a polyline using the OverlapDirect vertex control option.
-// This setting is intended to only be used in the context of point symbols. These
-// symbols are simply drawn without any wrapping, truncation, or other modification.
-//
-// The following rules apply to the StartOffset and EndOffset parameters:
-// - if StartOffset is specified (>=0) then a symbol is drawn at the start
-// offset location
-// - if EndOffset is specified (>=0) then a symbol is drawn at the end
-// offset location
-// - if StartOffset and EndOffset are both specified but their sum is greater
-// than the contour length (EndOffset offset comes before StartOffset)
-// then no symbols are drawn
-// - if StartOffset and EndOffset are both unspecified (< 0) then no symbols
-// are drawn
-void SE_Renderer::ProcessLineOverlapDirect(LineBuffer* geometry, SE_RenderLineStyle* style)
-{
- _ASSERT(style->repeat > 0.0);
-
- SE_Matrix symxf;
- bool yUp = YPointsUp();
- double px2su = GetScreenUnitsPerPixel();
-
- double baseAngleRad = style->angleRad;
-
- // precompute these - these are in renderer space, hence the check for yUp with the sine
- double baseAngleCos = cos(baseAngleRad);
- double baseAngleSin = sin(yUp? baseAngleRad : -baseAngleRad);
-
- // account for any viewport rotation
- double w2sAngleRad = GetWorldToScreenRotation();
-
- double angleRad, angleCos, angleSin;
- if (w2sAngleRad == 0.0)
- {
- angleRad = baseAngleRad;
- angleCos = baseAngleCos;
- angleSin = baseAngleSin;
- }
- else
- {
- angleRad = baseAngleRad + w2sAngleRad;
- angleCos = cos(angleRad);
- angleSin = sin(yUp? angleRad : -angleRad);
- }
-
- // screen coordinates of current line segment
- double segX0, segY0, segX1, segY1;
-
- // get segment lengths
- double* segLens = (double*)alloca(sizeof(double)*geometry->point_count());
- ComputeSegmentLengths(geometry, segLens);
-
- // used for segment group calculations
- int* segGroups = (int*)alloca(2*sizeof(int)*geometry->point_count());
- double* groupLens = (double*)alloca(sizeof(double)*geometry->point_count());
-
- // iterate over the contours
- for (int j=0; j<geometry->cntr_count(); ++j)
- {
- // get starting segment for current contour
- int start_seg_contour = geometry->contour_start_point(j);
-
- // skip zero-length contours
- if (segLens[start_seg_contour] == 0.0)
- continue;
-
- // get the distribution for the current contour
- double repeat = style->repeat;
- double startOffset = style->startOffset;
- double endOffset = style->endOffset;
- if (style->unitsControl == SE_UnitsControl_Parametric)
- {
- repeat *= segLens[start_seg_contour];
- startOffset *= segLens[start_seg_contour];
- endOffset *= segLens[start_seg_contour];
-
- // It makes no sense to distribute symbols using a repeat value
- // which is much less than one pixel. We'll scale up any value
- // less than 0.25 to 0.5.
- if (repeat > 0.0 && repeat < 0.25*px2su)
- {
- // just increase it by an integer multiple so the overall
- // distribution isn't affected
- int factor = (int)(0.5*px2su / repeat);
- repeat *= factor;
- }
- }
-
- // check if:
- // - the start offset goes beyond the end of the contour
- // - the end offset goes beyond the beginning of the contour
- // - the start offset goes beyond the end offset
- double offsetSum = rs_max(startOffset, 0.0) + rs_max(endOffset, 0.0);
- if (offsetSum > segLens[start_seg_contour])
- continue;
-
- // compute the segment groups for this contour based on the vertex angle limit
- int numGroups = ComputeSegmentGroups(geometry, j, style->vertexAngleLimit, segLens, segGroups);
- if (numGroups == 0)
- continue;
-
- // compute the group lengths
- ComputeGroupLengths(segLens, numGroups, segGroups, groupLens);
-
- // compute the starting group based on the style's start offset
- int start_group = 0;
- if (startOffset > 0.0)
- {
- for (int k=0; k<numGroups; ++k)
- {
- if (startOffset < groupLens[k])
- {
- start_group = k;
- break;
- }
-
- // adjust the start offset so it's relative to the starting group
- startOffset -= groupLens[k];
- }
- }
-
- // compute the ending group based on the style's end offset
- int end_group = numGroups-1;
- if (endOffset > 0.0)
- {
- for (int k=numGroups-1; k>=0; --k)
- {
- if (endOffset < groupLens[k])
- {
- end_group = k;
- break;
- }
-
- // adjust the end offset so it's relative to the ending group
- endOffset -= groupLens[k];
- }
- }
-
- // iterate over the relevant groups
- for (int k=start_group; k<=end_group; ++k)
- {
- // get segment range for current group
- int start_seg = segGroups[2*k];
- int end_seg = segGroups[2*k+1];
- int cur_seg = start_seg;
-
- // get the actual start / end offsets for the current group
- // - for the first group its start offset is the specified value, while
- // for subsequent groups it's zero (we draw a symbol directly at the
- // start of the group)
- // - for the last group the end offset is the specified value, while for
- // prior groups it's zero (we draw a symbol directly at the end of the
- // group)
- double startOffsetGroup = (k == start_group)? startOffset : 0.0;
- double endOffsetGroup = (k == end_group)? endOffset : 0.0;
-
- // Compute the symbol distribution for the group. The drawpos variable
- // is the position of the first symbol. If gap is > 0 then the next symbol
- // is offset by that amount. Subsequent symbols are then offset by the
- // repeat, except for the last one which is again offset by the gap.
- //
- // Here's a graphical depiction:
- //
- // |-----+-----+---+---+---+-----+--------|
- // s d | \__|__/ | e
- // t r g r g n
- // a a a e a d
- // r w p p p
- // t p e
- // o a
- // s t
- int numSymbols = 0;
- double drawpos = startOffsetGroup;
- double gap = 0.0;
- ComputeGroupDistribution(groupLens[k], startOffsetGroup, endOffsetGroup, repeat, 0.0,
- drawpos, gap, numSymbols);
- if (numSymbols == 0)
- continue;
-
- //-------------------------------------------------------
- // draw symbols along the group
- //-------------------------------------------------------
-
- int numDrawn = 0;
- double increment;
-
- // get start point of first segment in screen space
- WorldToScreenPoint(geometry->x_coord(cur_seg), geometry->y_coord(cur_seg), segX0, segY0);
-
- while (cur_seg < end_seg)
- {
- ++cur_seg;
-
- // skip zero-length segments - no need to update the start/end points
- double len = segLens[cur_seg];
- if (len == 0.0)
- continue;
-
- // get end point of current segment in screen space
- WorldToScreenPoint(geometry->x_coord(cur_seg), geometry->y_coord(cur_seg), segX1, segY1);
-
- // if our draw position falls within this segment then process
- if (drawpos <= len)
- {
- // compute linear deltas for x and y directions - we will use these
- // to quickly move along the line without having to do too much math
- double invlen = 1.0 / len;
- double dx_incr = (segX1 - segX0) * invlen;
- double dy_incr = (segY1 - segY0) * invlen;
-
- if (style->angleControl == SE_AngleControl_FromGeometry)
- {
- angleCos = dx_incr*baseAngleCos - dy_incr*baseAngleSin;
- angleSin = dy_incr*baseAngleCos + dx_incr*baseAngleSin;
- angleRad = atan2(dy_incr, dx_incr);
-
- // since dy_incr and dx_incr are in renderer space we need to
- // negate the angle if y points down
- if (!yUp)
- angleRad = -angleRad;
-
- angleRad += baseAngleRad;
- }
- double tx = segX0 + dx_incr * drawpos;
- double ty = segY0 + dy_incr * drawpos;
-
- symxf.setIdentity();
- symxf.rotate(angleSin, angleCos);
- symxf.translate(tx, ty);
-
- // loop-draw the symbol along the current segment,
- // incrementing the draw position by the appropriate amount
- while (drawpos <= len && numDrawn < numSymbols)
- {
- // don't draw the same symbol once at the end of a group
- // and again at the start of the next group
- if (k == start_group || numDrawn > 0)
- {
- // draw the symbol at the current position
- if (style->drawLast)
- AddLabel(geometry, style, symxf, angleRad);
- else
- DrawSymbol(style->symbol, symxf, angleRad, style->addToExclusionRegion);
- }
-
- ++numDrawn;
-
- // move forward
- increment = repeat;
- if (gap != 0.0)
- {
- // if we have a gap, then use it after drawing the first
- // symbol and before drawing the last symbol
- if (numDrawn == 1 || numDrawn == numSymbols - 1)
- increment = gap;
- }
-
- symxf.translate(dx_incr*increment, dy_incr*increment);
- drawpos += increment;
- }
- }
-
- drawpos -= len;
-
- // start point for next segment is current end point
- segX0 = segX1;
- segY0 = segY1;
- }
- }
- }
-}
-
-
//////////////////////////////////////////////////////////////////////////////
// Indicates whether rendering optimization is used by this renderer. For example, if we are rendering text and
// optimization is turned on, then text is rendered as a simple line when it is very small.
Modified: trunk/MgDev/Common/Stylization/SE_Renderer.h
===================================================================
--- trunk/MgDev/Common/Stylization/SE_Renderer.h 2010-08-12 16:42:37 UTC (rev 5088)
+++ trunk/MgDev/Common/Stylization/SE_Renderer.h 2010-08-12 17:38:29 UTC (rev 5089)
@@ -23,13 +23,13 @@
#include "SE_SymbolDefProxies.h"
#include "SE_RenderProxies.h"
+// forward declare
class RS_FontEngine;
+struct HotSpot;
class SE_Renderer : public Renderer
{
- friend class SE_LineRenderer;
-
public:
STYLIZATION_API SE_Renderer();
STYLIZATION_API virtual ~SE_Renderer();
@@ -114,7 +114,7 @@
// angles are in radians CCW
void AddLabel(LineBuffer* geom, SE_RenderStyle* style, const SE_Matrix& xform, double angleRad);
- // helper methods
+ // helper method
void ProcessLineLabels(LineBuffer* geometry, SE_RenderLineStyle* style);
// Indicates whether rendering optimization is used by this renderer. For example, we are rendering text and
@@ -123,13 +123,23 @@
STYLIZATION_API virtual bool OptimizeGeometry();
private:
+ void ProcessLineOverlapWrap(LineBuffer* geometry, SE_RenderLineStyle* style);
+ void ProcessLineOverlapNone(LineBuffer* geometry, SE_RenderLineStyle* style);
+ void ProcessLineOverlapDirect(LineBuffer* geometry, SE_RenderLineStyle* style);
+
+ int ConfigureHotSpots(LineBuffer* geometry, int cur_contour, SE_RenderLineStyle* style, RS_Bounds& styleBounds, HotSpot* hotspots);
+ int ComputePoints(LineBuffer* geometry, int cur_contour, HotSpot* hotspots);
+ void ChopLineBuffer(LineBuffer* inBuffer, LineBuffer* outBuffer);
+ LineBuffer* ClipPolyline(LineBufferPool* lbp, LineBuffer& geometry, double zMin, double zMax);
+ LineBuffer* ClipPolygon(LineBufferPool* lbp, LineBuffer& geometry, double zMin, double zMax);
+ int ClipLine(double zMin, double zMax, double* line, double* ret);
+ int ClipCode(double zMin, double zMax, double z);
+
void ComputeSegmentLengths(LineBuffer* geometry, double* segLens);
void ComputeGroupLengths(double* segLens, int numGroups, int* segGroups, double* groupLens);
int ComputeSegmentGroups(LineBuffer* geometry, int contour, double vertexAngleLimit, double* segLens, int* segGroups);
void ComputeGroupDistribution(double groupLen, double startOffset, double endOffset, double repeat, double symWidth,
double& startPos, double& gap, int& numSymbols);
- void ProcessLineOverlapNone(LineBuffer* geometry, SE_RenderLineStyle* style);
- void ProcessLineOverlapDirect(LineBuffer* geometry, SE_RenderLineStyle* style);
protected:
SE_BufferPool* m_pPool;
Modified: trunk/MgDev/Common/Stylization/Stylization.vcproj
===================================================================
--- trunk/MgDev/Common/Stylization/Stylization.vcproj 2010-08-12 16:42:37 UTC (rev 5088)
+++ trunk/MgDev/Common/Stylization/Stylization.vcproj 2010-08-12 17:38:29 UTC (rev 5089)
@@ -684,10 +684,6 @@
>
</File>
<File
- RelativePath=".\SE_LineRenderer.h"
- >
- </File>
- <File
RelativePath=".\SE_Matrix.cpp"
>
</File>
More information about the mapguide-commits
mailing list