下面列出了java.awt.geom.Line2D#getX1 ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
/**
* Compare the slopes of beams (when there are several lines)
*
* @return max slope gap between consecutive beams
*/
public double compareSlopes ()
{
double maxItemGap = 0;
Double prevItemSlope = null;
for (BeamLine line : lines) {
Line2D median = line.median;
double width = median.getX2() - median.getX1();
// Discard too short line, its slope is not reliable enough
if (width > params.maxHookWidth) {
double itemSlope = LineUtil.getSlope(median);
if (prevItemSlope != null) {
double beamSlopeGap = Math.abs(itemSlope - prevItemSlope);
maxItemGap = Math.max(maxItemGap, beamSlopeGap);
}
prevItemSlope = itemSlope;
}
}
return maxItemGap;
}
/**
* Creates a new line by extending an existing line.
*
* @param line the line (<code>null</code> not permitted).
* @param startPercent the amount to extend the line at the start point
* end.
* @param endPercent the amount to extend the line at the end point end.
*
* @return A new line.
*/
private Line2D extendLine(Line2D line, double startPercent,
double endPercent) {
if (line == null) {
throw new IllegalArgumentException("Null 'line' argument.");
}
double x1 = line.getX1();
double x2 = line.getX2();
double deltaX = x2 - x1;
double y1 = line.getY1();
double y2 = line.getY2();
double deltaY = y2 - y1;
x1 = x1 - (startPercent * deltaX);
y1 = y1 - (startPercent * deltaY);
x2 = x2 + (endPercent * deltaX);
y2 = y2 + (endPercent * deltaY);
return new Line2D.Double(x1, y1, x2, y2);
}
/**
* Creates a new line by extending an existing line.
*
* @param line the line (<code>null</code> not permitted).
* @param startPercent the amount to extend the line at the start point
* end.
* @param endPercent the amount to extend the line at the end point end.
*
* @return A new line.
*/
private Line2D extendLine(Line2D line, double startPercent,
double endPercent) {
if (line == null) {
throw new IllegalArgumentException("Null 'line' argument.");
}
double x1 = line.getX1();
double x2 = line.getX2();
double deltaX = x2 - x1;
double y1 = line.getY1();
double y2 = line.getY2();
double deltaY = y2 - y1;
x1 = x1 - (startPercent * deltaX);
y1 = y1 - (startPercent * deltaY);
x2 = x2 + (endPercent * deltaX);
y2 = y2 + (endPercent * deltaY);
return new Line2D.Double(x1, y1, x2, y2);
}
/**
* Creates a new line by extending an existing line.
*
* @param line the line (<code>null</code> not permitted).
* @param startPercent the amount to extend the line at the start point
* end.
* @param endPercent the amount to extend the line at the end point end.
*
* @return A new line.
*
* @since 1.0.18
*/
public static Line2D extendLine(Line2D line, double startPercent,
double endPercent) {
ParamChecks.nullNotPermitted(line, "line");
double x1 = line.getX1();
double x2 = line.getX2();
double deltaX = x2 - x1;
double y1 = line.getY1();
double y2 = line.getY2();
double deltaY = y2 - y1;
x1 = x1 - (startPercent * deltaX);
y1 = y1 - (startPercent * deltaY);
x2 = x2 + (endPercent * deltaX);
y2 = y2 + (endPercent * deltaY);
return new Line2D.Double(x1, y1, x2, y2);
}
public static Point2D getLineIntersectionPoint(Line2D line1, Line2D line2) {
double x1 = line1.getX1();
double y1 = line1.getY1();
double x2 = line1.getX2();
double y2 = line1.getY2();
double x3 = line2.getX1();
double y3 = line2.getY1();
double x4 = line2.getX2();
double y4 = line2.getY2();
double x = ((x2 - x1) * ((x3 * y4) - (x4 * y3)) - (x4 - x3) * ((x1 * y2) - (x2 * y1)))
/ ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4));
double y = ((y3 - y4) * ((x1 * y2) - (x2 * y1)) - (y1 - y2) * ((x3 * y4) - (x4 * y3)))
/ ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4));
return new Point2D.Double(x, y);
}
private void refitOverlappedIntervals(int st, int en, int newFrom, int newTo) {
List<Line2D> checkLimits = new ArrayList<>(intervalLimits.subList(st, en));
float newLimitX1, newLimitX2;
if (!checkLimits.isEmpty()) {
Line2D firstLimit = checkLimits.get(0);
Line2D lastLimit = checkLimits.get(checkLimits.size() - 1);
newLimitX1 = (float) ((newFrom < firstLimit.getX1()) ? newFrom : firstLimit.getX1());
newLimitX2 = (float) ((newTo > lastLimit.getX2()) ? newTo : lastLimit.getX2());
for (Line2D limit : checkLimits) {
intervalLimits.remove(st);
}
} else {
newLimitX1 = newFrom;
newLimitX2 = newTo;
}
intervalLimits.add(st, new Line2D.Float((float) newLimitX1, 0, (float) newLimitX2, 0));
}
/**
* Creates a new line by extending an existing line.
*
* @param line the line (<code>null</code> not permitted).
* @param startPercent the amount to extend the line at the start point
* end.
* @param endPercent the amount to extend the line at the end point end.
*
* @return A new line.
*
* @since 1.0.18
*/
public static Line2D extendLine(Line2D line, double startPercent,
double endPercent) {
ParamChecks.nullNotPermitted(line, "line");
double x1 = line.getX1();
double x2 = line.getX2();
double deltaX = x2 - x1;
double y1 = line.getY1();
double y2 = line.getY2();
double deltaY = y2 - y1;
x1 = x1 - (startPercent * deltaX);
y1 = y1 - (startPercent * deltaY);
x2 = x2 + (endPercent * deltaX);
y2 = y2 + (endPercent * deltaY);
return new Line2D.Double(x1, y1, x2, y2);
}
/**
* Define lookup area around the beam for potential stems
*
* @return the look up area
*/
private Area getLookupArea (Scale scale)
{
final Line2D top = getBorder(VerticalSide.TOP);
final Line2D bottom = getBorder(VerticalSide.BOTTOM);
final int xOut = scale.toPixels(BeamStemRelation.getXOutGapMaximum(manual));
final int yGap = scale.toPixels(BeamStemRelation.getYGapMaximum(manual));
final Path2D lu = new Path2D.Double();
double xMin = top.getX1() - xOut;
double xMax = top.getX2() + xOut;
Point2D topLeft = LineUtil.intersectionAtX(top, xMin);
lu.moveTo(topLeft.getX(), topLeft.getY() - yGap);
Point2D topRight = LineUtil.intersectionAtX(top, xMax);
lu.lineTo(topRight.getX(), topRight.getY() - yGap);
Point2D bottomRight = LineUtil.intersectionAtX(bottom, xMax);
lu.lineTo(bottomRight.getX(), bottomRight.getY() + yGap);
Point2D bottomLeft = LineUtil.intersectionAtX(bottom, xMin);
lu.lineTo(bottomLeft.getX(), bottomLeft.getY() + yGap);
lu.closePath();
return new Area(lu);
}
/**
*
* @param line1
* @param line2
* @param extrapolate
* @return
*/
public static Point2D getIntersectionPoint(Line2D line1, Line2D line2,
boolean extrapolate) {
if (extrapolate || line1.intersectsLine(line2)) {
float x1 = (float) line2.getX1();
float y1 = (float) line2.getY1();
float x2 = (float) line2.getX2();
float y2 = (float) line2.getY2();
float xp1 = (float) line1.getX1();
float yp1 = (float) line1.getY1();
float xp2 = (float) line1.getX2();
float yp2 = (float) line1.getY2();
float y = 0;
float x = 0;
float dy = y2 - y1;
float s = (x2 - x1) / dy;
float dpy = yp2 - yp1;
float sp = (xp2 - xp1) / dpy;
if (y1 == y2) {
if (dpy == 0) {
return null;
}
y = y1;
x = xp1 + sp * (y - yp1);
} else if (yp1 == yp2) {
if (dy == 0) {
return null;
}
y = yp1;
x = x1 + s * (y - y1);
} else {
if (dy == 0 || dpy == 0 || (s - sp) == 0) {
return null;
}
y = (xp1 - x1 + s * y1 - sp * yp1) / (s - sp);
x = x1 + s * (y - y1);
}
return new Point2D.Float(x, y);
}
return null;
}
/** Converts line's bounds by the bounds of the root pane. Drop glass pane
* is over this root pane. After covert a given line is set to drop glass pane.
* @param line line for show in drop glass pane */
private void convertBoundsAndSetDropLine(final Line2D line) {
int x1 = (int) line.getX1();
int x2 = (int) line.getX2();
int y1 = (int) line.getY1();
int y2 = (int) line.getY2();
Point p1 = SwingUtilities.convertPoint(table, x1, y1, table.getRootPane());
Point p2 = SwingUtilities.convertPoint(table, x2, y2, table.getRootPane());
line.setLine(p1, p2);
dropPane.setDropLine(line);
}
/**
* Creates a region surrounding a line segment by 'widening' the line
* segment. A typical use for this method is the creation of a
* 'clickable' region for a line that is displayed on-screen.
*
* @param line the line (<code>null</code> not permitted).
* @param width the width of the region.
*
* @return A region that surrounds the line.
*/
public static Shape createLineRegion(final Line2D line, final float width) {
final GeneralPath result = new GeneralPath();
final float x1 = (float) line.getX1();
final float x2 = (float) line.getX2();
final float y1 = (float) line.getY1();
final float y2 = (float) line.getY2();
if ((x2 - x1) != 0.0) {
final double theta = Math.atan((y2 - y1) / (x2 - x1));
final float dx = (float) Math.sin(theta) * width;
final float dy = (float) Math.cos(theta) * width;
result.moveTo(x1 - dx, y1 + dy);
result.lineTo(x1 + dx, y1 - dy);
result.lineTo(x2 + dx, y2 - dy);
result.lineTo(x2 - dx, y2 + dy);
result.closePath();
}
else {
// special case, vertical line
result.moveTo(x1 - width / 2.0f, y1);
result.lineTo(x1 + width / 2.0f, y1);
result.lineTo(x2 + width / 2.0f, y2);
result.lineTo(x2 - width / 2.0f, y2);
result.closePath();
}
return result;
}
/**
* Computation of rotation from first to last point, with middle as approximate
* middle point of the curve.
*
* @param line straight line from curve start to curve stop
* @param middle middle point of curve
* @return central rotation angle (in radians) from curve start to curve stop.
*/
public static double rotation (Line2D line,
Point2D middle)
{
double dx = line.getX2() - line.getX1();
double dy = line.getY2() - line.getY1();
double halfChordLengthSq = ((dx * dx) + (dy * dy)) / 4;
double sagittaSq = line.ptLineDistSq(middle);
return 4 * Math.atan(Math.sqrt(sagittaSq / halfChordLengthSq));
}
/**
* Clips the specified line to the given rectangle.
*
* @param line the line (<code>null</code> not permitted).
* @param rect the clipping rectangle (<code>null</code> not permitted).
*
* @return <code>true</code> if the clipped line is visible, and
* <code>false</code> otherwise.
*/
public static boolean clipLine(Line2D line, Rectangle2D rect) {
double x1 = line.getX1();
double y1 = line.getY1();
double x2 = line.getX2();
double y2 = line.getY2();
double minX = rect.getMinX();
double maxX = rect.getMaxX();
double minY = rect.getMinY();
double maxY = rect.getMaxY();
int f1 = rect.outcode(x1, y1);
int f2 = rect.outcode(x2, y2);
while ((f1 | f2) != 0) {
if ((f1 & f2) != 0) {
return false;
}
double dx = (x2 - x1);
double dy = (y2 - y1);
// update (x1, y1), (x2, y2) and f1 and f2 using intersections
// then recheck
if (f1 != 0) {
// first point is outside, so we update it against one of the
// four sides then continue
if ((f1 & Rectangle2D.OUT_LEFT) == Rectangle2D.OUT_LEFT
&& dx != 0.0) {
y1 = y1 + (minX - x1) * dy / dx;
x1 = minX;
}
else if ((f1 & Rectangle2D.OUT_RIGHT) == Rectangle2D.OUT_RIGHT
&& dx != 0.0) {
y1 = y1 + (maxX - x1) * dy / dx;
x1 = maxX;
}
else if ((f1 & Rectangle2D.OUT_BOTTOM) == Rectangle2D.OUT_BOTTOM
&& dy != 0.0) {
x1 = x1 + (maxY - y1) * dx / dy;
y1 = maxY;
}
else if ((f1 & Rectangle2D.OUT_TOP) == Rectangle2D.OUT_TOP
&& dy != 0.0) {
x1 = x1 + (minY - y1) * dx / dy;
y1 = minY;
}
f1 = rect.outcode(x1, y1);
}
else if (f2 != 0) {
// second point is outside, so we update it against one of the
// four sides then continue
if ((f2 & Rectangle2D.OUT_LEFT) == Rectangle2D.OUT_LEFT
&& dx != 0.0) {
y2 = y2 + (minX - x2) * dy / dx;
x2 = minX;
}
else if ((f2 & Rectangle2D.OUT_RIGHT) == Rectangle2D.OUT_RIGHT
&& dx != 0.0) {
y2 = y2 + (maxX - x2) * dy / dx;
x2 = maxX;
}
else if ((f2 & Rectangle2D.OUT_BOTTOM) == Rectangle2D.OUT_BOTTOM
&& dy != 0.0) {
x2 = x2 + (maxY - y2) * dx / dy;
y2 = maxY;
}
else if ((f2 & Rectangle2D.OUT_TOP) == Rectangle2D.OUT_TOP
&& dy != 0.0) {
x2 = x2 + (minY - y2) * dx / dy;
y2 = minY;
}
f2 = rect.outcode(x2, y2);
}
}
line.setLine(x1, y1, x2, y2);
return true; // the line is visible - if it wasn't, we'd have
// returned false from within the while loop above
}
/**
* Calculates the anchor point for a label.
*
* @param line the line for the crosshair.
* @param anchor the anchor point.
* @param deltaX the x-offset.
* @param deltaY the y-offset.
*
* @return The anchor point.
*/
private Point2D calculateLabelPoint(Line2D line, RectangleAnchor anchor,
double deltaX, double deltaY) {
double x, y;
boolean left = (anchor == RectangleAnchor.BOTTOM_LEFT
|| anchor == RectangleAnchor.LEFT
|| anchor == RectangleAnchor.TOP_LEFT);
boolean right = (anchor == RectangleAnchor.BOTTOM_RIGHT
|| anchor == RectangleAnchor.RIGHT
|| anchor == RectangleAnchor.TOP_RIGHT);
boolean top = (anchor == RectangleAnchor.TOP_LEFT
|| anchor == RectangleAnchor.TOP
|| anchor == RectangleAnchor.TOP_RIGHT);
boolean bottom = (anchor == RectangleAnchor.BOTTOM_LEFT
|| anchor == RectangleAnchor.BOTTOM
|| anchor == RectangleAnchor.BOTTOM_RIGHT);
Rectangle rect = line.getBounds();
// we expect the line to be vertical or horizontal
if (line.getX1() == line.getX2()) { // vertical
x = line.getX1();
y = (line.getY1() + line.getY2()) / 2.0;
if (left) {
x = x - deltaX;
}
if (right) {
x = x + deltaX;
}
if (top) {
y = Math.min(line.getY1(), line.getY2()) + deltaY;
}
if (bottom) {
y = Math.max(line.getY1(), line.getY2()) - deltaY;
}
}
else { // horizontal
x = (line.getX1() + line.getX2()) / 2.0;
y = line.getY1();
if (left) {
x = Math.min(line.getX1(), line.getX2()) + deltaX;
}
if (right) {
x = Math.max(line.getX1(), line.getX2()) - deltaX;
}
if (top) {
y = y - deltaY;
}
if (bottom) {
y = y + deltaY;
}
}
return new Point2D.Double(x, y);
}
/**
* Calculates the anchor point for a label.
*
* @param line the line for the crosshair.
* @param anchor the anchor point.
* @param deltaX the x-offset.
* @param deltaY the y-offset.
*
* @return The anchor point.
*/
private Point2D calculateLabelPoint(Line2D line, RectangleAnchor anchor,
double deltaX, double deltaY) {
double x = 0.0;
double y = 0.0;
boolean left = (anchor == RectangleAnchor.BOTTOM_LEFT
|| anchor == RectangleAnchor.LEFT
|| anchor == RectangleAnchor.TOP_LEFT);
boolean right = (anchor == RectangleAnchor.BOTTOM_RIGHT
|| anchor == RectangleAnchor.RIGHT
|| anchor == RectangleAnchor.TOP_RIGHT);
boolean top = (anchor == RectangleAnchor.TOP_LEFT
|| anchor == RectangleAnchor.TOP
|| anchor == RectangleAnchor.TOP_RIGHT);
boolean bottom = (anchor == RectangleAnchor.BOTTOM_LEFT
|| anchor == RectangleAnchor.BOTTOM
|| anchor == RectangleAnchor.BOTTOM_RIGHT);
Rectangle rect = line.getBounds();
// we expect the line to be vertical or horizontal
if (line.getX1() == line.getX2()) { // vertical
x = line.getX1();
y = (line.getY1() + line.getY2()) / 2.0;
if (left) {
x = x - deltaX;
}
if (right) {
x = x + deltaX;
}
if (top) {
y = Math.min(line.getY1(), line.getY2()) + deltaY;
}
if (bottom) {
y = Math.max(line.getY1(), line.getY2()) - deltaY;
}
}
else { // horizontal
x = (line.getX1() + line.getX2()) / 2.0;
y = line.getY1();
if (left) {
x = Math.min(line.getX1(), line.getX2()) + deltaX;
}
if (right) {
x = Math.max(line.getX1(), line.getX2()) - deltaX;
}
if (top) {
y = y - deltaY;
}
if (bottom) {
y = y + deltaY;
}
}
return new Point2D.Double(x, y);
}
/**
* Try to extend the provided beam on the desired side to another beam within reach.
* The other beam must be compatible in terms of gap (abscissa and ordinate) and beam slope.
* <p>
* If such a compatible beam is found, but the middle area between them is not correctly filled,
* we must remember this information to avoid any other extension attempt on this side.
*
* @param beam the beam to extend
* @param side the horizontal side
* @param other the side beam found
* @return true if extension was done, false otherwise
*/
private boolean extendToBeam (AbstractBeamInter beam,
HorizontalSide side,
AbstractBeamInter other)
{
// Check black ratio in the middle area (if its width is significant)
final Line2D beamMedian = beam.getMedian();
final Line2D otherMedian = other.getMedian();
double gap = (beamMedian.getX1() < otherMedian.getX1()) ? (otherMedian.getX1() - beamMedian
.getX2()) : (beamMedian.getX1() - otherMedian.getX2());
if (gap >= params.minBeamsGapX) {
Area middleArea = middleArea(beam, other);
AreaMask coreMask = new AreaMask(middleArea);
WrappedInteger core = new WrappedInteger(0);
int coreCount = coreMask.fore(core, pixelFilter);
double coreRatio = (double) core.value / coreCount;
if (coreRatio < params.minExtBlackRatio) {
return false;
}
}
BeamInter newBeam = mergeOf(beam, other);
if (newBeam == null) {
return false;
}
registerBeam(newBeam);
rawSystemBeams.add(newBeam);
if (beam.isVip() || other.isVip()) {
newBeam.setVip(true);
}
beam.remove();
other.remove();
if (newBeam.isVip() || logger.isDebugEnabled()) {
logger.info("VIP Merged {} & {} into {}", beam, other, newBeam);
}
return true;
}
/**
* Returns the intersection point of two lines.
*
* @param line1 First line
* @param line2 Second line
* @return The Point object where the two lines intersect. This method
* returns null if the two lines do not intersect.
* @throws <tt>MultipleIntersectionException</tt> when the two lines
* have more than one intersection point.
*/
static public Point getIntersection(Line2D line1, Line2D line2)
throws Exception {
double dyline1, dxline1;
double dyline2, dxline2, e, f;
double x1line1, y1line1, x2line1, y2line1;
double x1line2, y1line2, x2line2, y2line2;
if (!line1.intersectsLine(line2)) {
return null;
}
/* first, check to see if the segments intersect by parameterization
on s and t; if s and t are both between [0,1], then the
segments intersect */
x1line1 = line1.getX1();
y1line1 = line1.getY1();
x2line1 = line1.getX2();
y2line1 = line1.getY2();
x1line2 = line2.getX1();
y1line2 = line2.getY1();
x2line2 = line2.getX2();
y2line2 = line2.getY2();
/* check to see if the segments have any endpoints in common. If they do,
then return the endpoints as the intersection point */
if ((x1line1 == x1line2) && (y1line1 == y1line2)) {
return (new Point((int) x1line1, (int) y1line1));
}
if ((x1line1 == x2line2) && (y1line1 == y2line2)) {
return (new Point((int) x1line1, (int) y1line1));
}
if ((x2line1 == x1line2) && (y2line1 == y1line2)) {
return (new Point((int) x2line1, (int) y2line1));
}
if ((x2line1 == x2line2) && (y2line1 == y2line2)) {
return (new Point((int) x2line1, (int) y2line1));
}
dyline1 = -(y2line1 - y1line1);
dxline1 = x2line1 - x1line1;
dyline2 = -(y2line2 - y1line2);
dxline2 = x2line2 - x1line2;
e = -(dyline1 * x1line1) - (dxline1 * y1line1);
f = -(dyline2 * x1line2) - (dxline2 * y1line2);
/* compute the intersection point using
ax+by+e = 0 and cx+dy+f = 0
If there is more than 1 intersection point between two lines,
*/
if ((dyline1 * dxline2 - dyline2 * dxline1) == 0) {
throw new Exception("ZERO!");
}
return (new Point(
(int) (-(e * dxline2 - dxline1 * f) / (dyline1 * dxline2 - dyline2 * dxline1)),
(int) (-(dyline1 * f - dyline2 * e) / (dyline1 * dxline2 - dyline2 * dxline1))));
}
/**
* Check if a Beam-Stem link is possible between this beam and the provided stem.
*
* @param stem the provided stem
* @param headToBeam vertical direction (from head) to beam
* @param scale scaling information
* @return the link if OK, otherwise null
*/
public Link checkLink (StemInter stem,
VerticalSide headToBeam,
Scale scale)
{
if (isVip() && stem.isVip()) {
logger.info("VIP checkLink {} & {}", this, stem);
}
// Relation beam -> stem (if not yet present)
BeamStemRelation bRel;
final int yDir = (headToBeam == VerticalSide.TOP) ? (-1) : 1;
final Line2D beamBorder = getBorder(headToBeam.opposite());
bRel = new BeamStemRelation();
// Precise cross point
Point2D start = stem.getTop();
Point2D stop = stem.getBottom();
Point2D crossPt = LineUtil.intersection(stem.getMedian(), beamBorder);
// Extension point
bRel.setExtensionPoint(
new Point2D.Double(crossPt.getX(), crossPt.getY() + (yDir * (getHeight() - 1))));
// Abscissa -> beamPortion
// toLeft & toRight are >0 if within beam, <0 otherwise
double toLeft = crossPt.getX() - beamBorder.getX1();
double toRight = beamBorder.getX2() - crossPt.getX();
final double xGap;
final int maxBeamInDx = scale.toPixels(BeamStemRelation.getXInGapMaximum(manual));
if (this instanceof BeamInter && (Math.min(toLeft, toRight) > maxBeamInDx)) {
// It's a beam center connection
bRel.setBeamPortion(BeamPortion.CENTER);
xGap = 0;
} else if (toLeft < toRight) {
bRel.setBeamPortion(BeamPortion.LEFT);
xGap = Math.max(0, -toLeft);
} else {
bRel.setBeamPortion(BeamPortion.RIGHT);
xGap = Math.max(0, -toRight);
}
// Ordinate
final double yGap = (yDir > 0) ? Math.max(0, crossPt.getY() - stop.getY())
: Math.max(0, start.getY() - crossPt.getY());
bRel.setOutGaps(scale.pixelsToFrac(xGap), scale.pixelsToFrac(yGap), manual);
if (bRel.getGrade() >= bRel.getMinGrade()) {
logger.debug("{} {} {}", this, stem, bRel);
return new Link(stem, bRel, true);
} else {
return null;
}
}
/**
* Gets the distance and point along a Line2D at a specified x.
* If the Line2D is vertical this returns null.
*
* Based on a simplification of algorithm described by Paul Burke
* at http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ (April 1986)
*
* @param line the line
* @param x the value of x
* @return Object[] {fractional distance from line end, Point2D}
*/
private Object[] getDistanceAndPointAtX(Line2D line, double x) {
double dx = line.getX2()-line.getX1();
// if line is vertical, return null
if (dx==0) return null;
// parametric eqn of line: P = P1 + u(P2 - P1)
double u = (x-line.getX1())/dx;
double y = line.getY1() + u*(line.getY2()-line.getY1());
return new Object[] {u, new Point2D.Double(x, y)};
}
/**
* Creates and returns a line that is perpendicular to the specified
* line.
*
* @param line the reference line ({@code null} not permitted).
* @param pt1 a point on the reference line ({@code null} not
* permitted).
* @param size the length of the new line.
* @param opposingPoint an opposing point, to define which side of the
* reference line the perpendicular line will extend ({@code null}
* not permitted).
*
* @return The perpendicular line.
*/
public static Line2D createPerpendicularLine(Line2D line, Point2D pt1,
double size, Point2D opposingPoint) {
double dx = line.getX2() - line.getX1();
double dy = line.getY2() - line.getY1();
double length = Math.sqrt(dx * dx + dy * dy);
double pdx = dy / length;
double pdy = -dx / length;
int ccw = line.relativeCCW(opposingPoint);
Point2D pt2 = new Point2D.Double(pt1.getX() - ccw * size * pdx,
pt1.getY() - ccw * size * pdy);
return new Line2D.Double(pt1, pt2);
}