private List<Segment> segments = new ArrayList<>();
private double width;
+ private double widthd2;
private Type type;
+ private boolean keepLines = false;
+
public void setWidth(double gapWidth) {
this.width = gapWidth;
+ this.widthd2 = width * 0.5;
}
public double getWidth() {
public Type getType() {
return type;
}
+
+ /**
+ * When keep lines is enabled, we keep short lines in the start and end of a line segment, instead of removing them completely with gaps.
+ * @return
+ */
+ public boolean isKeepLines() {
+ return keepLines;
+ }
+
+ public void setKeepLines(boolean keepLines) {
+ this.keepLines = keepLines;
+ }
static class Segment {
public double x1, y1, x2, y2;
public Path2D modify(Path2D path) {
Path2D.Double path2 = new Path2D.Double();
PathIterator iter = path.getPathIterator(null);
+ if (!isKeepLines()) {
+ while (!iter.isDone()) {
+
+ double c[] = new double[6];
+ int i = iter.currentSegment(c);
+ switch (i) {
+ case PathIterator.SEG_MOVETO:
+ path2.moveTo(c[0], c[1]);
+ break;
+ case PathIterator.SEG_LINETO:
+ Segment l = new Segment(path2.getCurrentPoint().getX(), path2.getCurrentPoint().getY(), c[0], c[1]);
- while (!iter.isDone()) {
-
- double c[] = new double[6];
- int i = iter.currentSegment(c);
- switch (i) {
- case PathIterator.SEG_MOVETO:
- path2.moveTo(c[0], c[1]);
- break;
- case PathIterator.SEG_LINETO:
- Segment l = new Segment(path2.getCurrentPoint().getX(), path2.getCurrentPoint().getY(), c[0], c[1]);
-
- List<Double> gaps = new ArrayList<>();
- for (Segment old : segments) {
- Double t = lineLineIntersection(old, l);
- if (t != null) {
- gaps.add(t);
+ List<Double> gaps = new ArrayList<>();
+ for (Segment old : segments) {
+ Double t = lineLineIntersection(old, l);
+ if (t != null) {
+ gaps.add(t);
+ }
}
- }
- if (gaps.isEmpty()) {
- path2.lineTo(c[0], c[1]);
- } else {
- Collections.sort(gaps);
- double dx = l.x2 - l.x1;
- double dy = l.y2 - l.y1;
-
- double pos = 0.0;
- double len = Math.sqrt(dx*dx + dy*dy);
-
- boolean finish = true;
- Point2D prevGapEnd = null;
- for (Double gapCenter : gaps) {
- double pos2 = gapCenter - width / 2 / len;
- double pos3 = gapCenter + width / 2 / len;
- if (pos2 > pos) {
- handleGap(path2, prevGapEnd);
- prevGapEnd = null;
- path2.lineTo(l.x1 + pos2 * dx, l.y1 + pos2 * dy);
+ if (gaps.isEmpty()) {
+ path2.lineTo(c[0], c[1]);
+ } else {
+ Collections.sort(gaps);
+ double dx = l.x2 - l.x1;
+ double dy = l.y2 - l.y1;
+
+ double pos = 0.0;
+ double len = Math.sqrt(dx * dx + dy * dy);
+ double len1 = 1.0 / len;
+
+ boolean finish = true;
+ Point2D prevGapEnd = null;
+ for (Double gapCenter : gaps) {
+ double pos2 = gapCenter - widthd2 * len1;
+ double pos3 = gapCenter + widthd2 * len1;
+ if (pos2 > pos) {
+ handleGap(path2, prevGapEnd);
+ prevGapEnd = null;
+ path2.lineTo(l.x1 + pos2 * dx, l.y1 + pos2 * dy);
+ }
+ if (pos3 < 1.0) {
+ double x = l.x1 + pos3 * dx;
+ double y = l.y1 + pos3 * dy;
+ prevGapEnd = new Point2D.Double(x, y);
+ } else {
+ finish = false;
+ }
+ pos = pos3;
}
- if (pos3 < 1.0) {
- double x = l.x1 + pos3 * dx;
- double y = l.y1 + pos3 * dy;
- prevGapEnd = new Point2D.Double(x, y);
+
+ if (finish) {
+ handleGap(path2, prevGapEnd);
+ path2.lineTo(l.x2, l.y2);
} else {
- finish = false;
+ prevGapEnd = new Point2D.Double(l.x2, l.y2);
+ handleGap(path2, prevGapEnd);
}
- pos = pos3;
}
-
- if (finish) {
- handleGap(path2, prevGapEnd);
- path2.lineTo(l.x2, l.y2);
+ segments.add(l);
+
+ break;
+ case PathIterator.SEG_QUADTO:
+ // TODO: implement gaps
+ path2.quadTo(c[0], c[1], c[2], c[3]);
+ break;
+ case PathIterator.SEG_CUBICTO:
+ // TODO: implement gaps
+ path2.curveTo(c[0], c[1], c[2], c[3], c[4], c[5]);
+ break;
+ case PathIterator.SEG_CLOSE:
+ // TODO: implement gaps
+ path2.closePath();
+ break;
+ default:
+ throw new RuntimeException("Unexpected segment type " + i);
+ }
+ iter.next();
+ }
+ } else {
+ while (!iter.isDone()) {
+
+ double c[] = new double[6];
+ int i = iter.currentSegment(c);
+ switch (i) {
+ case PathIterator.SEG_MOVETO:
+ path2.moveTo(c[0], c[1]);
+ break;
+ case PathIterator.SEG_LINETO:
+ Segment l = new Segment(path2.getCurrentPoint().getX(), path2.getCurrentPoint().getY(), c[0], c[1]);
+
+ List<Double> gaps = new ArrayList<>();
+ for (Segment old : segments) {
+ Double t = lineLineIntersection(old, l);
+ if (t != null) {
+ gaps.add(t);
+ }
+ }
+
+ if (gaps.isEmpty()) {
+ path2.lineTo(c[0], c[1]);
} else {
- prevGapEnd = new Point2D.Double(l.x2, l.y2);
- handleGap(path2, prevGapEnd);
+ Collections.sort(gaps);
+ double dx = l.x2 - l.x1;
+ double dy = l.y2 - l.y1;
+
+ double pos = 0.0;
+ double len = Math.sqrt(dx * dx + dy * dy);
+ double len1 = 1.0 / len;
+
+ boolean finish = true;
+ Point2D prevGapEnd = null;
+ for (int j = 0; j < gaps.size(); j++) {
+ Double gapCenter = gaps.get(j);
+ double gc = gapCenter * len;
+ double pos2 = gc - widthd2;
+ double pos3 = gc + widthd2;
+ if (j == 0) {
+ if (pos2 < widthd2)
+ pos2 += (widthd2 - pos2) * 0.5;
+ }
+ if (j == gaps.size() - 1) {
+ double d = len - pos3;
+ if (d < widthd2)
+ pos3 -= (widthd2 - d) * 0.5;
+ }
+ if (pos2 > pos) {
+ handleGap(path2, prevGapEnd);
+ prevGapEnd = null;
+ pos2 *= len1;
+ path2.lineTo(l.x1 + pos2 * dx, l.y1 + pos2 * dy);
+ }
+ if (pos3 < len) {
+ pos3 *= len1;
+ double x = l.x1 + pos3 * dx;
+ double y = l.y1 + pos3 * dy;
+ prevGapEnd = new Point2D.Double(x, y);
+ } else {
+ finish = false;
+ }
+ pos = pos3;
+ }
+
+ if (finish) {
+ handleGap(path2, prevGapEnd);
+ path2.lineTo(l.x2, l.y2);
+ } else {
+ prevGapEnd = new Point2D.Double(l.x2, l.y2);
+ handleGap(path2, prevGapEnd);
+ }
}
- }
- segments.add(l);
+ segments.add(l);
- break;
- case PathIterator.SEG_QUADTO:
- // TODO: implement gaps
- path2.quadTo(c[0], c[1], c[2], c[3]);
- break;
- case PathIterator.SEG_CUBICTO:
- // TODO: implement gaps
- path2.curveTo(c[0], c[1], c[2], c[3], c[4], c[5]);
- break;
- case PathIterator.SEG_CLOSE:
- // TODO: implement gaps
- path2.closePath();
- break;
- default:
- throw new RuntimeException("Unexpected segment type " + i);
+ break;
+ case PathIterator.SEG_QUADTO:
+ // TODO: implement gaps
+ path2.quadTo(c[0], c[1], c[2], c[3]);
+ break;
+ case PathIterator.SEG_CUBICTO:
+ // TODO: implement gaps
+ path2.curveTo(c[0], c[1], c[2], c[3], c[4], c[5]);
+ break;
+ case PathIterator.SEG_CLOSE:
+ // TODO: implement gaps
+ path2.closePath();
+ break;
+ default:
+ throw new RuntimeException("Unexpected segment type " + i);
+ }
+ iter.next();
}
- iter.next();
}
return path2;
}