亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

Android 吸入動畫效果實現分解

2020-04-11 12:07:26
字體:
來源:轉載
供稿:網友
Android 吸入動畫效果詳解 .
 
這里,我要介紹的是如何在Android上面實現一個類似的效果。先看看我實現的效果圖。
 
上圖演示了動畫的某幾幀,其中從1 - 4,演示了圖片從原始圖形吸入到一個點(紅色標識)。
實現這樣的效果,我們利用了Canvas.drawBitmapMesh()方法,這里涉及到了一個Mesh的概念。
2,Mesh的概念
Mesh表示網格,說得通俗一點,可以將畫板想像成一張格子布,在這個張布上繪制圖片。對于一個網格端點均勻分布的網格來說,橫向有meshWidth + 1個頂點,縱向有meshHeight + 1個端點。頂點數據verts是以行優先的數組(二維數組以一維數組表示,先行后列)。網格可以不均勻分布。請看下圖所示:
 
上圖中顯示了把圖片分成很多格子,上圖中的每個格子是均勻的,它的頂點數是:(meshWidth + 1) * (meshHeight + 1)個,那么放這些頂點的一維數據的大小應該是:(meshWidth + 1) * (meshHeight + 1) * 2 (一個點包含x, y坐標)
復制代碼 代碼如下:

float[] vertices = new float[:(meshWidth + 1) * (meshHeight + 1) * 2];

試想,我們讓這個格子(mesh)不均勻分布,那么繪制出來的圖片就會變形,請看下圖所示:
 
3,如何構建Mesh
吸入動畫的核心是吸入到一個點,那么我們就是要在不同的時刻構造出不同的mesh的頂點坐標,我們是怎么做的呢?
3.1,創建兩條路徑(Path)
假如我們的吸入效果是從上到下吸入,我們構造的Path是如下圖所示:
 
上圖中藍色的線表示我們構造的Path,其實只要我們沿著這兩條Path來構造mesh頂點就可以了。
構建兩條Path的代碼如下:
復制代碼 代碼如下:

mFirstPathMeasure.setPath(mFirstPath, false);
mSecondPathMeasure.setPath(mSecondPath, false);
float w = mBmpWidth;
float h = mBmpHeight;
mFirstPath.reset();
mSecondPath.reset();
mFirstPath.moveTo(0, 0);
mSecondPath.moveTo(w, 0);
mFirstPath.lineTo(0, h);
mSecondPath.lineTo(w, h);
mFirstPath.quadTo(0, (endY + h) / 2, endX, endY);
mSecondPath.quadTo(w, (endY + h) / 2, endX, endY);

3.2,根據Path來計算頂點坐標
算法:
1,假如我們把格子分為WIDTH, HEIGHT份,把Path的長度分的20份,[0, length],表示20個時刻。
2,第0時間,我們要的形狀是一個矩形,第1時刻可能是梯形,第n時間可能是一個三角形。下圖說明了動畫過程中圖片的變化。
 
3,第一條(左)Path的長度為len1,第二條(右)Path的長度為len2,對于任意時刻 t [0 - 20],我們可以知道梯形的四個頂點距Path最頂端的length。
左上角:t * (len1 / 20)
左下角:t * (len1 / 20) + bitmapHeight
右上角:t * (len2 / 20)
右下角:t * (len2 / 20) + bitmapHeight
 
我們可以通過PathMeasure類根據length算出在Path上面點的坐標,也就是說,根據兩條Path,我們可以分別算了四個頂點的坐標,我這里分別叫做A, B, C, D(順時針方向),有了點的坐標,我們可以算出AD,BC的長度,并且將基進行HEIGHT等分(因為我們把mesh分成寬WIDTH,高HEIGHT等分),將AD,BC上面每等分的點連接起來形成一條直接,將再這條直接水平WIDTH等分,根據直線方程,依據x算出y,從而算出每一個頂點的坐標。(請參考上圖)
下面是計算頂點坐標的詳細代碼:
復制代碼 代碼如下:

private void buildMeshByPathOnVertical(int timeIndex)
{
mFirstPathMeasure.setPath(mFirstPath, false);
mSecondPathMeasure.setPath(mSecondPath, false);
int index = 0;
float[] pos1 = {0.0f, 0.0f};
float[] pos2 = {0.0f, 0.0f};
float firstLen = mFirstPathMeasure.getLength();
float secondLen = mSecondPathMeasure.getLength();
float len1 = firstLen / HEIGHT;
float len2 = secondLen / HEIGHT;
float firstPointDist = timeIndex * len1;
float secondPointDist = timeIndex * len2;
float height = mBmpHeight;
mFirstPathMeasure.getPosTan(firstPointDist, pos1, null);
mFirstPathMeasure.getPosTan(firstPointDist + height, pos2, null);
float x1 = pos1[0];
float x2 = pos2[0];
float y1 = pos1[1];
float y2 = pos2[1];
float FIRST_DIST = (float)Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );
float FIRST_H = FIRST_DIST / HEIGHT;
mSecondPathMeasure.getPosTan(secondPointDist, pos1, null);
mSecondPathMeasure.getPosTan(secondPointDist + height, pos2, null);
x1 = pos1[0];
x2 = pos2[0];
y1 = pos1[1];
y2 = pos2[1];
float SECOND_DIST = (float)Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );
float SECOND_H = SECOND_DIST / HEIGHT;
for (int y = 0; y <= HEIGHT; ++y)
{
mFirstPathMeasure.getPosTan(y * FIRST_H + firstPointDist, pos1, null);
mSecondPathMeasure.getPosTan(y * SECOND_H + secondPointDist, pos2, null);
float w = pos2[0] - pos1[0];
float fx1 = pos1[0];
float fx2 = pos2[0];
float fy1 = pos1[1];
float fy2 = pos2[1];
float dy = fy2 - fy1;
float dx = fx2 - fx1;
for (int x = 0; x <= WIDTH; ++x)
{
// y = x * dy / dx
float fx = x * w / WIDTH;
float fy = fx * dy / dx;
mVerts[index * 2 + 0] = fx + fx1;
mVerts[index * 2 + 1] = fy + fy1;
index += 1;
}
}
}

4,如何繪制
繪制代碼很簡單,調用Canvas.drawBitmapMesh方法。最本質是要計算出一個頂點數組。
復制代碼 代碼如下:

canvas.drawBitmapMesh(mBitmap,
mInhaleMesh.getWidth(),
mInhaleMesh.getHeight(),
mInhaleMesh.getVertices(),
0, null, 0, mPaint);

5,如何實現動畫
復制代碼 代碼如下:

protected void applyTransformation(float interpolatedTime, Transformation t)
{
int curIndex = 0;
Interpolator interpolator = this.getInterpolator();
if (null != interpolator)
{
float value = interpolator.getInterpolation(interpolatedTime);
interpolatedTime = value;
}
if (mReverse)
{
interpolatedTime = 1.0f - interpolatedTime;
}
curIndex = (int)(mFromIndex + (mEndIndex - mFromIndex) * interpolatedTime);
if (null != mListener)
{
mListener.onAnimUpdate(curIndex);
}
}

在動畫里面,我們計算出要做動畫的幀的index,假設我們把吸入動畫分為20幀,在動畫里面,計算出每一幀,最后通過onAnimUpdate(int index)方法回調,在這個方法實現里面,我們根據幀的index來重新計算一個新的mesh頂點數組,再用這個數組來繪制bitmap。這樣,我們就可以看來一組連續變化的mesh,也就能看到吸擴效果的動畫。
動畫類里面,最核心就是擴展Animation類,重寫applyTransformation方法。
6,總結
本文簡單介紹了吸放效果的實現,根據這個原理,我們可以構造更加復雜的Path來做更多的效果。同時,也能實現向上,向左,向右的吸入效果。
最本質是我們要理解Mesh的概念,最核心的工作就是構造出Mesh的頂點坐標。
計算Mesh通常是一個很復雜的工作,作一些簡單的變形還可以,對于太復雜的變形,可能還是不太方便。另外,像書籍翻頁的效果,用mesh其實也是可以做到的。只是算法復雜一點。
這里不能給出完整的代碼,原理可能不是說得太清楚,但愿給想實現的人一個思路指引吧。
7,實現代碼
InhaleAnimationActivity.java
復制代碼 代碼如下:

package com.nj1s.lib.test.anim;
import android.os.Bundle;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import com.nj1s.lib.mesh.InhaleMesh.InhaleDir;
import com.nj1s.lib.test.GABaseActivity;
import com.nj1s.lib.test.R;
import com.nj1s.lib.test.effect.BitmapMesh;
public class InhaleAnimationActivity extends GABaseActivity
{
private static final boolean DEBUG_MODE = false;
private BitmapMesh.SampleView mSampleView = null;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
LinearLayout linearLayout = new LinearLayout(this);
mSampleView = new BitmapMesh.SampleView(this);
mSampleView.setIsDebug(DEBUG_MODE);
mSampleView.setLayoutParams(new LinearLayout.LayoutParams(-1, -1));
Button btn = new Button(this);
btn.setText("Run");
btn.setTextSize(20.0f);
btn.setLayoutParams(new LinearLayout.LayoutParams(150, -2));
btn.setOnClickListener(new View.OnClickListener()
{
boolean mReverse = false;
@Override
public void onClick(View v)
{
if (mSampleView.startAnimation(mReverse))
{
mReverse = !mReverse;
}
}
});
linearLayout.setOrientation(LinearLayout.VERTICAL);
linearLayout.setGravity(Gravity.CENTER_VERTICAL);
linearLayout.addView(btn);
linearLayout.addView(mSampleView);
setContentView(linearLayout);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
getMenuInflater().inflate(R.menu.inhale_anim_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch(item.getItemId())
{
case R.id.menu_inhale_down:
mSampleView.setInhaleDir(InhaleDir.DOWN);
break;
case R.id.menu_inhale_up:
mSampleView.setInhaleDir(InhaleDir.UP);
break;
case R.id.menu_inhale_left:
mSampleView.setInhaleDir(InhaleDir.LEFT);
break;
case R.id.menu_inhale_right:
mSampleView.setInhaleDir(InhaleDir.RIGHT);
break;
}
return super.onOptionsItemSelected(item);
}
}

BitmapMesh.java
復制代碼 代碼如下:

/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nj1s.lib.test.effect;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Interpolator;
import android.view.animation.Transformation;
import com.nj1s.lib.mesh.InhaleMesh;
import com.nj1s.lib.mesh.InhaleMesh.InhaleDir;
import com.nj1s.lib.test.R;
public class BitmapMesh {
public static class SampleView extends View {
private static final int WIDTH = 40;
private static final int HEIGHT = 40;
private final Bitmap mBitmap;
private final Matrix mMatrix = new Matrix();
private final Matrix mInverse = new Matrix();
private boolean mIsDebug = false;
private Paint mPaint = new Paint();
private float[] mInhalePt = new float[] {0, 0};
private InhaleMesh mInhaleMesh = null;
public SampleView(Context context) {
super(context);
setFocusable(true);
mBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.beach);
mInhaleMesh = new InhaleMesh(WIDTH, HEIGHT);
mInhaleMesh.setBitmapSize(mBitmap.getWidth(), mBitmap.getHeight());
mInhaleMesh.setInhaleDir(InhaleDir.DOWN);
}
public void setIsDebug(boolean isDebug)
{
mIsDebug = isDebug;
}
public void setInhaleDir(InhaleMesh.InhaleDir dir)
{
mInhaleMesh.setInhaleDir(dir);
float w = mBitmap.getWidth();
float h = mBitmap.getHeight();
float endX = 0;
float endY = 0;
float dx = 10;
float dy = 10;
mMatrix.reset();
switch (dir)
{
case DOWN:
endX = w / 2;
endY = getHeight() - 20;
break;
case UP:
dy = getHeight() - h - 20;
endX = w / 2;
endY = -dy + 10;
break;
case LEFT:
dx = getWidth() - w - 20;
endX = -dx + 10;
endY = h / 2;
break;
case RIGHT:
endX = getWidth() - 20;
endY = h / 2;
break;
}
mMatrix.setTranslate(dx, dy);
mMatrix.invert(mInverse);
buildPaths(endX, endY);
buildMesh(w, h);
invalidate();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
float bmpW = mBitmap.getWidth();
float bmpH = mBitmap.getHeight();
mMatrix.setTranslate(10, 10);
//mMatrix.setTranslate(10, 10);
mMatrix.invert(mInverse);
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(2);
mPaint.setAntiAlias(true);
buildPaths(bmpW / 2, h - 20);
buildMesh(bmpW, bmpH);
}
public boolean startAnimation(boolean reverse)
{
Animation anim = this.getAnimation();
if (null != anim && !anim.hasEnded())
{
return false;
}
PathAnimation animation = new PathAnimation(0, HEIGHT + 1, reverse,
new PathAnimation.IAnimationUpdateListener()
{
@Override
public void onAnimUpdate(int index)
{
mInhaleMesh.buildMeshes(index);
invalidate();
}
});
if (null != animation)
{
animation.setDuration(1000);
this.startAnimation(animation);
}
return true;
}
@Override
protected void onDraw(Canvas canvas)
{
Log.i("leehong2", "onDraw =========== ");
canvas.drawColor(0xFFCCCCCC);
canvas.concat(mMatrix);
canvas.drawBitmapMesh(mBitmap,
mInhaleMesh.getWidth(),
mInhaleMesh.getHeight(),
mInhaleMesh.getVertices(),
0, null, 0, mPaint);
// ===========================================
// Draw the target point.
mPaint.setColor(Color.RED);
mPaint.setStyle(Style.FILL);
canvas.drawCircle(mInhalePt[0], mInhalePt[1], 5, mPaint);
if (mIsDebug)
{
// ===========================================
// Draw the mesh vertices.
canvas.drawPoints(mInhaleMesh.getVertices(), mPaint);
// ===========================================
// Draw the paths
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Style.STROKE);
Path[] paths = mInhaleMesh.getPaths();
for (Path path : paths)
{
canvas.drawPath(path, mPaint);
}
}
}
private void buildMesh(float w, float h)
{
mInhaleMesh.buildMeshes(w, h);
}
private void buildPaths(float endX, float endY)
{
mInhalePt[0] = endX;
mInhalePt[1] = endY;
mInhaleMesh.buildPaths(endX, endY);
}
int mLastWarpX = 0;
int mLastWarpY = 0;
@Override
public boolean onTouchEvent(MotionEvent event)
{
float[] pt = { event.getX(), event.getY() };
mInverse.mapPoints(pt);
if (event.getAction() == MotionEvent.ACTION_UP)
{
int x = (int)pt[0];
int y = (int)pt[1];
if (mLastWarpX != x || mLastWarpY != y) {
mLastWarpX = x;
mLastWarpY = y;
buildPaths(pt[0], pt[1]);
invalidate();
}
}
return true;
}
}
private static class PathAnimation extends Animation
{
public interface IAnimationUpdateListener
{
public void onAnimUpdate(int index);
}
private int mFromIndex = 0;
private int mEndIndex = 0;
private boolean mReverse = false;
private IAnimationUpdateListener mListener = null;
public PathAnimation(int fromIndex, int endIndex, boolean reverse, IAnimationUpdateListener listener)
{
mFromIndex = fromIndex;
mEndIndex = endIndex;
mReverse = reverse;
mListener = listener;
}
public boolean getTransformation(long currentTime, Transformation outTransformation) {
boolean more = super.getTransformation(currentTime, outTransformation);
Log.d("leehong2", "getTransformation more = " + more);
return more;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
int curIndex = 0;
Interpolator interpolator = this.getInterpolator();
if (null != interpolator)
{
float value = interpolator.getInterpolation(interpolatedTime);
interpolatedTime = value;
}
if (mReverse)
{
interpolatedTime = 1.0f - interpolatedTime;
}
curIndex = (int)(mFromIndex + (mEndIndex - mFromIndex) * interpolatedTime);
if (null != mListener)
{
Log.i("leehong2", "onAnimUpdate =========== curIndex = " + curIndex);
mListener.onAnimUpdate(curIndex);
}
}
}
}

最核心的類
InhaleMesh
復制代碼 代碼如下:

package com.nj1s.lib.mesh;
import android.graphics.Path;
import android.graphics.PathMeasure;
public class InhaleMesh extends Mesh
{
public enum InhaleDir
{
UP,
DOWN,
LEFT,
RIGHT,
}
private Path mFirstPath = new Path();
private Path mSecondPath = new Path();
private PathMeasure mFirstPathMeasure = new PathMeasure();
private PathMeasure mSecondPathMeasure = new PathMeasure();
private InhaleDir mInhaleDir = InhaleDir.DOWN;
public InhaleMesh(int width, int height)
{
super(width, height);
}
public void setInhaleDir(InhaleDir inhaleDir)
{
mInhaleDir = inhaleDir;
}
public InhaleDir getInhaleDir()
{
return mInhaleDir;
}
@Override
public void buildPaths(float endX, float endY)
{
if (mBmpWidth <= 0 || mBmpHeight <= 0)
{
throw new IllegalArgumentException(
"Bitmap size must be > 0, do you call setBitmapSize(int, int) method?");
}
switch (mInhaleDir)
{
case UP:
buildPathsUp(endX, endY);
break;
case DOWN:
buildPathsDown(endX, endY);
break;
case RIGHT:
buildPathsRight(endX, endY);
break;
case LEFT:
buildPathsLeft(endX, endY);
break;
}
}
@Override
public void buildMeshes(int index)
{
if (mBmpWidth <= 0 || mBmpHeight <= 0)
{
throw new IllegalArgumentException(
"Bitmap size must be > 0, do you call setBitmapSize(int, int) method?");
}
switch (mInhaleDir)
{
case UP:
case DOWN:
buildMeshByPathOnVertical(index);
break;
case RIGHT:
case LEFT:
buildMeshByPathOnHorizontal(index);
break;
}
}
public Path[] getPaths()
{
return new Path[] { mFirstPath, mSecondPath };
}
private void buildPathsDown(float endX, float endY)
{
mFirstPathMeasure.setPath(mFirstPath, false);
mSecondPathMeasure.setPath(mSecondPath, false);
float w = mBmpWidth;
float h = mBmpHeight;
mFirstPath.reset();
mSecondPath.reset();
mFirstPath.moveTo(0, 0);
mSecondPath.moveTo(w, 0);
mFirstPath.lineTo(0, h);
mSecondPath.lineTo(w, h);
mFirstPath.quadTo(0, (endY + h) / 2, endX, endY);
mSecondPath.quadTo(w, (endY + h) / 2, endX, endY);
}
private void buildPathsUp(float endX, float endY)
{
mFirstPathMeasure.setPath(mFirstPath, false);
mSecondPathMeasure.setPath(mSecondPath, false);
float w = mBmpWidth;
float h = mBmpHeight;
mFirstPath.reset();
mSecondPath.reset();
mFirstPath.moveTo(0, h);
mSecondPath.moveTo(w, h);
mFirstPath.lineTo(0, 0);
mSecondPath.lineTo(w, 0);
mFirstPath.quadTo(0, (endY - h) / 2, endX, endY);
mSecondPath.quadTo(w, (endY - h) / 2, endX, endY);
}
private void buildPathsRight(float endX, float endY)
{
mFirstPathMeasure.setPath(mFirstPath, false);
mSecondPathMeasure.setPath(mSecondPath, false);
float w = mBmpWidth;
float h = mBmpHeight;
mFirstPath.reset();
mSecondPath.reset();
mFirstPath.moveTo(0, 0);
mSecondPath.moveTo(0, h);
mFirstPath.lineTo(w, 0);
mSecondPath.lineTo(w, h);
mFirstPath.quadTo((endX + w) / 2, 0, endX, endY);
mSecondPath.quadTo((endX + w) / 2, h, endX, endY);
}
private void buildPathsLeft(float endX, float endY)
{
mFirstPathMeasure.setPath(mFirstPath, false);
mSecondPathMeasure.setPath(mSecondPath, false);
float w = mBmpWidth;
float h = mBmpHeight;
mFirstPath.reset();
mSecondPath.reset();
mFirstPath.moveTo(w, 0);
mSecondPath.moveTo(w, h);
mFirstPath.lineTo(0, 0);
mSecondPath.lineTo(0, h);
mFirstPath.quadTo((endX - w) / 2, 0, endX, endY);
mSecondPath.quadTo((endX - w) / 2, h, endX, endY);
}
private void buildMeshByPathOnVertical(int timeIndex)
{
mFirstPathMeasure.setPath(mFirstPath, false);
mSecondPathMeasure.setPath(mSecondPath, false);
int index = 0;
float[] pos1 = {0.0f, 0.0f};
float[] pos2 = {0.0f, 0.0f};
float firstLen = mFirstPathMeasure.getLength();
float secondLen = mSecondPathMeasure.getLength();
float len1 = firstLen / HEIGHT;
float len2 = secondLen / HEIGHT;
float firstPointDist = timeIndex * len1;
float secondPointDist = timeIndex * len2;
float height = mBmpHeight;
mFirstPathMeasure.getPosTan(firstPointDist, pos1, null);
mFirstPathMeasure.getPosTan(firstPointDist + height, pos2, null);
float x1 = pos1[0];
float x2 = pos2[0];
float y1 = pos1[1];
float y2 = pos2[1];
float FIRST_DIST = (float)Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );
float FIRST_H = FIRST_DIST / HEIGHT;
mSecondPathMeasure.getPosTan(secondPointDist, pos1, null);
mSecondPathMeasure.getPosTan(secondPointDist + height, pos2, null);
x1 = pos1[0];
x2 = pos2[0];
y1 = pos1[1];
y2 = pos2[1];
float SECOND_DIST = (float)Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );
float SECOND_H = SECOND_DIST / HEIGHT;
if (mInhaleDir == InhaleDir.DOWN)
{
for (int y = 0; y <= HEIGHT; ++y)
{
mFirstPathMeasure.getPosTan(y * FIRST_H + firstPointDist, pos1, null);
mSecondPathMeasure.getPosTan(y * SECOND_H + secondPointDist, pos2, null);
float w = pos2[0] - pos1[0];
float fx1 = pos1[0];
float fx2 = pos2[0];
float fy1 = pos1[1];
float fy2 = pos2[1];
float dy = fy2 - fy1;
float dx = fx2 - fx1;
for (int x = 0; x <= WIDTH; ++x)
{
// y = x * dy / dx
float fx = x * w / WIDTH;
float fy = fx * dy / dx;
mVerts[index * 2 + 0] = fx + fx1;
mVerts[index * 2 + 1] = fy + fy1;
index += 1;
}
}
}
else if (mInhaleDir == InhaleDir.UP)
{
for (int y = HEIGHT; y >= 0; --y)
{
mFirstPathMeasure.getPosTan(y * FIRST_H + firstPointDist, pos1, null);
mSecondPathMeasure.getPosTan(y * SECOND_H + secondPointDist, pos2, null);
float w = pos2[0] - pos1[0];
float fx1 = pos1[0];
float fx2 = pos2[0];
float fy1 = pos1[1];
float fy2 = pos2[1];
float dy = fy2 - fy1;
float dx = fx2 - fx1;
for (int x = 0; x <= WIDTH; ++x)
{
// y = x * dy / dx
float fx = x * w / WIDTH;
float fy = fx * dy / dx;
mVerts[index * 2 + 0] = fx + fx1;
mVerts[index * 2 + 1] = fy + fy1;
index += 1;
}
}
}
}
private void buildMeshByPathOnHorizontal(int timeIndex)
{
mFirstPathMeasure.setPath(mFirstPath, false);
mSecondPathMeasure.setPath(mSecondPath, false);
int index = 0;
float[] pos1 = {0.0f, 0.0f};
float[] pos2 = {0.0f, 0.0f};
float firstLen = mFirstPathMeasure.getLength();
float secondLen = mSecondPathMeasure.getLength();
float len1 = firstLen / WIDTH;
float len2 = secondLen / WIDTH;
float firstPointDist = timeIndex * len1;
float secondPointDist = timeIndex * len2;
float width = mBmpWidth;
mFirstPathMeasure.getPosTan(firstPointDist, pos1, null);
mFirstPathMeasure.getPosTan(firstPointDist + width, pos2, null);
float x1 = pos1[0];
float x2 = pos2[0];
float y1 = pos1[1];
float y2 = pos2[1];
float FIRST_DIST = (float)Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );
float FIRST_X = FIRST_DIST / WIDTH;
mSecondPathMeasure.getPosTan(secondPointDist, pos1, null);
mSecondPathMeasure.getPosTan(secondPointDist + width, pos2, null);
x1 = pos1[0];
x2 = pos2[0];
y1 = pos1[1];
y2 = pos2[1];
float SECOND_DIST = (float)Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );
float SECOND_X = SECOND_DIST / WIDTH;
if (mInhaleDir == InhaleDir.RIGHT)
{
for (int x = 0; x <= WIDTH; ++x)
{
mFirstPathMeasure.getPosTan(x * FIRST_X + firstPointDist, pos1, null);
mSecondPathMeasure.getPosTan(x * SECOND_X + secondPointDist, pos2, null);
float h = pos2[1] - pos1[1];
float fx1 = pos1[0];
float fx2 = pos2[0];
float fy1 = pos1[1];
float fy2 = pos2[1];
float dy = fy2 - fy1;
float dx = fx2 - fx1;
for (int y = 0; y <= HEIGHT; ++y)
{
// x = y * dx / dy
float fy = y * h / HEIGHT;
float fx = fy * dx / dy;
index = y * (WIDTH + 1) + x;
mVerts[index * 2 + 0] = fx + fx1;
mVerts[index * 2 + 1] = fy + fy1;
}
}
}
else if (mInhaleDir == InhaleDir.LEFT)
{
for (int x = WIDTH; x >= 0; --x)
//for (int x = 0; x <= WIDTH; ++x)
{
mFirstPathMeasure.getPosTan(x * FIRST_X + firstPointDist, pos1, null);
mSecondPathMeasure.getPosTan(x * SECOND_X + secondPointDist, pos2, null);
float h = pos2[1] - pos1[1];
float fx1 = pos1[0];
float fx2 = pos2[0];
float fy1 = pos1[1];
float fy2 = pos2[1];
float dy = fy2 - fy1;
float dx = fx2 - fx1;
for (int y = 0; y <= HEIGHT; ++y)
{
// x = y * dx / dy
float fy = y * h / HEIGHT;
float fx = fy * dx / dy;
index = y * (WIDTH + 1) + WIDTH - x;
mVerts[index * 2 + 0] = fx + fx1;
mVerts[index * 2 + 1] = fy + fy1;
}
}
}
}
}

Mesh類的實現
復制代碼 代碼如下:

/*
* System: CoreLib
* @version 1.00
*
* Copyright (C) 2010, LZT Corporation.
*
*/
package com.nj1s.lib.mesh;
public abstract class Mesh
{
protected int WIDTH = 40;
protected int HEIGHT = 40;
protected int mBmpWidth = -1;
protected int mBmpHeight = -1;
protected final float[] mVerts;
public Mesh(int width, int height)
{
WIDTH = width;
HEIGHT = height;
mVerts = new float[(WIDTH + 1) * (HEIGHT + 1) * 2];
}
public float[] getVertices()
{
return mVerts;
}
public int getWidth()
{
return WIDTH;
}
public int getHeight()
{
return HEIGHT;
}
public static void setXY(float[] array, int index, float x, float y)
{
array[index*2 + 0] = x;
array[index*2 + 1] = y;
}
public void setBitmapSize(int w, int h)
{
mBmpWidth = w;
mBmpHeight = h;
}
public abstract void buildPaths(float endX, float endY);
public abstract void buildMeshes(int index);
public void buildMeshes(float w, float h)
{
int index = 0;
for (int y = 0; y <= HEIGHT; ++y)
{
float fy = y * h / HEIGHT;
for (int x = 0; x <= WIDTH; ++x)
{
float fx = x * w / WIDTH;
setXY(mVerts, index, fx, fy);
index += 1;
}
}
}
}
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
高清日韩电视剧大全免费播放在线观看| 91亚洲精品一区二区| 中文字幕视频一区二区在线有码| 日韩午夜在线视频| 日韩欧美高清视频| 亚洲一区二区中文| 亚洲国产精久久久久久久| 国产日韩欧美黄色| 欧美日韩国产在线播放| 日韩综合中文字幕| 91精品久久久久久久| 欧美性受xxxx黑人猛交| 奇米影视亚洲狠狠色| 欧美乱人伦中文字幕在线| 欧美成人在线免费| 久久综合九色九九| 欧美特黄级在线| 国产精品久久二区| 日韩一区av在线| 欧美成人中文字幕在线| 91久久久亚洲精品| 国产日韩在线看片| 国产精品欧美日韩| 久久久噜噜噜久久中文字免| 国产精品免费一区豆花| 国产精品69久久久久| 久久久久在线观看| 欧美大肥婆大肥bbbbb| 日韩免费在线观看视频| 91精品在线观看视频| 成人黄色午夜影院| 亚洲精品wwww| 国产精品欧美一区二区| 久久国产精品免费视频| 69**夜色精品国产69乱| 久久综合久久88| 亚洲第五色综合网| 精品爽片免费看久久| 国产一区深夜福利| 4p变态网欧美系列| 成人动漫网站在线观看| 精品久久久久久久久久ntr影视| 亚洲无av在线中文字幕| 欧美成人免费全部| 久久久久久高潮国产精品视| 欧美午夜影院在线视频| 亚洲成人久久网| 国产成人在线播放| 韩国精品美女www爽爽爽视频| 成人妇女免费播放久久久| 日韩欧亚中文在线| 日本三级韩国三级久久| 亚洲毛片在线观看.| 欧美色视频日本高清在线观看| 亚洲亚裔videos黑人hd| 日本欧美国产在线| 这里只有视频精品| 精品亚洲一区二区| 国产精品a久久久久久| 日韩免费在线视频| 久久久久成人网| 91精品国产高清久久久久久久久| 国产九九精品视频| 欧美日韩国产中文精品字幕自在自线| 国产精品第3页| 国产日韩av高清| 91视频88av| 欧美日韩国产一区二区| 自拍偷拍亚洲精品| 中文字幕欧美专区| 精品国产成人在线| 亚洲欧美激情另类校园| 欧美成在线观看| 欧美在线激情视频| 国产午夜精品一区理论片飘花| 久久精品久久久久久国产 免费| 国产精品成久久久久三级| 欧美成人精品不卡视频在线观看| 亚洲电影免费观看高清完整版在线观看| 欧美精品激情视频| 亚洲人成网站999久久久综合| 国产一区二中文字幕在线看| 97碰在线观看| 国产一区二区欧美日韩| 亚洲欧美日韩国产中文| 欧美在线激情网| 自拍偷拍亚洲精品| 久久久亚洲成人| 亚洲自拍小视频免费观看| 久久久久九九九九| 亚洲国产欧美精品| 国产成人综合av| 亚洲欧美制服丝袜| 日本一区二区在线播放| 夜夜躁日日躁狠狠久久88av| 久久精品国产成人精品| 亚洲精品福利免费在线观看| 国产精品三级在线| 疯狂欧美牲乱大交777| 91精品国产综合久久香蕉最新版| 精品久久久久久久久久久久| 日本伊人精品一区二区三区介绍| 国产欧美一区二区白浆黑人| 中国china体内裑精亚洲片| 欧美激情视频网址| 国产一级揄自揄精品视频| 日韩在线视频二区| 亚洲自拍中文字幕| 亚洲一级片在线看| 亚洲第一区中文99精品| 欧美另类69精品久久久久9999| 国产精品激情自拍| 国产高清视频一区三区| 亚洲自拍偷拍一区| 久久免费少妇高潮久久精品99| 亚洲综合最新在线| 91chinesevideo永久地址| 一本一本久久a久久精品牛牛影视| 国产精品h片在线播放| 日韩成人黄色av| 欧洲一区二区视频| 欧美激情视频在线观看| 欧美一区二区三区精品电影| 一区二区成人av| 久久偷看各类女兵18女厕嘘嘘| 26uuu国产精品视频| 欧美寡妇偷汉性猛交| 色噜噜狠狠狠综合曰曰曰| 国内免费精品永久在线视频| 欧美日韩激情小视频| 国产精品久久久久久久久久免费| 精品国产视频在线| 国产日韩欧美日韩大片| 最好看的2019的中文字幕视频| 中文字幕日韩高清| 国产在线精品一区免费香蕉| 国产精品电影久久久久电影网| 国产成人久久精品| 一区二区三区美女xx视频| 国产成人鲁鲁免费视频a| 色综合天天狠天天透天天伊人| 国语对白做受69| 最近2019年日本中文免费字幕| 在线看日韩欧美| 亚洲三级免费看| 欧美一区二区三区免费视| 欧美激情久久久久| 国产精品综合久久久| 不卡伊人av在线播放| 国产成人福利网站| 久久久久久久久久久久av| 欧美一级高清免费播放| 成人午夜在线观看| 欧美视频在线观看免费| 91久久久亚洲精品| 成人在线视频福利| 欧美夜福利tv在线| 久久久精品国产网站| 欧美精品aaa| 亚洲图中文字幕| 国产91精品黑色丝袜高跟鞋| 国产精品吴梦梦| 宅男66日本亚洲欧美视频| 久久久av亚洲男天堂|