java.awt.geom.Line2D#getP2 ( )源码实例Demo

下面列出了java.awt.geom.Line2D#getP2 ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。

源代码1 项目: maze-harvester   文件: SegmentSorter.java
public void paint(Graphics2D g2d) {
  g2d.setStroke(new BasicStroke(0.16f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));

  // Greedily gather segments into polyline paths.
  List<Line2D> polyLine = new ArrayList<>();
  Point2D lastPoint = null;
  for (Color color : collector.getColors()) {
    g2d.setPaint(color);
    List<Line2D> sortedSegments = quadraticSort(collector.getSegments(color));
    for (Line2D seg : sortedSegments) {
      if (lastPoint == null || lastPoint.distance(seg.getP1()) > COLLAPSE_THRESH) {
        polyLine = emitCurrentPolyLine(g2d, polyLine);
      }
      polyLine.add(seg);
      lastPoint = seg.getP2();
    }
    polyLine = emitCurrentPolyLine(g2d, polyLine);
  }
}
 
源代码2 项目: audiveris   文件: BeamsBuilder.java
/**
 * Report the gap area between two beams.
 * (The beams are merge candidates assumed to be co-linear)
 *
 * @param one a beam
 * @param two another beam
 * @return the area between them
 */
private Area middleArea (AbstractBeamInter one,
                         AbstractBeamInter two)
{
    final Line2D oneMedian = one.getMedian();
    final Line2D twoMedian = two.getMedian();

    // Height
    double oneWidth = oneMedian.getX2() - oneMedian.getX1();
    double twoWidth = twoMedian.getX2() - twoMedian.getX1();
    double height = ((one.getHeight() * oneWidth) + (two.getHeight() * twoWidth)) / (oneWidth
                                                                                             + twoWidth);

    // Median
    final Line2D median;

    if (oneMedian.getX1() < twoMedian.getX1()) {
        median = new Line2D.Double(oneMedian.getP2(), twoMedian.getP1());
    } else {
        median = new Line2D.Double(twoMedian.getP2(), oneMedian.getP1());
    }

    return AreaUtil.horizontalParallelogram(median.getP1(), median.getP2(), height);
}
 
源代码3 项目: workcraft   文件: Polyline.java
private Rectangle2D getSegmentBoundsWithThreshold(Line2D segment) {
    Point2D pt1 = segment.getP1();
    Point2D pt2 = segment.getP2();

    Rectangle2D bb = new Rectangle2D.Double(pt1.getX(), pt1.getY(), 0, 0);
    bb.add(pt2);
    Point2D lineVec = new Point2D.Double(pt2.getX() - pt1.getX(), pt2.getY() - pt1.getY());

    double mag = lineVec.distance(0, 0);
    if (mag != 0) {
        lineVec.setLocation(lineVec.getY() * VisualConnection.HIT_THRESHOLD / mag,
                -lineVec.getX() * VisualConnection.HIT_THRESHOLD / mag);
        bb.add(pt1.getX() + lineVec.getX(), pt1.getY() + lineVec.getY());
        bb.add(pt2.getX() + lineVec.getX(), pt2.getY() + lineVec.getY());
        bb.add(pt1.getX() - lineVec.getX(), pt1.getY() - lineVec.getY());
        bb.add(pt2.getX() - lineVec.getX(), pt2.getY() - lineVec.getY());
    }
    return bb;
}
 
源代码4 项目: Pixelitor   文件: CompositionGuide.java
/**
 * Get projected point P' of P on given line
 *
 * @return projected point p.
 */
private static Point2D.Double getProjectedPointOnLine(Line2D line, Point2D.Double p) {
    Point2D.Double l1 = (Point2D.Double) line.getP1();
    Point2D.Double l2 = (Point2D.Double) line.getP2();

    // get dot product of vectors v1, v2
    Point2D.Double v1 = new Point2D.Double(l2.x - l1.x, l2.y - l1.y);
    Point2D.Double v2 = new Point2D.Double(p.x - l1.x, p.y - l1.y);
    double d = v1.x * v2.x + v1.y * v2.y;

    // get squared length of vector v1
    double v1Length = v1.x * v1.x + v1.y * v1.y;
    if (v1Length == 0) {
        return l1;
    }

    return new Point2D.Double(
        (int) (l1.x + (d * v1.x) / v1Length),
        (int) (l1.y + (d * v1.y) / v1Length));
}
 
源代码5 项目: pentaho-reporting   文件: ShapeTransform.java
/**
 * Normalize the line; the point with the lowest X is the primary point, if both points have the same X, that point
 * with the lowest Y value wins.
 *
 * @param line
 *          the original line
 * @return the normalized line
 */
private static Line2D getNormalizedLine( final Line2D line ) {
  final Line2D lineClone = (Line2D) line.clone();

  final Point2D p1 = line.getP1();
  final Point2D p2 = line.getP2();
  if ( p1.getX() < p2.getX() ) {
    return lineClone;
  }
  if ( p1.getX() > p2.getX() ) {
    lineClone.setLine( p2, p1 );
    return lineClone;
  }
  if ( p1.getY() < p2.getY() ) {
    return lineClone;
  }
  lineClone.setLine( p2, p1 );
  return lineClone;
}
 
源代码6 项目: maze-harvester   文件: SegmentSorter.java
private static List<Line2D> quadraticSort(ImmutableList<Line2D> input) {
  ArrayList<Line2D> segments = new ArrayList<>(input);
  ArrayList<Line2D> output = new ArrayList<>();

  // Pick the first segment arbitrarily.
  Line2D nextSegment = segments.remove(0);
  output.add(nextSegment);

  // Now find nearby segments with scans of the segments array. O(n^2)!
  while (segments.size() > 0) {
    Point2D currentPen = nextSegment.getP2();

    int bestIndex = 0;
    int bestDirection = 0;
    double bestDistance = Double.MAX_VALUE;
    
    for (int i=0; i<segments.size(); i++) {
      Line2D thisLine = segments.get(i);
      for (int direction = 0; direction < 2; direction++) {
        Point2D thisPoint = direction==0 ? thisLine.getP1() : thisLine.getP2();
        double thisDistance = currentPen.distance(thisPoint);
        if (thisDistance < bestDistance) {
          bestIndex = i;
          bestDirection = direction;
          bestDistance = thisDistance;
        }
      }
    }

    nextSegment = segments.remove(bestIndex);
    if (bestDirection == 1) {
      nextSegment = new Line2D.Double(nextSegment.getP2(), nextSegment.getP1());
    }
    output.add(nextSegment);
  }
  return output;
}
 
源代码7 项目: audiveris   文件: BeamsBuilder.java
/**
 * (Try to) create a new BeamInter instance that represents a merge of the provided
 * beams.
 *
 * @param one a beam
 * @param two another beam
 * @return the resulting beam, or null if failed
 */
private BeamInter mergeOf (AbstractBeamInter one,
                           AbstractBeamInter two)
{
    final Line2D oneMedian = one.getMedian();
    final Line2D twoMedian = two.getMedian();

    // Mean dist
    double distImpact = (((Impacts) one.getImpacts()).getDistImpact() + ((Impacts) two
            .getImpacts()).getDistImpact()) / 2;

    // Height
    double oneWidth = oneMedian.getX2() - oneMedian.getX1();
    double twoWidth = twoMedian.getX2() - twoMedian.getX1();
    double height = ((one.getHeight() * oneWidth) + (two.getHeight() * twoWidth)) / (oneWidth
                                                                                             + twoWidth);

    // Median & width
    final Line2D median;

    if (oneMedian.getX1() < twoMedian.getX1()) {
        median = new Line2D.Double(oneMedian.getP1(), twoMedian.getP2());
    } else {
        median = new Line2D.Double(twoMedian.getP1(), oneMedian.getP2());
    }

    BeamItem newItem = new BeamItem(median, height);

    if (one.isVip() || two.isVip()) {
        newItem.setVip(true);
    }

    Impacts impacts = computeBeamImpacts(newItem, true, true, distImpact);

    if ((impacts != null) && (impacts.getGrade() >= BeamInter.getMinGrade())) {
        return new BeamInter(impacts, median, height);
    } else {
        return null;
    }
}
 
源代码8 项目: pentaho-reporting   文件: ShapeTransform.java
/**
 * Resizes a line. Instead of creating a GeneralPath (as AffineTransform's scale would do) we modify the line itself.
 *
 * @param line
 *          the line that should be scaled
 * @param width
 *          the new width of the line bounds
 * @param height
 *          the new height of the line bounds
 * @return the scale Line2D object.
 */
private static Line2D resizeLine( final Line2D line, final double width, final double height ) {
  final Line2D newLine = getNormalizedLine( line );
  final Point2D p1 = newLine.getP1();
  final Point2D p2 = newLine.getP2();
  final double normPointX = ( p1.getX() - p2.getX() );
  final double normPointY = ( p1.getY() - p2.getY() );
  final double scaleX = ( normPointX == 0 ) ? 1 : width / Math.abs( normPointX );
  final double scaleY = ( normPointY == 0 ) ? 1 : height / Math.abs( normPointY );
  p2.setLocation( ( p2.getX() - p1.getX() ) * scaleX + p1.getX(), ( p2.getY() - p1.getY() ) * scaleY + p1.getY() );
  newLine.setLine( p1, p2 );
  return newLine;
}
 
源代码9 项目: mil-sym-java   文件: clsClipQuad.java
/**
 * Gets theoretical intersection of an edge with the line connecting previous and current points.
 * @param pt0
 * @param pt1
 * @param currentEdge the current edge of the clip area, assumed to not be vertical
 * @return 
 */
private static Point2D intersectPoint2(Point2D previous, 
        Point2D current,
        Line2D currentEdge) 
{
    
    Point2D ptIntersect=null;
    try
    {                        
        Point2D ll=currentEdge.getP1();
        Point2D ul=currentEdge.getP2();
        
        //no vertical client segments
        //if(current.getX()==previous.getX())            
        if(Math.abs(current.getX()-previous.getX())<1)            
            current.setLocation(current.getX()+1, current.getY());                
        
        double m1=( ul.getY()-ll.getY() )/( ul.getX()-ll.getX() );
        double m2=( current.getY()-previous.getY() )/( current.getX()-previous.getX() );
        double b1=ul.getY()-m1*ul.getX();
        double b2=current.getY()-m2*current.getX(); 
        ptIntersect=CalcTrueIntersectDouble(m1,b1,m2,b2,1,1,0,0);                    
    }
    catch (Exception exc) {
        ErrorLogger.LogException(_className, "intersectPoint2",
                new RendererException("Failed inside intersectPoint2", exc));
    }        
    return ptIntersect;
}
 
源代码10 项目: audiveris   文件: WedgesBuilder.java
private WedgeInter createWedgeInter (SegmentInter s1,
                                     SegmentInter s2,
                                     boolean rev,
                                     GradeImpacts impacts)
{
    Shape shape = rev ? Shape.CRESCENDO : Shape.DIMINUENDO;

    Rectangle box = new Rectangle(s1.getBounds());
    box.add(s2.getBounds());

    // Determine precise closed ends
    Line2D l1 = new Line2D.Double(s1.getInfo().getEnd(true), s1.getInfo().getEnd(false));
    Line2D l2 = new Line2D.Double(s2.getInfo().getEnd(true), s2.getInfo().getEnd(false));

    // Beware s1 and s2 are in no particular order
    final boolean swap;

    if (shape == Shape.CRESCENDO) {
        swap = l2.getY2() < l1.getY2();
    } else {
        swap = l2.getY1() < l1.getY1();
    }

    if (swap) {
        Line2D temp = l1;
        l1 = l2;
        l2 = temp;
    }

    WedgeInter inter = new WedgeInter(l1, l2, box, shape, impacts);

    /* For a wedge, we can restrict the containing systems as just the closest one. */
    Point2D refPoint = (shape == Shape.CRESCENDO) ? l1.getP1() : l1.getP2();
    Staff staff = sheet.getStaffManager().getClosestStaff(refPoint);
    staff.getSystem().getSig().addVertex(inter);

    // Build the underlying glyph as the compound of the two segments glyphs
    inter.setGlyph(
            sheet.getGlyphIndex().registerOriginal(
                    GlyphFactory.buildGlyph(Arrays.asList(s1.getGlyph(), s2.getGlyph()))));

    return inter;
}
 
源代码11 项目: audiveris   文件: BeamStructure.java
/**
 * Add or extend observed border lines.
 * Beam items may be merged due to stuck pixels, resulting in missing (portions of) borders.
 * In theory, we should have pairs of top & bottom borders with identical length, each pair
 * corresponding to a beam item.
 *
 * @param yDir        -1 for going upward, +1 downward
 * @param globalSlope global structure slope
 * @param baseMap     (input/output) configuration of base lines
 * @param otherMap    (input/output) configuration of other lines
 */
private void completeBorderLines (double yDir,
                                  double globalSlope,
                                  SortedMap<Double, Line2D> baseMap,
                                  SortedMap<Double, Line2D> otherMap)
{
    double dy = yDir * params.typicalHeight;

    // For each base border line, look for corresponding other border line
    for (Entry<Double, Line2D> baseEntry : baseMap.entrySet()) {
        Line2D base = baseEntry.getValue();
        double targetY = baseEntry.getKey() + dy;
        Entry<Double, Line2D> otherEntry = lookupLine(targetY, otherMap);

        if (otherEntry == null) {
            // Create a brand new map entry
            otherMap.put(
                    targetY,
                    new Line2D.Double(
                            base.getX1(),
                            base.getY1() + dy,
                            base.getX2(),
                            base.getY2() + dy));
        } else {
            // Extend the map entry if needed
            Line2D other = otherEntry.getValue();
            double xMid = (other.getX1() + other.getX2()) / 2;
            double yMid = (other.getY1() + other.getY2()) / 2;
            double height = yMid - LineUtil.yAtX(base, xMid);
            Point2D p1 = (base.getX1() < other.getX1()) ? new Point2D.Double(
                    base.getX1(),
                    base.getY1() + height) : other.getP1();
            Point2D p2 = (base.getX2() > other.getX2()) ? new Point2D.Double(
                    base.getX2(),
                    base.getY2() + height) : other.getP2();
            double x = (p1.getX() + p2.getX()) / 2;
            double y = LineUtil.yAtX(p1, p2, x);
            double offset = y - LineUtil.yAtX(center, globalSlope, x);

            otherMap.remove(otherEntry.getKey());
            otherMap.put(offset, new Line2D.Double(p1, p2));
        }
    }
}
 
源代码12 项目: audiveris   文件: BeamsBuilder.java
/**
 * Try to extend the provided beam in parallel with a another beam (in the same
 * group of beams).
 *
 * @param beam the beam to extend
 * @param side the horizontal side
 * @return true if extension was done, false otherwise
 */
private boolean extendInParallel (AbstractBeamInter beam,
                                  HorizontalSide side)
{
    final boolean logging = beam.isVip() || logger.isDebugEnabled();

    // Look for a parallel beam just above or below
    final Line2D median = beam.getMedian();
    final double height = beam.getHeight();
    final double slope = LineUtil.getSlope(median);

    Area luArea = AreaUtil.horizontalParallelogram(median.getP1(), median.getP2(), 3 * height);
    beam.addAttachment("=", luArea);

    List<Inter> others = Inters.intersectedInters(rawSystemBeams, GeoOrder.NONE, luArea);

    others.remove(beam); // Safer

    if (!others.isEmpty()) {
        // Use a closer look
        final Point2D endPt = (side == LEFT) ? median.getP1() : median.getP2();

        for (Inter ib : others) {
            AbstractBeamInter other = (AbstractBeamInter) ib;

            if (logging) {
                logger.info("VIP {} found parallel {}", beam, other);
            }

            // Check concrete intersection (using areas rather than rectangles)
            if (!AreaUtil.intersection(other.getArea(), luArea)) {
                if (logging) {
                    logger.info("VIP too distant beams {} and {}", beam, other);
                }

                continue;
            }

            // Check they are really parallel?
            final Line2D otherMedian = other.getMedian();
            final double otherSlope = LineUtil.getSlope(otherMedian);

            if (Math.abs(otherSlope - slope) > params.maxBeamSlopeGap) {
                if (logging) {
                    logger.info("VIP {} not parallel with {}", beam, other);
                }

                continue;
            }

            // Side-effect: measure the actual vertical gap between such parallel beams
            // (avoiding a given pair to be counted 4 times...)
            if ((side == LEFT) && (beam.getId() < other.getId())) {
                measureVerticalDistance(beam, other);
            }

            // Check the other beam can really extend the current beam
            final Point2D otherEndPt = (side == LEFT) ? otherMedian.getP1()
                    : otherMedian.getP2();
            double extDx = (side == LEFT) ? (endPt.getX() - otherEndPt.getX())
                    : (otherEndPt.getX() - endPt.getX());

            if (extDx < (2 * params.maxStemBeamGapX)) {
                if (logging) {
                    logger.info("VIP {} no increment with {}", beam, other);
                }

                continue;
            }

            // Make sure the end side is fully in the same system as the current one
            Point2D extPt = LineUtil.intersectionAtX(median, otherEndPt.getX());

            if (!isSideInSystem(extPt, height)) {
                continue;
            }

            // Make sure this does not include another beam
            AbstractBeamInter includedBeam = getSideBeam(beam, side, extDx);

            if (includedBeam != null) {
                continue;
            }

            return extendToPoint(beam, side, extPt);
        }
    }

    return false;
}
 
源代码13 项目: audiveris   文件: BeamsBuilder.java
/**
 * Try to extend the beam on provided side until the target extension point.
 *
 * @param beam  the beam to extend
 * @param side  the horizontal side
 * @param extPt the targeted extension point
 * @return true if extension was done, false otherwise
 */
private boolean extendToPoint (AbstractBeamInter beam,
                               HorizontalSide side,
                               Point2D extPt)
{
    final boolean logging = beam.isVip() || logger.isDebugEnabled();

    if (logging) {
        logger.info("VIP extendToPoint for {}", beam);
    }

    final Line2D median = beam.getMedian();
    final double height = beam.getHeight();

    // Check we have a concrete extension
    Point2D endPt = (side == LEFT) ? median.getP1() : median.getP2();
    double extDx = (side == LEFT) ? (endPt.getX() - extPt.getX())
            : (extPt.getX() - endPt.getX());

    // (to cope with rounding we use 1 instead of 0)
    if (extDx <= 1) {
        return false;
    }

    // Check we have a high enough black ratio in the extension zone
    Area extArea = sideAreaOf("+", beam, side, 0, extDx, 0);
    AreaMask extMask = new AreaMask(extArea);
    WrappedInteger extCore = new WrappedInteger(0);
    int extCoreCount = extMask.fore(extCore, pixelFilter);
    double extCoreRatio = (double) extCore.value / extCoreCount;

    if (extCoreRatio < params.minExtBlackRatio) {
        if (logging) {
            logger.info("VIP {} lacks pixels in stem extension", beam);
        }

        return false;
    }

    // Resulting median
    final Line2D newMedian;

    if (side == LEFT) {
        newMedian = new Line2D.Double(extPt, median.getP2());
    } else {
        newMedian = new Line2D.Double(median.getP1(), extPt);
    }

    // Impacts
    final double distImpact = ((Impacts) beam.getImpacts()).getDistImpact();
    BeamItem newItem = new BeamItem(newMedian, height);

    if (beam.isVip()) {
        newItem.setVip(true);
    }

    Impacts impacts = computeBeamImpacts(newItem, true, true, distImpact);

    if ((impacts != null) && (impacts.getGrade() >= BeamInter.getMinGrade())) {
        BeamInter newBeam = new BeamInter(impacts, newMedian, height);

        if (beam.isVip()) {
            newBeam.setVip(true);
        }

        registerBeam(newBeam);
        rawSystemBeams.add(newBeam);
        beam.remove();

        if (logging) {
            logger.info("VIP {} extended as {} {}", beam, newBeam, newBeam.getImpacts());
        }

        return true;
    } else {
        if (logging) {
            logger.info("VIP {} extension failed", beam);
        }

        return false;
    }
}
 
源代码14 项目: audiveris   文件: BeamsBuilder.java
/**
 * Look for a compatible beam inter next to the provided one (in a same beam line).
 * They either can be merged or give a limit to other extension modes.
 *
 * @param beam     the provided beam
 * @param side     which side to look on
 * @param maxGapDx max gap width between the two beams, or default value if null
 * @return the sibling beam found if any
 */
private AbstractBeamInter getSideBeam (AbstractBeamInter beam,
                                       final HorizontalSide side,
                                       Double maxGapDx)
{
    Area luArea = (maxGapDx != null) ? sideAreaOf(null, beam, side, 0, maxGapDx, 0)
            : sideAreaOf("-", beam, side, 0, params.maxSideBeamDx, 0);

    List<Inter> others = Inters.intersectedInters(rawSystemBeams, GeoOrder.NONE, luArea);
    others.remove(beam); // Safer

    if (!others.isEmpty()) {
        // Use a closer look, using colinearity
        final Line2D median = beam.getMedian();
        final Point2D endPt = (side == LEFT) ? median.getP1() : median.getP2();
        final double slope = LineUtil.getSlope(median);

        for (Iterator<Inter> it = others.iterator(); it.hasNext();) {
            AbstractBeamInter other = (AbstractBeamInter) it.next();

            // Check connection point & beam slopes are OK
            Line2D otherMedian = other.getMedian();

            if ((Math.abs(LineUtil.getSlope(otherMedian) - slope) > params.maxBeamSlopeGap)
                        || (otherMedian.ptLineDist(endPt) > params.maxBeamsGapY)) {
                it.remove();
            }
        }

        // Keep just the closest one to current beam (abscissa-wise)
        if (others.size() > 1) {
            final double endX = endPt.getX();
            Collections.sort(others, new Comparator<Inter>()
                     {
                         @Override
                         public int compare (Inter o1,
                                             Inter o2)
                         {
                             AbstractBeamInter b1 = (AbstractBeamInter) o1;
                             AbstractBeamInter b2 = (AbstractBeamInter) o2;

                             if (side == LEFT) {
                                 return Double.compare(
                                         endX - b1.getMedian().getX2(),
                                         endX - b2.getMedian().getX2());
                             } else {
                                 return Double.compare(
                                         b1.getMedian().getX1() - endX,
                                         b2.getMedian().getX1() - endX);
                             }
                         }
                     });
        }

        if (!others.isEmpty()) {
            return (AbstractBeamInter) others.get(0);
        }
    }

    return null;
}
 
源代码15 项目: audiveris   文件: Jaxb.java
Line2DFacade (Line2D line)
{
    Objects.requireNonNull(line, "Cannot create Line2DFacade with a null line");
    p1 = new Point2DFacade(line.getP1());
    p2 = new Point2DFacade(line.getP2());
}
 
源代码16 项目: mil-sym-java   文件: clsClipPolygon2.java
/**
 * Calculate the point the line intersects an edge of the clipbounds
 * @param pt0 start point of the line
 * @param pt1 end point of the line
 * @param currentEdge
 * @return
 */
private static Point2D intersectPoint(Point2D pt0, 
        Point2D pt1,
        Line2D currentEdge) {
    Point2D ptIntersect = null;
    try {
        Point2D edgePt1 = currentEdge.getP1();
        Point2D edgePt2 = currentEdge.getP2();
        double edge_x = 0, edge_y = 0, m = 0;
        double deltaX = 0, deltaY = 0;
        //vertical edge
        if (Math.abs(edgePt1.getX() - edgePt2.getX()) < Math.abs(edgePt1.getY() - edgePt2.getY()))
        {
            ptIntersect=new Point2D.Double();
            edge_x = edgePt1.getX();
            //if (pt1.getX() == pt0.getX())
            if (Math.abs(pt1.getX() - pt0.getX())<1)
                pt1.setLocation(pt1.getX()+1, pt1.getY());

            m = (pt1.getY() - pt0.getY()) / (pt1.getX() - pt0.getX());
            deltaX = edge_x - pt0.getX();
            ptIntersect.setLocation(edge_x, pt0.getY() + m * deltaX);
        }
        //horizontal edge
        else
        {
            ptIntersect=new Point2D.Double();
            edge_y = edgePt1.getY();
            //if (pt1.getX() == pt0.getX())
            if (Math.abs(pt1.getX() - pt0.getX())<1)
                pt1.setLocation(pt1.getX()+1, pt1.getY());

            m = (pt1.getY() - pt0.getY()) / (pt1.getX() - pt0.getX());
            deltaY = edge_y - pt0.getY();
            ptIntersect.setLocation(pt0.getX() + deltaY / m, edge_y);
        }
    } catch (Exception exc) {
        ErrorLogger.LogException(_className, "intersectPoint",
                new RendererException("Failed inside intersectPoint", exc));
    }
    return ptIntersect;
}
 
源代码17 项目: consulo   文件: GeometryUtil.java
public static String toString(Line2D aLine) {
  return aLine.getP1() + ":" + aLine.getP2();
}