├── .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] 10f) {
213 | matrix.set(savedMatrix);
214 | float scale = newDist / oldDist;
215 |
216 | Log.v("Scaling","Scale: "+scale);
217 |
218 | Matrix test = new Matrix(matrix);
219 |
220 |
221 | // test scale matrix
222 | test.postScale(scale, scale, mid.x, mid.y);
223 | float[] svalues = new float[9];
224 | test.getValues(svalues);
225 |
226 | //if(svalues[0]>=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 |
--------------------------------------------------------------------------------