50d02a854391 — pouya@nohup.io 4 months ago
wrapping is now done properly instead of using hacks
9 files changed, 78 insertions(+), 50 deletions(-)

M Makefile
M README
M cart.c
M cart.h
M draw.c
M draw.h
M gshhg.c
M main.c
M smap.1
M Makefile +1 -1
@@ 22,7 22,7 @@ distclean: clean
 .SUFFIXES: .c .o
 
 .c.o:
-	${CC} ${CCFLAGS} -P -cbdu ${.IMPSRC} -o ${.TARGET}
+	${CC} ${CCFLAGS} -c ${.IMPSRC} -o ${.TARGET}
 
 README: ${TARG}.1
 	nroff -man -Tascii -c -rcR=1 ${.ALLSRC} | col -b >${.TARGET}

          
M README +0 -4
@@ 128,10 128,6 @@ LICENCE
 BUGS
        There likely are quite a few.
 
-       jranslations of the origin with -lat, -lon are handled in a hackish way
-       and may in particular produce funny outcomes when using the Hammer pro-
-       jection.
-
        Handling of 1-bit PBM icons is somewhat buggy.
 
 REFERENCES

          
M cart.c +13 -7
@@ 20,15 20,20 @@ crop(double a, double min, double max) {
 }
 
 rPt
+wrap(rPt latlon, void *wrp) {
+	return (rPt){latlon.x, unwind(latlon.y, -180+((Cartobj*)wrp)->latlon0.y, 180+((Cartobj*)wrp)->latlon0.y)};
+}
+
+rPt
 equirect(rPt latlon, void *prj) {
-	return (rPt){(latlon.y-((Prj*)prj)->latlon0.y)/180.0, (latlon.x-((Prj*)prj)->latlon0.x)/90.0};
+	return (rPt){(latlon.y-((Cartobj*)prj)->latlon0.y)/180.0, (latlon.x-((Cartobj*)prj)->latlon0.x)/90.0};
 }
 
 rPt
 mercator(rPt latlon, void *prj) {
 	rPt p;
-	p.x = (latlon.y - ((Prj*)prj)->latlon0.y) / 180.0;
-	p.y = log(tan(PI_4+latlon.x/2.0*DEG))/PI - log(tan(PI_4+((Prj*)prj)->latlon0.x/2.0*DEG))/PI;
+	p.x = (latlon.y - ((Cartobj*)prj)->latlon0.y) / 180.0;
+	p.y = log(tan(PI_4+latlon.x/2.0*DEG))/PI - log(tan(PI_4+((Cartobj*)prj)->latlon0.x/2.0*DEG))/PI;
 	p.y = crop(p.y, -1.0, 1.0);
 	return p;
 }

          
@@ 36,8 41,8 @@ mercator(rPt latlon, void *prj) {
 rPt
 gallpeters(rPt latlon, void *prj) {
 	rPt p;
-	p.x = (latlon.y - ((Prj*)prj)->latlon0.y) / 180.0;
-	p.y = sin(latlon.x*DEG) - sin(((Prj*)prj)->latlon0.x*DEG);
+	p.x = (latlon.y - ((Cartobj*)prj)->latlon0.y) / 180.0;
+	p.y = sin(latlon.x*DEG) - sin(((Cartobj*)prj)->latlon0.x*DEG);
 	p.y = crop(p.y, -1.0, 1.0);
 	return p;
 }

          
@@ 47,11 52,12 @@ hammer(rPt latlon, void *prj) {
 	rPt p;
 	double d;
 
-	latlon.y = latlon.y - ((Prj*)prj)->latlon0.y;
+	latlon.y = latlon.y - ((Cartobj*)prj)->latlon0.y;
 
 	d = sqrt(1+cos(latlon.x*DEG)*cos(latlon.y/2.0*DEG));
 
 	p.x = cos(latlon.x*DEG)*sin(latlon.y/2.0*DEG)/d;
-	p.y = sin(latlon.x*DEG)/d - sin(((Prj*)prj)->latlon0.x*DEG)/d;
+	p.y = sin(latlon.x*DEG)/d - sin(((Cartobj*)prj)->latlon0.x*DEG)/d;
+
 	return p;
 }

          
M cart.h +6 -3
@@ 5,17 5,20 @@ 
 #define PI_4 0.78539816339744830962
 #define DEG (PI/180.0)
 
-typedef struct Prj Prj;
+typedef struct Cartobj Cartobj;
 
-struct Prj {
+struct Cartobj {
 	rPt latlon0;
 };
 
 double unwind(double a, double min, double max);
 double crop(double a, double min, double max);
 
-/* (rPt){.x,.y} are used for lat,lon */ 
+/* (rPt){.x,.y} are used for lat, lon in latlon*/
+rPt wrap(rPt latlon, void *wrp);
+
 rPt equirect(rPt latlon, void *prj);
 rPt mercator(rPt latlon, void *prj);
 rPt gallpeters(rPt latlon, void *prj);
 rPt hammer(rPt latlon, void *prj);
+

          
M draw.c +44 -12
@@ 11,6 11,7 @@ 
 #define absv(a) ((a)>0 ? (a) : -(a))
 #define min(a, b) ((a)<(b) ? (a) : (b))
 #define max(a, b) ((a)>(b) ? (a) : (b))
+#define feq(a,b) (absv((a)-(b))<1e-9)
 
 #define x2i(c, a) (((a) - (c)->min.x)/dPx((c)->min, (c)->max)*(double)(c)->wd)
 #define y2j(c, a) (((a) - (c)->min.y)/dPy((c)->min, (c)->max)*(double)(c)->ht)

          
@@ 20,12 21,13 @@ 
 
 Ink *rawpx(Canvas *c, int i, int j);
 Ink *rawpt(Canvas *c, double x, double y);
+void linew(Canvas *c, rPt p0, rPt p1, int sty, Ink ink, int nowrap);
 void rawline(Canvas *c, double x0, double y0, double x1, double y1, int sty, Ink ink);
 Font *font(int fid);
 void rawglyph(Canvas *c, int i0, int j0, int wd, int ht, int stride, uchar max, uchar *data, Ink ink);
 
 int
-initcvs(Canvas *c, rPt min, rPt max, int wd, int ht, Ink bg, rPt (*proj)(rPt,void*), void *prj) {
+initcvs(Canvas *c, rPt min, rPt max, int wd, int ht, Ink bg, rPt (*wrap)(rPt,void*), void *wrp, rPt (*proj)(rPt,void*), void *prj) {
 	int i;
 	long n;
 	Ink *in;

          
@@ 40,6 42,8 @@ initcvs(Canvas *c, rPt min, rPt max, int
 	c->wd = wd;
 	c->ht = ht;
 	c->bg = bg;
+	c->wrap = wrap;
+	c->wrp = wrp;
 	c->proj = proj;
 	c->prj = prj;
 

          
@@ 149,30 153,54 @@ closeicn(Icon *icn) {
 
 Ink *
 pt(Canvas *c, rPt p, Ink ink) {
+	if(c->wrap) p = (*c->wrap)(p, c->wrp);
 	if(c->proj) p = (*c->proj)(p, c->prj);
 	return rawpt(c, p.x, p.y);
 }
 
 void
 line(Canvas *c, rPt p0, rPt p1, int sty, Ink ink) {
-	int i, j, i0, j0, nsegx, nsegy, nseg;
-	rPt p, pp0, pp;
+	linew(c, p0, p1, sty, ink, 0);
+}
+
+void
+linew(Canvas *c, rPt p0, rPt p1, int sty, Ink ink, int nowrap) {
+	int i, j, i0, j0, nsegx, nsegy, nseg, crosswrap;
+	rPt p, pp0, pp, p0w, p1w;
 	double dx, dy;
 
+	dx = dPx(p0, p1);
+	dy = dPy(p0, p1);
+
+	/* check if the line crosses wrap boundries */
+	p0w = c->wrap ? (*c->wrap)(p0, c->wrp) : p0;
+	p1w  = c->wrap ? (*c->wrap)(p1, c->wrp) : p1;
+	crosswrap = (!feq(dPx(p0w, p1), dPx(p0, p1)) || !feq(dPy(p0w, p1w), dPy(p0, p1)));
+	if(!crosswrap) {
+		p0 = p0w;
+		p1 = p1w;
+	/* otherwise draw twice, once from p0w to p1 (by calling line again below) and once from p0 to p1w (in the remainder of the current call to line); this requires wrap to be idempotent to work correctly */
+	} else if(!nowrap && !(feq(p0w.x, p0.x) && feq(p0w.y, p0.y))) {
+		linew(c, p0w, (rPt){p0w.x+dx,p0w.y+dy}, sty, ink, 1);
+	} else if(!nowrap && !(feq(p1w.x, p1.x) && feq(p1w.y, p1.y))) {
+		linew(c, (rPt){p1w.x-dx,p1w.y-dy}, p1w, sty, ink, 1);
+	}
+
+	/* for efficiency at high zoom levels, skip lines where both ends fall outside visible boundaries, even though strictly speaking some segments of the line may fall within view */
 	pp0 = c->proj ? (*c->proj)(p0, c->prj) : p0;
-	pp = c->proj ? (*c->proj)(p, c->prj) : p;
-	/* for efficiency at high zoom levels, skip lines where both ends fall outside visible boundaries, even though strictly speaking some segments of the line may fall within view */
+	pp = c->proj ? (*c->proj)(p1, c->prj) : p1;
+
 	i0 = x2i(c,pp0.x); j0=y2j(c,pp0.y);
 	i = x2i(c,pp.x); j=y2j(c,pp.y);
 	if((i0<0 && i<0) || (i0>=c->wd && i>=c->wd) ||
 		(j0<0 && j<0) || (j0>=c->ht && j>=c->ht)) return;
 
-	nsegx = absv(dPx(p0, p1)/dPx(c->min, c->max))*(double)c->wd / SEGMENT_FACTOR;
-	nsegy = absv(dPy(p0, p1)/dPy(c->min, c->max))*(double)c->ht / SEGMENT_FACTOR;
-	nseg = max(nsegx, nsegy);
+	nsegx = absv(dx/dPx(c->min, c->max))*(double)c->wd / SEGMENT_FACTOR;
+	nsegy = absv(dy/dPy(c->min, c->max))*(double)c->ht / SEGMENT_FACTOR;
+	nseg = max(nsegx, nsegy)+1;
 
-	dx = dPx(p0, p1)/(double)nseg;
-	dy = dPy(p0, p1)/(double)nseg;
+	dx /= (double)nseg;
+	dy /= (double)nseg;
 
 	p = p0;
 	p.x += dx;

          
@@ 193,6 221,7 @@ mark(Canvas *c, rPt p, int size, int typ
 	sx = size*resx(c);
 	sy = size*resy(c);
 
+	if(c->wrap) p = (*c->wrap)(p, c->wrp);
 	if(c->proj) p = (*c->proj)(p, c->prj);
 	switch(typ) {
 	case Mplus:

          
@@ 227,6 256,7 @@ icon(Canvas *c, rPt p, Icon *icn, int po
 	uchar ch, *data;
 	double d, r, g, b;
 
+	if(c->wrap) p = (*c->wrap)(p, c->wrp);
 	if(c->proj) p = (*c->proj)(p, c->prj);
 	i0 = x2i(c, p.x);
 	j0 = y2j(c, p.y);

          
@@ 272,6 302,7 @@ text(Canvas *c, rPt p, uchar *s, int fid
 		chp = ch;
 	}
 
+	if(c->wrap) p = (*c->wrap)(p, c->wrp);
 	if(c->proj) p = (*c->proj)(p, c->prj);
 	i0 = x2i(c, p.x);
 	j0 = y2j(c, p.y);

          
@@ 342,7 373,7 @@ void
 rawline(Canvas *c, double x0, double y0, double x1, double y1, int sty, Ink ink) {
 	double dx, dy, r, u;
 	int sdx, sdy;
-	Ink *in;
+	Ink *in0, *in;
 	int i;
 
 	x0 = x2i(c, x0); y0 = y2j(c, y0);

          
@@ 354,8 385,9 @@ rawline(Canvas *c, double x0, double y0,
 	sdx = dx > 0 ? 1 : -1;
 	sdy = dy > 0 ? 1 : -1;
 
-	in = rawpx(c, (int)x0, (int)y0); if(in) *in = ink;
+	in0 = rawpx(c, (int)x0, (int)y0); if(in0) *in0 = ink;
 	in = rawpx(c, (int)x1, (int)y1); if(in) *in = ink;
+	if(in0==in) return;
 	if(dx*sdx > dy*sdy) {
 		if(x0>x1) {
 			u = x0; x0 = x1; x1 = u;

          
M draw.h +4 -1
@@ 45,6 45,9 @@ struct Canvas {
 	int wd, ht;
 	Ink bg;
 
+	rPt (*wrap)(rPt,void*);
+	void *wrp;
+
 	rPt (*proj)(rPt,void*);
 	void *prj;
 

          
@@ 67,7 70,7 @@ struct Font {
 #define dPx(p1,p2) ((p2).x-(p1).x)
 #define dPy(p1,p2) ((p2).y-(p1).y)
 
-int initcvs(Canvas *c, rPt min, rPt max, int wd, int ht, Ink bg, rPt (*proj)(rPt,void*), void *prj);
+int initcvs(Canvas *c, rPt min, rPt max, int wd, int ht, Ink bg, rPt (*wrap)(rPt,void*), void *wrp, rPt (*proj)(rPt,void*), void *prj);
 void closecvs(Canvas *c);
 
 int initicn(Icon *icn, char *name, int wd, int ht, int depth, uchar *data);

          
M gshhg.c +4 -12
@@ 31,20 31,12 @@ drawshapes(char *fn, Canvas *c, Ink ink)
 		if(obj->nSHPType) for(j=0; j<obj->nParts; j++) {
 			nextr = j>=obj->nParts-1 ? obj->nVertices : obj->panPartStart[j+1];
  			while(k++<nextr-1) {
-				p0l.x = p0r.x = p0.x = *x++;
-				p1l.x = p1r.x = p1.x = *x;
-				p0l.y = p0r.y = p0.y = *y++;
-				p1l.y = p1r.y = p1.y = *y;
-
-				p0l.y -= 360.0; p1l.y -= 360.0;
-				p0r.y += 360.0; p1r.y += 360.0;
+				p0.x = *x++;
+				p1.x = *x;
+				p0.y = *y++;
+				p1.y = *y;
 
 				line(c, p0, p1, Lsolid, ink);
-				/* draw 3 times to simulate wrap-around */
-				if(c->proj==&equirect || c->proj==&mercator || c->proj==&gallpeters) {
-					line(c, p0l, p1l, Lsolid, ink);
-					line(c, p0r, p1r, Lsolid, ink);
-				}
 			}
 		}
 

          
M main.c +6 -5
@@ 103,7 103,7 @@ usage:
 int
 main(int argc, char **argv) {
 	Gshhg gs;
-	Prj prj;
+	Cartobj crt;
 	Canvas c;
 	Cmd cmd;
 

          
@@ 111,7 111,7 @@ main(int argc, char **argv) {
 	int i;
 
 	memset(&gs, 0, sizeof gs);
-	memset(&prj, 0, sizeof prj);
+	memset(&crt, 0, sizeof crt);
 	memset(&c, 0, sizeof c);
 	memset(&cmd, 0, sizeof cmd);
 

          
@@ 126,14 126,15 @@ main(int argc, char **argv) {
 	gs.iborder = ink_gshhg[2];
 
 	/* (rPt){.x,.y} are used for lat,lon */
-	prj.latlon0.x = ctxt.lat;
-	prj.latlon0.y = ctxt.lon;
+	crt.latlon0.x = ctxt.lat;
+	crt.latlon0.y = ctxt.lon;
 
 	if(initcvs(&c,
 		(rPt){-1.0/ctxt.zoom,-1.0/ctxt.zoom},
 		(rPt){ 1.0/ctxt.zoom, 1.0/ctxt.zoom},
 		ctxt.wd, ctxt.ht, ink_bg,
-		ctxt.proj, &prj)) {
+		&wrap, &crt,
+		ctxt.proj, &crt)) {
 		emsg = "initcvs";
 		goto term;
 	}

          
M smap.1 +0 -5
@@ 265,11 265,6 @@ for included third-party code.
 .P
 There likely are quite a few.
 .P
-jranslations of the origin with
-'BR -lat ,
-.B -lon
-are handled in a hackish way and may in particular produce funny outcomes when using the Hammer projection.
-.P
 Handling of 1-bit PBM icons is somewhat buggy.
 .SH REFERENCES
 .TP