refactor TileClipper:

- move clipLine() to LineClipper
This commit is contained in:
Hannes Janetzek 2014-05-13 01:53:58 +02:00
parent f62ea65f25
commit dfe6ca99ce
3 changed files with 130 additions and 122 deletions

View File

@ -238,7 +238,7 @@ public class PathLayer extends Layer {
public Worker(Map map) { public Worker(Map map) {
super(map, 0, new Task(), new Task()); super(map, 0, new Task(), new Task());
mClipper = new LineClipper(-max, -max, max, max, true); mClipper = new LineClipper(-max, -max, max, max);
mPPoints = new float[0]; mPPoints = new float[0];
} }
@ -339,6 +339,8 @@ public class PathLayer extends Layer {
float prevX = x; float prevX = x;
float prevY = y; float prevY = y;
float[] segment = null;
for (int j = 2; j < size * 2; j += 2) { for (int j = 2; j < size * 2; j += 2) {
x = (int) ((mPreprojected[j + 0] - mx) * scale); x = (int) ((mPreprojected[j + 0] - mx) * scale);
y = (int) ((mPreprojected[j + 1] - my) * scale); y = (int) ((mPreprojected[j + 1] - my) * scale);
@ -368,10 +370,11 @@ public class PathLayer extends Layer {
ll.addLine(projected, i, false); ll.addLine(projected, i, false);
if (clip < 0) { if (clip < 0) {
// add line segment /* add line segment */
ll.addLine(mClipper.out, 4, false); segment = mClipper.getLine(segment, 0);
prevX = mClipper.out[2]; ll.addLine(segment, 4, false);
prevY = mClipper.out[3]; prevX = mClipper.outX2;
prevY = mClipper.outY2;
} }
i = 0; i = 0;
continue; continue;

View File

@ -16,6 +16,8 @@
*/ */
package org.oscim.utils.geom; package org.oscim.utils.geom;
import org.oscim.core.GeometryBuffer;
/** /**
* from http://en.wikipedia.org/wiki/Cohen%E2%80%93 * from http://en.wikipedia.org/wiki/Cohen%E2%80%93
* Sutherland_algorithm * Sutherland_algorithm
@ -29,34 +31,30 @@ public class LineClipper {
public static final int BOTTOM = 4; // 0100 public static final int BOTTOM = 4; // 0100
public static final int TOP = 8; // 1000 public static final int TOP = 8; // 1000
private final int xmin, xmax, ymin, ymax; private float xmin, xmax, ymin, ymax;
public final float[] out;
public LineClipper(int minx, int miny, int maxx, int maxy) { public LineClipper(float minx, float miny, float maxx, float maxy) {
this.xmin = minx; this.xmin = minx;
this.ymin = miny; this.ymin = miny;
this.xmax = maxx; this.xmax = maxx;
this.ymax = maxy; this.ymax = maxy;
this.out = null;
} }
public LineClipper(int minx, int miny, int maxx, int maxy, boolean keepResult) { public void setRect(float minx, float miny, float maxx, float maxy) {
this.xmin = minx; this.xmin = minx;
this.ymin = miny; this.ymin = miny;
this.xmax = maxx; this.xmax = maxx;
this.ymax = maxy; this.ymax = maxy;
if (keepResult)
this.out = new float[4];
else
this.out = null;
} }
private int mPrevOutcode; private int mPrevOutcode;
private float mPrevX; private float mPrevX;
private float mPrevY; private float mPrevY;
//public int outX; public float outX1;
//public int outY; public float outY1;
public float outX2;
public float outY2;
public boolean clipStart(float x0, float y0) { public boolean clipStart(float x0, float y0) {
mPrevX = x0; mPrevX = x0;
@ -76,8 +74,6 @@ public class LineClipper {
} }
/** /**
* @param x1 ...
* @param y1 ...
* @return 0 if not intersection, 1 fully within, -1 clipped (and 'out' set * @return 0 if not intersection, 1 fully within, -1 clipped (and 'out' set
* to new points) * to new points)
*/ */
@ -101,8 +97,7 @@ public class LineClipper {
// Bitwise AND is not 0. Trivially reject // Bitwise AND is not 0. Trivially reject
accept = 0; accept = 0;
} else { } else {
accept = clip(mPrevX, mPrevY, x1, y1, xmin, ymin, xmax, ymax, mPrevOutcode, outcode, accept = clip(mPrevX, mPrevY, x1, y1, mPrevOutcode, outcode) ? -1 : 0;
this.out) ? -1 : 0;
} }
mPrevOutcode = outcode; mPrevOutcode = outcode;
mPrevX = x1; mPrevX = x1;
@ -111,45 +106,51 @@ public class LineClipper {
return accept; return accept;
} }
// CohenSutherland clipping algorithm clips a line from public int clipSegment(float x1, float y1, float x2, float y2) {
// P0 = (x0, y0) to P1 = (x1, y1) against a rectangle with clipStart(x1, y1);
// diagonal from (xmin, ymin) to (xmax, ymax). return clipNext(x2, y2);
private static boolean clip(float x0, float y0, float x1, float y1, }
int xmin, int ymin, int xmax, int ymax, int outcode0, int outcode1, float[] out) {
/* CohenSutherland clipping algorithm clips a line from
* P0 = (x0, y0) to P1 = (x1, y1) against a rectangle with
* diagonal from (xmin, ymin) to (xmax, ymax).
* based on en.wikipedia.org/wiki/Cohen-Sutherland */
private boolean clip(float x0, float y0, float x1, float y1, int outcode0, int outcode1) {
boolean accept = false; boolean accept = false;
while (true) { while (true) {
if ((outcode0 | outcode1) == 0) { if ((outcode0 | outcode1) == 0) {
// Bitwise OR is 0. Trivially accept and get out of loop /* Bitwise OR is 0. Trivially accept and get out of loop */
accept = true; accept = true;
break; break;
} else if ((outcode0 & outcode1) != 0) { } else if ((outcode0 & outcode1) != 0) {
// Bitwise AND is not 0. Trivially reject and get out of loop /* Bitwise AND is not 0. Trivially reject and get out of loop */
break; break;
} else { } else {
// failed both tests, so calculate the line segment to clip /* failed both tests, so calculate the line segment to clip
// from an outside point to an intersection with clip edge * from an outside point to an intersection with clip edge */
float x = 0; float x = 0;
float y = 0; float y = 0;
// At least one endpoint is outside the clip rectangle; pick it. /* At least one endpoint is outside the clip rectangle; pick it. */
int outcodeOut = (outcode0 == 0) ? outcode1 : outcode0; int outcodeOut = (outcode0 == 0) ? outcode1 : outcode0;
// Now find the intersection point; /* Now find the intersection point;
// use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0) * use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope)
if ((outcodeOut & TOP) != 0) { // point is above the clip rectangle * * (y - y0) */
if ((outcodeOut & TOP) != 0) {
/* point is above the clip rectangle */
x = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0); x = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0);
y = ymax; y = ymax;
} else if ((outcodeOut & BOTTOM) != 0) { } else if ((outcodeOut & BOTTOM) != 0) {
// point is below the clip rectangle /* point is below the clip rectangle */
x = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0); x = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0);
y = ymin; y = ymin;
} else if ((outcodeOut & RIGHT) != 0) { } else if ((outcodeOut & RIGHT) != 0) {
// point is to the right of clip rectangle /* point is to the right of clip rectangle */
y = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0); y = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0);
x = xmax; x = xmax;
} else if ((outcodeOut & LEFT) != 0) { } else if ((outcodeOut & LEFT) != 0) {
// point is to the left of clip rectangle /* point is to the left of clip rectangle */
y = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0); y = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0);
x = xmin; x = xmin;
} }
@ -164,8 +165,8 @@ public class LineClipper {
else if (y > ymax) else if (y > ymax)
outcode |= TOP; outcode |= TOP;
// Now we move outside point to intersection point to clip /* Now we move outside point to intersection point to clip
// and get ready for next pass. * and get ready for next pass. */
if (outcodeOut == outcode0) { if (outcodeOut == outcode0) {
x0 = x; x0 = x;
y0 = y; y0 = y;
@ -177,12 +178,88 @@ public class LineClipper {
} }
} }
} }
if (accept && out != null) { if (accept) {
out[0] = x0; outX1 = x0;
out[1] = y0; outY1 = y0;
out[2] = x1; outX2 = x1;
out[3] = y1; outY2 = y1;
} }
return accept; return accept;
} }
public float[] getLine(float out[], int offset) {
if (out == null)
return new float[] { outX1, outY1, outX2, outY2 };
out[offset + 0] = outX1;
out[offset + 1] = outY1;
out[offset + 2] = outX2;
out[offset + 3] = outY2;
return out;
}
public int clipLine(GeometryBuffer in, GeometryBuffer out) {
int pointPos = 0;
int numLines = 0;
for (int i = 0, n = in.index.length; i < n; i++) {
int len = in.index[i];
if (len < 0)
break;
if (len < 4) {
pointPos += len;
continue;
}
if (len == 0) {
continue;
}
int inPos = pointPos;
int end = inPos + len;
float prevX = in.points[inPos + 0];
float prevY = in.points[inPos + 1];
boolean inside = clipStart(prevX, prevY);
if (inside) {
out.startLine();
out.addPoint(prevX, prevY);
numLines++;
}
for (inPos += 2; inPos < end; inPos += 2) {
/* get the current way point coordinates */
float curX = in.points[inPos];
float curY = in.points[inPos + 1];
int clip;
if ((clip = clipNext(curX, curY)) != 0) {
if (clip < 0) {
if (inside) {
/* previous was inside */
out.addPoint(outX2, outY2);
inside = false;
} else {
/* previous was outside */
out.startLine();
numLines++;
out.addPoint(outX1, outY1);
out.addPoint(outX2, outY2);
inside = clipStart(curX, curY);
}
} else {
out.addPoint(curX, curY);
}
} else {
inside = false;
}
}
pointPos += len;
}
return numLines;
}
} }

View File

@ -19,12 +19,12 @@ package org.oscim.utils.geom;
import org.oscim.core.GeometryBuffer; import org.oscim.core.GeometryBuffer;
/** /**
* Clip polygon to a rectangle. Output cannot expected to be valid * Clip polygons and lines to a rectangle. Output cannot expected to be valid
* Simple-Feature geometry, i.e. all rings are clipped independently * Simple-Feature geometry, i.e. all polygon rings are clipped independently
* so that inner and outer rings might touch, etc. * so that inner and outer rings might touch, etc.
* *
* based on http://www.cs.rit.edu/~icss571/clipTrans/PolyClipBack.html * based on http://www.cs.rit.edu/~icss571/clipTrans/PolyClipBack.html
* */ */
public class TileClipper { public class TileClipper {
private float minX; private float minX;
private float maxX; private float maxX;
@ -36,7 +36,7 @@ public class TileClipper {
this.minY = minY; this.minY = minY;
this.maxX = maxX; this.maxX = maxX;
this.maxY = maxY; this.maxY = maxY;
mLineClipper = new LineClipper((int) minX, (int) minY, (int) maxX, (int) maxY, true); mLineClipper = new LineClipper(minX, minY, maxX, maxY);
} }
public void setRect(float minX, float minY, float maxX, float maxY) { public void setRect(float minX, float minY, float maxX, float maxY) {
@ -44,10 +44,10 @@ public class TileClipper {
this.minY = minY; this.minY = minY;
this.maxX = maxX; this.maxX = maxX;
this.maxY = maxY; this.maxY = maxY;
mLineClipper = new LineClipper((int) minX, (int) minY, (int) maxX, (int) maxY, true); mLineClipper.setRect(minX, minY, maxX, maxY);
} }
private LineClipper mLineClipper; private final LineClipper mLineClipper;
private final GeometryBuffer mGeomOut = new GeometryBuffer(10, 1); private final GeometryBuffer mGeomOut = new GeometryBuffer(10, 1);
@ -77,7 +77,7 @@ public class TileClipper {
GeometryBuffer out = mGeomOut; GeometryBuffer out = mGeomOut;
out.clear(); out.clear();
int numLines = clipLine(geom, out); int numLines = mLineClipper.clipLine(geom, out);
short idx[] = geom.ensureIndexSize(numLines + 1, false); short idx[] = geom.ensureIndexSize(numLines + 1, false);
System.arraycopy(out.index, 0, idx, 0, numLines); System.arraycopy(out.index, 0, idx, 0, numLines);
@ -255,76 +255,4 @@ public class TileClipper {
py = cy; py = cy;
} }
} }
private int clipLine(GeometryBuffer in, GeometryBuffer out) {
int pointPos = 0;
int numLines = 0;
for (int i = 0, n = in.index.length; i < n; i++) {
int len = in.index[i];
if (len < 0)
break;
if (len < 4) {
pointPos += len;
continue;
}
if (len == 0) {
continue;
}
int inPos = pointPos;
int end = inPos + len;
float prevX = in.points[inPos + 0];
float prevY = in.points[inPos + 1];
boolean inside = mLineClipper.clipStart(prevX, prevY);
if (inside) {
out.startLine();
out.addPoint(prevX, prevY);
numLines++;
}
for (inPos += 2; inPos < end; inPos += 2) {
// get the current way point coordinates
float curX = in.points[inPos];
float curY = in.points[inPos + 1];
int clip;
if ((clip = mLineClipper.clipNext(curX, curY)) != 0) {
//System.out.println(inside + " clip: " + clip + " "
// + Arrays.toString(mLineClipper.out));
if (clip < 0) {
if (inside) {
// previous was inside
out.addPoint(mLineClipper.out[2], mLineClipper.out[3]);
inside = false;
} else {
// previous was outside
out.startLine();
numLines++;
out.addPoint(mLineClipper.out[0], mLineClipper.out[1]);
out.addPoint(mLineClipper.out[2], mLineClipper.out[3]);
inside = mLineClipper.clipStart(curX, curY);
}
} else {
out.addPoint(curX, curY);
}
} else {
inside = false;
}
}
pointPos += len;
}
return numLines;
}
} }