下面列出了java.awt.geom.Line2D#getP2 ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
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);
}
}
/**
* 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);
}
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;
}
/**
* 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));
}
/**
* 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;
}
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;
}
/**
* (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;
}
}
/**
* 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;
}
/**
* 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;
}
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;
}
/**
* 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));
}
}
}
/**
* 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;
}
/**
* 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;
}
}
/**
* 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;
}
Line2DFacade (Line2D line)
{
Objects.requireNonNull(line, "Cannot create Line2DFacade with a null line");
p1 = new Point2DFacade(line.getP1());
p2 = new Point2DFacade(line.getP2());
}
/**
* 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;
}
public static String toString(Line2D aLine) {
return aLine.getP1() + ":" + aLine.getP2();
}