[mapserver-commits] r8613 - trunk/mapserver
svn at osgeo.org
svn at osgeo.org
Thu Feb 26 00:10:53 EST 2009
Author: sdlime
Date: 2009-02-26 00:10:53 -0500 (Thu, 26 Feb 2009)
New Revision: 8613
Modified:
trunk/mapserver/HISTORY.TXT
trunk/mapserver/maputil.c
Log:
Fixed offset polyline computation. (#2868)
Modified: trunk/mapserver/HISTORY.TXT
===================================================================
--- trunk/mapserver/HISTORY.TXT 2009-02-26 00:16:31 UTC (rev 8612)
+++ trunk/mapserver/HISTORY.TXT 2009-02-26 05:10:53 UTC (rev 8613)
@@ -12,6 +12,8 @@
Current Version (SVN trunk):
----------------------------
+- Fixed a problem with offset polylines (AGG only) (#2868)
+
- Generate SLD version 1.1.0 (#473)
- Tracking original geometry type so we can make better decisions on what
Modified: trunk/mapserver/maputil.c
===================================================================
--- trunk/mapserver/maputil.c 2009-02-26 00:16:31 UTC (rev 8612)
+++ trunk/mapserver/maputil.c 2009-02-26 05:10:53 UTC (rev 8613)
@@ -1470,103 +1470,171 @@
msTransformShapeToPixel(shape, extent, cellsize);
}
-shapeObj* msOffsetPolyline(shapeObj *p, double offsetx, double offsety) {
- int i, j, first,idx;
- double ox=0, oy=0, limit;
- double dx,dy,dx0=0, dy0=0,x, y, x0=0.0, y0=0.0, k=0.0, k0=0.0, q=0.0, q0=0.0;
- float par=(float)0.71;
- shapeObj *ret = (shapeObj*)malloc(sizeof(shapeObj));
- msInitShape(ret);
- ret->numlines = p->numlines;
- ret->line=(lineObj*)malloc(sizeof(lineObj)*ret->numlines);
- for(i=0;i<ret->numlines;i++) {
- ret->line[i].numpoints=p->line[i].numpoints;
- ret->line[i].point=(pointObj*)malloc(sizeof(pointObj)*ret->line[i].numpoints);
- }
- if(offsety == -99) {
- limit = offsetx*offsetx/4;
- for (i = 0; i < p->numlines; i++) {
- idx=0;
- first = 1;
- for(j=1; j<p->line[i].numpoints; j++) {
- ox=0; oy=0;
- x = dx = (p->line[i].point[j].x - p->line[i].point[j-1].x);
- y = dy = (p->line[i].point[j].y - p->line[i].point[j-1].y);
+/*
+** Helper functions supplied as part of bug #2868 solution. Consider moving these to
+** mapprimitive.c for more general use.
+*/
- /* offset setting - quick approximation, may be changed with goniometric functions */
- if(dx==0) { /* vertical line */
- if(dy==0) {
- continue; /* checking unique points */
- }
- ox=(dy>0) ? -offsetx : offsetx;
- } else {
- k = dy/dx;
- if(MS_ABS(k)<0.5) {
- oy = (dx>0) ? offsetx : -offsetx;
- } else {
- if (MS_ABS(k)<2.1) {
- oy = (dx>0) ? offsetx*par : -offsetx*par;
- ox = (dy>0) ? -offsetx*par : offsetx*par;
- } else
- ox = (dy>0) ? -offsetx : offsetx;
- }
- q = p->line[i].point[j-1].y+oy - k*(p->line[i].point[j-1].x+ox);
- }
+/* vector difference */
+static pointObj point_diff(const pointObj a, const pointObj b) {
+ pointObj retv = {a.x-b.x,a.y-b.y
+#ifdef USE_POINT_Z_M
+ ,a.z-b.z,a.m-b.m
+#endif
+ };
+ return retv;
+}
- /* offset line points computation */
- if(first==1) { /* first point */
- first = 0;
- x = ret->line[i].point[idx].x = p->line[i].point[j-1].x+ox;
- y = ret->line[i].point[idx].y = p->line[i].point[j-1].y+oy;
- idx++;
- } else { /* middle points */
- if((dx*dx+dy*dy)>limit){ /* if the points are too close */
- if(dx0==0) { /* checking verticals */
- if(dx==0) {
- continue;
- }
- x = x0;
- y = k*x + q;
- } else {
- if(dx==0) {
- x = p->line[i].point[j-1].x+ox;
- y = k0*x + q0;
- } else {
- if(k==k0) continue; /* checking equal direction */
- x = (q-q0)/(k0-k);
- y = k*x+q;
- }
- }
- }else{/* need to be refined */
- x = p->line[i].point[j-1].x+ox;
- y = p->line[i].point[j-1].y+oy;
- }
- ret->line[i].point[idx].x=x;
- ret->line[i].point[idx].y=y;
- idx++;
- }
- dx0 = dx; dy0 = dy; x0 = x, y0 = y; k0 = k; q0=q;
- }
- /* last point */
- if(first==0) {
- ret->line[i].point[idx].x=p->line[i].point[p->line[i].numpoints-1].x+ox;
- ret->line[i].point[idx].y=p->line[i].point[p->line[i].numpoints-1].y+oy;
- idx++;
- }
- if(idx!=p->line[i].numpoints) {
- /*printf("shouldn't happen :(\n");*/
- ret->line[i].numpoints=idx;
- ret->line=realloc(ret->line,ret->line[i].numpoints*sizeof(pointObj));
- }
+/* vector sum */
+static pointObj point_sum(const pointObj a, const pointObj b) {
+ pointObj retv = {a.x+b.x,a.y+b.y
+#ifdef USE_POINT_Z_M
+ ,a.z+b.z,a.m+b.m
+#endif
+ };
+ return retv;
+}
+
+/* vector multiply */
+static pointObj point_mul(const pointObj a, double b) {
+ pointObj retv= {a.x*b,a.y*b
+#ifdef USE_POINT_Z_M
+ ,a.z*b,a.m*b
+#endif
+ };
+ return retv;
+}
+
+/* vector ??? */
+static double point_abs2(const pointObj a) {
+#ifdef USE_POINT_Z_M
+ return a.x*a.x+a.y*a.y+a.z*a.z+a.m*a.m;
+#else
+ return a.x*a.x+a.y*a.y;
+#endif
+}
+
+/* vector normal */
+static pointObj point_norm(const pointObj a) {
+ double lenmul;
+ pointObj retv;
+
+#ifdef USE_POINT_Z_M
+ if (a.x==0 && a.y==0 && a.z==0 && a.m==0)
+#else
+ if (a.x==0 && a.y==0)
+#endif
+ return a;
+
+ lenmul=1.0/sqrt(point_abs2(a)); /* this seems to be the costly operation */
+
+ retv.x = a.x*lenmul;
+ retv.y = a.y*lenmul;
+#ifdef USE_POINT_Z_M
+ retv.z = a.z*lenmul;
+ retv.m = a.m*lenmul;
+#endif
+
+ return retv;
+}
+
+/* rotate a vector 90 degrees */
+static pointObj point_rotz90(const pointObj a) {
+ double nx=-1.0*a.y, ny=a.x;
+ pointObj retv=a;
+ retv.x=nx; retv.y=ny;
+ return retv;
+}
+
+/* vector cross product (warning: z and m dimensions are ignored!) */
+static double point_cross(const pointObj a, const pointObj b) {
+ return a.x*b.y-a.y*b.x;
+}
+
+/*
+** For offset corner point calculation 1/sin() is used
+** to avoid 1/0 division (and long spikes) we define a
+** limit for sin().
+*/
+#define CURVE_SIN_LIMIT 0.3
+
+shapeObj *msOffsetPolyline(shapeObj *p, double offsetx, double offsety) {
+ int i, j, first,idx;
+
+ shapeObj *ret = (shapeObj*)malloc(sizeof(shapeObj));
+ msInitShape(ret);
+ ret->numlines = p->numlines;
+ ret->line=(lineObj*)malloc(sizeof(lineObj)*ret->numlines);
+ for(i=0;i<ret->numlines;i++) {
+ ret->line[i].numpoints=p->line[i].numpoints;
+ ret->line[i].point=(pointObj*)malloc(sizeof(pointObj)*ret->line[i].numpoints);
+ }
+
+ if(offsety == -99) { /* complex calculations */
+ for (i = 0; i < p->numlines; i++) {
+ idx=0;
+ first = 1;
+
+ /* saved metrics of the last processed point */
+ pointObj old_pt, old_diffdir, old_offdir;
+ if (p->line[i].numpoints>0)
+ old_pt=p->line[i].point[0];
+ for(j=1; j<p->line[i].numpoints; j++) {
+ const pointObj pt = p->line[i].point[j]; /* place of the point */
+ const pointObj diffdir = point_norm(point_diff(pt,old_pt)); /* direction of the line */
+ const pointObj offdir = point_rotz90(diffdir); /* direction where the distance between the line and the offset is measured */
+ pointObj offpt; /* this will be the corner point of the offset line */
+
+ /* offset line points computation */
+ if(first == 1) { /* first point */
+ first = 0;
+ offpt = point_sum(old_pt,point_mul(offdir,offsetx));
+ } else { /* middle points */
+ /* curve is the angle of the last and the current line's direction (supplementary angle of the shape's inner angle) */
+ double sin_curve = point_cross(diffdir,old_diffdir);
+ double cos_curve = point_cross(old_offdir,diffdir);
+ if ((-1.0)*CURVE_SIN_LIMIT < sin_curve && sin_curve < CURVE_SIN_LIMIT) {
+ /* do not calculate 1/sin, instead use a corner point approximation: average of the last and current offset direction and length */
+
+ /*
+ ** TODO: fair for obtuse inner angles, however, positive and negative
+ ** acute inner angles would need special handling - similar to LINECAP
+ ** to avoid drawing of long spikes
+ */
+ offpt = point_sum(old_pt, point_mul(point_sum(offdir, old_offdir),0.5*offsetx));
+ } else {
+ double base_shift = -1.0*(1.0+cos_curve)/sin_curve;
+ offpt = point_sum(old_pt, point_mul(point_sum(point_mul(diffdir,base_shift),offdir), offsetx));
+ }
}
- } else { /* normal offset (eg. drop shadow) */
- for (i = 0; i < p->numlines; i++)
- for(j=1; j<p->line[i].numpoints; j++) {
- ret->line[i].point[j-1].x=p->line[i].point[j-1].x+offsetx;
- ret->line[i].point[j-1].y=p->line[i].point[j-1].y+offsety;
- }
+ ret->line[i].point[idx]=offpt;
+ idx++;
+ old_pt=pt; old_diffdir=diffdir; old_offdir=offdir;
+ }
+
+ /* last point */
+ if(first == 0) {
+ pointObj offpt=point_sum(old_pt,point_mul(old_offdir,offsetx));
+ ret->line[i].point[idx]=offpt;
+ idx++;
+ }
+
+ if(idx != p->line[i].numpoints) {
+ /* printf("shouldn't happen :(\n"); */
+ ret->line[i].numpoints=idx;
+ ret->line=realloc(ret->line,ret->line[i].numpoints*sizeof(pointObj));
+ }
}
- return ret;
+ } else { /* normal offset (eg. drop shadow) */
+ for (i = 0; i < p->numlines; i++) {
+ for(j=1; j<p->line[i].numpoints; j++) {
+ ret->line[i].point[j-1].x=p->line[i].point[j-1].x+offsetx;
+ ret->line[i].point[j-1].y=p->line[i].point[j-1].y+offsety;
+ }
+ }
+ }
+
+ return ret;
}
/*
More information about the mapserver-commits
mailing list