├── .classpath ├── .gitignore ├── .project ├── AndroidManifest.xml ├── README.md ├── default.properties ├── res ├── anim │ ├── slide_left_in.xml │ ├── slide_left_out.xml │ ├── slide_right_in.xml │ └── slide_right_out.xml ├── drawable │ ├── icon.png │ ├── logo.png │ └── wait.png ├── layout │ ├── grid.xml │ └── main.xml └── values │ ├── mimetypes.xml │ └── strings.xml └── src └── com └── buuuk └── android ├── gallery ├── ImageGrid.java └── ImageViewFlipper.java ├── ui └── touch │ ├── EclairMotionEvent.java │ ├── TouchActivity.java │ └── WrapMotionEvent.java └── util ├── FileUtils.java └── Geometry.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated GUI files 12 | *R.java 13 | 14 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | ImageViewFlipper 4 | 5 | 6 | 7 | 8 | 9 | com.android.ide.eclipse.adt.ResourceManagerBuilder 10 | 11 | 12 | 13 | 14 | com.android.ide.eclipse.adt.PreCompilerBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.jdt.core.javabuilder 20 | 21 | 22 | 23 | 24 | com.android.ide.eclipse.adt.ApkBuilder 25 | 26 | 27 | 28 | 29 | 30 | com.android.ide.eclipse.adt.AndroidNature 31 | org.eclipse.jdt.core.javanature 32 | 33 | 34 | -------------------------------------------------------------------------------- /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Android image gallery sample 2 | 3 | #### Reference 4 | - based on [ImageViewFlipper](http://androidworkz.com/2010/07/06/source-code-imageview-flipper-sd-card-scanner/) 5 | - based on [Hello Android! example for zoom](http://pragprog.com/titles/eband/hello-android) 6 | 7 | #### Purpose 8 | - sample codes for who want to implement gallery feature in your android app 9 | 10 | #### Basic Gallery Features 11 | - gallery view 12 | - full page view 13 | - drag 14 | - zoom in/out 15 | - swipe to next picture 16 | 17 | #### contributed by 18 | - [@alvinsj](http://twitter.com/alvinsj) 19 | -------------------------------------------------------------------------------- /default.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system use, 7 | # "build.properties", and override values to adapt the script to your 8 | # project structure. 9 | 10 | # Indicates whether an apk should be generated for each density. 11 | split.density=false 12 | # Project target. 13 | target=android-8 14 | -------------------------------------------------------------------------------- /res/anim/slide_left_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /res/anim/slide_left_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /res/anim/slide_right_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /res/anim/slide_right_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /res/drawable/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alvinsj/android-image-gallery/d0fb44085c367fde3719b36bc14b9538320c258f/res/drawable/icon.png -------------------------------------------------------------------------------- /res/drawable/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alvinsj/android-image-gallery/d0fb44085c367fde3719b36bc14b9538320c258f/res/drawable/logo.png -------------------------------------------------------------------------------- /res/drawable/wait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alvinsj/android-image-gallery/d0fb44085c367fde3719b36bc14b9538320c258f/res/drawable/wait.png -------------------------------------------------------------------------------- /res/layout/grid.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 31 | -------------------------------------------------------------------------------- /res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 10 | 17 | 18 | 19 | 23 | 30 | 31 | 32 | 36 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /res/values/mimetypes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | bmp 5 | cmx 6 | cod 7 | gif 8 | ico 9 | ief 10 | jpe 11 | jpeg 12 | jpg 13 | jfif 14 | pbm 15 | pgm 16 | png 17 | pnm 18 | ppm 19 | ras 20 | rgb 21 | svg 22 | tif 23 | tiff 24 | xbm 25 | xpm 26 | xwd 27 | 28 | 29 | -------------------------------------------------------------------------------- /res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ImageViewFlipper 5 | 6 | -------------------------------------------------------------------------------- /src/com/buuuk/android/gallery/ImageGrid.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.buuuk.android.gallery; 18 | 19 | import android.app.Activity; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.content.SharedPreferences; 23 | import android.content.pm.ResolveInfo; 24 | import android.content.res.Resources; 25 | import android.graphics.Bitmap; 26 | import android.graphics.BitmapFactory; 27 | import android.graphics.Matrix; 28 | import android.graphics.drawable.BitmapDrawable; 29 | import android.graphics.drawable.Drawable; 30 | import android.os.AsyncTask; 31 | import android.os.Bundle; 32 | import android.os.SystemClock; 33 | import android.view.View; 34 | import android.view.ViewGroup; 35 | import android.view.View.OnClickListener; 36 | import android.widget.AbsListView; 37 | import android.widget.BaseAdapter; 38 | import android.widget.GridView; 39 | import android.widget.ImageView; 40 | import android.widget.Toast; 41 | import android.widget.AbsListView.OnScrollListener; 42 | 43 | import java.io.File; 44 | import java.io.FilenameFilter; 45 | import java.lang.ref.SoftReference; 46 | import java.util.ArrayList; 47 | import java.util.HashMap; 48 | import java.util.List; 49 | import java.util.Map; 50 | 51 | 52 | //Need the following import to get access to the app resources, since this 53 | //class is in a sub-package. 54 | import com.buuuk.android.gallery.R; 55 | import com.buuuk.android.util.FileUtils; 56 | 57 | 58 | public class ImageGrid extends Activity implements OnScrollListener{ 59 | 60 | GridView mGrid; 61 | private static final String DIRECTORY = "/sdcard/"; 62 | private static final String DATA_DIRECTORY = "/sdcard/.ImageViewFlipper/"; 63 | private static final String DATA_FILE = "/sdcard/.ImageViewFlipper/imagelist.dat"; 64 | List ImageList; 65 | 66 | @Override 67 | protected void onCreate(Bundle savedInstanceState) { 68 | super.onCreate(savedInstanceState); 69 | 70 | loadApps(); // do this in onresume? 71 | 72 | setContentView(R.layout.grid); 73 | mGrid = (GridView) findViewById(R.id.myGrid); 74 | mGrid.setOnScrollListener(this); 75 | 76 | 77 | File data_directory = new File(DATA_DIRECTORY); 78 | if (!data_directory.exists()) { 79 | if (data_directory.mkdir()) { 80 | FileUtils savedata = new FileUtils(); 81 | Toast toast = Toast.makeText(ImageGrid.this, 82 | "Please wait while we search your SD Card for images...", Toast.LENGTH_SHORT); 83 | toast.show(); 84 | SystemClock.sleep(100); 85 | ImageList = FindFiles(); 86 | savedata.saveArray(DATA_FILE, ImageList); 87 | 88 | } else { 89 | ImageList = FindFiles(); 90 | } 91 | 92 | } 93 | else { 94 | File data_file= new File(DATA_FILE); 95 | if (!data_file.exists()) { 96 | FileUtils savedata = new FileUtils(); 97 | Toast toast = Toast.makeText(ImageGrid.this, 98 | "Please wait while we search your SD Card for images...", Toast.LENGTH_SHORT); 99 | toast.show(); 100 | SystemClock.sleep(100); 101 | ImageList = FindFiles(); 102 | savedata.saveArray(DATA_FILE, ImageList); 103 | } else { 104 | FileUtils readdata = new FileUtils(); 105 | ImageList = readdata.loadArray(DATA_FILE); 106 | } 107 | } 108 | mAdapter = new AppsAdapter(); 109 | mGrid.setAdapter(mAdapter); 110 | mThumbnails = new HashMap>(); 111 | mThumbnailImages = new HashMap>(); 112 | } 113 | 114 | private List FindFiles() { 115 | final List tFileList = new ArrayList(); 116 | Resources resources = getResources(); 117 | // array of valid image file extensions 118 | String[] imageTypes = resources.getStringArray(R.array.image); 119 | FilenameFilter[] filter = new FilenameFilter[imageTypes.length]; 120 | 121 | int i = 0; 122 | for (final String type : imageTypes) { 123 | filter[i] = new FilenameFilter() { 124 | public boolean accept(File dir, String name) { 125 | return name.endsWith("." + type); 126 | } 127 | }; 128 | i++; 129 | } 130 | 131 | FileUtils fileUtils = new FileUtils(); 132 | File[] allMatchingFiles = fileUtils.listFilesAsArray( 133 | new File(DIRECTORY), filter, -1); 134 | for (File f : allMatchingFiles) { 135 | tFileList.add(f.getAbsolutePath()); 136 | } 137 | return tFileList; 138 | } 139 | 140 | private List mApps; 141 | 142 | private void loadApps() { 143 | Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); 144 | mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); 145 | 146 | mApps = getPackageManager().queryIntentActivities(mainIntent, 0); 147 | } 148 | 149 | public AppsAdapter mAdapter; 150 | public class AppsAdapter extends BaseAdapter { 151 | public AppsAdapter() { 152 | map = new HashMap(); 153 | } 154 | 155 | public Map> map; 156 | public View getView(final int position, View convertView, ViewGroup parent) { 157 | ImageView i; 158 | 159 | if (convertView == null) { 160 | i = new ImageView(ImageGrid.this); 161 | i.setScaleType(ImageView.ScaleType.FIT_CENTER); 162 | i.setLayoutParams(new GridView.LayoutParams(80, 80)); 163 | } else { 164 | i = (ImageView) convertView; 165 | } 166 | 167 | if(!mBusy && mThumbnailImages.containsKey(position) 168 | && mThumbnailImages.get(position).get()!=null) { 169 | i.setImageBitmap(mThumbnailImages.get(position).get()); 170 | } 171 | else { 172 | i.setImageBitmap(null); 173 | if(!mBusy)loadThumbnail(i,position); 174 | } 175 | 176 | i.setOnClickListener(new OnClickListener() { 177 | 178 | @Override 179 | public void onClick(View v) { 180 | Toast.makeText(ImageGrid.this, "Opening Image...", Toast.LENGTH_LONG).show(); 181 | 182 | // TODO Auto-generated method stub 183 | SharedPreferences indexPrefs = getSharedPreferences("currentIndex", 184 | MODE_PRIVATE); 185 | 186 | SharedPreferences.Editor indexEditor = indexPrefs.edit(); 187 | indexEditor.putInt("currentIndex", position); 188 | indexEditor.commit(); 189 | final Intent intent = new Intent(ImageGrid.this, ImageViewFlipper.class); 190 | startActivity(intent); 191 | 192 | } 193 | }); 194 | 195 | 196 | return i; 197 | } 198 | 199 | 200 | public final int getCount() { 201 | return ImageList.size(); 202 | } 203 | 204 | public final Object getItem(int position) { 205 | return ImageList.get(position); 206 | } 207 | 208 | public final long getItemId(int position) { 209 | return position; 210 | } 211 | } 212 | public void onScroll(AbsListView view, int firstVisibleItem, 213 | int visibleItemCount, int totalItemCount) { 214 | 215 | } 216 | 217 | public boolean mBusy = false; 218 | public void onScrollStateChanged(AbsListView view, int scrollState) { 219 | switch (scrollState) { 220 | case OnScrollListener.SCROLL_STATE_IDLE: 221 | mBusy = false; 222 | mAdapter.notifyDataSetChanged(); 223 | break; 224 | case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: 225 | mBusy = true; 226 | // mStatus.setText("Touch scroll"); 227 | break; 228 | case OnScrollListener.SCROLL_STATE_FLING: 229 | mBusy = true; 230 | // mStatus.setText("Fling"); 231 | break; 232 | } 233 | } 234 | 235 | private Map> mThumbnails; 236 | private Map> mThumbnailImages; 237 | 238 | private void loadThumbnail( ImageView iv, int position ){ 239 | mThumbnails.put(position,new SoftReference(iv)); 240 | try{new LoadThumbnailTask().execute(position);}catch(Exception e){} 241 | } 242 | public void onThumbnailLoaded( int position, Bitmap bm, LoadThumbnailTask t ){ 243 | Bitmap tn = bm; 244 | if( mThumbnails.get(position).get() != null && tn!=null) 245 | mThumbnails.get(position).get().setImageBitmap(tn); 246 | 247 | t.cancel(true); 248 | } 249 | 250 | public class LoadThumbnailTask extends AsyncTask{ 251 | private int position; 252 | @Override 253 | protected Bitmap doInBackground(Integer... params) { 254 | try{ 255 | position = params[0]; 256 | Bitmap bitmapOrg = BitmapFactory.decodeFile(ImageList.get(position)); 257 | 258 | int width = bitmapOrg.getWidth(); 259 | int height = bitmapOrg.getHeight(); 260 | 261 | //new width / height 262 | int newWidth = 80; 263 | int newHeight = 80; 264 | 265 | // calculate the scale 266 | float scaleWidth = (float) newWidth / width; 267 | float scaleHeight = (float) newHeight/ (height * scaleWidth) ; 268 | // create a matrix for the manipulation 269 | Matrix matrix = new Matrix(); 270 | 271 | // resize the bit map 272 | matrix.postScale(scaleWidth, scaleWidth); 273 | matrix.postScale(scaleHeight, scaleHeight); 274 | 275 | // recreate the new Bitmap and set it back 276 | Bitmap bm = Bitmap.createBitmap(bitmapOrg, 0, 0,width, height, matrix, true); 277 | 278 | mThumbnailImages.put(position, new SoftReference(bm)); 279 | System.gc(); 280 | return bm; 281 | }catch(Exception e){ 282 | 283 | } 284 | 285 | 286 | return null; 287 | } 288 | protected void onPostExecute(Bitmap bm) { 289 | 290 | onThumbnailLoaded(position, bm, this); 291 | } 292 | 293 | } 294 | 295 | } 296 | -------------------------------------------------------------------------------- /src/com/buuuk/android/gallery/ImageViewFlipper.java: -------------------------------------------------------------------------------- 1 | package com.buuuk.android.gallery; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileOutputStream; 6 | import java.io.FilenameFilter; 7 | import java.io.IOException; 8 | import java.io.ObjectInputStream; 9 | import java.io.ObjectOutputStream; 10 | import java.io.OutputStreamWriter; 11 | import java.util.ArrayList; 12 | import java.util.Collection; 13 | import java.util.List; 14 | import java.util.Timer; 15 | import java.util.TimerTask; 16 | import java.util.Vector; 17 | import java.util.zip.GZIPInputStream; 18 | import java.util.zip.GZIPOutputStream; 19 | 20 | import com.buuuk.android.gallery.R; 21 | import com.buuuk.android.ui.touch.TouchActivity; 22 | import com.buuuk.android.ui.touch.WrapMotionEvent; 23 | import com.buuuk.android.util.FileUtils; 24 | 25 | import android.app.Activity; 26 | import android.content.SharedPreferences; 27 | import android.content.res.Resources; 28 | import android.graphics.Bitmap; 29 | import android.graphics.BitmapFactory; 30 | import android.graphics.Matrix; 31 | import android.graphics.PointF; 32 | import android.graphics.drawable.Drawable; 33 | import android.os.Bundle; 34 | import android.os.SystemClock; 35 | import android.util.Log; 36 | import android.view.Display; 37 | import android.view.GestureDetector; 38 | import android.view.Menu; 39 | import android.view.MenuItem; 40 | import android.view.MotionEvent; 41 | import android.view.ScaleGestureDetector; 42 | import android.view.Surface; 43 | import android.view.View; 44 | import android.view.Window; 45 | import android.view.WindowManager; 46 | import android.view.GestureDetector.SimpleOnGestureListener; 47 | import android.view.animation.Animation; 48 | import android.view.animation.AnimationUtils; 49 | import android.widget.FrameLayout; 50 | import android.widget.ImageView; 51 | import android.widget.Toast; 52 | import android.widget.ViewFlipper; 53 | 54 | public class ImageViewFlipper extends TouchActivity { 55 | 56 | private static final int EXIT = 0; 57 | private static final int SWIPE_MIN_DISTANCE = 120; 58 | private static final int SWIPE_MAX_OFF_PATH = 250; 59 | private static final int SWIPE_THRESHOLD_VELOCITY = 200; 60 | private static final String DIRECTORY = "/sdcard/"; 61 | private static final String DATA_DIRECTORY = "/sdcard/.ImageViewFlipper/"; 62 | private static final String DATA_FILE = "/sdcard/.ImageViewFlipper/imagelist.dat"; 63 | private GestureDetector gestureDetector; 64 | View.OnTouchListener gestureListener; 65 | private Animation slideLeftIn; 66 | private Animation slideLeftOut; 67 | private Animation slideRightIn; 68 | private Animation slideRightOut; 69 | private ViewFlipper viewFlipper; 70 | private int currentView = 0; 71 | List ImageList; 72 | private int currentIndex = 0; 73 | private int maxIndex = 0; 74 | private ImageView currentImageView = null; 75 | 76 | private float mMinZoomScale=1; 77 | 78 | FileOutputStream output = null; 79 | OutputStreamWriter writer = null; 80 | 81 | @Override 82 | public void onCreate(Bundle savedInstanceState) { 83 | super.onCreate(savedInstanceState); 84 | 85 | //requestWindowFeature(Window.FEATURE_NO_TITLE); 86 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 87 | WindowManager.LayoutParams.FLAG_FULLSCREEN); 88 | 89 | setContentView(R.layout.main); 90 | ImageView iv = (ImageView) findViewById(R.id.zero); 91 | /*iv.setOnTouchListener(this); 92 | findViewById(R.id.one).setOnTouchListener(this); 93 | findViewById(R.id.two).setOnTouchListener(this);*/ 94 | 95 | File data_directory = new File(DATA_DIRECTORY); 96 | if (!data_directory.exists()) { 97 | if (data_directory.mkdir()) { 98 | FileUtils savedata = new FileUtils(); 99 | Toast toast = Toast.makeText(ImageViewFlipper.this, 100 | "Please wait while we search your SD Card for images...", Toast.LENGTH_SHORT); 101 | toast.show(); 102 | SystemClock.sleep(100); 103 | ImageList = FindFiles(); 104 | savedata.saveArray(DATA_FILE, ImageList); 105 | 106 | } else { 107 | ImageList = FindFiles(); 108 | } 109 | 110 | } 111 | else { 112 | File data_file= new File(DATA_FILE); 113 | if (!data_file.exists()) { 114 | FileUtils savedata = new FileUtils(); 115 | Toast toast = Toast.makeText(ImageViewFlipper.this, 116 | "Please wait while we search your SD Card for images...", Toast.LENGTH_SHORT); 117 | toast.show(); 118 | SystemClock.sleep(100); 119 | ImageList = FindFiles(); 120 | savedata.saveArray(DATA_FILE, ImageList); 121 | } else { 122 | FileUtils readdata = new FileUtils(); 123 | ImageList = readdata.loadArray(DATA_FILE); 124 | } 125 | } 126 | 127 | if (ImageList == null) { 128 | quit(); 129 | } 130 | 131 | SharedPreferences indexPrefs = getSharedPreferences("currentIndex", 132 | MODE_PRIVATE); 133 | if (indexPrefs.contains("currentIndex")) { 134 | currentIndex = indexPrefs.getInt("currentIndex", 0); 135 | } 136 | 137 | maxIndex = ImageList.size() - 1; 138 | 139 | Log.v("currentIndex", "Index: "+currentIndex); 140 | 141 | viewFlipper = (ViewFlipper) findViewById(R.id.flipper); 142 | 143 | slideLeftIn = AnimationUtils.loadAnimation(this, R.anim.slide_left_in); 144 | slideLeftOut = AnimationUtils 145 | .loadAnimation(this, R.anim.slide_left_out); 146 | slideRightIn = AnimationUtils 147 | .loadAnimation(this, R.anim.slide_right_in); 148 | slideRightOut = AnimationUtils.loadAnimation(this, 149 | R.anim.slide_right_out); 150 | 151 | viewFlipper.setInAnimation(AnimationUtils.loadAnimation(this, 152 | android.R.anim.fade_in)); 153 | viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(this, 154 | android.R.anim.fade_out)); 155 | Drawable d = Drawable.createFromPath(ImageList 156 | .get(currentIndex)); 157 | iv.setImageDrawable(d); 158 | resetImage(iv,d); 159 | System.gc(); 160 | 161 | gestureDetector = new GestureDetector(new MyGestureDetector()); 162 | gestureListener = new View.OnTouchListener() { 163 | public boolean onTouch(View v, MotionEvent event) { 164 | if (gestureDetector.onTouchEvent(event)) { 165 | return true; 166 | } 167 | return false; 168 | } 169 | }; 170 | } 171 | 172 | @Override 173 | public boolean onCreateOptionsMenu(Menu menu) { 174 | super.onCreateOptionsMenu(menu); 175 | 176 | int NONE = Menu.NONE; 177 | menu.add(NONE, EXIT, NONE, "Exit"); 178 | return true; 179 | } 180 | 181 | public boolean onOptionsItemSelected(MenuItem item) { 182 | switch (item.getItemId()) { 183 | case EXIT: 184 | quit(); 185 | break; 186 | } 187 | 188 | return super.onOptionsItemSelected(item); 189 | } 190 | 191 | protected void onPause() { 192 | super.onPause(); 193 | 194 | SharedPreferences indexPrefs = getSharedPreferences("currentIndex", 195 | MODE_PRIVATE); 196 | 197 | SharedPreferences.Editor indexEditor = indexPrefs.edit(); 198 | indexEditor.putInt("currentIndex", currentIndex); 199 | indexEditor.commit(); 200 | } 201 | 202 | protected void onResume() { 203 | super.onResume(); 204 | SharedPreferences indexPrefs = getSharedPreferences("currentIndex", 205 | MODE_PRIVATE); 206 | if (indexPrefs.contains("currentIndex")) { 207 | currentIndex = indexPrefs.getInt("currentIndex", 0); 208 | } 209 | } 210 | 211 | private List FindFiles() { 212 | final List tFileList = new ArrayList(); 213 | Resources resources = getResources(); 214 | // array of valid image file extensions 215 | String[] imageTypes = resources.getStringArray(R.array.image); 216 | FilenameFilter[] filter = new FilenameFilter[imageTypes.length]; 217 | 218 | int i = 0; 219 | for (final String type : imageTypes) { 220 | filter[i] = new FilenameFilter() { 221 | public boolean accept(File dir, String name) { 222 | return name.endsWith("." + type); 223 | } 224 | }; 225 | i++; 226 | } 227 | 228 | FileUtils fileUtils = new FileUtils(); 229 | File[] allMatchingFiles = fileUtils.listFilesAsArray( 230 | new File(DIRECTORY), filter, -1); 231 | for (File f : allMatchingFiles) { 232 | tFileList.add(f.getAbsolutePath()); 233 | } 234 | return tFileList; 235 | } 236 | 237 | 238 | 239 | class MyGestureDetector extends SimpleOnGestureListener { 240 | private int toggleCount = 0; 241 | @Override 242 | public boolean onDoubleTap(final MotionEvent e){ 243 | 244 | 245 | ImageView view = (ImageView)findViewById(R.id.zero); 246 | switch(currentView){ 247 | case 0: view = (ImageView)findViewById(R.id.zero); break; 248 | case 1: view = (ImageView)findViewById(R.id.one); break; 249 | case 2:view = (ImageView)findViewById(R.id.two); break; 250 | } 251 | 252 | resetImage(view,view.getDrawable()); 253 | return true; 254 | } 255 | 256 | @Override 257 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, 258 | float velocityY) { 259 | try { 260 | if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) 261 | return false; 262 | // right to left swipe 263 | if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE 264 | && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { 265 | viewFlipper.setInAnimation(slideLeftIn); 266 | viewFlipper.setOutAnimation(slideLeftOut); 267 | 268 | if (currentIndex == maxIndex) { 269 | currentIndex = 0; 270 | } else { 271 | currentIndex = currentIndex + 1; 272 | } 273 | ImageView iv; 274 | Drawable d = Drawable.createFromPath(ImageList 275 | .get(currentIndex)); 276 | 277 | if (currentView == 0) { 278 | currentView = 1; 279 | iv = (ImageView) findViewById(R.id.one); 280 | 281 | iv.setImageDrawable(d); 282 | 283 | 284 | System.gc(); 285 | } else if (currentView == 1) { 286 | currentView = 2; 287 | iv = (ImageView) findViewById(R.id.two); 288 | 289 | iv.setImageDrawable(d); 290 | System.gc(); 291 | } else { 292 | currentView = 0; 293 | iv = (ImageView) findViewById(R.id.zero); 294 | 295 | iv.setImageDrawable(d); 296 | System.gc(); 297 | } 298 | resetImage(iv,d); 299 | Log.v("ImageViewFlipper", "Current View: " + currentView); 300 | viewFlipper.showNext(); 301 | 302 | return true; 303 | } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE 304 | && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { 305 | viewFlipper.setInAnimation(slideRightIn); 306 | viewFlipper.setOutAnimation(slideRightOut); 307 | 308 | 309 | 310 | if (currentIndex == 0) { 311 | currentIndex = maxIndex; 312 | } else { 313 | currentIndex = currentIndex - 1; 314 | } 315 | ImageView iv; 316 | Drawable d = Drawable.createFromPath(ImageList 317 | .get(currentIndex)); 318 | if (currentView == 0) { 319 | currentView = 2; 320 | iv = (ImageView) findViewById(R.id.two); 321 | iv.setImageDrawable(d); 322 | System.gc(); 323 | } else if (currentView == 2) { 324 | currentView = 1; 325 | iv = (ImageView) findViewById(R.id.one); 326 | iv.setImageDrawable(d); 327 | System.gc(); 328 | } else { 329 | currentView = 0; 330 | iv = (ImageView) findViewById(R.id.zero); 331 | iv.setImageDrawable(d); 332 | System.gc(); 333 | } 334 | resetImage(iv,d); 335 | Log.v("ImageViewFlipper", "Current View: " + currentView); 336 | viewFlipper.showPrevious(); 337 | return true; 338 | } 339 | } catch (Exception e) { 340 | // nothing 341 | } 342 | return false; 343 | } 344 | 345 | } 346 | @Override 347 | public void resetImage(ImageView iv, Drawable draw) { 348 | Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay(); 349 | int rotation = display.getRotation(); 350 | 351 | int orientation = 0; 352 | if( rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) 353 | orientation = 0; 354 | else 355 | orientation = 1; 356 | 357 | matrix = new Matrix(); 358 | matrix.setTranslate(1f, 1f); 359 | float scale = 1; 360 | 361 | mMinZoomScale = 1; 362 | if( orientation==0 363 | //&& (float)draw.getIntrinsicWidth() > (float)getWindowManager().getDefaultDisplay().getWidth() 364 | ) { 365 | 366 | scale = (float)getWindowManager().getDefaultDisplay().getWidth()/(float)draw.getIntrinsicWidth(); 367 | mMinZoomScale = scale; 368 | matrix.postScale(scale,scale); 369 | 370 | iv.setImageMatrix(matrix); 371 | }else if( orientation==1 372 | //&& (float)draw.getIntrinsicHeight() > (float)getWindowManager().getDefaultDisplay().getHeight() 373 | ){ 374 | scale = (float)getWindowManager().getDefaultDisplay().getHeight()/(float)draw.getIntrinsicHeight(); 375 | mMinZoomScale = scale; 376 | matrix.postScale(scale,scale); 377 | 378 | iv.setImageMatrix(matrix); 379 | } 380 | 381 | 382 | float transX = (float) getWindowManager().getDefaultDisplay().getWidth()/2 383 | - (float)(draw.getIntrinsicWidth()*scale)/2 384 | ; 385 | 386 | float transY = (float) 387 | getWindowManager().getDefaultDisplay().getHeight()/2 388 | - (float)(draw.getIntrinsicHeight()*scale)/2 389 | 390 | ; 391 | matrix.postTranslate(transX,transY); 392 | iv.setImageMatrix(matrix); 393 | } 394 | 395 | 396 | @Override 397 | public float getMinZoomScale(){ 398 | return mMinZoomScale; 399 | } 400 | 401 | @Override 402 | public boolean onTouchEvent(MotionEvent rawEvent) { 403 | if(gestureDetector.onTouchEvent(rawEvent)) 404 | return true; 405 | 406 | 407 | ImageView view = (ImageView)findViewById(R.id.zero); 408 | switch(currentView){ 409 | case 0: view = (ImageView)findViewById(R.id.zero); break; 410 | case 1: view = (ImageView)findViewById(R.id.one); break; 411 | case 2:view = (ImageView)findViewById(R.id.two); break; 412 | } 413 | onTouchEvented(view, rawEvent); 414 | 415 | return true; 416 | } 417 | 418 | 419 | public void quit() { 420 | SharedPreferences indexPrefs = getSharedPreferences("currentIndex", 421 | MODE_PRIVATE); 422 | 423 | SharedPreferences.Editor indexEditor = indexPrefs.edit(); 424 | indexEditor.putInt("currentIndex", 0); 425 | indexEditor.commit(); 426 | 427 | File settings = new File(DATA_FILE); 428 | settings.delete(); 429 | finish(); 430 | int pid = android.os.Process.myPid(); 431 | android.os.Process.killProcess(pid); 432 | System.exit(0); 433 | } 434 | 435 | 436 | } -------------------------------------------------------------------------------- /src/com/buuuk/android/ui/touch/EclairMotionEvent.java: -------------------------------------------------------------------------------- 1 | /*** 2 | * Excerpted from "Hello, Android! 3e", 3 | * published by The Pragmatic Bookshelf. 4 | * Copyrights apply to this code. It may not be used to create training material, 5 | * courses, books, articles, and the like. Contact us if you are in doubt. 6 | * We make no guarantees that this code is fit for any purpose. 7 | * Visit http://www.pragmaticprogrammer.com/titles/eband3 for more book information. 8 | ***/ 9 | package com.buuuk.android.ui.touch; 10 | 11 | import android.view.MotionEvent; 12 | 13 | public class EclairMotionEvent extends WrapMotionEvent { 14 | 15 | protected EclairMotionEvent(MotionEvent event) { 16 | super(event); 17 | } 18 | 19 | public float getX(int pointerIndex) { 20 | return event.getX(pointerIndex); 21 | } 22 | 23 | public float getY(int pointerIndex) { 24 | return event.getY(pointerIndex); 25 | } 26 | 27 | public int getPointerCount() { 28 | return event.getPointerCount(); 29 | } 30 | 31 | public int getPointerId(int pointerIndex) { 32 | return event.getPointerId(pointerIndex); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/com/buuuk/android/ui/touch/TouchActivity.java: -------------------------------------------------------------------------------- 1 | /*** 2 | * Excerpted from "Hello, Android! 3e", 3 | * published by The Pragmatic Bookshelf. 4 | * Copyrights apply to this code. It may not be used to create training material, 5 | * courses, books, articles, and the like. Contact us if you are in doubt. 6 | * We make no guarantees that this code is fit for any purpose. 7 | * Visit http://www.pragmaticprogrammer.com/titles/eband3 for more book information. 8 | ***/ 9 | package com.buuuk.android.ui.touch; 10 | 11 | import com.buuuk.android.util.Geometry; 12 | 13 | import android.R; 14 | import android.app.Activity; 15 | import android.graphics.Matrix; 16 | import android.graphics.PointF; 17 | import android.graphics.Rect; 18 | import android.graphics.drawable.Drawable; 19 | import android.os.Bundle; 20 | import android.util.FloatMath; 21 | import android.util.Log; 22 | import android.view.Display; 23 | import android.view.MotionEvent; 24 | import android.view.Surface; 25 | import android.view.View; 26 | import android.view.WindowManager; 27 | import android.view.View.OnTouchListener; 28 | import android.widget.ImageView; 29 | 30 | public abstract class TouchActivity extends Activity { 31 | private static final String TAG = "Touch"; 32 | // These matrices will be used to move and zoom image 33 | protected Matrix matrix = new Matrix(); 34 | Matrix savedMatrix = new Matrix(); 35 | 36 | // We can be in one of these 3 states 37 | static final int NONE = 0; 38 | static final int DRAG = 1; 39 | static final int ZOOM = 2; 40 | int mode = NONE; 41 | 42 | // Remember some things for zooming 43 | PointF start = new PointF(); 44 | PointF refLineStart = null; 45 | PointF refLineEnd = null; 46 | PointF prevLineStart = null; 47 | PointF prevLineEnd = null; 48 | PointF newStart = new PointF(); 49 | PointF newEnd = new PointF(); 50 | float oldAngle = 0f; 51 | 52 | PointF mid = new PointF(); 53 | float oldDist = 1f; 54 | Float mRotateAngle = 0f; 55 | float scaleFactor = 0f; 56 | 57 | @Override 58 | public void onCreate(Bundle savedInstanceState) { 59 | super.onCreate(savedInstanceState); 60 | //setContentView(getContentView()); 61 | //ImageView view = getImageView(); 62 | //view.setOnTouchListener(this); 63 | 64 | // ... 65 | // Work around a Cupcake bug 66 | matrix.setTranslate(1f, 1f); 67 | //view.setImageMatrix(matrix); 68 | } 69 | 70 | public abstract float getMinZoomScale(); 71 | public abstract void resetImage(ImageView iv, Drawable draw); 72 | 73 | 74 | public boolean onTouchEvented(View v, MotionEvent rawEvent) { 75 | 76 | WrapMotionEvent event = WrapMotionEvent.wrap(rawEvent); 77 | // ... 78 | ImageView view = (ImageView)v; 79 | 80 | // Dump touch event to log 81 | dumpEvent(event); 82 | 83 | // Handle touch events here... 84 | switch (event.getAction() & MotionEvent.ACTION_MASK) { 85 | case MotionEvent.ACTION_DOWN: 86 | savedMatrix.set(matrix); 87 | start.set(event.getX(), event.getY()); 88 | Log.d(TAG, "mode=DRAG"); 89 | mode = DRAG; 90 | break; 91 | case MotionEvent.ACTION_POINTER_DOWN: 92 | oldDist = spacing(event); 93 | Log.d(TAG, "oldDist=" + oldDist); 94 | if (oldDist > 10f) { 95 | savedMatrix.set(matrix); 96 | midPoint(mid, event); 97 | mode = ZOOM; 98 | Log.d(TAG, "mode=ZOOM"); 99 | 100 | // set the old line if first attempt 101 | refLineStart = new PointF(); 102 | refLineEnd = new PointF(); 103 | refLineStart.set(event.getX(0), event.getY(0)); 104 | refLineEnd.set(event.getX(1), event.getY(1)); 105 | prevLineStart = new PointF(); 106 | prevLineEnd = new PointF(); 107 | prevLineStart.set(event.getX(0), event.getY(0)); 108 | prevLineEnd.set(event.getX(1), event.getY(1)); 109 | } 110 | break; 111 | case MotionEvent.ACTION_UP: 112 | case MotionEvent.ACTION_POINTER_UP: 113 | mode = NONE; 114 | Log.d(TAG, "mode=NONE"); 115 | refLineStart = null; 116 | refLineEnd = null; 117 | mRotateAngle = 0f; 118 | 119 | boolean isScaleChanged=true; 120 | float[] values = new float[9]; 121 | view.getImageMatrix().getValues(values); 122 | if(values[0]<=getMinZoomScale()) { 123 | resetImage(view,view.getDrawable()); 124 | isScaleChanged=false; 125 | } 126 | 127 | 128 | 129 | Rect rect = new Rect(); 130 | view.getDrawingRect(rect); 131 | Log.d("ImageView","Drawing Rect: "+rect.top+","+rect.right+","+rect.bottom+","+rect.left); 132 | 133 | float[] point = new float[2]; 134 | 135 | //for top and left 136 | point[0]= rect.left; 137 | point[1]= rect.top; 138 | float[] topleft = new float[2]; 139 | view.getImageMatrix().mapPoints(topleft,point); // topleft image point after applying matrix 140 | 141 | point[0] = view.getDrawable().getIntrinsicWidth(); 142 | point[1] = view.getDrawable().getIntrinsicHeight(); 143 | float[] bottomright = new float[2]; 144 | view.getImageMatrix().mapPoints(bottomright,point); //bottomright image point after applying matrix 145 | 146 | 147 | // get orientation 148 | Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay(); 149 | int rotation = display.getRotation(); 150 | 151 | int orientation = 0; 152 | if( rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) 153 | orientation = 0; 154 | else 155 | orientation = 1; 156 | 157 | 158 | boolean direction = true; 159 | 160 | float width = bottomright[0]-topleft[0]; 161 | float height = bottomright[1]-topleft[1]; 162 | 163 | 164 | if( (width>rect.right && height>rect.bottom) ) 165 | direction=true; 166 | else 167 | direction=false; 168 | 169 | 170 | 171 | // snap to topright 172 | if(topleft[0]>rect.left && bottomright[0]>rect.right && isScaleChanged && direction){ 173 | matrix.postTranslate(-topleft[0], 0); 174 | } 175 | else if(bottomright[0]rect.left && bottomright[0]>rect.right && isScaleChanged && !direction){ 179 | matrix.postTranslate(-(bottomright[0]-rect.right), 0); 180 | } 181 | else if(bottomright[0]rect.bottom && topleft[1]>rect.top && isScaleChanged && direction){ 187 | matrix.postTranslate(0, -topleft[1]); 188 | } 189 | else if(bottomright[1]rect.bottom && topleft[1]>rect.top && isScaleChanged && !direction){ 193 | matrix.postTranslate(0, -(bottomright[1]-rect.bottom)); 194 | } 195 | else if(bottomright[1]=getMinZoomScale()) 227 | matrix.postScale(scale, scale, mid.x, mid.y); 228 | //else 229 | // matrix.setScale(getMinZoomScale(), getMinZoomScale(), mid.x, mid.y); 230 | 231 | // get the latest line 232 | newStart.set(event.getX(0), event.getY(0)); 233 | newEnd.set(event.getX(1), event.getY(1)); 234 | 235 | float angle = new Float(angleBetweenLinesInRadians(prevLineStart,prevLineEnd,newStart,newEnd)).floatValue(); 236 | 237 | 238 | 239 | // calculate the angle difference and do rotation 240 | 241 | mRotateAngle = mRotateAngle + angle; 242 | 243 | //matrix.postRotate( mRotateAngle ,mid.x, mid.y ); 244 | // record the old line value 245 | prevLineStart.set(event.getX(0), event.getY(0)); 246 | prevLineEnd.set(event.getX(1), event.getY(1)); 247 | } 248 | 249 | } 250 | break; 251 | } 252 | 253 | Log.v("Matrix","scale matrix: "+matrix); 254 | 255 | view.setImageMatrix(matrix); 256 | 257 | 258 | System.gc(); 259 | 260 | return true; // indicate event was handled 261 | } 262 | 263 | public double angleBetweenLinesInRadians( PointF line1Start, PointF line1End, PointF line2Start, PointF line2End) { 264 | 265 | float a = line1End.x - line1Start.x; 266 | float b = line1End.y - line1Start.y; 267 | float c = line2End.x - line2Start.x; 268 | float d = line2End.y - line2Start.y; 269 | 270 | float line1Slope = (line1End.y - line1Start.y) / (line1End.x - line1Start.x); 271 | float line2Slope = (line2End.y - line2Start.y) / (line2End.x - line2Start.x); 272 | 273 | double subf = ((a*c) + (b*d)) / ((Math.sqrt(a*a + b*b)) * (Math.sqrt(c*c + d*d))); 274 | 275 | subf = subf>1?1:subf; 276 | subf = subf<-1?-1:subf; 277 | double degs = Math.acos(subf); 278 | 279 | double result = (line2Slope > line1Slope) ? degs*180/3.142 : -degs*180/3.142; 280 | 281 | return result; 282 | } 283 | 284 | /** Show an event in the LogCat view, for debugging */ 285 | private void dumpEvent(WrapMotionEvent event) { 286 | // ... 287 | String names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE", 288 | "POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?" }; 289 | StringBuilder sb = new StringBuilder(); 290 | int action = event.getAction(); 291 | int actionCode = action & MotionEvent.ACTION_MASK; 292 | sb.append("event ACTION_").append(names[actionCode]); 293 | if (actionCode == MotionEvent.ACTION_POINTER_DOWN 294 | || actionCode == MotionEvent.ACTION_POINTER_UP) { 295 | sb.append("(pid ").append( 296 | action >> MotionEvent.ACTION_POINTER_ID_SHIFT); 297 | sb.append(")"); 298 | } 299 | sb.append("["); 300 | for (int i = 0; i < event.getPointerCount(); i++) { 301 | sb.append("#").append(i); 302 | sb.append("(pid ").append(event.getPointerId(i)); 303 | sb.append(")=").append((int) event.getX(i)); 304 | sb.append(",").append((int) event.getY(i)); 305 | if (i + 1 < event.getPointerCount()) 306 | sb.append(";"); 307 | } 308 | sb.append("]"); 309 | Log.d(TAG, sb.toString()); 310 | } 311 | 312 | /** Determine the space between the first two fingers */ 313 | private float spacing(WrapMotionEvent event) { 314 | // ... 315 | float x = event.getX(0) - event.getX(1); 316 | float y = event.getY(0) - event.getY(1); 317 | return FloatMath.sqrt(x * x + y * y); 318 | } 319 | 320 | /** Calculate the mid point of the first two fingers */ 321 | private void midPoint(PointF point, WrapMotionEvent event) { 322 | // ... 323 | float x = event.getX(0) + event.getX(1); 324 | float y = event.getY(0) + event.getY(1); 325 | point.set(x / 2, y / 2); 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /src/com/buuuk/android/ui/touch/WrapMotionEvent.java: -------------------------------------------------------------------------------- 1 | /*** 2 | * Excerpted from "Hello, Android! 3e", 3 | * published by The Pragmatic Bookshelf. 4 | * Copyrights apply to this code. It may not be used to create training material, 5 | * courses, books, articles, and the like. Contact us if you are in doubt. 6 | * We make no guarantees that this code is fit for any purpose. 7 | * Visit http://www.pragmaticprogrammer.com/titles/eband3 for more book information. 8 | ***/ 9 | 10 | 11 | package com.buuuk.android.ui.touch; 12 | 13 | import android.view.MotionEvent; 14 | 15 | public class WrapMotionEvent { 16 | protected MotionEvent event; 17 | 18 | 19 | 20 | 21 | protected WrapMotionEvent(MotionEvent event) { 22 | this.event = event; 23 | } 24 | 25 | static public WrapMotionEvent wrap(MotionEvent event) { 26 | try { 27 | return new EclairMotionEvent(event); 28 | } catch (VerifyError e) { 29 | return new WrapMotionEvent(event); 30 | } 31 | } 32 | 33 | 34 | 35 | public int getAction() { 36 | return event.getAction(); 37 | } 38 | 39 | public float getX() { 40 | return event.getX(); 41 | } 42 | 43 | public float getX(int pointerIndex) { 44 | verifyPointerIndex(pointerIndex); 45 | return getX(); 46 | } 47 | 48 | public float getY() { 49 | return event.getY(); 50 | } 51 | 52 | public float getY(int pointerIndex) { 53 | verifyPointerIndex(pointerIndex); 54 | return getY(); 55 | } 56 | 57 | public int getPointerCount() { 58 | return 1; 59 | } 60 | 61 | public int getPointerId(int pointerIndex) { 62 | verifyPointerIndex(pointerIndex); 63 | return 0; 64 | } 65 | 66 | private void verifyPointerIndex(int pointerIndex) { 67 | if (pointerIndex > 0) { 68 | throw new IllegalArgumentException( 69 | "Invalid pointer index for Donut/Cupcake"); 70 | } 71 | } 72 | 73 | } 74 | 75 | -------------------------------------------------------------------------------- /src/com/buuuk/android/util/FileUtils.java: -------------------------------------------------------------------------------- 1 | package com.buuuk.android.util; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileOutputStream; 6 | import java.io.FilenameFilter; 7 | import java.io.IOException; 8 | import java.io.ObjectInputStream; 9 | import java.io.ObjectOutputStream; 10 | import java.util.Collection; 11 | import java.util.List; 12 | import java.util.Vector; 13 | import java.util.zip.GZIPInputStream; 14 | import java.util.zip.GZIPOutputStream; 15 | 16 | import android.util.Log; 17 | 18 | public class FileUtils { 19 | 20 | public void saveArray(String filename, List output_field) { 21 | try { 22 | FileOutputStream fos = new FileOutputStream(filename); 23 | GZIPOutputStream gzos = new GZIPOutputStream(fos); 24 | ObjectOutputStream out = new ObjectOutputStream(gzos); 25 | out.writeObject(output_field); 26 | out.flush(); 27 | out.close(); 28 | } 29 | catch (IOException e) { 30 | e.getStackTrace(); 31 | } 32 | } 33 | 34 | @SuppressWarnings("unchecked") 35 | public List loadArray(String filename) { 36 | try { 37 | FileInputStream fis = new FileInputStream(filename); 38 | GZIPInputStream gzis = new GZIPInputStream(fis); 39 | ObjectInputStream in = new ObjectInputStream(gzis); 40 | List read_field = (List)in.readObject(); 41 | in.close(); 42 | return read_field; 43 | } 44 | catch (Exception e) { 45 | e.getStackTrace(); 46 | } 47 | return null; 48 | } 49 | 50 | public File[] listFilesAsArray(File directory, FilenameFilter[] filter, 51 | int recurse) { 52 | Collection files = listFiles(directory, filter, recurse); 53 | 54 | File[] arr = new File[files.size()]; 55 | return files.toArray(arr); 56 | } 57 | 58 | public Collection listFiles(File directory, 59 | FilenameFilter[] filter, int recurse) { 60 | 61 | Vector files = new Vector(); 62 | 63 | File[] entries = directory.listFiles(); 64 | 65 | if (entries != null) { 66 | for (File entry : entries) { 67 | for (FilenameFilter filefilter : filter) { 68 | if (filter == null 69 | || filefilter 70 | .accept(directory, entry.getName())) { 71 | files.add(entry); 72 | Log.v("ImageViewFlipper", "Added: " 73 | + entry.getName()); 74 | } 75 | } 76 | if ((recurse <= -1) || (recurse > 0 && entry.isDirectory())) { 77 | recurse--; 78 | files.addAll(listFiles(entry, filter, recurse)); 79 | recurse++; 80 | } 81 | } 82 | } 83 | return files; 84 | } 85 | } -------------------------------------------------------------------------------- /src/com/buuuk/android/util/Geometry.java: -------------------------------------------------------------------------------- 1 | package com.buuuk.android.util; 2 | 3 | 4 | /* 5 | * (C) 2004 - Geotechnical Software Services 6 | * 7 | * This code is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Lesser General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2.1 of the License, or (at your option) any later version. 11 | * 12 | * This code is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public 18 | * License along with this program; if not, write to the Free 19 | * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, 20 | * MA 02111-1307, USA. 21 | */ 22 | 23 | 24 | 25 | /** 26 | * Collection of geometry utility methods. All methods are static. 27 | * 28 | * @author Jacob Dreyer 29 | */ 30 | public final class Geometry 31 | { 32 | /** 33 | * Return true if c is between a and b. 34 | */ 35 | private static boolean isBetween (int a, int b, int c) 36 | { 37 | return b > a ? c >= a && c <= b : c >= b && c <= a; 38 | } 39 | 40 | 41 | 42 | /** 43 | * Return true if c is between a and b. 44 | */ 45 | private static boolean isBetween (double a, double b, double c) 46 | { 47 | return b > a ? c >= a && c <= b : c >= b && c <= a; 48 | } 49 | 50 | 51 | 52 | /** 53 | * Check if two double precision numbers are "equal", i.e. close enough 54 | * to a given limit. 55 | * 56 | * @param a First number to check 57 | * @param b Second number to check 58 | * @param limit The definition of "equal". 59 | * @return True if the twho numbers are "equal", false otherwise 60 | */ 61 | private static boolean equals (double a, double b, double limit) 62 | { 63 | return Math.abs (a - b) < limit; 64 | } 65 | 66 | 67 | 68 | /** 69 | * Check if two double precision numbers are "equal", i.e. close enough 70 | * to a prespecified limit. 71 | * 72 | * @param a First number to check 73 | * @param b Second number to check 74 | * @return True if the twho numbers are "equal", false otherwise 75 | */ 76 | private static boolean equals (double a, double b) 77 | { 78 | return equals (a, b, 1.0e-5); 79 | } 80 | 81 | 82 | 83 | /** 84 | * Return smallest of four numbers. 85 | * 86 | * @param a First number to find smallest among. 87 | * @param b Second number to find smallest among. 88 | * @param c Third number to find smallest among. 89 | * @param d Fourth number to find smallest among. 90 | * @return Smallest of a, b, c and d. 91 | */ 92 | private static double min (double a, double b, double c, double d) 93 | { 94 | return Math.min (Math.min (a, b), Math.min (c, d)); 95 | } 96 | 97 | 98 | 99 | /** 100 | * Return largest of four numbers. 101 | * 102 | * @param a First number to find largest among. 103 | * @param b Second number to find largest among. 104 | * @param c Third number to find largest among. 105 | * @param d Fourth number to find largest among. 106 | * @return Largest of a, b, c and d. 107 | */ 108 | private static double max (double a, double b, double c, double d) 109 | { 110 | return Math.max (Math.max (a, b), Math.max (c, d)); 111 | } 112 | 113 | 114 | 115 | /** 116 | * Check if a specified point is inside a specified rectangle. 117 | * 118 | * @param x0, y0, x1, y1 Upper left and lower right corner of rectangle 119 | * (inclusive) 120 | * @param x,y Point to check. 121 | * @return True if the point is inside the rectangle, 122 | * false otherwise. 123 | */ 124 | public static boolean isPointInsideRectangle (int x0, int y0, int x1, int y1, 125 | int x, int y) 126 | { 127 | return x >= x0 && x < x1 && y >= y0 && y < y1; 128 | } 129 | 130 | 131 | 132 | /** 133 | * Check if a given point is inside a given (complex) polygon. 134 | * 135 | * @param x, y Polygon. 136 | * @param pointX, pointY Point to check. 137 | * @return True if the given point is inside the polygon, false otherwise. 138 | */ 139 | public static boolean isPointInsidePolygon (double[] x, double[] y, 140 | double pointX, double pointY) 141 | { 142 | boolean isInside = false; 143 | int nPoints = x.length; 144 | 145 | int j = 0; 146 | for (int i = 0; i < nPoints; i++) { 147 | j++; 148 | if (j == nPoints) j = 0; 149 | 150 | if (y[i] < pointY && y[j] >= pointY || y[j] < pointY && y[i] >= pointY) { 151 | if (x[i] + (pointY - y[i]) / (y[j] - y[i]) * (x[j] - x[i]) < pointX) { 152 | isInside = !isInside; 153 | } 154 | } 155 | } 156 | 157 | return isInside; 158 | } 159 | 160 | 161 | 162 | /** 163 | * Check if a given point is inside a given polygon. Integer domain. 164 | * 165 | * @param x, y Polygon. 166 | * @param pointX, pointY Point to check. 167 | * @return True if the given point is inside the polygon, false otherwise. 168 | */ 169 | public static boolean isPointInsidePolygon (int[] x, int[] y, 170 | int pointX, int pointY) 171 | { 172 | boolean isInside = false; 173 | int nPoints = x.length; 174 | 175 | int j = 0; 176 | for (int i = 0; i < nPoints; i++) { 177 | j++; 178 | if (j == nPoints) j = 0; 179 | 180 | if (y[i] < pointY && y[j] >= pointY || y[j] < pointY && y[i] >= pointY) { 181 | if (x[i] + (double) (pointY - y[i]) / (double) (y[j] - y[i]) * 182 | (x[j] - x[i]) < pointX) { 183 | isInside = !isInside; 184 | } 185 | } 186 | } 187 | 188 | return isInside; 189 | } 190 | 191 | 192 | 193 | /** 194 | * Find the point on the line p0,p1 [x,y,z] a given fraction from p0. 195 | * Fraction of 0.0 whould give back p0, 1.0 give back p1, 0.5 returns 196 | * midpoint of line p0,p1 and so on. F 197 | * raction can be >1 and it can be negative to return any point on the 198 | * line specified by p0,p1. 199 | * 200 | * @param p0 First coordinale of line [x,y,z]. 201 | * @param p0 Second coordinale of line [x,y,z]. 202 | * @param fractionFromP0 Point we are looking for coordinates of 203 | * @param p Coordinate of point we are looking for 204 | */ 205 | public static double[] computePointOnLine (double[] p0, double[] p1, 206 | double fractionFromP0) 207 | { 208 | double[] p = new double[3]; 209 | 210 | p[0] = p0[0] + fractionFromP0 * (p1[0] - p0[0]); 211 | p[1] = p0[1] + fractionFromP0 * (p1[1] - p0[1]); 212 | p[2] = p0[2] + fractionFromP0 * (p1[2] - p0[2]); 213 | 214 | return p; 215 | } 216 | 217 | 218 | 219 | /** 220 | * Find the point on the line defined by x0,y0,x1,y1 a given fraction 221 | * from x0,y0. 2D version of method above.. 222 | * 223 | * @param x0, y0 First point defining the line 224 | * @param x1, y1 Second point defining the line 225 | * @param fractionFrom0 Distance from (x0,y0) 226 | * @return x, y Coordinate of point we are looking for 227 | */ 228 | public static double[] computePointOnLine (double x0, double y0, 229 | double x1, double y1, 230 | double fractionFrom0) 231 | { 232 | double[] p0 = {x0, y0, 0.0}; 233 | double[] p1 = {x1, y1, 0.0}; 234 | 235 | double[] p = Geometry.computePointOnLine (p0, p1, fractionFrom0); 236 | 237 | double[] r = {p[0], p[1]}; 238 | return r; 239 | } 240 | 241 | 242 | 243 | /** 244 | * Extend a given line segment to a specified length. 245 | * 246 | * @param p0, p1 Line segment to extend [x,y,z]. 247 | * @param toLength Length of new line segment. 248 | * @param anchor Specifies the fixed point during extension. 249 | * If anchor is 0.0, p0 is fixed and p1 is adjusted. 250 | * If anchor is 1.0, p1 is fixed and p0 is adjusted. 251 | * If anchor is 0.5, the line is adjusted equally in each 252 | * direction and so on. 253 | */ 254 | public static void extendLine (double[] p0, double[] p1, double toLength, 255 | double anchor) 256 | { 257 | double[] p = Geometry.computePointOnLine (p0, p1, anchor); 258 | 259 | double length0 = toLength * anchor; 260 | double length1 = toLength * (1.0 - anchor); 261 | 262 | Geometry.extendLine (p, p0, length0); 263 | Geometry.extendLine (p, p1, length1); 264 | } 265 | 266 | 267 | 268 | /** 269 | * Extend a given line segment to a given length and holding the first 270 | * point of the line as fixed. 271 | * 272 | * @param p0, p1 Line segment to extend. p0 is fixed during extension 273 | * @param length Length of new line segment. 274 | */ 275 | public static void extendLine (double[] p0, double[] p1, double toLength) 276 | { 277 | double oldLength = Geometry.length (p0, p1); 278 | double lengthFraction = oldLength != 0.0 ? toLength / oldLength : 0.0; 279 | 280 | p1[0] = p0[0] + (p1[0] - p0[0]) * lengthFraction; 281 | p1[1] = p0[1] + (p1[1] - p0[1]) * lengthFraction; 282 | p1[2] = p0[2] + (p1[2] - p0[2]) * lengthFraction; 283 | } 284 | 285 | 286 | 287 | /** 288 | * Return the length of a vector. 289 | * 290 | * @param v Vector to compute length of [x,y,z]. 291 | * @return Length of vector. 292 | */ 293 | public static double length (double[] v) 294 | { 295 | return Math.sqrt (v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); 296 | } 297 | 298 | 299 | 300 | /** 301 | * Compute distance between two points. 302 | * 303 | * @param p0, p1 Points to compute distance between [x,y,z]. 304 | * @return Distance between points. 305 | */ 306 | public static double length (double[] p0, double[] p1) 307 | { 308 | double[] v = Geometry.createVector (p0, p1); 309 | return length (v); 310 | } 311 | 312 | 313 | 314 | /** 315 | * Compute the length of the line from (x0,y0) to (x1,y1) 316 | * 317 | * @param x0, y0 First line end point. 318 | * @param x1, y1 Second line end point. 319 | * @return Length of line from (x0,y0) to (x1,y1). 320 | */ 321 | public static double length (int x0, int y0, int x1, int y1) 322 | { 323 | return Geometry.length ((double) x0, (double) y0, 324 | (double) x1, (double) y1); 325 | } 326 | 327 | 328 | 329 | /** 330 | * Compute the length of the line from (x0,y0) to (x1,y1) 331 | * 332 | * @param x0, y0 First line end point. 333 | * @param x1, y1 Second line end point. 334 | * @return Length of line from (x0,y0) to (x1,y1). 335 | */ 336 | public static double length (double x0, double y0, double x1, double y1) 337 | { 338 | double dx = x1 - x0; 339 | double dy = y1 - y0; 340 | 341 | return Math.sqrt (dx*dx + dy*dy); 342 | } 343 | 344 | 345 | 346 | /** 347 | * Compute the length of a polyline. 348 | * 349 | * @param x, y Arrays of x,y coordinates 350 | * @param nPoints Number of elements in the above. 351 | * @param isClosed True if this is a closed polygon, false otherwise 352 | * @return Length of polyline defined by x, y and nPoints. 353 | */ 354 | public static double length (int[] x, int[] y, boolean isClosed) 355 | { 356 | double length = 0.0; 357 | 358 | int nPoints = x.length; 359 | for (int i = 0; i < nPoints-1; i++) 360 | length += Geometry.length (x[i], y[i], x[i+1], y[i+1]); 361 | 362 | // Add last leg if this is a polygon 363 | if (isClosed && nPoints > 1) 364 | length += Geometry.length (x[nPoints-1], y[nPoints-1], x[0], y[0]); 365 | 366 | return length; 367 | } 368 | 369 | 370 | 371 | /** 372 | * Return distance bwetween the line defined by (x0,y0) and (x1,y1) 373 | * and the point (x,y). 374 | * Ref: http://astronomy.swin.edu.au/pbourke/geometry/pointline/ 375 | * The 3D case should be similar. 376 | * 377 | * @param x0, y0 First point of line. 378 | * @param x1, y1 Second point of line. 379 | * @param x, y, Point to consider. 380 | * @return Distance from x,y down to the (extended) line defined 381 | * by x0, y0, x1, y1. 382 | */ 383 | public static double distance (int x0, int y0, int x1, int y1, 384 | int x, int y) 385 | { 386 | // If x0,y0,x1,y1 is same point, we return distance to that point 387 | double length = Geometry.length (x0, y0, x1, y1); 388 | if (length == 0.0) 389 | return Geometry.length (x0, y0, x, y); 390 | 391 | // If u is [0,1] then (xp,yp) is on the line segment (x0,y0),(x1,y1). 392 | double u = ((x - x0) * (x1 - x0) + (y - y0) * (y1 - y0)) / 393 | (length * length); 394 | 395 | // This is the intersection point of the normal. 396 | // TODO: Might consider returning this as well. 397 | double xp = x0 + u * (x1 - x0); 398 | double yp = y0 + u * (y1 - y0); 399 | 400 | length = Geometry.length (xp, yp, x, y); 401 | return length; 402 | } 403 | 404 | 405 | 406 | /** 407 | * Find the angle between twree points. P0 is center point 408 | * 409 | * @param p0, p1, p2 Three points finding angle between [x,y,z]. 410 | * @return Angle (in radians) between given points. 411 | */ 412 | public static double computeAngle (double[] p0, double[] p1, double[] p2) 413 | { 414 | double[] v0 = Geometry.createVector (p0, p1); 415 | double[] v1 = Geometry.createVector (p0, p2); 416 | 417 | double dotProduct = Geometry.computeDotProduct (v0, v1); 418 | 419 | double length1 = Geometry.length (v0); 420 | double length2 = Geometry.length (v1); 421 | 422 | double denominator = length1 * length2; 423 | 424 | double product = denominator != 0.0 ? dotProduct / denominator : 0.0; 425 | 426 | double angle = Math.acos (product); 427 | 428 | return angle; 429 | } 430 | 431 | 432 | 433 | /** 434 | * Compute the dot product (a scalar) between two vectors. 435 | * 436 | * @param v0, v1 Vectors to compute dot product between [x,y,z]. 437 | * @return Dot product of given vectors. 438 | */ 439 | public static double computeDotProduct (double[] v0, double[] v1) 440 | { 441 | return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2]; 442 | } 443 | 444 | 445 | 446 | /** 447 | * Compute the cross product (a vector) of two vectors. 448 | * 449 | * @param v0, v1 Vectors to compute cross product between [x,y,z]. 450 | * @param crossProduct Cross product of specified vectors [x,y,z]. 451 | */ 452 | public static double[] computeCrossProduct (double[] v0, double[] v1) 453 | { 454 | double crossProduct[] = new double[3]; 455 | 456 | crossProduct[0] = v0[1] * v1[2] - v0[2] * v1[1]; 457 | crossProduct[1] = v0[2] * v1[0] - v0[0] * v1[2]; 458 | crossProduct[2] = v0[0] * v1[1] - v0[1] * v1[0]; 459 | 460 | return crossProduct; 461 | } 462 | 463 | 464 | 465 | /** 466 | * Construct the vector specified by two points. 467 | * 468 | * @param p0, p1 Points the construct vector between [x,y,z]. 469 | * @return v Vector from p0 to p1 [x,y,z]. 470 | */ 471 | public static double[] createVector (double[] p0, double[] p1) 472 | { 473 | double v[] = {p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]}; 474 | return v; 475 | } 476 | 477 | 478 | 479 | /** 480 | * Check if two points are on the same side of a given line. 481 | * Algorithm from Sedgewick page 350. 482 | * 483 | * @param x0, y0, x1, y1 The line. 484 | * @param px0, py0 First point. 485 | * @param px1, py1 Second point. 486 | * @return <0 if points on opposite sides. 487 | * =0 if one of the points is exactly on the line 488 | * >0 if points on same side. 489 | */ 490 | private static int sameSide (double x0, double y0, double x1, double y1, 491 | double px0, double py0, double px1, double py1) 492 | { 493 | int sameSide = 0; 494 | 495 | double dx = x1 - x0; 496 | double dy = y1 - y0; 497 | double dx1 = px0 - x0; 498 | double dy1 = py0 - y0; 499 | double dx2 = px1 - x1; 500 | double dy2 = py1 - y1; 501 | 502 | // Cross product of the vector from the endpoint of the line to the point 503 | double c1 = dx * dy1 - dy * dx1; 504 | double c2 = dx * dy2 - dy * dx2; 505 | 506 | if (c1 != 0 && c2 != 0) 507 | sameSide = c1 < 0 != c2 < 0 ? -1 : 1; 508 | else if (dx == 0 && dx1 == 0 && dx2 == 0) 509 | sameSide = !isBetween (y0, y1, py0) && !isBetween (y0, y1, py1) ? 1 : 0; 510 | else if (dy == 0 && dy1 == 0 && dy2 == 0) 511 | sameSide = !isBetween (x0, x1, px0) && !isBetween (x0, x1, px1) ? 1 : 0; 512 | 513 | return sameSide; 514 | } 515 | 516 | 517 | 518 | /** 519 | * Check if two points are on the same side of a given line. Integer domain. 520 | * 521 | * @param x0, y0, x1, y1 The line. 522 | * @param px0, py0 First point. 523 | * @param px1, py1 Second point. 524 | * @return <0 if points on opposite sides. 525 | * =0 if one of the points is exactly on the line 526 | * >0 if points on same side. 527 | */ 528 | private static int sameSide (int x0, int y0, int x1, int y1, 529 | int px0, int py0, int px1, int py1) 530 | { 531 | return sameSide ((double) x0, (double) y0, (double) x1, (double) y1, 532 | (double) px0, (double) py0, (double) px1, (double) py1); 533 | } 534 | 535 | 536 | 537 | /** 538 | * Check if two line segments intersects. Integer domain. 539 | * 540 | * @param x0, y0, x1, y1 End points of first line to check. 541 | * @param x2, yy, x3, y3 End points of second line to check. 542 | * @return True if the two lines intersects. 543 | */ 544 | public static boolean isLineIntersectingLine (int x0, int y0, int x1, int y1, 545 | int x2, int y2, int x3, int y3) 546 | { 547 | int s1 = Geometry.sameSide (x0, y0, x1, y1, x2, y2, x3, y3); 548 | int s2 = Geometry.sameSide (x2, y2, x3, y3, x0, y0, x1, y1); 549 | 550 | return s1 <= 0 && s2 <= 0; 551 | } 552 | 553 | 554 | 555 | /** 556 | * Check if a specified line intersects a specified rectangle. 557 | * Integer domain. 558 | * 559 | * @param lx0, ly0 1st end point of line 560 | * @param ly1, ly1 2nd end point of line 561 | * @param x0, y0, x1, y1 Upper left and lower right corner of rectangle 562 | * (inclusive). 563 | * @return True if the line intersects the rectangle, 564 | * false otherwise. 565 | */ 566 | public static boolean isLineIntersectingRectangle (int lx0, int ly0, 567 | int lx1, int ly1, 568 | int x0, int y0, 569 | int x1, int y1) 570 | { 571 | // Is one of the line endpoints inside the rectangle 572 | if (Geometry.isPointInsideRectangle (x0, y0, x1, y1, lx0, ly0) || 573 | Geometry.isPointInsideRectangle (x0, y0, x1, y1, lx1, ly1)) 574 | return true; 575 | 576 | // If it intersects it goes through. Need to check three sides only. 577 | 578 | // Check against top rectangle line 579 | if (Geometry.isLineIntersectingLine (lx0, ly0, lx1, ly1, 580 | x0, y0, x1, y0)) 581 | return true; 582 | 583 | // Check against left rectangle line 584 | if (Geometry.isLineIntersectingLine (lx0, ly0, lx1, ly1, 585 | x0, y0, x0, y1)) 586 | return true; 587 | 588 | // Check against bottom rectangle line 589 | if (Geometry.isLineIntersectingLine (lx0, ly0, lx1, ly1, 590 | x0, y1, x1, y1)) 591 | return true; 592 | 593 | return false; 594 | } 595 | 596 | 597 | 598 | /** 599 | * Check if a specified polyline intersects a specified rectangle. 600 | * Integer domain. 601 | * 602 | * @param x, y Polyline to check. 603 | * @param x0, y0, x1, y1 Upper left and lower left corner of rectangle 604 | * (inclusive). 605 | * @return True if the polyline intersects the rectangle, 606 | * false otherwise. 607 | */ 608 | public static boolean isPolylineIntersectingRectangle (int[] x, int[] y, 609 | int x0, int y0, 610 | int x1, int y1) 611 | { 612 | if (x.length == 0) return false; 613 | 614 | if (Geometry.isPointInsideRectangle (x[0], y[0], x0, y0, x1, y1)) 615 | return true; 616 | 617 | else if (x.length == 1) 618 | return false; 619 | 620 | for (int i = 1; i < x.length; i++) { 621 | if (x[i-1] != x[i] || y[i-1] != y[i]) 622 | if (Geometry.isLineIntersectingRectangle (x[i-1], y[i-1], 623 | x[i], y[i], 624 | x0, y0, x1, y1)) 625 | return true; 626 | } 627 | 628 | return false; 629 | } 630 | 631 | 632 | 633 | /** 634 | * Check if a specified polygon intersects a specified rectangle. 635 | * Integer domain. 636 | * 637 | * @param x X coordinates of polyline. 638 | * @param y Y coordinates of polyline. 639 | * @param x0 X of upper left corner of rectangle. 640 | * @param y0 Y of upper left corner of rectangle. 641 | * @param x1 X of lower right corner of rectangle. 642 | * @param y1 Y of lower right corner of rectangle. 643 | * @return True if the polyline intersects the rectangle, false otherwise. 644 | */ 645 | public static boolean isPolygonIntersectingRectangle (int[] x, int[] y, 646 | int x0, int y0, 647 | int x1, int y1) 648 | { 649 | int n = x.length; 650 | 651 | if (n == 0) 652 | return false; 653 | 654 | if (n == 1) 655 | return Geometry.isPointInsideRectangle (x0, y0, x1, y1, x[0], y[0]); 656 | 657 | // 658 | // If the polyline constituting the polygon intersects the rectangle 659 | // the polygon does too. 660 | // 661 | if (Geometry.isPolylineIntersectingRectangle (x, y, x0, y0, x1, y1)) 662 | return true; 663 | 664 | // Check last leg as well 665 | if (Geometry.isLineIntersectingRectangle (x[n-2], y[n-2], x[n-1], y[n-1], 666 | x0, y0, x1, y1)) 667 | return true; 668 | 669 | // 670 | // The rectangle and polygon are now completely including each other 671 | // or separate. 672 | // 673 | if (Geometry.isPointInsidePolygon (x, y, x0, y0) || 674 | Geometry.isPointInsideRectangle (x0, y0, x1, y1, x[0], y[0])) 675 | return true; 676 | 677 | // Separate 678 | return false; 679 | } 680 | 681 | 682 | 683 | /** 684 | * Compute the area of the specfied polygon. 685 | * 686 | * @param x X coordinates of polygon. 687 | * @param y Y coordinates of polygon. 688 | * @return Area of specified polygon. 689 | */ 690 | public static double computePolygonArea (double[] x, double[] y) 691 | { 692 | int n = x.length; 693 | 694 | double area = 0.0; 695 | for (int i = 0; i < n - 1; i++) 696 | area += (x[i] * y[i+1]) - (x[i+1] * y[i]); 697 | area += (x[n-1] * y[0]) - (x[0] * y[n-1]); 698 | 699 | area *= 0.5; 700 | 701 | return area; 702 | } 703 | 704 | 705 | 706 | /** 707 | * Compute the area of the specfied polygon. 708 | * 709 | * @param xy Geometry of polygon [x,y,...] 710 | * @return Area of specified polygon. 711 | */ 712 | public static double computePolygonArea (double[] xy) 713 | { 714 | int n = xy.length; 715 | 716 | double area = 0.0; 717 | for (int i = 0; i < n - 2; i += 2) 718 | area += (xy[i] * xy[i+3]) - (xy[i+2] * xy[i+1]); 719 | area += (xy[xy.length-2] * xy[1]) - (xy[0] * xy[xy.length-1]); 720 | 721 | area *= 0.5; 722 | 723 | return area; 724 | } 725 | 726 | 727 | 728 | /** 729 | * Compute centorid (center of gravity) of specified polygon. 730 | * 731 | * @param x X coordinates of polygon. 732 | * @param y Y coordinates of polygon. 733 | * @return Centroid [x,y] of specified polygon. 734 | */ 735 | public static double[] computePolygonCentroid (double[] x, double[] y) 736 | { 737 | double cx = 0.0; 738 | double cy = 0.0; 739 | 740 | int n = x.length; 741 | for (int i = 0; i < n - 1; i++) { 742 | double a = x[i] * y[i+1] - x[i+1] * y[i]; 743 | cx += (x[i] + x[i+1]) * a; 744 | cy += (y[i] + y[i+1]) * a; 745 | } 746 | double a = x[n-1] * y[0] - x[0] * y[n-1]; 747 | cx += (x[n-1] + x[0]) * a; 748 | cy += (y[n-1] + y[0]) * a; 749 | 750 | double area = Geometry.computePolygonArea (x, y); 751 | 752 | cx /= 6 * area; 753 | cy /= 6 * area; 754 | 755 | return new double[] {cx, cy}; 756 | } 757 | 758 | 759 | /** 760 | * Find the 3D extent of a polyline. 761 | * 762 | * @param x X coordinates of polyline. 763 | * @param y Y coordinates of polyline. 764 | * @param z Z coordinates of polyline. 765 | * May be null if this is a 2D case. 766 | * @param xExtent Will upon return contain [xMin,xMax]. 767 | * @param yExtent Will upon return contain [xMin,xMax]. 768 | * @param zExtent Will upon return contain [xMin,xMax]. Unused (may be 769 | * set to null) if z is null. 770 | */ 771 | public static void findPolygonExtent (double[] x, double[] y, double[] z, 772 | double[] xExtent, 773 | double[] yExtent, 774 | double[] zExtent) 775 | { 776 | double xMin = +Double.MAX_VALUE; 777 | double xMax = -Double.MAX_VALUE; 778 | 779 | double yMin = +Double.MAX_VALUE; 780 | double yMax = -Double.MAX_VALUE; 781 | 782 | double zMin = +Double.MAX_VALUE; 783 | double zMax = -Double.MAX_VALUE; 784 | 785 | for (int i = 0; i < x.length; i++) { 786 | if (x[i] < xMin) xMin = x[i]; 787 | if (x[i] > xMax) xMax = x[i]; 788 | 789 | if (y[i] < yMin) yMin = y[i]; 790 | if (y[i] > yMax) yMax = y[i]; 791 | 792 | if (z != null) { 793 | if (z[i] < zMin) zMin = z[i]; 794 | if (z[i] > zMax) zMax = z[i]; 795 | } 796 | } 797 | 798 | xExtent[0] = xMin; 799 | xExtent[1] = xMax; 800 | 801 | yExtent[0] = yMin; 802 | yExtent[1] = yMax; 803 | 804 | if (z != null) { 805 | zExtent[0] = zMin; 806 | zExtent[1] = zMax; 807 | } 808 | } 809 | 810 | 811 | 812 | /** 813 | * Find the extent of a polygon. 814 | * 815 | * @param x X coordinates of polygon. 816 | * @param y Y coordinates of polygon. 817 | * @param xExtent Will upon return contain [xMin, xMax] 818 | * @param yExtent Will upon return contain [yMin, yMax] 819 | */ 820 | public static void findPolygonExtent (int[] x, int[] y, 821 | int[] xExtent, // xMin, xMax 822 | int[] yExtent) // yMin, yMax 823 | { 824 | int xMin = + Integer.MAX_VALUE; 825 | int xMax = - Integer.MAX_VALUE; 826 | 827 | int yMin = + Integer.MAX_VALUE; 828 | int yMax = - Integer.MAX_VALUE; 829 | 830 | for (int i = 0; i < x.length; i++) { 831 | if (x[i] < xMin) xMin = x[i]; 832 | if (x[i] > xMax) xMax = x[i]; 833 | 834 | if (y[i] < yMin) yMin = y[i]; 835 | if (y[i] > yMax) yMax = y[i]; 836 | } 837 | 838 | xExtent[0] = xMin; 839 | xExtent[1] = xMax; 840 | 841 | yExtent[0] = yMin; 842 | yExtent[1] = yMax; 843 | } 844 | 845 | 846 | 847 | /** 848 | * Compute the intersection between two line segments, or two lines 849 | * of infinite length. 850 | * 851 | * @param x0 X coordinate first end point first line segment. 852 | * @param y0 Y coordinate first end point first line segment. 853 | * @param x1 X coordinate second end point first line segment. 854 | * @param y1 Y coordinate second end point first line segment. 855 | * @param x2 X coordinate first end point second line segment. 856 | * @param y2 Y coordinate first end point second line segment. 857 | * @param x3 X coordinate second end point second line segment. 858 | * @param y3 Y coordinate second end point second line segment. 859 | * @param intersection[2] Preallocated by caller to double[2] 860 | * @return -1 if lines are parallel (x,y unset), 861 | * -2 if lines are parallel and overlapping (x, y center) 862 | * 0 if intesrection outside segments (x,y set) 863 | * +1 if segments intersect (x,y set) 864 | */ 865 | public static int findLineSegmentIntersection (double x0, double y0, 866 | double x1, double y1, 867 | double x2, double y2, 868 | double x3, double y3, 869 | double[] intersection) 870 | { 871 | // TODO: Make limit depend on input domain 872 | final double LIMIT = 1e-5; 873 | final double INFINITY = 1e10; 874 | 875 | double x, y; 876 | 877 | // 878 | // Convert the lines to the form y = ax + b 879 | // 880 | 881 | // Slope of the two lines 882 | double a0 = Geometry.equals (x0, x1, LIMIT) ? 883 | INFINITY : (y0 - y1) / (x0 - x1); 884 | double a1 = Geometry.equals (x2, x3, LIMIT) ? 885 | INFINITY : (y2 - y3) / (x2 - x3); 886 | 887 | double b0 = y0 - a0 * x0; 888 | double b1 = y2 - a1 * x2; 889 | 890 | // Check if lines are parallel 891 | if (Geometry.equals (a0, a1)) { 892 | if (!Geometry.equals (b0, b1)) 893 | return -1; // Parallell non-overlapping 894 | 895 | else { 896 | if (Geometry.equals (x0, x1)) { 897 | if (Math.min (y0, y1) < Math.max (y2, y3) || 898 | Math.max (y0, y1) > Math.min (y2, y3)) { 899 | double twoMiddle = y0 + y1 + y2 + y3 - 900 | Geometry.min (y0, y1, y2, y3) - 901 | Geometry.max (y0, y1, y2, y3); 902 | y = (twoMiddle) / 2.0; 903 | x = (y - b0) / a0; 904 | } 905 | else return -1; // Parallell non-overlapping 906 | } 907 | else { 908 | if (Math.min (x0, x1) < Math.max (x2, x3) || 909 | Math.max (x0, x1) > Math.min (x2, x3)) { 910 | double twoMiddle = x0 + x1 + x2 + x3 - 911 | Geometry.min (x0, x1, x2, x3) - 912 | Geometry.max (x0, x1, x2, x3); 913 | x = (twoMiddle) / 2.0; 914 | y = a0 * x + b0; 915 | } 916 | else return -1; 917 | } 918 | 919 | intersection[0] = x; 920 | intersection[1] = y; 921 | return -2; 922 | } 923 | } 924 | 925 | // Find correct intersection point 926 | if (Geometry.equals (a0, INFINITY)) { 927 | x = x0; 928 | y = a1 * x + b1; 929 | } 930 | else if (Geometry.equals (a1, INFINITY)) { 931 | x = x2; 932 | y = a0 * x + b0; 933 | } 934 | else { 935 | x = - (b0 - b1) / (a0 - a1); 936 | y = a0 * x + b0; 937 | } 938 | 939 | intersection[0] = x; 940 | intersection[1] = y; 941 | 942 | // Then check if intersection is within line segments 943 | double distanceFrom1; 944 | if (Geometry.equals (x0, x1)) { 945 | if (y0 < y1) 946 | distanceFrom1 = y < y0 ? Geometry.length (x, y, x0, y0) : 947 | y > y1 ? Geometry.length (x, y, x1, y1) : 0.0; 948 | else 949 | distanceFrom1 = y < y1 ? Geometry.length (x, y, x1, y1) : 950 | y > y0 ? Geometry.length (x, y, x0, y0) : 0.0; 951 | } 952 | else { 953 | if (x0 < x1) 954 | distanceFrom1 = x < x0 ? Geometry.length (x, y, x0, y0) : 955 | x > x1 ? Geometry.length (x, y, x1, y1) : 0.0; 956 | else 957 | distanceFrom1 = x < x1 ? Geometry.length (x, y, x1, y1) : 958 | x > x0 ? Geometry.length (x, y, x0, y0) : 0.0; 959 | } 960 | 961 | double distanceFrom2; 962 | if (Geometry.equals (x2, x3)) { 963 | if (y2 < y3) 964 | distanceFrom2 = y < y2 ? Geometry.length (x, y, x2, y2) : 965 | y > y3 ? Geometry.length (x, y, x3, y3) : 0.0; 966 | else 967 | distanceFrom2 = y < y3 ? Geometry.length (x, y, x3, y3) : 968 | y > y2 ? Geometry.length (x, y, x2, y2) : 0.0; 969 | } 970 | else { 971 | if (x2 < x3) 972 | distanceFrom2 = x < x2 ? Geometry.length (x, y, x2, y2) : 973 | x > x3 ? Geometry.length (x, y, x3, y3) : 0.0; 974 | else 975 | distanceFrom2 = x < x3 ? Geometry.length (x, y, x3, y3) : 976 | x > x2 ? Geometry.length (x, y, x2, y2) : 0.0; 977 | } 978 | 979 | return Geometry.equals (distanceFrom1, 0.0) && 980 | Geometry.equals (distanceFrom2, 0.0) ? 1 : 0; 981 | } 982 | 983 | 984 | 985 | /** 986 | * Find the intersections between a polygon and a straight line. 987 | * 988 | * NOTE: This method is only guaranteed to work if the polygon 989 | * is first preprocessed so that "unneccesary" vertices are removed 990 | * (i.e vertices on the straight line between its neighbours). 991 | * 992 | * @param x X coordinates of polygon. 993 | * @param y Y coordinates of polygon. 994 | * @param x0 X first end point of line. 995 | * @param x0 Y first end point of line. 996 | * @param x0 X second end point of line. 997 | * @param x0 Y second end point of line. 998 | * @return Intersections [x,y,x,y...]. 999 | */ 1000 | public static double[] findLinePolygonIntersections (double[] x, double[] y, 1001 | double x0, double y0, 1002 | double x1, double y1) 1003 | { 1004 | double x2, y2, x3, y3; 1005 | double xi, yi; 1006 | int nPoints = x.length; 1007 | 1008 | int nIntersections = 0; 1009 | double[] intersections = new double[24]; // Result vector x,y,x,y,... 1010 | double[] intersection = new double[2]; // Any given intersection x,y 1011 | 1012 | for (int i = 0; i < nPoints; i++) { 1013 | int next = i == nPoints - 1 ? 0 : i + 1; 1014 | 1015 | // The line segment of the polyline to check 1016 | x2 = x[i]; 1017 | y2 = y[i]; 1018 | x3 = x[next]; 1019 | y3 = y[next]; 1020 | 1021 | boolean isIntersecting = false; 1022 | 1023 | // Ignore segments of zero length 1024 | if (Geometry.equals (x2, x3) && Geometry.equals (y2, y3)) 1025 | continue; 1026 | 1027 | int type = Geometry.findLineSegmentIntersection (x0, y0, x1, y1, 1028 | x2, y2, x3, y3, 1029 | intersection); 1030 | 1031 | if (type == -2) { // Overlapping 1032 | int p1 = i == 0 ? nPoints - 1 : i - 1; 1033 | int p2 = next == nPoints - 1 ? 0 : next + 1; 1034 | 1035 | int side = Geometry.sameSide (x0, y0, x1, y1, 1036 | x[p1], y[p1], x[p2], y[p2]); 1037 | 1038 | if (side < 0) 1039 | isIntersecting = true; 1040 | } 1041 | else if (type == 1) 1042 | isIntersecting = true; 1043 | 1044 | // Add the intersection point 1045 | if (isIntersecting) { 1046 | 1047 | // Reallocate if necessary 1048 | if (nIntersections << 1 == intersections.length) { 1049 | double[] newArray = new double[nIntersections << 2]; 1050 | System.arraycopy (intersections, 0, newArray, 0, 1051 | intersections.length); 1052 | intersections = newArray; 1053 | } 1054 | 1055 | // Then add 1056 | intersections[nIntersections << 1 + 0] = intersection[0]; 1057 | intersections[nIntersections << 1 + 1] = intersection[1]; 1058 | 1059 | nIntersections++; 1060 | } 1061 | } 1062 | 1063 | if (nIntersections == 0) return null; 1064 | 1065 | // Reallocate result so array match number of intersections 1066 | double[] finalArray = new double[nIntersections << 2]; 1067 | System.arraycopy (intersections, 0, finalArray, 0, finalArray.length); 1068 | 1069 | return finalArray; 1070 | } 1071 | 1072 | 1073 | 1074 | /** 1075 | * Return the geometry of an ellipse based on its four top points. 1076 | * Integer domain. The method use the generic createEllipse() 1077 | * method for the main task, and then transforms this according 1078 | * to any rotation or skew defined by the given top points. 1079 | * 1080 | * @param x X array of four top points of ellipse. 1081 | * @param y Y array of four top points of ellipse. 1082 | * @return Geometry of ellipse [x,y,x,y...]. 1083 | */ 1084 | public static int[] createEllipse (int[] x, int[] y) 1085 | { 1086 | // Center of ellipse 1087 | int x0 = (x[0] + x[2]) / 2; 1088 | int y0 = (y[0] + y[2]) / 2; 1089 | 1090 | // Angle between axis define skew 1091 | double[] p0 = {(double) x0, (double) y0, 0.0}; 1092 | double[] p1 = {(double) x[0], (double) y[0], 0.0}; 1093 | double[] p2 = {(double) x[1], (double) y[1], 0.0}; 1094 | 1095 | double axisAngle = Geometry.computeAngle (p0, p1, p2); 1096 | 1097 | // dx / dy 1098 | double dx = Geometry.length (x0, y0, x[1], y[1]); 1099 | double dy = Geometry.length (x0, y0, x[0], y[0]) * Math.sin (axisAngle); 1100 | 1101 | // Create geometry for unrotated / unsheared ellipse 1102 | int[] ellipse = createEllipse (x0, y0, (int) Math.round (dx), 1103 | (int) Math.round (dy)); 1104 | int nPoints = ellipse.length / 2; 1105 | 1106 | // Shear if neccessary. If angle is close to 90 there is no shear. 1107 | // If angle is close to 0 or 180 shear is infinite, and we set 1108 | // it to zero as well. 1109 | if (!Geometry.equals (axisAngle, Math.PI/2.0, 0.1) && 1110 | !Geometry.equals (axisAngle, Math.PI, 0.1) && 1111 | !Geometry.equals (axisAngle, 0.0, 0.1)) { 1112 | double xShear = 1.0 / Math.tan (axisAngle); 1113 | for (int i = 0; i < nPoints; i++) 1114 | ellipse[i*2 + 0] += Math.round ((ellipse[i*2 + 1] - y0) * xShear); 1115 | } 1116 | 1117 | // Rotate 1118 | int ddx = x[1] - x0; 1119 | int ddy = y0 - y[1]; 1120 | 1121 | double angle; 1122 | if (ddx == 0 && ddy == 0) angle = 0.0; 1123 | else if (ddx == 0) angle = Math.PI / 2.0; 1124 | else angle = Math. atan ((double) ddy / 1125 | (double) ddx); 1126 | 1127 | double cosAngle = Math.cos (angle); 1128 | double sinAngle = Math.sin (angle); 1129 | 1130 | for (int i = 0; i < nPoints; i++) { 1131 | int xr = (int) Math.round (x0 + 1132 | (ellipse[i*2+0] - x0) * cosAngle - 1133 | (ellipse[i*2+1] - y0) * sinAngle); 1134 | int yr = (int) Math.round (y0 - 1135 | (ellipse[i*2+1] - y0) * cosAngle - 1136 | (ellipse[i*2+0] - x0) * sinAngle); 1137 | 1138 | ellipse[i*2+0] = xr; 1139 | ellipse[i*2+1] = yr; 1140 | } 1141 | 1142 | return ellipse; 1143 | } 1144 | 1145 | 1146 | 1147 | /** 1148 | * Create the geometry for an unrotated, unskewed ellipse. 1149 | * Integer domain. 1150 | * 1151 | * @param x0 X center of ellipse. 1152 | * @param y0 Y center of ellipse. 1153 | * @param dx X ellipse radius. 1154 | * @param dy Y ellipse radius. 1155 | * @return Ellipse geometry [x,y,x,y,...]. 1156 | */ 1157 | public static int[] createEllipse (int x0, int y0, int dx, int dy) 1158 | { 1159 | // Make sure deltas are positive 1160 | dx = Math.abs (dx); 1161 | dy = Math.abs (dy); 1162 | 1163 | // This is an approximate number of points we need to make a smooth 1164 | // surface on a quater of the ellipse 1165 | int nPoints = dx > dy ? dx : dy; 1166 | nPoints /= 2; 1167 | if (nPoints < 1) nPoints = 1; 1168 | 1169 | // Allocate arrays for holding the complete set of vertices around 1170 | // the ellipse. Note that this is a complete ellipse: First and last 1171 | // point coincide. 1172 | int[] ellipse = new int[nPoints*8 + 2]; 1173 | 1174 | // Compute some intermediate results to save time in the inner loop 1175 | int dxdy = dx * dy; 1176 | int dx2 = dx * dx; 1177 | int dy2 = dy * dy; 1178 | 1179 | // Handcode the entries in the four "corner" points of the ellipse, 1180 | // i.e. at point 0, 90, 180, 270 and 360 degrees 1181 | ellipse[nPoints*0 + 0] = x0 + dx; 1182 | ellipse[nPoints*0 + 1] = y0; 1183 | 1184 | ellipse[nPoints*8 + 0] = x0 + dx; 1185 | ellipse[nPoints*8 + 1] = y0; 1186 | 1187 | ellipse[nPoints*2 + 0] = x0; 1188 | ellipse[nPoints*2 + 1] = y0 - dy; 1189 | 1190 | ellipse[nPoints*4 + 0] = x0 - dx; 1191 | ellipse[nPoints*4 + 1] = y0; 1192 | 1193 | ellipse[nPoints*6 + 0] = x0; 1194 | ellipse[nPoints*6 + 1] = y0 + dy; 1195 | 1196 | // Find the angle step 1197 | double angleStep = nPoints > 0 ? Math.PI / 2.0 / nPoints : 0.0; 1198 | 1199 | // Loop over angles from 0 to 90. The rest of the ellipse can be derrived 1200 | // from this first quadrant. For each angle set the four corresponding 1201 | // ellipse points. 1202 | double a = 0.0; 1203 | for (int i = 1; i < nPoints; i++) { 1204 | a += angleStep; 1205 | 1206 | double t = Math.tan (a); 1207 | 1208 | double x = (double) dxdy / Math.sqrt (t * t * dx2 + dy2); 1209 | double y = x * t; 1210 | 1211 | int xi = (int) (x + 0.5); 1212 | int yi = (int) (y + 0.5); 1213 | 1214 | ellipse[(nPoints*0 + i) * 2 + 0] = x0 + xi; 1215 | ellipse[(nPoints*2 - i) * 2 + 0] = x0 - xi; 1216 | ellipse[(nPoints*2 + i) * 2 + 0] = x0 - xi; 1217 | ellipse[(nPoints*4 - i) * 2 + 0] = x0 + xi; 1218 | 1219 | ellipse[(nPoints*0 + i) * 2 + 1] = y0 - yi; 1220 | ellipse[(nPoints*2 - i) * 2 + 1] = y0 - yi; 1221 | ellipse[(nPoints*2 + i) * 2 + 1] = y0 + yi; 1222 | ellipse[(nPoints*4 - i) * 2 + 1] = y0 + yi; 1223 | } 1224 | 1225 | return ellipse; 1226 | } 1227 | 1228 | 1229 | 1230 | /** 1231 | * Create the geometry for an unrotated, unskewed ellipse. 1232 | * Floating point domain. 1233 | * 1234 | * @param x0 X center of ellipse. 1235 | * @param y0 Y center of ellipse. 1236 | * @param dx X ellipse radius. 1237 | * @param dy Y ellipse radius. 1238 | * @return Ellipse geometry [x,y,x,y,...]. 1239 | */ 1240 | public static double[] createEllipse (double x0, double y0, 1241 | double dx, double dy) 1242 | { 1243 | // Make sure deltas are positive 1244 | dx = Math.abs (dx); 1245 | dy = Math.abs (dy); 1246 | 1247 | // As we don't know the resolution of the appliance of the ellipse 1248 | // we create one vertex per 2nd degree. The nPoints variable holds 1249 | // number of points in a quater of the ellipse. 1250 | int nPoints = 45; 1251 | 1252 | // Allocate arrays for holding the complete set of vertices around 1253 | // the ellipse. Note that this is a complete ellipse: First and last 1254 | // point coincide. 1255 | double[] ellipse = new double[nPoints*8 + 2]; 1256 | 1257 | // Compute some intermediate results to save time in the inner loop 1258 | double dxdy = dx * dy; 1259 | double dx2 = dx * dx; 1260 | double dy2 = dy * dy; 1261 | 1262 | // Handcode the entries in the four "corner" points of the ellipse, 1263 | // i.e. at point 0, 90, 180, 270 and 360 degrees 1264 | ellipse[nPoints*0 + 0] = x0 + dx; 1265 | ellipse[nPoints*0 + 1] = y0; 1266 | 1267 | ellipse[nPoints*8 + 0] = x0 + dx; 1268 | ellipse[nPoints*8 + 1] = y0; 1269 | 1270 | ellipse[nPoints*2 + 0] = x0; 1271 | ellipse[nPoints*2 + 1] = y0 - dy; 1272 | 1273 | ellipse[nPoints*4 + 0] = x0 - dx; 1274 | ellipse[nPoints*4 + 1] = y0; 1275 | 1276 | ellipse[nPoints*6 + 0] = x0; 1277 | ellipse[nPoints*6 + 1] = y0 + dy; 1278 | 1279 | // Find the angle step 1280 | double angleStep = nPoints > 0 ? Math.PI / 2.0 / nPoints : 0.0; 1281 | 1282 | // Loop over angles from 0 to 90. The rest of the ellipse can be derrived 1283 | // from this first quadrant. For each angle set the four corresponding 1284 | // ellipse points. 1285 | double a = 0.0; 1286 | for (int i = 1; i < nPoints; i++) { 1287 | a += angleStep; 1288 | 1289 | double t = Math.tan (a); 1290 | 1291 | double x = (double) dxdy / Math.sqrt (t * t * dx2 + dy2); 1292 | double y = x * t + 0.5; 1293 | 1294 | ellipse[(nPoints*0 + i) * 2 + 0] = x0 + x; 1295 | ellipse[(nPoints*2 - i) * 2 + 0] = x0 - x; 1296 | ellipse[(nPoints*2 + i) * 2 + 0] = x0 - x; 1297 | ellipse[(nPoints*4 - i) * 2 + 0] = x0 + x; 1298 | 1299 | ellipse[(nPoints*0 + i) * 2 + 1] = y0 - y; 1300 | ellipse[(nPoints*2 - i) * 2 + 1] = y0 - y; 1301 | ellipse[(nPoints*2 + i) * 2 + 1] = y0 + y; 1302 | ellipse[(nPoints*4 - i) * 2 + 1] = y0 + y; 1303 | } 1304 | 1305 | return ellipse; 1306 | } 1307 | 1308 | 1309 | 1310 | /** 1311 | * Create geometry for a circle. Integer domain. 1312 | * 1313 | * @param x0 X center of circle. 1314 | * @param y0 Y center of circle. 1315 | * @param radius Radius of circle. 1316 | * @return Geometry of circle [x,y,...] 1317 | */ 1318 | public static int[] createCircle (int x0, int y0, int radius) 1319 | { 1320 | return createEllipse (x0, y0, radius, radius); 1321 | } 1322 | 1323 | 1324 | 1325 | /** 1326 | * Create geometry for a circle. Floating point domain. 1327 | * 1328 | * @param x0 X center of circle. 1329 | * @param y0 Y center of circle. 1330 | * @param radius Radius of circle. 1331 | * @return Geometry of circle [x,y,...] 1332 | */ 1333 | public static double[] createCircle (double x0, double y0, double radius) 1334 | { 1335 | return createEllipse (x0, y0, radius, radius); 1336 | } 1337 | 1338 | 1339 | 1340 | 1341 | /** 1342 | * Create the geometry of a sector of an ellipse. 1343 | * 1344 | * @param x0 X coordinate of center of ellipse. 1345 | * @param y0 Y coordinate of center of ellipse. 1346 | * @param dx X radius of ellipse. 1347 | * @param dy Y radius of ellipse. 1348 | * @param angle0 First angle of sector (in radians). 1349 | * @param angle1 Second angle of sector (in radians). 1350 | * @return Geometry of secor [x,y,...] 1351 | */ 1352 | public static int[] createSector (int x0, int y0, int dx, int dy, 1353 | double angle0, double angle1) 1354 | { 1355 | // Determine a sensible number of points for arc 1356 | double angleSpan = Math.abs (angle1 - angle0); 1357 | double arcDistance = Math.max (dx, dy) * angleSpan; 1358 | int nPoints = (int) Math.round (arcDistance / 15); 1359 | double angleStep = angleSpan / (nPoints - 1); 1360 | 1361 | int[] xy = new int[nPoints*2 + 4]; 1362 | 1363 | int index = 0; 1364 | for (int i = 0; i < nPoints; i++) { 1365 | double angle = angle0 + angleStep * i; 1366 | double x = dx * Math.cos (angle); 1367 | double y = dy * Math.sin (angle); 1368 | 1369 | xy[index+0] = x0 + (int) Math.round (x); 1370 | xy[index+1] = y0 - (int) Math.round (y); 1371 | 1372 | index += 2; 1373 | } 1374 | 1375 | // Start and end geometry at center of ellipse to make it a closed polygon 1376 | xy[nPoints*2 + 0] = x0; 1377 | xy[nPoints*2 + 1] = y0; 1378 | xy[nPoints*2 + 2] = xy[0]; 1379 | xy[nPoints*2 + 3] = xy[1]; 1380 | 1381 | return xy; 1382 | } 1383 | 1384 | 1385 | 1386 | /** 1387 | * Create the geometry of a sector of a circle. 1388 | * 1389 | * @param x0 X coordinate of center of ellipse. 1390 | * @param y0 Y coordinate of center of ellipse. 1391 | * @param dx X radius of ellipse. 1392 | * @param dy Y radius of ellipse. 1393 | * @param angle0 First angle of sector (in radians). 1394 | * @param angle1 Second angle of sector (in radians). 1395 | * @return Geometry of secor [x,y,...] 1396 | */ 1397 | public static int[] createSector (int x0, int y0, int radius, 1398 | double angle0, double angle1) 1399 | { 1400 | return createSector (x0, y0, radius, radius, angle0, angle1); 1401 | } 1402 | 1403 | 1404 | 1405 | /** 1406 | * Create the geometry of an arrow. The arrow is positioned at the 1407 | * end (last point) of the specified polyline, as follows: 1408 | * 1409 | * 0,4--, 1410 | * \ --, 1411 | * \ --, 1412 | * \ --, 1413 | * \ --, 1414 | * -------------------------3-----------1 1415 | * / --' 1416 | * / --' 1417 | * / --' 1418 | * / --' 1419 | * 2--' 1420 | * 1421 | * @param x X coordinates of polyline of where arrow is positioned 1422 | * in the end. Must contain at least two points. 1423 | * @param y Y coordinates of polyline of where arrow is positioned 1424 | * in the end. 1425 | * @param length Length along the main axis from point 1 to the 1426 | * projection of point 0. 1427 | * @param angle Angle between the main axis and the line 1,0 1428 | * (and 1,2) in radians. 1429 | * @param inset Specification of point 3 [0.0-1.0], 1.0 will put 1430 | * point 3 at distance length from 1, 0.0 will put it 1431 | * at point 1. 1432 | * @return Array of the five coordinates [x,y,...]. 1433 | */ 1434 | public static int[] createArrow (int[] x, int[] y, 1435 | double length, double angle, double inset) 1436 | { 1437 | int[] arrow = new int[10]; 1438 | 1439 | int x0 = x[x.length - 1]; 1440 | int y0 = y[y.length - 1]; 1441 | 1442 | arrow[2] = x0; 1443 | arrow[3] = y0; 1444 | 1445 | // Find position of interior of the arrow along the polyline 1446 | int[] pos1 = new int[2]; 1447 | Geometry.findPolygonPosition (x, y, length, pos1); 1448 | 1449 | // Angles 1450 | double dx = x0 - pos1[0]; 1451 | double dy = y0 - pos1[1]; 1452 | 1453 | // Polyline angle 1454 | double v = dx == 0.0 ? Math.PI / 2.0 : Math.atan (Math.abs (dy / dx)); 1455 | 1456 | v = dx > 0.0 && dy <= 0.0 ? Math.PI + v : 1457 | dx > 0.0 && dy >= 0.0 ? Math.PI - v : 1458 | dx <= 0.0 && dy < 0.0 ? -v : 1459 | dx <= 0.0 && dy > 0.0 ? +v : 0.0; 1460 | 1461 | double v0 = v + angle; 1462 | double v1 = v - angle; 1463 | 1464 | double edgeLength = length / Math.cos (angle); 1465 | 1466 | arrow[0] = x0 + (int) Math.round (edgeLength * Math.cos (v0)); 1467 | arrow[1] = y0 - (int) Math.round (edgeLength * Math.sin (v0)); 1468 | 1469 | arrow[4] = x0 + (int) Math.round (edgeLength * Math.cos (v1)); 1470 | arrow[5] = y0 - (int) Math.round (edgeLength * Math.sin (v1)); 1471 | 1472 | double c1 = inset * length; 1473 | 1474 | arrow[6] = x0 + (int) Math.round (c1 * Math.cos (v)); 1475 | arrow[7] = y0 - (int) Math.round (c1 * Math.sin (v)); 1476 | 1477 | // Close polygon 1478 | arrow[8] = arrow[0]; 1479 | arrow[9] = arrow[1]; 1480 | 1481 | return arrow; 1482 | } 1483 | 1484 | 1485 | 1486 | /** 1487 | * Create geometry for an arrow along the specified line and with 1488 | * tip at x1,y1. See general method above. 1489 | * 1490 | * @param x0 X first end point of line. 1491 | * @param y0 Y first end point of line. 1492 | * @param x1 X second end point of line. 1493 | * @param y1 Y second end point of line. 1494 | * @param length Length along the main axis from point 1 to the 1495 | * projection of point 0. 1496 | * @param angle Angle between the main axis and the line 1,0 1497 | * (and 1.2) 1498 | * @param inset Specification of point 3 [0.0-1.0], 1.0 will put 1499 | * point 3 at distance length from 1, 0.0 will put it 1500 | * at point 1. 1501 | * @return Array of the four coordinates [x,y,...]. 1502 | */ 1503 | public static int[] createArrow (int x0, int y0, int x1, int y1, 1504 | double length, double angle, double inset) 1505 | { 1506 | int[] x = {x0, x1}; 1507 | int[] y = {y0, y1}; 1508 | 1509 | return createArrow (x, y, length, angle, inset); 1510 | } 1511 | 1512 | 1513 | 1514 | /** 1515 | * Create geometry for a rectangle. Returns a closed polygon; first 1516 | * and last points matches. Integer domain. 1517 | * 1518 | * @param x0 X corner of rectangle. 1519 | * @param y0 Y corner of rectangle. 1520 | * @param width Width (may be negative to indicate leftwards direction) 1521 | * @param height Height (may be negative to indicaten upwards direction) 1522 | */ 1523 | public static int[] createRectangle (int x0, int y0, int width, int height) 1524 | { 1525 | return new int[] {x0, y0, 1526 | x0 + (width - 1), y0, 1527 | x0 + (width - 1), y0 + (height - 1), 1528 | x0, y0 + (height - 1), 1529 | x0, y0}; 1530 | } 1531 | 1532 | 1533 | 1534 | /** 1535 | * Create geometry for a rectangle. Returns a closed polygon; first 1536 | * and last points matches. Floating point domain. 1537 | * 1538 | * @param x0 X corner of rectangle. 1539 | * @param y0 Y corner of rectangle. 1540 | * @param width Width (may be negative to indicate leftwards direction) 1541 | * @param height Height (may be negative to indicaten upwards direction) 1542 | */ 1543 | public static double[] createRectangle (double x0, double y0, 1544 | double width, double height) 1545 | { 1546 | return new double[] {x0, y0, 1547 | x0 + width, y0, 1548 | x0 + width, y0 + height, 1549 | x0, y0 + height, 1550 | x0, y0}; 1551 | } 1552 | 1553 | 1554 | 1555 | /** 1556 | * Create geometry of a star. Integer domain. 1557 | * 1558 | * @param x0 X center of star. 1559 | * @param y0 Y center of star. 1560 | * @param innerRadius Inner radis of arms. 1561 | * @param outerRadius Outer radius of arms. 1562 | * @param nArms Number of arms. 1563 | * @return Geometry of star [x,y,x,y,...]. 1564 | */ 1565 | public static int[] createStar (int x0, int y0, 1566 | int innerRadius, int outerRadius, 1567 | int nArms) 1568 | { 1569 | int nPoints = nArms * 2 + 1; 1570 | 1571 | int[] xy = new int[nPoints * 2]; 1572 | 1573 | double angleStep = 2.0 * Math.PI / nArms / 2.0; 1574 | 1575 | for (int i = 0; i < nArms * 2; i++) { 1576 | double angle = i * angleStep; 1577 | double radius = (i % 2) == 0 ? innerRadius : outerRadius; 1578 | 1579 | double x = x0 + radius * Math.cos (angle); 1580 | double y = y0 + radius * Math.sin (angle); 1581 | 1582 | xy[i*2 + 0] = (int) Math.round (x); 1583 | xy[i*2 + 1] = (int) Math.round (y); 1584 | } 1585 | 1586 | // Close polygon 1587 | xy[nPoints*2 - 2] = xy[0]; 1588 | xy[nPoints*2 - 1] = xy[1]; 1589 | 1590 | return xy; 1591 | } 1592 | 1593 | 1594 | 1595 | /** 1596 | * Create geometry of a star. Floating point domain. 1597 | * 1598 | * @param x0 X center of star. 1599 | * @param y0 Y center of star. 1600 | * @param innerRadius Inner radis of arms. 1601 | * @param outerRadius Outer radius of arms. 1602 | * @param nArms Number of arms. 1603 | * @return Geometry of star [x,y,x,y,...]. 1604 | */ 1605 | public static double[] createStar (double x0, double y0, 1606 | double innerRadius, double outerRadius, 1607 | int nArms) 1608 | { 1609 | int nPoints = nArms * 2 + 1; 1610 | 1611 | double[] xy = new double[nPoints * 2]; 1612 | 1613 | double angleStep = 2.0 * Math.PI / nArms / 2.0; 1614 | 1615 | for (int i = 0; i < nArms * 2; i++) { 1616 | double angle = i * angleStep; 1617 | double radius = (i % 2) == 0 ? innerRadius : outerRadius; 1618 | 1619 | xy[i*2 + 0] = x0 + radius * Math.cos (angle); 1620 | xy[i*2 + 1] = y0 + radius * Math.sin (angle); 1621 | } 1622 | 1623 | // Close polygon 1624 | xy[nPoints*2 - 2] = xy[0]; 1625 | xy[nPoints*2 - 1] = xy[1]; 1626 | 1627 | return xy; 1628 | } 1629 | 1630 | 1631 | 1632 | /** 1633 | * Return the x,y position at distance "length" into the given polyline. 1634 | * 1635 | * @param x X coordinates of polyline 1636 | * @param y Y coordinates of polyline 1637 | * @param length Requested position 1638 | * @param position Preallocated to int[2] 1639 | * @return True if point is within polyline, false otherwise 1640 | */ 1641 | public static boolean findPolygonPosition (int[] x, int[] y, 1642 | double length, 1643 | int[] position) 1644 | { 1645 | if (length < 0) return false; 1646 | 1647 | double accumulatedLength = 0.0; 1648 | for (int i = 1; i < x.length; i++) { 1649 | double legLength = Geometry.length (x[i-1], y[i-1], x[i], y[i]); 1650 | if (legLength + accumulatedLength >= length) { 1651 | double part = length - accumulatedLength; 1652 | double fraction = part / legLength; 1653 | position[0] = (int) Math.round (x[i-1] + fraction * (x[i] - x[i-1])); 1654 | position[1] = (int) Math.round (y[i-1] + fraction * (y[i] - y[i-1])); 1655 | return true; 1656 | } 1657 | 1658 | accumulatedLength += legLength; 1659 | } 1660 | 1661 | // Length is longer than polyline 1662 | return false; 1663 | } 1664 | } 1665 | --------------------------------------------------------------------------------