Java tutorial
/*---------------------------------------------------------------------------- WhiteBoardCorrection This code is part of the following publication and was subject to peer review: "WhiteBoardCorrection" by Nekomeshi Copyright (c) Nekomeshi <Nekomeshi312@gmail.com> This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. ----------------------------------------------------------------------------*/ package com.nekomeshi312.whiteboardcorrection; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import org.opencv.core.Core; import org.opencv.core.CvType; import org.opencv.core.Mat; import org.opencv.core.Point; import org.opencv.core.Scalar; import android.util.Log; public class WhiteBoardDetect { private static final String LOG_TAG = "WhiteBoardDetect"; static final int ANGLE_DIV_NUM = 30; static final double DIV_ANGLE = Math.PI / (double) ANGLE_DIV_NUM; private int mViewWidth; private int mViewHeight; private int mCenterX; private int mCenterY; ArrayList<LineInfo> mLineInfo = new ArrayList<LineInfo>(); public WhiteBoardDetect(int width, int height) { mViewWidth = width; mViewHeight = height; mCenterX = (width >> 1); mCenterY = (height >> 1); } /** * ?????????????????LineInfo?mLineInfo.mLocationFlg???? * @param lines???? * @return true:?false: */ private boolean divByAngle(int lines[], int lineNum, Mat img) { double angleHist[] = new double[ANGLE_DIV_NUM]; for (int i = 0; i < ANGLE_DIV_NUM; i++) angleHist[i] = 0.0; for (int i = 0; i < lineNum; i++) { int i5 = i * 5; LineInfo li = new LineInfo(); li.setLineInfo((double) lines[i5 + 0], (double) lines[i5 + 1], (double) lines[i5 + 2], (double) lines[i5 + 3], (double) lines[i5 + 4]); if (!li.mIsOK) { continue;//? } final int anglePos = (int) (li.mAngle / DIV_ANGLE); angleHist[anglePos] += li.mLength; mLineInfo.add(li); } //???????????0PI //??????http://imagingsolution.blog107.fc2.com/blog-entry-113.html if (MyDebug.DEBUG) { for (int i = 0; i < ANGLE_DIV_NUM; i++) { Log.d(LOG_TAG, "hist(" + i + ") = " + angleHist[i]); } } int thres1 = 0; int thres2 = 0; double integral[] = new double[ANGLE_DIV_NUM]; double average[] = new double[ANGLE_DIV_NUM]; double maxSeparation = -1.0; for (int t1 = 0; t1 < ANGLE_DIV_NUM; t1++) { integral[0] = angleHist[t1]; average[0] = 0.0; for (int i = 1; i < ANGLE_DIV_NUM; i++) { integral[i] = integral[i - 1] + angleHist[(i + t1) % ANGLE_DIV_NUM]; average[i] = average[i - 1] + angleHist[(i + t1) % ANGLE_DIV_NUM] * (double) i; } final double allNum = integral[ANGLE_DIV_NUM - 1]; final double allAvg = average[ANGLE_DIV_NUM - 1]; final double avg = allAvg / allNum;//?? double variance = 0.0;//???? int maxT2 = 0; double maxVal = -1; double omega1 = 0.0;//? double omega2 = 0.0;//2? for (int t2 = 0; t2 < ANGLE_DIV_NUM; t2++) { final double tmp = t2 - avg; variance += tmp * tmp * angleHist[(t2 + t1) % ANGLE_DIV_NUM]; final double n1 = integral[t2]; // final double n2 = allNum - n1; // final double m1 = n1 == 0 ? 0.0 : average[t2] / n1; final double m2 = n2 == 0 ? 0.0 : (allAvg - average[t2]) / n2; final double subm1m2 = m1 - m2; final double val = n1 * n2 * subm1m2 * subm1m2; if (val > maxVal) { maxVal = val; maxT2 = (t2 + t1) % ANGLE_DIV_NUM; omega1 = n1; omega2 = n2; } } variance /= allNum;//? final double sumOmega = omega1 + omega2; //?/(-) //????maxVal/(omega1 + omega2)^2 double separation = maxVal / (sumOmega * sumOmega); separation = separation / (variance - separation); if (separation > maxSeparation) { if (t1 < maxT2) { thres1 = t1; thres2 = maxT2; } else { thres1 = maxT2 + 1; thres2 = t1 - 1; } maxSeparation = separation; } } if (MyDebug.DEBUG) { Log.d(LOG_TAG, "thres = " + thres1 + "/" + thres2); } int angle0Count = 0; int angle1Count = 0; for (LineInfo li : mLineInfo) { final int pos = (int) (li.mAngle / DIV_ANGLE); if (pos > thres1 && pos <= thres2) { li.mLocationFlg = LineInfo.ANGLE0; angle0Count++; } else { li.mLocationFlg = LineInfo.ANGLE1; angle1Count++; } //??????(debug) // Scalar color[] = new Scalar[2]; // color[0] = new Scalar(0xff, 0x00, 0x80); // color[1] = new Scalar(0x00, 0xff, 0x80); // if(img != null){ // if(li.mLocationFlg == LineInfo.ANGLE0){ // Core.line(img, li.mStart, li.mEnd, color[0], 5); // } // else{ // Core.line(img, li.mStart, li.mEnd, color[1], 5); // } // } } if (angle0Count == 0 || angle1Count == 0) return false; return true; } /** * ?????????????????? * @param img????null?????? * @return true:?false: */ private boolean divByIntersept(Mat img) { int[] sectionNumX = { 0, 0 }; int[] sectionNumY = { 0, 0 }; for (LineInfo li : mLineInfo) { final int angleNo = li.mLocationFlg == LineInfo.ANGLE0 ? 0 : 1; if (Math.abs(li.mLineEq.a) > Math.abs(li.mLineEq.b)) { sectionNumX[angleNo]++; } else { sectionNumY[angleNo]++; } } for (LineInfo li : mLineInfo) { final int angleNo = li.mLocationFlg == LineInfo.ANGLE0 ? 0 : 1; if (sectionNumX[angleNo] > sectionNumY[angleNo]) {//x??? if (li.mLineEq.a > 0.0) { li.mLocationFlg |= LineInfo.LOCAT0; } else { li.mLocationFlg |= LineInfo.LOCAT1; } } else { if (li.mLineEq.b > 0.0) { li.mLocationFlg |= LineInfo.LOCAT0; } else { li.mLocationFlg |= LineInfo.LOCAT1; } } if (img != null) { Scalar color[] = new Scalar[4]; color[0] = new Scalar(0xff, 0x00, 0x00); color[1] = new Scalar(0x00, 0xff, 0x00); color[2] = new Scalar(0x00, 0x00, 0xff); color[3] = new Scalar(0xff, 0x00, 0xff); for (LineInfo linfo : mLineInfo) { int col = 0; if (linfo.mLocationFlg == (LineInfo.LOCAT0 | LineInfo.ANGLE0)) col = 0; if (linfo.mLocationFlg == (LineInfo.LOCAT0 | LineInfo.ANGLE1)) col = 1; if (linfo.mLocationFlg == (LineInfo.LOCAT1 | LineInfo.ANGLE0)) col = 2; if (linfo.mLocationFlg == (LineInfo.LOCAT1 | LineInfo.ANGLE1)) col = 3; Core.line(img, linfo.mStart, linfo.mEnd, color[col], 5); } } } return true; } /** * ???? 2x2=4???????? * @param lineEq ?????(ax+by=1) ??[angle][section] * @param img????null?????? * @return true:?false: */ private boolean selectLines(StraightLineEquation lineEq[][], Mat img) { // //????array ArrayList<LineInfo>[][] classifiedLines = new ArrayList[2][2]; LineInfo[][] designateLine = new LineInfo[2][2]; for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { classifiedLines[i][j] = new ArrayList<LineInfo>(); designateLine[i][j] = null; } } for (LineInfo li : mLineInfo) { final int agl = (li.mLocationFlg & LineInfo.ANGLE0) == LineInfo.ANGLE0 ? 0 : 1; final int sec = (li.mLocationFlg & LineInfo.LOCAT0) == LineInfo.LOCAT0 ? 0 : 1; classifiedLines[agl][sec].add(li); } final float centerX = (float) (mViewWidth >> 1); final float centerY = (float) (mViewHeight >> 1); //4????(????? if (classifiedLines[0][0].size() == 0 || classifiedLines[0][1].size() == 0 || classifiedLines[1][0].size() == 0 || classifiedLines[1][1].size() == 0) { return false; } for (int ang = 0; ang < 2; ang++) {//????? for (int sec = 0; sec < 2; sec++) {//??? //?? ax+by=1? a?b??????????a, b????? final int HIST_DIV_NUM = 50; final int OUTLIER_LOOPNUM = 2; //a, b??2????? final double SIGMA_MUL = 1.0; double aveA = 0.0; double aveB = 0.0; double stdevA = 0.0; double stdevB = 0.0; double aMax = Double.MIN_VALUE; double aMin = Double.MAX_VALUE; double bMax = Double.MIN_VALUE; double bMin = Double.MAX_VALUE; for (int i = 0; i < OUTLIER_LOOPNUM; i++) { if (classifiedLines[ang][sec].size() == 0) { return false; } aveA = 0.0; aveB = 0.0; stdevA = 0.0; stdevB = 0.0; double aveL = 0.0; for (LineInfo li : classifiedLines[ang][sec]) { aveA += li.mLineEq.a * li.mLength * li.mWidth; aveB += li.mLineEq.b * li.mLength * li.mWidth; aveL += li.mLength * li.mWidth; } aveA /= aveL; aveB /= aveL; for (LineInfo li : classifiedLines[ang][sec]) { final double aa = li.mLineEq.a - aveA; final double bb = li.mLineEq.b - aveB; stdevA += aa * aa; stdevB += bb * bb; } stdevA = Math.sqrt(stdevA / classifiedLines[ang][sec].size()) * SIGMA_MUL; stdevB = Math.sqrt(stdevB / classifiedLines[ang][sec].size()) * SIGMA_MUL; aMax = aveA + stdevA; aMin = aveA - stdevA; bMax = aveB + stdevB; bMin = aveB - stdevB; if (i < OUTLIER_LOOPNUM - 1) { ArrayList<LineInfo> tmp = new ArrayList<LineInfo>(); for (LineInfo li : classifiedLines[ang][sec]) { //???? a, b??????????? if (li.mLineEq.a > aMax || li.mLineEq.a < aMin || li.mLineEq.b > bMax || li.mLineEq.b < bMin) continue; tmp.add(li); } if (tmp.size() > 0) { classifiedLines[ang][sec] = tmp; } else { for (LineInfo li : classifiedLines[ang][sec]) { //???????????a,b????? if ((li.mLineEq.a > aMax || li.mLineEq.a < aMin) && (li.mLineEq.b > bMax || li.mLineEq.b < bMin)) continue; tmp.add(li); } classifiedLines[ang][sec] = tmp; } } } //max/min???? aMax = Double.MIN_VALUE; aMin = Double.MAX_VALUE; bMax = Double.MIN_VALUE; bMin = Double.MAX_VALUE; for (LineInfo li : classifiedLines[ang][sec]) { if (li.mLineEq.a > aMax) aMax = li.mLineEq.a; if (li.mLineEq.a < aMin) aMin = li.mLineEq.a; if (li.mLineEq.b > bMax) bMax = li.mLineEq.b; if (li.mLineEq.b < bMin) bMin = li.mLineEq.b; } final double aDiv = (aMax - aMin) / (double) HIST_DIV_NUM; final double bDiv = (bMax - bMin) / (double) HIST_DIV_NUM; LineList hist[][] = new LineList[HIST_DIV_NUM][HIST_DIV_NUM]; for (int i = 0; i < HIST_DIV_NUM; i++) { for (int j = 0; j < HIST_DIV_NUM; j++) { hist[i][j] = new LineList(); } } int linenum = 0; for (LineInfo li : classifiedLines[ang][sec]) { int aPos = (int) ((li.mLineEq.a - aMin) / aDiv); if (aPos == HIST_DIV_NUM) aPos--; int bPos = (int) ((li.mLineEq.b - bMin) / bDiv); if (bPos == HIST_DIV_NUM) bPos--; hist[aPos][bPos].pushLine(li); linenum++; } if (linenum == 0) { return false; } int maxAPos = 0; int maxBPos = 0; double maxLen = 0.0; for (int a = 0; a < HIST_DIV_NUM; a++) { for (int b = 0; b < HIST_DIV_NUM; b++) { if (hist[a][b].getLineListNum() == 0) { continue; } double len = 0.0; for (LineInfo li : hist[a][b].mLineList) { len += li.mLength; } if (maxLen < len) { maxAPos = a; maxBPos = b; maxLen = len; } } } if (linenum == 1) { lineEq[ang][sec].a = hist[maxAPos][maxBPos].mLineList.get(0).mLineEq.a; lineEq[ang][sec].b = hist[maxAPos][maxBPos].mLineList.get(0).mLineEq.b; } else { lineEq[ang][sec].a = ((double) maxAPos + 0.5) * aDiv + aMin; lineEq[ang][sec].b = ((double) maxBPos + 0.5) * bDiv + bMin; } if (img != null) { final double aa = lineEq[ang][sec].a; final double bb = lineEq[ang][sec].b; Point pt1 = new Point(); Point pt2 = new Point(); if (Math.abs(bb) > Math.abs(aa)) { pt1.x = 0.0; pt1.y = (1.0 - aa * (float) (-centerX)) / bb + (float) centerY; pt2.x = (float) mViewWidth; pt2.y = (1.0 - aa * (float) (centerX)) / bb + (float) centerY; } else { pt1.x = (1.0 - bb * (float) (-centerY)) / aa + (float) centerX; pt1.y = 0.0; pt2.x = (1.0 - bb * (float) (centerY)) / aa + (float) centerX; pt2.y = (float) mViewHeight; } if (MyDebug.DEBUG) { if (Math.abs(bb) > 0.001 && Math.abs(aa / bb) > 0.3 && Math.abs(aa / bb) < 2) { Log.d(LOG_TAG, "ang = " + ang + " sec = " + sec + " max a/b = " + maxAPos + ":" + maxBPos); //Core.line(img, pt1, pt2, new Scalar(0xff, 0x00, 0x00), 5); } else { //Core.line(img, pt1, pt2, new Scalar(0xff, 0xff, 0xff), 5); } } //??????(debug) Scalar color[] = new Scalar[4]; color[0] = new Scalar(0xff, 0x00, 0x00); color[1] = new Scalar(0x00, 0xff, 0x00); color[2] = new Scalar(0x00, 0x00, 0xff); color[3] = new Scalar(0xff, 0x00, 0xff); for (LineInfo li : mLineInfo) { int c = 0; if (li.mLocationFlg == (LineInfo.ANGLE0 | LineInfo.LOCAT0)) { c = 0; } else if (li.mLocationFlg == (LineInfo.ANGLE0 | LineInfo.LOCAT1)) { c = 1; } if (li.mLocationFlg == (LineInfo.ANGLE1 | LineInfo.LOCAT0)) { c = 2; } else if (li.mLocationFlg == (LineInfo.ANGLE1 | LineInfo.LOCAT1)) { c = 3; } Core.line(img, li.mStart, li.mEnd, color[c], 1); Core.circle(img, li.mStart, 10, color[0]); Core.circle(img, li.mEnd, 10, color[1]); } } } } return true; } private float mPointCenterX = 0.0f; private float mPointCenterY = 0.0f; private class PointComparator implements Comparator<Object> { @Override public int compare(Object lhs, Object rhs) { // TODO Auto-generated method stub Point p1 = (Point) lhs; Point p2 = (Point) rhs; final double angle1 = Math.atan2(p1.y - mPointCenterY, p1.x - mPointCenterX); final double angle2 = Math.atan2(p2.y - mPointCenterY, p2.x - mPointCenterX); if (angle1 > angle2) { return 1; } else if (angle1 < angle2) { return -1; } else { return 0; } } } /** * ???????? * @param lineEq ?????(ax+by=1) ??[angle][section] * @param points ?ArrayList * @param img ????null?????? * @return true:? false: */ private boolean calcSquare(StraightLineEquation lineEq[][], ArrayList<Point> points, Mat img) { //2?? Mat mat = new Mat(2, 2, CvType.CV_32F); mPointCenterX = 0.0f; mPointCenterY = 0.0f; int counter = 0; for (int ang0sec = 0; ang0sec < 2; ang0sec++) { mat.put(0, 0, lineEq[0][ang0sec].a); mat.put(0, 1, lineEq[0][ang0sec].b); for (int ang1sec = 0; ang1sec < 2; ang1sec++) { mat.put(1, 0, lineEq[1][ang1sec].a); mat.put(1, 1, lineEq[1][ang1sec].b); Mat matAns; try { matAns = mat.inv(); if (matAns == null) return false; } catch (Exception e) {//?????????? e.printStackTrace(); return false; } float x = (float) (matAns.get(0, 0)[0] + matAns.get(0, 1)[0] + mCenterX); float y = (float) (matAns.get(1, 0)[0] + matAns.get(1, 1)[0] + mCenterY); Point p = new Point(x, y); points.add(p); mPointCenterX += x; mPointCenterY += y; counter++; } } mPointCenterX /= (float) counter; mPointCenterY /= (float) counter; //???? Collections.sort(points, new PointComparator()); if (img != null) { Scalar color[] = new Scalar[4]; color[0] = new Scalar(0xff, 0x00, 0x00); color[1] = new Scalar(0x00, 0xff, 0x00); color[2] = new Scalar(0x00, 0x00, 0xff); color[3] = new Scalar(0xff, 0x00, 0xff); for (int i = 0; i < 4; i++) { Core.circle(img, points.get(i), 30, color[i], 5); } } if (MyDebug.DEBUG) { for (int i = 0; i < 4; i++) { Log.d(LOG_TAG, "point(" + i + ") = " + points.get(i).x + ":" + points.get(i).y); } } return true; } /** * ????? * @param lines?StartX, StartY, EndX, EndY, Width ????? * @param lineNum? * @param points???4??? * @param img??null?????????????null?????????? * @return true:?false: */ public boolean detectWhiteBoard(int lines[], int lineNum, ArrayList<Point> points, Mat img) { if (lineNum == 0) return false; mLineInfo.clear(); long oldSec; long newSec; if (MyDebug.DEBUG) { oldSec = System.currentTimeMillis(); } //??????? if (!divByAngle(lines, lineNum, img)) return false; if (MyDebug.DEBUG) { newSec = System.currentTimeMillis(); Log.d(LOG_TAG, "divByAngle" + (newSec - oldSec) + "mS"); oldSec = newSec; } //????????????(2x2=4) if (!divByIntersept(img)) return false; if (MyDebug.DEBUG) { newSec = System.currentTimeMillis(); Log.d(LOG_TAG, "divByIntersept" + (newSec - oldSec) + "mS"); oldSec = newSec; } //???? StraightLineEquation[][] lineEq = new StraightLineEquation[2][2]; for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { lineEq[i][j] = new StraightLineEquation(); } } if (!selectLines(lineEq, img)) return false; if (MyDebug.DEBUG) { newSec = System.currentTimeMillis(); Log.d(LOG_TAG, "selectLines" + (newSec - oldSec) + "mS"); oldSec = newSec; } //??(?). if (!calcSquare(lineEq, points, img)) return false; if (MyDebug.DEBUG) { newSec = System.currentTimeMillis(); Log.d(LOG_TAG, "calcSquare " + (newSec - oldSec) + "mS"); oldSec = newSec; } return true; } /** * //ax+by=1? * @author masaki * */ private class StraightLineEquation { private double a = 0.0; private double b = 0.0; } public ArrayList<Point> getUnAcceptableLineArea() { ArrayList<Point> points = new ArrayList<Point>(); int qx = mViewWidth >> 2; int hx = mViewWidth >> 1; int q3x = 3 * qx; int qy = mViewHeight >> 2; int hy = mViewHeight >> 1; int q3y = 3 * qy; Point p = new Point(qx, hy); points.add(p); p = new Point(hx, qy); points.add(p); p = new Point(q3x, hy); points.add(p); p = new Point(hx, q3y); points.add(p); return points; } private class LineInfo implements Cloneable { private static final int ANGLE0 = 0x01; private static final int ANGLE1 = 0x02; private static final int LOCAT0 = 0x04; private static final int LOCAT1 = 0x08; private Point mStart = new Point(); private Point mEnd = new Point(); private double mWidth = 0.0;// private StraightLineEquation mLineEq = new StraightLineEquation(); private boolean mIsOK = false; private double mLength = 0.0; //?? private double mAngle = 0.0; //??? 0PI private int mLocationFlg; //???2x2?????? private boolean mIsSteep; //45???true /** * ?????NG???? * @return */ private boolean checkLineArea() { double xp = mLineEq.a == 0.0 ? Double.MAX_VALUE : 1.0 / mLineEq.a; double yp = mLineEq.b == 0.0 ? Double.MAX_VALUE : 1.0 / mLineEq.b; xp = Math.abs(xp); yp = Math.abs(yp); if (xp > mViewWidth / 4.0 && yp > mViewHeight / 4.0) { return true; } else { return false; } } private void setLineInfo(double stX, double stY, double edX, double edY, double width) { mIsOK = false; if (stX < 0.0 || stX >= mViewWidth || stY < 0.0 || stY >= mViewHeight) return; mWidth = width; mStart.x = stX; mStart.y = stY; mEnd.x = edX; mEnd.y = edY; final double vecX = mStart.x - mEnd.x; final double vecY = mStart.y - mEnd.y; final double absVecX = Math.abs(vecX); final double absVecY = Math.abs(vecY); if (absVecX < 1.0 && absVecY < 1.0) { return; } final float centerX = (float) mCenterX; final float centerY = (float) mCenterY; mLength = Math.sqrt(vecX * vecX + vecY * vecY); mAngle = Math.atan2(vecY, vecX); while (mAngle >= Math.PI) mAngle -= Math.PI; while (mAngle < 0.0) mAngle += Math.PI; //???45???? mIsSteep = absVecX < absVecY; //??? Mat mat = new Mat(2, 2, CvType.CV_32F); mat.put(0, 0, mStart.x - centerX); mat.put(0, 1, mStart.y - centerY); mat.put(1, 0, mEnd.x - centerX); mat.put(1, 1, mEnd.y - centerY); try { mat = mat.inv(); if (mat == null) return; } catch (Exception e) {//????? e.printStackTrace(); return; } mLineEq.a = (float) (mat.get(0, 0)[0] + mat.get(0, 1)[0]); mLineEq.b = (float) (mat.get(1, 0)[0] + mat.get(1, 1)[0]); //?????NG???? if (!checkLineArea()) return; mIsOK = true; } /* (non-Javadoc) * @see java.lang.Object#clone() */ @Override protected Object clone() { // TODO Auto-generated method stub LineInfo li; try { li = (LineInfo) super.clone(); } catch (CloneNotSupportedException e) { // TODO Auto-generated catch block e.printStackTrace(); return null; } li.mLineEq.a = mLineEq.a; li.mLineEq.b = mLineEq.b; li.mStart.x = mStart.x; li.mStart.y = mStart.y; li.mEnd.x = mEnd.x; li.mEnd.y = mEnd.y; return li; } } private class LineList { private ArrayList<LineInfo> mLineList = null; private int getLineListNum() { if (mLineList == null) return 0; return mLineList.size(); } private boolean mSortByX = true; /** * mSortByX==true???x?????????????y?????mStart < mEnd????? * @param li */ private void correctDirection(LineInfo li) { boolean reverse = false; if (mSortByX) { if (li.mStart.x > li.mEnd.x) { reverse = true; } } else { if (li.mStart.y > li.mEnd.y) { reverse = true; } } if (reverse) { Point tmp = li.mStart; li.mStart = li.mEnd; li.mEnd = tmp; } } /** * Point1?Point2?????mSortByX?? * @param p1 * @param p2 * @return 1:p1>p2 -1:p1<p2 0:p1==p2 */ private int compare(Point p1, Point p2) { double v1, v2; if (mSortByX) { v1 = p1.x; v2 = p2.x; } else { v1 = p1.y; v2 = p2.y; } if (v1 > v2) { return 1; } else if (v1 < v2) { return -1; } else { return 0; } } /** * ?????????????????????????????? * @param lineInfo */ private void pushLine(LineInfo lineInfo) { LineInfo li = (LineInfo) lineInfo.clone(); if (mLineList == null) { mLineList = new ArrayList<LineInfo>(); if (!li.mIsSteep) { mSortByX = true; } else { mSortByX = false; } correctDirection(li); mLineList.add(li); return; } correctDirection(li); //stIncluded == true?????????????stLine????? //stLine == mLineList.size()???????????????? //stLine < mLineList.size()????????? stLine-1?stLine??? int stLine; boolean stIncluded = false; final int lineListSize = mLineList.size(); for (stLine = 0; stLine < lineListSize; stLine++) { final int compST = compare(li.mStart, mLineList.get(stLine).mStart); if (compST < 0) break;//???????? final int compED = compare(li.mStart, mLineList.get(stLine).mEnd); if (compED <= 0) {//li???????? stIncluded = true; break; } } if (stLine == lineListSize) {//????????? mLineList.add(li); return; } //edInclude == true????????????edLine????? //edLine < 0????????????????? //edLine >= 0 ?????????? edLine?edLIne+1???? int edLine; boolean edIncluded = false; for (edLine = lineListSize - 1; edLine >= 0; edLine--) { final int compED = compare(li.mEnd, mLineList.get(edLine).mEnd); if (compED > 0) break;//???????? final int compST = compare(li.mEnd, mLineList.get(edLine).mStart); if (compST >= 0) {//li???????? edIncluded = true; break; } } if (edLine < 0) {//?????????? mLineList.add(0, li); return; } if (stIncluded && edIncluded) {//li?????? if (stLine == edLine) { //????????????????????? } else { //stLine?edLine????? mLineList.get(stLine).setLineInfo(mLineList.get(stLine).mStart.x, mLineList.get(stLine).mStart.y, mLineList.get(edLine).mEnd.x, mLineList.get(edLine).mEnd.y, mLineList.get(edLine).mWidth);//width????? for (int i = stLine + 1; i <= edLine; i++) { mLineList.remove(stLine + 1); } } return; } else if (stIncluded) {//li??????? //stLine??li.mEnd???? mLineList.get(stLine).setLineInfo(mLineList.get(stLine).mStart.x, mLineList.get(stLine).mStart.y, li.mEnd.x, li.mEnd.y, mLineList.get(stLine).mWidth);//width????? for (int i = stLine + 1; i <= edLine; i++) { mLineList.remove(stLine + 1); } return; } else if (edIncluded) {//li??????? //edLine??li.mStart???? mLineList.get(edLine).setLineInfo(li.mStart.x, li.mStart.y, mLineList.get(edLine).mEnd.x, mLineList.get(edLine).mEnd.y, mLineList.get(edLine).mWidth);//width????? for (int i = stLine; i < edLine; i++) { mLineList.remove(stLine); } } else {//??????? for (int i = stLine; i <= edLine; i++) { mLineList.remove(stLine); } mLineList.add(stLine, li); } } } }