Exemple #1
0
  private void lookAtBoard(float fovy, float aspect, float boardWidth, float boardLength) {
    float tgA2 = (float) Math.tan(fovy / 2 * (Math.PI / 180));
    float sinA = (float) Math.sin(fovy * (Math.PI / 180));

    float K = boardWidth / (2 * tgA2 * aspect);
    float R = boardLength / (K * sinA);

    float eyeZ;
    {
      float a = R * R;
      float b = 2 * boardLength;
      float c = K * K + boardLength * boardLength - R * R * K * K;
      float d = b * b - 4 * a * c;
      d = FloatMath.sqrt(d);
      eyeZ = (-b + d) / (2 * a);
    }

    float eyeY = FloatMath.sqrt(K * K - eyeZ * eyeZ);

    float tgO = eyeZ / eyeY;
    float centerZ = eyeY * (tgO + tgA2) / (1 - tgO * tgA2) - eyeZ;

    ReGLU.gluLookAt(m_modelviewMatrix, 0, eyeY, -eyeZ, 0, 0, centerZ, 0, 1, 0);
    m_lookEye[1] = eyeY;
    m_lookEye[2] = -eyeZ;
    m_lookCenter[2] = centerZ;
  }
  @Override
  public boolean onDown(MotionEvent event) {
    scrolling = false;

    synchronized (GlobalData.audioScene) {
      // determine transformed coordinate of touch point
      touchPoint[0] = event.getX();
      touchPoint[1] = event.getY();
      inverseViewportTransformation.mapPoints(touchPoint);
      GlobalData.audioScene.inverseMapPoint(touchPoint);

      // try to find nearest sound source
      lastTouchSoundSource = GlobalData.audioScene.getNearestSoundSource(touchPoint);
      if (lastTouchSoundSource != null) {
        // get distance (touch point to source) in pixels
        selectionOffset[0] = lastTouchSoundSource.getX();
        selectionOffset[1] = lastTouchSoundSource.getY();
        GlobalData.audioScene.mapPoint(selectionOffset);
        viewportTransformation.mapPoints(selectionOffset);
        selectionOffset[0] -= event.getX();
        selectionOffset[1] -= event.getY();
        float distance =
            FloatMath.sqrt(
                selectionOffset[0] * selectionOffset[0] + selectionOffset[1] * selectionOffset[1]);

        // select source?
        if (distance > SOURCE_SELECT_RADIUS) {
          lastTouchSoundSource = null;
        }
      }
    }

    return true;
  }
  public void onSensorChanged(SensorEvent event) {
    long currentTime = System.currentTimeMillis();
    long diffTime = currentTime - mLastUpdateTime;
    if (diffTime > UPDATE_INTERVAL) {
      if (mLastUpdateTime != 0) {
        float x = event.values[0];
        float y = event.values[1];
        float z = event.values[2];
        float deltaX = x - mLastX;
        float deltaY = y - mLastY;
        float deltaZ = z - mLastZ;
        float delta =
            FloatMath.sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ) / diffTime * 10000;
        if (delta > SHAKE_THRESHOLD) {
          if (!shaken) {
            shaken = true;
            finish();
          }

          if (listener != null) {
            listener.onShake();
          }
        }
        mLastX = x;
        mLastY = y;
        mLastZ = z;
      }
      mLastUpdateTime = currentTime;
    }
  }
  // segment coordinations of this ellipse
  private float[] segments() {
    float a = M_PI / 2;
    int segs = (int) ((rx + ry) / 60 * 20);
    if (segs < 20) segs = 20;
    float coef = 2.0f * M_PI / segs;

    float vertices[] = new float[2 * (segs + 1 + (super.isSolid() ? 1 : 0))];

    float rads, distance, angle, j, k;
    for (int i = 0; i <= segs; ++i) {
      rads = i * coef;
      float xd = FloatMath.sin(rads) * rx;
      float yd = FloatMath.cos(rads) * ry;
      distance = FloatMath.sqrt(xd * xd + yd * yd);
      angle = (float) Math.atan2(FloatMath.sin(rads) * rx, FloatMath.cos(rads) * ry);
      j = distance * FloatMath.cos(angle + a) + center.x;
      k = distance * FloatMath.sin(angle + a) + center.y;

      vertices[i * 2] = j;
      vertices[i * 2 + 1] = k;
    }
    if (super.isSolid()) {
      vertices[(segs + 1) * 2] = center.x;
      vertices[(segs + 1) * 2 + 1] = center.y;
    }
    return vertices;
  }
  // with_arrow: put a arrow-tick before the first point
  void makeStraight(boolean with_arrow) {
    // Log.v( TopoDroidApp.TAG, "make straight with arrow " + with_arrow + " size " + mPoints.size()
    // );
    // if ( mPoints.size() < 2 ) return;
    // LinePoint first = mPoints.get( 0 );
    // LinePoint last  = mPoints.get( mPoints.size() - 1 );
    if (mSize < 2) return;
    LinePoint first = mFirst;
    LinePoint last = mLast;

    clear();
    if (with_arrow) {
      float dy = first.mX - last.mX;
      float dx = -first.mY + last.mY;
      float d = dx * dx + dy * dy;
      if (d > 0.00001f) {
        d = TDSetting.mArrowLength * TDSetting.mUnit / FloatMath.sqrt(d);
        dx *= d;
        dy *= d;
        addStartPoint(first.mX + dx, first.mY + dy);
        addPoint(first.mX, first.mY);
      } else {
        addStartPoint(first.mX, first.mY);
      }
    } else {
      addStartPoint(first.mX, first.mY);
    }
    addPoint(last.mX, last.mY);
    if (with_arrow) {
      mDx = mDy = 0;
    } else {
      computeUnitNormal();
    }
    // Log.v( TopoDroidApp.TAG, "make straight final size " + mPoints.size() );
  }
 void makeReduce() {
   if (mSize > 2) {
     int size = 1;
     LinePoint prev = mFirst;
     LinePoint pt = mFirst.mNext;
     while (pt != mLast) {
       LinePoint next = pt.mNext; // pt.mNext != null because pt < mLast
       float x1 = pt.mX - prev.mX;
       float y1 = pt.mY - prev.mY;
       float x2 = next.mX - pt.mX;
       float y2 = next.mY - pt.mY;
       float cos = (x1 * x2 + y1 * y2) / FloatMath.sqrt((x1 * x1 + y1 * y1) * (x2 * x2 + y2 * y2));
       if (cos >= 0.7) {
         prev.mNext = next;
         next.mPrev = prev;
       } else {
         ++size;
         prev = pt;
       }
       pt = next;
     }
     ++size; // for the mLast point
     mSize = size;
   }
   retracePath();
 }
  /**
   * This function is borrowed from the Android reference at
   * http://developer.android.com/reference/android/hardware/SensorEvent.html#values It calculates a
   * rotation vector from the gyroscope angular speed values.
   */
  private void getRotationVectorFromGyro(
      float[] gyroValues, float[] deltaRotationVector, float timeFactor) {
    float[] normValues = new float[3];

    // Calculate the angular speed of the sample
    float omegaMagnitude =
        FloatMath.sqrt(
            gyroValues[0] * gyroValues[0]
                + gyroValues[1] * gyroValues[1]
                + gyroValues[2] * gyroValues[2]);

    // Normalize the rotation vector if it's big enough to get the axis
    if (omegaMagnitude > EPSILON) {
      normValues[0] = gyroValues[0] / omegaMagnitude;
      normValues[1] = gyroValues[1] / omegaMagnitude;
      normValues[2] = gyroValues[2] / omegaMagnitude;
    }

    // Integrate around this axis with the angular speed by the timestep
    // in order to get a delta rotation from this sample over the timestep
    // We will convert this axis-angle representation of the delta rotation
    // into a quaternion before turning it into the rotation matrix.
    float thetaOverTwo = omegaMagnitude * timeFactor;
    float sinThetaOverTwo = (float) FloatMath.sin(thetaOverTwo);
    float cosThetaOverTwo = (float) FloatMath.cos(thetaOverTwo);
    deltaRotationVector[0] = sinThetaOverTwo * normValues[0];
    deltaRotationVector[1] = sinThetaOverTwo * normValues[1];
    deltaRotationVector[2] = sinThetaOverTwo * normValues[2];
    deltaRotationVector[3] = cosThetaOverTwo;
  }
  private float spacing(MotionEvent event) {
    if (event == null || (event.getPointerCount() < 2)) return 0.0f;

    float x = event.getX(0) - event.getX(1);
    float y = event.getY(0) - event.getY(1);
    return FloatMath.sqrt(x * x + y * y);
  }
Exemple #9
0
  /**
   * http://ogre.sourcearchive.com/documentation/1.4.5/classOgre_1_1Vector3_eeef4472ad0c4d5f34a038a9f2faa819.html#eeef4472ad0c4d5f34a038a9f2faa819
   *
   * @param direction
   * @return
   */
  public Quaternion getRotationTo(Number3D direction) {
    // Based on Stan Melax's article in Game Programming Gems
    Quaternion q = new Quaternion();
    // Copy, since cannot modify local
    Number3D v0 = this;
    Number3D v1 = direction;
    v0.normalize();
    v1.normalize();

    float d = Number3D.dot(v0, v1);
    // If dot == 1, vectors are the same
    if (d >= 1.0f) {
      q.setIdentity();
    }
    if (d < 0.000001f - 1.0f) {
      // Generate an axis
      Number3D axis = Number3D.cross(Number3D.getAxisVector(Axis.X), this);
      if (axis.length() == 0) // pick another if colinear
      axis = Number3D.cross(Number3D.getAxisVector(Axis.Y), this);
      axis.normalize();
      q.fromAngleAxis(MathUtil.radiansToDegrees(MathUtil.PI), axis);
    } else {
      float s = FloatMath.sqrt((1 + d) * 2);
      float invs = 1f / s;

      Number3D c = Number3D.cross(v0, v1);

      q.x = c.x * invs;
      q.y = c.y * invs;
      q.z = c.z * invs;
      q.w = s * 0.5f;
      q.normalize();
    }
    return q;
  }
 /**
  * Return the current distance between the two pointers forming the gesture in progress.
  *
  * @return Distance between pointers in pixels.
  */
 public float getCurrentSpan() {
   if (mCurrLen == -1) {
     final float cvx = mCurrFingerDiffX;
     final float cvy = mCurrFingerDiffY;
     mCurrLen = FloatMath.sqrt(cvx * cvx + cvy * cvy);
   }
   return mCurrLen;
 }
 /**
  * Return the previous distance between the two pointers forming the gesture in progress.
  *
  * @return Previous distance between pointers in pixels.
  */
 public float getPreviousSpan() {
   if (mPrevLen == -1) {
     final float pvx = mPrevFingerDiffX;
     final float pvy = mPrevFingerDiffY;
     mPrevLen = FloatMath.sqrt(pvx * pvx + pvy * pvy);
   }
   return mPrevLen;
 }
 private float spacing(MotionEvent event) {
   if (event.getPointerCount() >= 2) {
     float x = event.getX(0) - event.getX(1);
     float y = event.getY(0) - event.getY(1);
     return FloatMath.sqrt(x * x + y * y);
   }
   return this.oldDist;
 }
 /**
  * calculate the distance between two touch points
  *
  * <p>複数タッチしたポイントの距離
  *
  * <p>计算两点触控时两点之间的距离
  *
  * @param event
  * @return float
  *     <p>distance
  *     <p>距離
  *     <p>距离
  */
 protected float calcDistance(MotionEvent event) {
   if (event.getPointerCount() <= 1) {
     return 0f;
   } else {
     float x = event.getX(0) - event.getX(1);
     float y = event.getY(0) - event.getY(1);
     return FloatMath.sqrt(x * x + y * y);
   }
 }
 /**
  * 计算2点之间的距离
  *
  * @param event
  * @return
  */
 private float spacing(MotionEvent event) {
   try {
     float x = event.getX(0) - event.getX(1);
     float y = event.getY(0) - event.getY(1);
     return FloatMath.sqrt(x * x + y * y);
   } catch (Exception e) {
     e.printStackTrace();
     return 1.1f;
   }
 }
Exemple #15
0
  public float normalize() {
    float mod = FloatMath.sqrt(x * x + y * y + z * z);

    if (mod != 0 && mod != 1) {
      mod = 1 / mod;
      this.x *= mod;
      this.y *= mod;
      this.z *= mod;
    }

    return mod;
  }
  public static Bitmap decodeThumbnail(
      JobContext jc, FileDescriptor fd, Options options, int targetSize, int type) {
    if (options == null) options = new Options();
    jc.setCancelListener(new DecodeCanceller(options));

    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFileDescriptor(fd, null, options);
    if (jc.isCancelled()) return null;

    int w = options.outWidth;
    int h = options.outHeight;

    if (type == MediaItem.TYPE_MICROTHUMBNAIL) {
      // We center-crop the original image as it's micro thumbnail. In this case,
      // we want to make sure the shorter side >= "targetSize".
      float scale = (float) targetSize / Math.min(w, h);
      options.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale);

      // For an extremely wide image, e.g. 300x30000, we may got OOM when decoding
      // it for TYPE_MICROTHUMBNAIL. So we add a max number of pixels limit here.
      final int MAX_PIXEL_COUNT = 640000; // 400 x 1600
      if ((w / options.inSampleSize) * (h / options.inSampleSize) > MAX_PIXEL_COUNT) {
        options.inSampleSize =
            BitmapUtils.computeSampleSize(FloatMath.sqrt((float) MAX_PIXEL_COUNT / (w * h)));
      }
    } else {
      // For screen nail, we only want to keep the longer side >= targetSize.
      float scale = (float) targetSize / Math.max(w, h);
      options.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale);
    }

    options.inJustDecodeBounds = false;
    setOptionsMutable(options);

    Bitmap result = BitmapFactory.decodeFileDescriptor(fd, null, options);
    if (result == null) return null;

    // We need to resize down if the decoder does not support inSampleSize
    // (For example, GIF images)
    float scale =
        (float) targetSize
            / (type == MediaItem.TYPE_MICROTHUMBNAIL
                ? Math.min(result.getWidth(), result.getHeight())
                : Math.max(result.getWidth(), result.getHeight()));

    if (scale <= 0.5) result = BitmapUtils.resizeBitmapByScale(result, scale, true);
    return ensureGLCompatibleBitmap(result);
  }
  private static final float computeAlpha(
      float low, float high, float[] current, float[] previous) {
    if (previous.length != 3 || current.length != 3) return ALPHA_DEFAULT;

    float x1 = current[0], y1 = current[1], z1 = current[2];
    float x2 = previous[0], y2 = previous[1], z2 = previous[2];
    float distance =
        FloatMath.sqrt(
            (float)
                (Math.pow((double) (x2 - x1), 2d)
                    + Math.pow((double) (y2 - y1), 2d)
                    + Math.pow((double) (z2 - z1), 2d)));

    if (distance < low) {
      return ALPHA_STEADY;
    } else if (distance >= low || distance < high) {
      return ALPHA_START_MOVING;
    }
    return ALPHA_MOVING;
  }
Exemple #18
0
  boolean intersects(RectF tRect, float tRot, RectF sRect, float sRot) {
    if (Math.abs(tRot) < Math.PI / 15 && Math.abs(sRot) < Math.PI / 15) {
      return RectF.intersects(tRect, sRect);
    }
    float dist =
        FloatMath.sqrt(
            sqr(tRect.centerX() - sRect.centerX()) + sqr(tRect.centerY() - sRect.centerY()));
    if (dist < 3) {
      return true;
    }

    // difference close to 90/270 degrees
    if (Math.abs(Math.cos(tRot - sRot)) < 0.3) {
      // rotate one rectangle to 90 degrees
      tRot += Math.PI / 2;
      float l = tRect.centerX() - tRect.height() / 2;
      float t = tRect.centerY() - tRect.width() / 2;
      tRect = new RectF(l, t, l + tRect.height(), t + tRect.width());
    }

    // determine difference close to 180/0 degrees
    if (Math.abs(FloatMath.sin(tRot - sRot)) < 0.3) {
      // rotate t box
      // (calculate offset for t center suppose we rotate around s center)
      float diff =
          (float)
              (-Math.atan2(tRect.centerX() - sRect.centerX(), tRect.centerY() - sRect.centerY())
                  + Math.PI / 2);
      diff -= sRot;
      float left = sRect.centerX() + dist * FloatMath.cos(diff) - tRect.width() / 2;
      float top = sRect.centerY() - dist * FloatMath.sin(diff) - tRect.height() / 2;
      RectF nRect = new RectF(left, top, left + tRect.width(), top + tRect.height());
      return RectF.intersects(nRect, sRect);
    }

    // TODO other cases not covered
    return RectF.intersects(tRect, sRect);
  }
 private float spacing(MotionEvent event) {
   float x = event.getX(0) - event.getX(1);
   float y = event.getY(0) - event.getY(1);
   return FloatMath.sqrt(x * x + y * y);
 }
 /**
  * 两个点之间的距离
  *
  * @param x1
  * @param y1
  * @param x2
  * @param y2
  * @return
  */
 private float distance4PointF(PointF pf1, PointF pf2) {
   float disX = pf2.x - pf1.x;
   float disY = pf2.y - pf1.y;
   return FloatMath.sqrt(disX * disX + disY * disY);
 }
Exemple #21
0
 /**
  * This function will take 2 float points and return the distance between them.
  *
  * @param x1 The x component of point x.
  * @param y1 The y component of point x
  * @param x2 The x component of point y.
  * @param y2 The y component of point y.
  * @return The distance between two float points.
  */
 public static float distance(float x1, float y1, float x2, float y2) {
   // Not using a power here, since there is no FloatMath pow function, so this is easiest.
   return (FloatMath.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)));
 } /* CommonInstrumentationTest */
 public static float distance(PointF p1, PointF p2) {
   float x = p1.x - p2.x;
   float y = p1.y - p2.y;
   return FloatMath.sqrt(x * x + y * y);
 }
  public boolean onTouchEvent(MotionEvent event) {
    //		if(!isEditable){
    ////			switch (event.getAction() ) {
    ////			case MotionEvent.ACTION_DOWN:
    ////				setEditable(true);
    ////				return true;
    ////			}
    //			return super.onTouchEvent(event);
    //		}
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
        //			Log.e("zzd", "event.getX():" + event.getX() + ", event.getY():" + event.getY());
        mPreMovePointF.set(event.getX(), event.getY());
        mStatus = JudgeStatus(event.getX(), event.getY());

        if (mLTPoint != null && mLBPoint != null && mRTPoint != null && mRBPoint != null) {
          if (calculatePoint(
              mLTPoint, mLBPoint, mRTPoint, mRBPoint, new PointF(event.getX(), event.getY()))) {
            if (!isEditable) {
              setEditable(true);
            }
          } else {
            // 如果点击的时候是STATUS_ROTATE_ZOOM状态,就不设置isEditable为false
            if (mStatus != STATUS_ROTATE_ZOOM) {
              setEditable(false);
              super.onTouchEvent(event);
            }
          }

          //				Log.e("zzd", "isEditable:" + isEditable);
        }

        break;
      case MotionEvent.ACTION_UP:
        mStatus = STATUS_INIT;
        break;
      case MotionEvent.ACTION_MOVE:
        if (!isEditable) {
          return super.onTouchEvent(event);
        }
        mCurMovePointF.set(event.getX(), event.getY());

        if (mStatus == STATUS_ROTATE_ZOOM) {
          float scale = 1f;

          int halfBitmapWidth = mWaterMarkBitmap.getWidth() / 2;
          int halfBitmapHeight = mWaterMarkBitmap.getHeight() / 2;

          // 图片某个点到图片中心的距离
          float bitmapToCenterDistance =
              FloatMath.sqrt(
                  halfBitmapWidth * halfBitmapWidth + halfBitmapHeight * halfBitmapHeight);

          // 移动的点到图片中心的距离
          float moveToCenterDistance = distance4PointF(mCenterPoint, mCurMovePointF);

          // 计算缩放比例
          scale = moveToCenterDistance / bitmapToCenterDistance;

          // 缩放比例的界限判断
          if (scale <= MIN_SCALE) {
            scale = MIN_SCALE;
          } else if (scale >= MAX_SCALE) {
            scale = MAX_SCALE;
          }

          // 角度
          double a = distance4PointF(mCenterPoint, mPreMovePointF);
          double b = distance4PointF(mPreMovePointF, mCurMovePointF);
          double c = distance4PointF(mCenterPoint, mCurMovePointF);

          double cosb = (a * a + c * c - b * b) / (2 * a * c);

          if (cosb >= 1) {
            cosb = 1f;
          }

          double radian = Math.acos(cosb);
          float newDegree = (float) radianToDegree(radian);

          // center -> proMove的向量, 我们使用PointF来实现
          PointF centerToProMove =
              new PointF((mPreMovePointF.x - mCenterPoint.x), (mPreMovePointF.y - mCenterPoint.y));

          // center -> curMove 的向量
          PointF centerToCurMove =
              new PointF((mCurMovePointF.x - mCenterPoint.x), (mCurMovePointF.y - mCenterPoint.y));

          // 向量叉乘结果, 如果结果为负数, 表示为逆时针, 结果为正数表示顺时针
          float result =
              centerToProMove.x * centerToCurMove.y - centerToProMove.y * centerToCurMove.x;

          if (result < 0) {
            newDegree = -newDegree;
          }

          mDegree = mDegree + newDegree;
          mScale = scale;
          //				Log.e(TAG,"mScale:" + mScale);
          transformDraw();
        } else if (mStatus == STATUS_DRAG) {
          // 修改中心点
          mCenterPoint.x += mCurMovePointF.x - mPreMovePointF.x;
          mCenterPoint.y += mCurMovePointF.y - mPreMovePointF.y;

          //				adjustLayout();
          transformDraw();
        }

        mPreMovePointF.set(mCurMovePointF);
        break;
    }
    return true;
  }
 public static float distance(float x1, float y1, float x2, float y2) {
   float x = x1 - x2;
   float y = y1 - y2;
   return FloatMath.sqrt(x * x + y * y);
 }
 private static float hypot(float x, float y) {
   return FloatMath.sqrt(x * x + y * y);
 }
 @Override
 public float getPercentageDone(final float n, final float n2, final float n3, final float n4) {
   final float n5 = n / n2 - 1.0f;
   return n3 + n4 * FloatMath.sqrt(1.0f - n5 * n5);
 }
Exemple #27
0
 @Override
 public float getScale() {
   return FloatMath.sqrt(
       (float) Math.pow(getValue(mSuppMatrix, Matrix.MSCALE_X), 2)
           + (float) Math.pow(getValue(mSuppMatrix, Matrix.MSKEW_Y), 2));
 }
Exemple #28
0
  static void write(BufferedWriter out, DistoXNum num, DrawingCommandManager plot, long type) {
    VERSION = TDSetting.mAcadVersion;

    float scale = TDSetting.mDxfScale;
    int handle = 0;
    float xmin = 10000f, xmax = -10000f, ymin = 10000f, ymax = -10000f;
    // compute BBox
    for (ICanvasCommand cmd : plot.getCommands()) {
      if (cmd.commandType() != 0) continue;
      DrawingPath p = (DrawingPath) cmd;

      if (p.mType == DrawingPath.DRAWING_PATH_LINE) {
        DrawingLinePath lp = (DrawingLinePath) p;
        if (lp.lineType() == DrawingBrushPaths.mLineLib.mLineWallIndex) {
          // ArrayList< LinePoint > pts = lp.mPoints;
          // for ( LinePoint pt : pts )
          for (LinePoint pt = lp.mFirst; pt != null; pt = pt.mNext) {
            if (pt.mX < xmin) xmin = pt.mX;
            if (pt.mX > xmax) xmax = pt.mX;
            if (pt.mY < ymin) ymin = pt.mY;
            if (pt.mY > ymax) ymax = pt.mY;
          }
        }
      } else if (p.mType == DrawingPath.DRAWING_PATH_POINT) {
        DrawingPointPath pp = (DrawingPointPath) p;
        if (pp.cx < xmin) xmin = pp.cx;
        if (pp.cx > xmax) xmax = pp.cx;
        if (pp.cy < ymin) ymin = pp.cy;
        if (pp.cy > ymax) ymax = pp.cy;
      } else if (p.mType == DrawingPath.DRAWING_PATH_STATION) {
        DrawingStationPath st = (DrawingStationPath) p;
        if (st.cx < xmin) xmin = st.cx;
        if (st.cx > xmax) xmax = st.cx;
        if (st.cy < ymin) ymin = st.cy;
        if (st.cy > ymax) ymax = st.cy;
      }
    }
    xmin *= scale;
    xmax *= scale;
    ymin *= scale;
    ymax *= scale;

    // Log.v("DistoX", "DXF X " + xmin + " " + xmax + " Y " + ymin + " " + ymax );

    try {
      // header
      writeComment(out, "DXF created by TopoDroid v. " + TopoDroidApp.VERSION);
      writeSection(out, "HEADER");

      xmin -= 2f;
      ymax += 2f;

      writeString(out, 9, "$ACADVER");
      String ACAD_VERSION = (VERSION == 13) ? "AC1012" : "AC1009";
      writeString(out, 1, ACAD_VERSION);

      if (VERSION >= 13) {
        writeString(out, 9, "$DWGCODEPAGE");
        writeString(out, 3, "ANSI_1251");
      }

      writeString(out, 9, "$INSBASE");
      {
        StringWriter sw1 = new StringWriter();
        PrintWriter pw1 = new PrintWriter(sw1);
        printXYZ(pw1, 0.0f, 0.0f, 0.0f); // FIXME (0,0,0)
        printString(pw1, 9, "$EXTMIN");
        printXYZ(pw1, xmin, -ymax, 0.0f);
        printString(pw1, 9, "$EXTMAX");
        printXYZ(pw1, xmax * scale, -ymin * scale, 0.0f);
        out.write(sw1.getBuffer().toString());
      }
      writeEndSection(out);

      String lt_continuous = "CONTINUOUS";
      writeSection(out, "TABLES");
      {
        if (VERSION >= 13) {
          ++handle;
          writeBeginTable(out, "VPORT", handle, 1);
          {
            writeString(out, 0, "VPORT");
            ++handle;
            writeAcDb(out, handle, "AcDbSymbolTableRecord", "AcDbViewportTableRecord");
            writeString(out, 2, "MyViewport");
            writeInt(out, 70, 0);
            writeString(out, 10, zero);
            writeString(out, 20, zero);
            writeString(out, 11, one);
            writeString(out, 21, one);
            writeString(out, 12, zero);
            writeString(out, 22, zero);
            writeString(out, 13, zero);
            writeString(out, 23, zero);
            writeString(out, 14, half);
            writeString(out, 24, half);
            writeString(out, 15, half);
            writeString(out, 25, half);
            writeString(out, 16, zero);
            writeString(out, 26, zero);
            writeString(out, 36, one);
            writeString(out, 17, zero);
            writeString(out, 27, zero);
            writeString(out, 37, zero);
            writeString(out, 40, zero);
            writeString(out, 41, "2.0");
            writeString(out, 42, "50.0");
          }
          writeEndTable(out);
        }

        ++handle;
        writeBeginTable(out, "LTYPE", handle, 1);
        {
          // int flag = 64;
          writeString(out, 0, "LTYPE");
          ++handle;
          writeAcDb(out, handle, "AcDbSymbolTableRecord", "AcDbLinetypeTableRecord");
          writeString(out, 2, lt_continuous);
          writeInt(out, 70, 64);
          writeString(out, 3, "Solid line");
          writeInt(out, 72, 65);
          writeInt(out, 73, 0);
          writeString(out, 40, zero);
        }
        writeEndTable(out);

        SymbolLineLibrary linelib = DrawingBrushPaths.mLineLib;
        SymbolAreaLibrary arealib = DrawingBrushPaths.mAreaLib;
        int nr_layers = 6 + linelib.mSymbolNr + arealib.mSymbolNr;
        ++handle;
        writeBeginTable(out, "LAYER", handle, nr_layers);
        {
          StringWriter sw2 = new StringWriter();
          PrintWriter pw2 = new PrintWriter(sw2);

          // 2 layer name, 70 flag (64), 62 color code, 6 line type
          int flag = 0;
          int color = 1;
          ++handle;
          printLayer(pw2, handle, "LEG", flag, color, lt_continuous);
          ++color;
          ++handle;
          printLayer(pw2, handle, "SPLAY", flag, color, lt_continuous);
          ++color;
          ++handle;
          printLayer(pw2, handle, "STATION", flag, color, lt_continuous);
          ++color;
          ++handle;
          printLayer(pw2, handle, "LINE", flag, color, lt_continuous);
          ++color;
          ++handle;
          printLayer(pw2, handle, "POINT", flag, color, lt_continuous);
          ++color;
          // ++handle; printLayer( pw2, handle, "AREA",    flag, color, lt_continuous ); ++color;
          ++handle;
          printLayer(pw2, handle, "REF", flag, color, lt_continuous);
          ++color;

          if (linelib != null) {
            for (Symbol line : linelib.getSymbols()) {
              String lname = "L_" + line.getThName().replace(':', '-');
              ++handle;
              printLayer(pw2, handle, lname, flag, color, lt_continuous);
              ++color;
            }
          }

          if (arealib != null) {
            for (Symbol s : arealib.getSymbols()) {
              String aname = "A_" + s.getThName().replace(':', '-');
              ++handle;
              printLayer(pw2, handle, aname, flag, color, lt_continuous);
              ++color;
            }
          }
          out.write(sw2.getBuffer().toString());
        }
        writeEndTable(out);

        if (VERSION >= 13) {
          ++handle;
          writeBeginTable(out, "STYLE", handle, 1);
          {
            writeString(out, 0, "STYLE");
            ++handle;
            writeAcDb(out, handle, "AcDbSymbolTableRecord", "AcDbTextStyleTableRecord");
            writeString(out, 2, "MyStyle"); // name
            writeInt(out, 70, 0); // flag
            writeString(out, 40, zero);
            writeString(out, 41, one);
            writeString(out, 42, one);
            writeString(out, 3, "arial.ttf"); // fonts
          }
          writeEndTable(out);
        }

        ++handle;
        writeBeginTable(out, "VIEW", handle, 0);
        writeEndTable(out);

        ++handle;
        writeBeginTable(out, "UCS", handle, 0);
        writeEndTable(out);

        if (VERSION >= 13) {
          ++handle;
          writeBeginTable(out, "STYLE", handle, 0);
          writeEndTable(out);
        }

        ++handle;
        writeBeginTable(out, "APPID", handle, 1);
        {
          writeString(out, 0, "APPID");
          ++handle;
          writeAcDb(out, handle, "AcDbSymbolTableRecord", "AcDbRegAppTableRecord");
          writeString(out, 2, "ACAD"); // applic. name
          writeInt(out, 70, 0); // flag
        }
        writeEndTable(out);

        if (VERSION >= 13) {
          ++handle;
          writeBeginTable(out, "DIMSTYLE", handle, -1);
          writeString(out, 100, "AcDbDimStyleTable");
          writeInt(out, 70, 1);
          writeEndTable(out);

          ++handle;
          writeBeginTable(out, "BLOCK_RECORD", handle, DrawingBrushPaths.mPointLib.mSymbolNr);
          {
            for (int n = 0; n < DrawingBrushPaths.mPointLib.mSymbolNr; ++n) {
              String block =
                  "P_" + DrawingBrushPaths.mPointLib.getSymbolThName(n).replace(':', '-');
              writeString(out, 0, "BLOCK_RECORD");
              ++handle;
              writeAcDb(out, handle, "AcDbSymbolTableRecord", "AcDbBlockTableRecord");
              writeString(out, 2, block);
              writeInt(out, 70, 0); // flag
            }
          }
          writeEndTable(out);
        }
      }
      writeEndSection(out);
      out.flush();

      writeSection(out, "BLOCKS");
      {
        // // 8 layer (0), 2 block name,
        for (int n = 0; n < DrawingBrushPaths.mPointLib.mSymbolNr; ++n) {
          SymbolPoint pt = (SymbolPoint) DrawingBrushPaths.mPointLib.getSymbolByIndex(n);
          String block = "P_" + pt.getThName().replace(':', '-');

          writeString(out, 0, "BLOCK");
          ++handle;
          writeAcDb(out, handle, "AcDbEntity", "AcDbBlockBegin");
          writeString(out, 8, "POINT");
          writeString(out, 2, block);
          writeInt(out, 70, 64); // flag 64 = this definition is referenced
          writeString(out, 10, "0.0");
          writeString(out, 20, "0.0");
          writeString(out, 30, "0.0");

          out.write(pt.getDxf());
          // out.write( DrawingBrushPaths.mPointLib.getPoint(n).getDxf() );

          writeString(out, 0, "ENDBLK");
          if (VERSION >= 13) {
            ++handle;
            writeAcDb(out, handle, "AcDbEntity", "AcDbBlockEnd");
            writeString(out, 8, "POINT");
          }
        }
      }
      writeEndSection(out);
      out.flush();

      writeSection(out, "ENTITIES");
      {
        float SCALE_FIX = DrawingUtil.SCALE_FIX;

        // reference
        {
          StringWriter sw9 = new StringWriter();
          PrintWriter pw9 = new PrintWriter(sw9);
          printString(pw9, 0, "LINE");
          ++handle;
          printAcDb(pw9, handle, "AcDbEntity", "AcDbLine");
          printString(pw9, 8, "REF");
          // printInt(  pw9, 39, 0 );         // line thickness
          printXYZ(pw9, xmin, -ymax, 0.0f);
          printXYZ1(pw9, (xmin + 10 * SCALE_FIX), -ymax, 0.0f);
          out.write(sw9.getBuffer().toString());
        }
        {
          StringWriter sw8 = new StringWriter();
          PrintWriter pw8 = new PrintWriter(sw8);
          printString(pw8, 0, "LINE");
          ++handle;
          printAcDb(pw8, handle, "AcDbEntity", "AcDbLine");
          printString(pw8, 8, "REF");
          // printInt(  pw8, 39, 0 );         // line thickness
          printXYZ(pw8, xmin, -ymax, 0.0f);
          printXYZ1(pw8, xmin, -ymax + 10 * SCALE_FIX, 0.0f);
          out.write(sw8.getBuffer().toString());
        }
        {
          StringWriter sw7 = new StringWriter();
          PrintWriter pw7 = new PrintWriter(sw7);
          printString(pw7, 0, "TEXT");
          ++handle;
          printAcDb(pw7, handle, "AcDbEntity", "AcDbText");
          printString(pw7, 8, "REF");
          // pw7.printf("%s\n  0\n", "\"10\"" );
          printXYZ(pw7, (xmin + 10 * SCALE_FIX + 1), -ymax, 0.0f);
          printFloat(pw7, 40, 0.3f);
          printString(pw7, 1, "\"10\"");
          out.write(sw7.getBuffer().toString());
        }
        {
          StringWriter sw6 = new StringWriter();
          PrintWriter pw6 = new PrintWriter(sw6);
          printString(pw6, 0, "TEXT");
          ++handle;
          printAcDb(pw6, handle, "AcDbEntity", "AcDbText");
          printString(pw6, 8, "REF");
          // pw6.printf("%s\n  0\n", "\"10\"" );
          printXYZ(pw6, xmin, -ymax + 10 * SCALE_FIX + 1, 0.0f);
          printFloat(pw6, 40, 0.3f);
          // printFloat( pw6, 50, 90.0f ); // rotation
          printString(pw6, 1, "\"10\"");
          out.write(sw6.getBuffer().toString());
        }
        out.flush();

        // centerline data
        if (type == PlotInfo.PLOT_PLAN || type == PlotInfo.PLOT_EXTENDED) {
          for (DrawingPath sh : plot.getLegs()) {
            DistoXDBlock blk = sh.mBlock;
            if (blk == null) continue;

            StringWriter sw4 = new StringWriter();
            PrintWriter pw4 = new PrintWriter(sw4);
            // if ( sh.mType == DrawingPath.DRAWING_PATH_FIXED ) {
            NumStation f = num.getStation(blk.mFrom);
            NumStation t = num.getStation(blk.mTo);

            printString(pw4, 0, "LINE");
            ++handle;
            printAcDb(pw4, handle, "AcDbEntity", "AcDbLine");
            printString(pw4, 8, "LEG");
            // printInt( pw4, 39, 2 );         // line thickness

            if (type == PlotInfo.PLOT_PLAN) {
              float x = scale * DrawingUtil.toSceneX(f.e);
              float y = scale * DrawingUtil.toSceneY(f.s);
              float x1 = scale * DrawingUtil.toSceneX(t.e);
              float y1 = scale * DrawingUtil.toSceneY(t.s);
              printXYZ(pw4, x, -y, 0.0f);
              printXYZ1(pw4, x1, -y1, 0.0f);
            } else if (type == PlotInfo.PLOT_EXTENDED) {
              float x = scale * DrawingUtil.toSceneX(f.h);
              float y = scale * DrawingUtil.toSceneY(f.v);
              float x1 = scale * DrawingUtil.toSceneX(t.h);
              float y1 = scale * DrawingUtil.toSceneY(t.v);
              printXYZ(pw4, x, -y, 0.0f);
              printXYZ1(pw4, x1, -y1, 0.0f);
            } else if (type == PlotInfo.PLOT_SECTION) {
              // nothing
            }
            // }
            out.write(sw4.getBuffer().toString());
            out.flush();
          }
          for (DrawingPath sh : plot.getSplays()) {
            DistoXDBlock blk = sh.mBlock;
            if (blk == null) continue;

            StringWriter sw41 = new StringWriter();
            PrintWriter pw41 = new PrintWriter(sw41);
            // if ( sh.mType == DrawingPath.DRAWING_PATH_SPLAY ) {
            NumStation f = num.getStation(blk.mFrom);

            printString(pw41, 0, "LINE");
            ++handle;
            printAcDb(pw41, handle, "AcDbEntity", "AcDbLine");
            printString(pw41, 8, "SPLAY");
            // printInt( pw41, 39, 1 );         // line thickness

            float dhs =
                scale * blk.mLength * FloatMath.cos(blk.mClino * grad2rad) * SCALE_FIX; // scaled dh
            if (type == PlotInfo.PLOT_PLAN) {
              float x = scale * DrawingUtil.toSceneX(f.e);
              float y = scale * DrawingUtil.toSceneY(f.s);
              float de = dhs * FloatMath.sin(blk.mBearing * grad2rad);
              float ds = -dhs * FloatMath.cos(blk.mBearing * grad2rad);
              printXYZ(pw41, x, -y, 0.0f);
              printXYZ1(pw41, x + de, -(y + ds), 0.0f);
            } else if (type == PlotInfo.PLOT_EXTENDED) {
              float x = scale * DrawingUtil.toSceneX(f.h);
              float y = scale * DrawingUtil.toSceneY(f.v);
              float dv = -blk.mLength * FloatMath.sin(blk.mClino * grad2rad) * SCALE_FIX;
              printXYZ(pw41, x, -y, 0.0f);
              printXYZ1(pw41, x + dhs * blk.mExtend, -(y + dv), 0.0f);
            } else if (type == PlotInfo.PLOT_SECTION) {
              // nothing
            }
            // }
            out.write(sw41.getBuffer().toString());
            out.flush();
          }
        }

        // FIXME station scale is 0.3
        float POINT_SCALE = 10.0f;
        for (ICanvasCommand cmd : plot.getCommands()) {
          if (cmd.commandType() != 0) continue;
          DrawingPath path = (DrawingPath) cmd;

          StringWriter sw5 = new StringWriter();
          PrintWriter pw5 = new PrintWriter(sw5);
          if (path.mType == DrawingPath.DRAWING_PATH_STATION) {
            DrawingStationPath st = (DrawingStationPath) path;

            printString(pw5, 0, "TEXT");
            printString(pw5, 8, "STATION");
            if (VERSION >= 13) {
              ++handle;
              printAcDb(pw5, handle, "AcDbEntity", "AcDbText");
              pw5.printf("%s\n  0\n", st.mName);
            }
            printXYZ(pw5, st.cx * scale, -st.cy * scale, 0.0f);
            printFloat(pw5, 40, POINT_SCALE);
            printString(pw5, 1, st.mName);
          } else if (path.mType == DrawingPath.DRAWING_PATH_LINE) {
            DrawingLinePath line = (DrawingLinePath) path;
            String layer =
                "L_"
                    + DrawingBrushPaths.mLineLib.getSymbolThName(line.lineType()).replace(':', '-');
            // String layer = "LINE";
            int flag = 0;
            boolean use_spline = false;
            if (VERSION >= 13) {
              for (LinePoint p = line.mFirst; p != null; p = p.mNext) {
                if (p.has_cp) {
                  use_spline = true;
                  break;
                }
              }
            }
            if (use_spline) {
              printString(pw5, 0, "SPLINE");
              ++handle;
              printAcDb(pw5, handle, "AcDbEntity", "AcDbSpline");
              printString(pw5, 8, layer);
              printString(pw5, 6, lt_continuous);
              printFloat(pw5, 48, 1.0f); // scale
              printInt(pw5, 60, 0); // visibilty (0: visible, 1: invisible)
              printInt(pw5, 66, 1); // group 1
              // printInt( pw5, 67, 0 ); // in model space [default]
              printInt(pw5, 210, 0);
              printInt(pw5, 220, 0);
              printInt(pw5, 230, 1);

              float xt = 0, yt = 0;
              int np = 2;
              LinePoint p = line.mFirst;
              LinePoint pn = p.mNext;
              if (pn != null) {
                if (pn.has_cp) {
                  xt = pn.mX1 - p.mX;
                  yt = pn.mY1 - p.mY;
                } else {
                  xt = pn.mX - p.mX;
                  yt = pn.mY - p.mY;
                }
                float d = FloatMath.sqrt(xt * xt + yt * yt);
                printFloat(pw5, 12, xt / d);
                printFloat(pw5, 22, -yt / d);
                printFloat(pw5, 32, 0);

                while (pn.mNext != null) {
                  p = pn;
                  pn = pn.mNext;
                  ++np;
                }
                if (pn.has_cp) {
                  xt = pn.mX - pn.mX2;
                  yt = pn.mY - pn.mY2;
                } else {
                  xt = pn.mX - p.mX;
                  yt = pn.mY - p.mY;
                }
                d = FloatMath.sqrt(xt * xt + yt * yt);
                printFloat(pw5, 13, xt / d);
                printFloat(pw5, 23, -yt / d);
                printFloat(pw5, 33, 0);
              }

              int ncp = np + 3 * (np - 1) - 1;
              int nk = ncp + 4 - (np - 2);
              printInt(pw5, 70, 1064);
              printInt(pw5, 71, 3); // degree
              printInt(pw5, 72, nk); // nr. of knots
              printInt(pw5, 73, ncp); // nr. of control pts
              printInt(pw5, 74, np); // nr. of fix points

              printInt(pw5, 40, 0);
              for (int k = 0; k < np; ++k) {
                for (int j = 0; j < 3; ++j) printInt(pw5, 40, k);
              }
              printInt(pw5, 40, np - 1);

              p = line.mFirst;
              xt = p.mX;
              yt = p.mY;
              printXYZ(pw5, p.mX * scale, -p.mY * scale, 0.0f);
              for (p = p.mNext; p != null; p = p.mNext) {
                if (p.has_cp) {
                  printXYZ(pw5, p.mX1 * scale, -p.mY1 * scale, 0.0f);
                  printXYZ(pw5, p.mX2 * scale, -p.mY2 * scale, 0.0f);
                } else {
                  printXYZ(pw5, xt * scale, -yt * scale, 0.0f);
                  printXYZ(pw5, p.mX * scale, -p.mY * scale, 0.0f);
                }
                printXYZ(pw5, p.mX * scale, -p.mY * scale, 0.0f);
                xt = p.mX;
                yt = p.mY;
              }
              for (p = line.mFirst; p != null; p = p.mNext) {
                printXYZ1(pw5, p.mX * scale, -p.mY * scale, 0.0f);
              }
            } else {
              printString(pw5, 0, "POLYLINE");
              ++handle;
              printAcDb(pw5, handle, "AcDbEntity", "AcDbPolyline");
              printString(pw5, 8, layer);
              // printInt(  pw5, 39, 1 );         // line thickness
              printInt(pw5, 66, 1); // group 1
              printInt(pw5, 70, 0); // flag
              for (LinePoint p = line.mFirst; p != null; p = p.mNext) {
                printString(pw5, 0, "VERTEX");
                if (VERSION >= 13) {
                  ++handle;
                  printAcDb(pw5, handle, "AcDbVertex", "AcDb3dPolylineVertex");
                  printInt(pw5, 70, 32);
                }
                printString(pw5, 8, layer);
                printXYZ(pw5, p.mX * scale, -p.mY * scale, 0.0f);
              }
            }
            pw5.printf("  0\nSEQEND\n");
            if (VERSION >= 13) {
              ++handle;
              printHex(pw5, 5, handle);
            }
          } else if (path.mType == DrawingPath.DRAWING_PATH_AREA) {
            DrawingAreaPath area = (DrawingAreaPath) path;
            String layer =
                "A_"
                    + DrawingBrushPaths.mAreaLib.getSymbolThName(area.areaType()).replace(':', '-');
            printString(pw5, 0, "HATCH"); // entity type HATCH
            // ++handle; printAcDb( pw5, handle, "AcDbEntity", "AcDbHatch" );
            // printString( pw5, 8, "AREA" );  // layer (color BYLAYER)
            printString(pw5, 8, layer); // layer (color BYLAYER)

            // printXYZ( pw5, 0f, 0f, 0f );
            printFloat(pw5, 210, 0f); // extrusion direction
            printFloat(pw5, 220, 0f);
            printFloat(pw5, 230, 1f);
            printInt(pw5, 70, 1); // solid fill
            printInt(pw5, 71, 1); // associative
            printInt(pw5, 91, 1); // nr. boundary paths: 1
            printInt(pw5, 92, 3); // flag: external (bit-0) polyline (bit-1)
            printInt(pw5, 93, area.size()); // nr. of edges /  vertices
            printInt(pw5, 72, 0); // edge type (0: default)
            printInt(pw5, 73, 1); // is-closed flag
            for (LinePoint p = area.mFirst; p != null; p = p.mNext) {
              printXY(pw5, p.mX * scale, -p.mY * scale);
            }

            // printInt( pw5, 97, 0 );            // nr. source boundary objects

            // printInt( pw5, 75, 1 );            // hatch style (normal)
            // printInt( pw5, 76, 1 );
            // printFloat( pw5, 52, 1.5708f );    // hatch pattern angle
            // printFloat( pw5, 41, 3f );         // hatch pattern scale
            // printInt( pw5, 77, 0 );            // hatch pattern double flag (0: not double)
            // printInt( pw5, 78, 1 );            // nr. pattern lines

            // printFloat( pw5, 53, 1.5708f );    // pattern line angle
            // printFloat( pw5, 43, 0f );         // pattern base point
            // printFloat( pw5, 44, 0f );
            // printFloat( pw5, 45, 1f );         // pattern line offset
            // printFloat( pw5, 46, 1f );
            // printInt( pw5, 79, 0 );            // nr. dash length items
            // // printFloat( pw5, 49, 3f );         // dash length (repeated nr. times)

            // printFloat( pw5, 47, 1f );         // pixel size
            // printInt( pw5, 98, 2 );            // nr. seed points
            // printXYZ( pw5, 0f, 0f, 0f );
            // printXYZ( pw5, 0f, 0f, 0f );
            // printInt( pw5, 451, 0 );
            // printFloat( pw5, 460, 0f );
            // printFloat( pw5, 461, 0f );
            // printInt( pw5, 452, 1 );
            // printFloat( pw5, 462, 1f );
            // printInt( pw5, 453, 2 );
            // printFloat( pw5, 463, 0f );
            // printFloat( pw5, 463, 1f );
            // printString( pw5, 470, "LINEAR" );
          } else if (path.mType == DrawingPath.DRAWING_PATH_POINT) {
            // FIXME point scale factor is 0.3
            DrawingPointPath point = (DrawingPointPath) path;
            String block =
                "P_"
                    + DrawingBrushPaths.mPointLib
                        .getSymbolThName(point.mPointType)
                        .replace(':', '-');
            // int idx = 1 + point.mPointType;
            printString(pw5, 0, "INSERT");
            ++handle;
            printAcDb(pw5, handle, "AcDbBlockReference");
            printString(pw5, 8, "POINT");
            printString(pw5, 2, block);
            printFloat(pw5, 41, POINT_SCALE);
            printFloat(pw5, 42, POINT_SCALE);
            printFloat(pw5, 50, 360 - (float) (point.mOrientation));
            printXYZ(pw5, point.cx * scale, -point.cy * scale, 0.0f);
          }
          out.write(sw5.getBuffer().toString());
          out.flush();
        }
      }
      writeEndSection(out);

      writeString(out, 0, "EOF");
      out.flush();
    } catch (IOException e) {
      // FIXME
      TDLog.Error("DXF io-exception " + e.toString());
    }
  }
 private float calcDistance(MotionEvent event) {
   float x = event.getX(0) - event.getX(1);
   float y = event.getY(0) - event.getY(1);
   return FloatMath.sqrt(x * x + y * y);
 }
  /**
   * Calculates the distance between two points.
   *
   * @param x1 the x-coordinate of the origin
   * @param y1 the y-coordinate of the origin
   * @param x2 the x-coordinate of the extent
   * @param y2 the y-coordinate of the extent
   * @return the distance between (x1, y1) and (x2, y2)
   */
  public static float distanceBetween(float x1, float y1, float x2, float y2) {
    float dx = x2 - x1;
    float dy = y2 - y1;

    return FloatMath.sqrt(dx * dx + dy * dy);
  }