();
+
+ if (!mapOverlays.contains(mRoadOverlay) && mRoadNodeMarkers.size() != 0) {
+ list.clear();
+ list.add("Clear Route Node Only");
+ list.add("Clear All");
+ } else if (mRoadNodeMarkers.size() == 0 && mapOverlays.contains(mRoadOverlay)) {
+ list.clear();
+ list.add("Clear Route Only");
+ list.add("Clear All");
+ } else if (!mapOverlays.contains(mRoadOverlay) && mRoadNodeMarkers.size() == 0
+ && markerDestination == null) {
+ list.clear();
+ list.add("Nothing to Clear");
+ } else if (!mapOverlays.contains(mRoadOverlay) && mRoadNodeMarkers.size() == 0
+ && markerDestination != null) {
+ list.clear();
+ list.add("Clear All");
+ } else {
+ list.clear();
+ list.add("Clear Route Node Only");
+ list.add("Clear Route Only");
+ list.add("Clear All");
+ }
+ final String[] test = new String[list.size()];
+ for (int i = 0; i < list.size(); i++) {
+ test[i] = list.get(i);
+ }
+ builder.setTitle("Clear");
+ builder.setItems(test, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if (test[which].equals("Clear Route Only")) {
+ removeRoadPath();
+ } else if (test[which].equals("Clear Route Node Only")) {
+ removeRoadNodes();
+ } else if (test[which].equals("Clear All")) {
+ removeAllOverlay();
+ }
+ // The 'which' argument contains the index position
+ // of the selected item
+ }
+ });
+ AlertDialog alertDialog = builder.create();
+
+ alertDialog.show();
+ return true;
+
+ default:
+ }
+ return false;
+ }
+
+ class ViaPointInfoWindow extends DefaultInfoWindow {
+
+ int mSelectedPoint;
+
+ public ViaPointInfoWindow(int layoutResId, MapView mapView) {
+ super(layoutResId, mapView);
+
+ Button btnDelete = (Button) (mView.findViewById(R.id.bubble_delete));
+ btnDelete.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ removePoint(mSelectedPoint);
+ close();
+ }
+ });
+ }
+
+ @Override
+ public void onOpen(ExtendedOverlayItem item) {
+ mSelectedPoint = ((Integer) item.getRelatedObject()).intValue();
+ super.onOpen(item);
+ }
+
+ }
+}
diff --git a/TileMapApp/src/org/oscim/app/TileMap.java b/TileMapApp/src/org/oscim/app/TileMap.java
new file mode 100755
index 0000000..7398ee2
--- /dev/null
+++ b/TileMapApp/src/org/oscim/app/TileMap.java
@@ -0,0 +1,584 @@
+/* Copyright 2010, 2011, 2012 mapsforge.org
+ * Copyright 2012 Hannes Janetzek
+ * Copyright 2012 osmdroidbonuspack: M.Kergall
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+
+package org.oscim.app;
+
+import java.io.FileNotFoundException;
+
+import org.oscim.app.filepicker.FilePicker;
+import org.oscim.app.preferences.EditPreferences;
+import org.oscim.core.GeoPoint;
+import org.oscim.database.MapDatabases;
+import org.oscim.database.MapOptions;
+import org.oscim.theme.InternalRenderTheme;
+import org.oscim.utils.AndroidUtils;
+import org.oscim.view.DebugSettings;
+import org.oscim.view.MapActivity;
+import org.oscim.view.MapView;
+import org.osmdroid.location.POI;
+import org.osmdroid.overlays.MapEventsOverlay;
+import org.osmdroid.overlays.MapEventsReceiver;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.PowerManager.WakeLock;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Toast;
+
+public class TileMap extends MapActivity implements MapEventsReceiver {
+ static final String TAG = TileMap.class.getSimpleName();
+
+ MapView map;
+
+ private static final String BUNDLE_SHOW_MY_LOCATION = "showMyLocation";
+ private static final String BUNDLE_SNAP_TO_LOCATION = "snapToLocation";
+ private static final int DIALOG_ENTER_COORDINATES = 0;
+ private static final int DIALOG_LOCATION_PROVIDER_DISABLED = 2;
+
+ // Intents
+ private static final int SELECT_RENDER_THEME_FILE = 1;
+ protected static final int POIS_REQUEST = 2;
+
+ LocationHandler mLocation;
+
+ private MapDatabases mMapDatabase;
+
+ //private WakeLock mWakeLock;
+ private Menu mMenu = null;
+
+ POISearch mPoiSearch;
+ RouteSearch mRouteSearch;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ setContentView(R.layout.activity_tilemap);
+ map = (MapView) findViewById(R.id.mapView);
+
+ setMapDatabase(preferences);
+
+ App.map = map;
+ App.mainActivity = this;
+
+ map.setClickable(true);
+ map.setFocusable(true);
+
+ mLocation = new LocationHandler(this);
+
+ // get the pointers to different system services
+ //PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ //mWakeLock = powerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "AMV");
+
+ if (savedInstanceState != null &&
+ savedInstanceState.getBoolean(BUNDLE_SHOW_MY_LOCATION) &&
+ savedInstanceState.getBoolean(BUNDLE_SNAP_TO_LOCATION))
+ mLocation.enableSnapToLocation(false);
+
+ MapEventsOverlay overlay = new MapEventsOverlay(map, this);
+ map.getOverlays().add(overlay);
+
+ App.poiSearch = mPoiSearch = new POISearch(this);
+
+ registerForContextMenu(map);
+ mRouteSearch = new RouteSearch(this);
+
+ //map.getOverlays().add(new AreaSelectionOverlay(map));
+
+ final Intent intent = getIntent();
+ if (intent != null) {
+ Uri uri = intent.getData();
+ if (uri != null) {
+ String scheme = uri.getSchemeSpecificPart();
+ Log.d(TAG, "got intent " + (scheme == null ? "" : scheme));
+ }
+ }
+ }
+
+ private void setMapDatabase(SharedPreferences preferences) {
+ MapDatabases mapDatabaseNew;
+ String dbname = preferences.getString("mapDatabase",
+ MapDatabases.OSCIMAP_READER.name());
+
+ try {
+ mapDatabaseNew = MapDatabases.valueOf(dbname);
+ } catch (IllegalArgumentException e) {
+ mapDatabaseNew = MapDatabases.OSCIMAP_READER;
+ }
+
+ if (mapDatabaseNew != mMapDatabase) {
+ Log.d(TAG, "set map database " + mapDatabaseNew);
+ MapOptions options = null;
+
+ switch (mapDatabaseNew) {
+ case PBMAP_READER:
+ options = new MapOptions(mapDatabaseNew);
+ options.put("url",
+ "http://city.informatik.uni-bremen.de:80/osmstache/test/");
+ break;
+ case OSCIMAP_READER:
+ options = new MapOptions(mapDatabaseNew);
+ options.put("url",
+ "http://city.informatik.uni-bremen.de:80/osci/map-live/");
+ //"http://city.informatik.uni-bremen.de:80/osci/oscim/");
+ break;
+ case TEST_READER:
+ options = new MapOptions(MapDatabases.OSCIMAP_READER);
+ options.put("url",
+ "http://city.informatik.uni-bremen.de:8000/");
+ break;
+ //case OSCIMAP_READER_TEST:
+ // options = new MapOptions(MapDatabases.OSCIMAP_READER);
+ // options.put("url",
+ // "http://city.informatik.uni-bremen.de:80/osci/map3/");
+ //break;
+ default:
+ break;
+ }
+
+ map.setMapDatabase(options);
+ mMapDatabase = mapDatabaseNew;
+ }
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ Uri uri = intent.getData();
+ if (uri != null) {
+ String scheme = uri.getSchemeSpecificPart();
+ Log.d(TAG, "got new intent >>> " + (scheme == null ? "" : scheme));
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.options_menu, menu);
+ mMenu = menu;
+ toggleMenuCheck();
+ return true;
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+
+ case R.id.menu_info_about:
+ startActivity(new Intent(this, InfoView.class));
+ return true;
+
+ case R.id.menu_position:
+ return true;
+
+ case R.id.menu_rotation_enable:
+ if (!item.isChecked()) {
+ item.setChecked(true);
+ map.enableRotation(true);
+ } else {
+ item.setChecked(false);
+ map.enableRotation(false);
+ }
+ toggleMenuCheck();
+ return true;
+
+ case R.id.menu_nearby:
+ Intent intent = new Intent(this, POIActivity.class);
+ startActivityForResult(intent, TileMap.POIS_REQUEST);
+ return true;
+
+ case R.id.menu_compass_enable:
+ if (!item.isChecked()) {
+ item.setChecked(true);
+ map.enableCompass(true);
+ } else {
+ item.setChecked(false);
+ map.enableCompass(false);
+ }
+ toggleMenuCheck();
+ return true;
+
+ case R.id.menu_position_my_location_enable:
+ toggleMenuItem(mMenu,
+ R.id.menu_position_my_location_enable,
+ R.id.menu_position_my_location_disable,
+ !mLocation.enableShowMyLocation(true));
+ return true;
+
+ case R.id.menu_position_my_location_disable:
+ toggleMenuItem(mMenu,
+ R.id.menu_position_my_location_enable,
+ R.id.menu_position_my_location_disable,
+ mLocation.disableShowMyLocation());
+ return true;
+
+ case R.id.menu_position_enter_coordinates:
+ showDialog(DIALOG_ENTER_COORDINATES);
+ return true;
+
+ case R.id.menu_preferences:
+ startActivity(new Intent(this, EditPreferences.class));
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private void toggleMenuCheck() {
+ mMenu.findItem(R.id.menu_rotation_enable).setChecked(map.getRotationEnabled());
+ mMenu.findItem(R.id.menu_compass_enable).setChecked(map.getCompassEnabled());
+ }
+
+ private static void toggleMenuItem(Menu menu, int id, int id2, boolean enable) {
+ menu.findItem(id).setVisible(enable);
+ menu.findItem(id).setEnabled(enable);
+ menu.findItem(id2).setVisible(!enable);
+ menu.findItem(id2).setEnabled(!enable);
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+
+ if (!isPreHoneyComb()) {
+ menu.clear();
+ onCreateOptionsMenu(menu);
+ }
+
+ toggleMenuItem(menu,
+ R.id.menu_position_my_location_enable,
+ R.id.menu_position_my_location_disable,
+ !mLocation.isShowMyLocationEnabled());
+
+ // if (mMapDatabase == MapDatabases.MAP_READER) {
+ // menu.findItem(R.id.menu_mapfile).setVisible(true);
+ // menu.findItem(R.id.menu_position_map_center).setVisible(true);
+ // }
+ // else {
+ // menu.findItem(R.id.menu_mapfile).setVisible(false);
+ // menu.findItem(R.id.menu_position_map_center).setVisible(false);
+ // }
+
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent event) {
+ // forward the event to the MapView
+ return map.onTrackballEvent(event);
+ }
+
+ // private void startMapFilePicker() {
+ // FilePicker.setFileDisplayFilter(FILE_FILTER_EXTENSION_MAP);
+ // FilePicker.setFileSelectFilter(new ValidMapFile());
+ // startActivityForResult(new Intent(this, FilePicker.class),
+ // SELECT_MAP_FILE);
+ // }
+
+ // private void startRenderThemePicker() {
+ // FilePicker.setFileDisplayFilter(FILE_FILTER_EXTENSION_XML);
+ // FilePicker.setFileSelectFilter(new ValidRenderTheme());
+ // startActivityForResult(new Intent(this, FilePicker.class),
+ // SELECT_RENDER_THEME_FILE);
+ // }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ switch (requestCode) {
+ case POIS_REQUEST:
+ Log.d(TAG, "result: POIS_REQUEST");
+ if (resultCode == RESULT_OK) {
+ int id = intent.getIntExtra("ID", 0);
+ Log.d(TAG, "result: POIS_REQUEST: " + id);
+
+ mPoiSearch.poiMarkers.showBubbleOnItem(id, map);
+
+ POI poi = mPoiSearch.getPOIs().get(id);
+
+ if (poi.bbox != null)
+ map.getMapViewPosition().animateTo(poi.bbox);
+ else
+ map.getMapViewPosition().animateTo(poi.location);
+ }
+ break;
+ case SELECT_RENDER_THEME_FILE:
+ if (resultCode == RESULT_OK && intent != null
+ && intent.getStringExtra(FilePicker.SELECTED_FILE) != null) {
+ try {
+ map.setRenderTheme(intent
+ .getStringExtra(FilePicker.SELECTED_FILE));
+ } catch (FileNotFoundException e) {
+ showToastOnUiThread(e.getLocalizedMessage());
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ // if (requestCode == SELECT_MAP_FILE) {
+ // if (resultCode == RESULT_OK) {
+ //
+ // location.disableSnapToLocation(true);
+ //
+ // if (intent != null) {
+ // if (intent.getStringExtra(FilePicker.SELECTED_FILE) != null) {
+ // map.setMapFile(intent
+ // .getStringExtra(FilePicker.SELECTED_FILE));
+ // }
+ // }
+ // } else if (resultCode == RESULT_CANCELED) {
+ // startActivity(new Intent(this, EditPreferences.class));
+ // }
+ // } else
+ // if (requestCode == SELECT_RENDER_THEME_FILE && resultCode == RESULT_OK
+ // && intent != null
+ // && intent.getStringExtra(FilePicker.SELECTED_FILE) != null) {
+ // try {
+ // map.setRenderTheme(intent
+ // .getStringExtra(FilePicker.SELECTED_FILE));
+ // } catch (FileNotFoundException e) {
+ // showToastOnUiThread(e.getLocalizedMessage());
+ // }
+ // }
+ }
+
+ static boolean isPreHoneyComb() {
+ return Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB;
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ if (id == DIALOG_ENTER_COORDINATES) {
+ if (mLocationDialog == null)
+ mLocationDialog = new LocationDialog();
+
+ return mLocationDialog.createDialog(this);
+
+ } else if (id == DIALOG_LOCATION_PROVIDER_DISABLED) {
+ builder.setIcon(android.R.drawable.ic_menu_info_details);
+ builder.setTitle(R.string.error);
+ builder.setMessage(R.string.no_location_provider_available);
+ builder.setPositiveButton(R.string.ok, null);
+ return builder.create();
+ } else {
+ // no dialog will be created
+ return null;
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mLocation.disableShowMyLocation();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ // release the wake lock if necessary
+ //if (mWakeLock.isHeld()) {
+ // mWakeLock.release();
+ //}
+ }
+
+ LocationDialog mLocationDialog;
+
+ @SuppressWarnings("deprecation")
+ @Override
+ protected void onPrepareDialog(int id, final Dialog dialog) {
+ if (id == DIALOG_ENTER_COORDINATES) {
+
+ mLocationDialog.prepareDialog(map, dialog);
+
+ } else {
+ super.onPrepareDialog(id, dialog);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ // MapScaleBar mapScaleBar = mapView.getMapScaleBar();
+ // mapScaleBar.setShowMapScaleBar(preferences.getBoolean("showScaleBar",
+ // false));
+ // String scaleBarUnitDefault =
+ // getString(R.string.preferences_scale_bar_unit_default);
+ // String scaleBarUnit = preferences.getString("scaleBarUnit",
+ // scaleBarUnitDefault);
+ // mapScaleBar.setImperialUnits(scaleBarUnit.equals("imperial"));
+
+ if (preferences.contains("mapDatabase")) {
+ setMapDatabase(preferences);
+ }
+ if (preferences.contains("theme")) {
+ String name = preferences.getString("theme",
+ "OSMARENDER");
+ InternalRenderTheme theme = null;
+
+ try {
+ theme = InternalRenderTheme.valueOf(name);
+ } catch (IllegalArgumentException e) {
+ }
+ if (theme == null)
+ map.setRenderTheme(InternalRenderTheme.DEFAULT);
+ else
+ map.setRenderTheme(theme);
+ }
+ // try {
+ // String textScaleDefault =
+ // getString(R.string.preferences_text_scale_default);
+ // map.setTextScale(Float.parseFloat(preferences.getString("textScale",
+ // textScaleDefault)));
+ // } catch (NumberFormatException e) {
+ // map.setTextScale(1);
+ // }
+
+ if (preferences.getBoolean("fullscreen", false)) {
+ Log.i("mapviewer", "FULLSCREEN");
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
+ } else {
+ Log.i("mapviewer", "NO FULLSCREEN");
+ getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
+ }
+ if (preferences.getBoolean("fixOrientation", true)) {
+ this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ // this all returns the orientation which is not currently active?!
+ // getWindow().getWindowManager().getDefaultDisplay().getRotation());
+ // getWindow().getWindowManager().getDefaultDisplay().getOrientation());
+ }
+
+ //if (preferences.getBoolean("wakeLock", false) && !mWakeLock.isHeld()) {
+ // mWakeLock.acquire();
+ //}
+
+ boolean drawTileFrames = preferences.getBoolean("drawTileFrames", false);
+ boolean drawTileCoordinates = preferences.getBoolean("drawTileCoordinates", false);
+ boolean disablePolygons = preferences.getBoolean("disablePolygons", false);
+ boolean drawUnmatchedWays = preferences.getBoolean("drawUnmatchedWays", false);
+ boolean debugLabels = preferences.getBoolean("debugLabels", false);
+
+ DebugSettings cur = map.getDebugSettings();
+ if (cur.disablePolygons != disablePolygons
+ || cur.drawTileCoordinates != drawTileCoordinates
+ || cur.drawTileFrames != drawTileFrames
+ || cur.debugTheme != drawUnmatchedWays
+ || cur.debugLabels != debugLabels) {
+
+ DebugSettings debugSettings = new DebugSettings(drawTileCoordinates,
+ drawTileFrames, disablePolygons, drawUnmatchedWays, debugLabels);
+
+ map.setDebugSettings(debugSettings);
+ }
+
+ map.redrawMap(false);
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(BUNDLE_SHOW_MY_LOCATION, mLocation.isShowMyLocationEnabled());
+ // outState.putBoolean(BUNDLE_CENTER_AT_FIRST_FIX,
+ // mMyLocationListener.isCenterAtFirstFix());
+ // outState.putBoolean(BUNDLE_SNAP_TO_LOCATION, mSnapToLocation);
+ }
+
+ /**
+ * Uses the UI thread to display the given text message as toast
+ * notification.
+ * @param text
+ * the text message to display
+ */
+ void showToastOnUiThread(final String text) {
+
+ if (AndroidUtils.currentThreadIsUiThread()) {
+ Toast toast = Toast.makeText(this, text, Toast.LENGTH_LONG);
+ toast.show();
+ } else {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast toast = Toast.makeText(TileMap.this, text, Toast.LENGTH_LONG);
+ toast.show();
+ }
+ });
+ }
+ }
+
+ //----------- Context Menu when clicking on the map
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ // Log.d(TAG, "create context menu");
+
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.map_menu, menu);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ // Log.d(TAG, "context menu item selected " + item.getItemId());
+
+ if (mPoiSearch.onContextItemSelected(item))
+ return true;
+
+ if (mRouteSearch.onContextItemSelected(item))
+ return true;
+
+ return super.onContextItemSelected(item);
+ }
+
+ //------------ MapEventsReceiver implementation
+
+ @Override
+ public boolean singleTapUpHelper(GeoPoint p) {
+ mPoiSearch.singleTapUp();
+ mRouteSearch.singleTapUp();
+ return false;
+ }
+
+ @Override
+ public boolean longPressHelper(GeoPoint p) {
+ mRouteSearch.longPress(p);
+
+ openContextMenu(map);
+
+ return true;
+ }
+
+}
diff --git a/VectorTileMap/src/org/mapsforge/app/filefilter/FilterByFileExtension.java b/TileMapApp/src/org/oscim/app/filefilter/FilterByFileExtension.java
similarity index 96%
rename from VectorTileMap/src/org/mapsforge/app/filefilter/FilterByFileExtension.java
rename to TileMapApp/src/org/oscim/app/filefilter/FilterByFileExtension.java
index 9868eb1..4fc1ff3 100644
--- a/VectorTileMap/src/org/mapsforge/app/filefilter/FilterByFileExtension.java
+++ b/TileMapApp/src/org/oscim/app/filefilter/FilterByFileExtension.java
@@ -12,13 +12,14 @@
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see .
*/
-package org.mapsforge.app.filefilter;
+package org.oscim.app.filefilter;
import java.io.File;
import java.io.FileFilter;
/**
- * Accepts all readable directories and all readable files with a given extension.
+ * Accepts all readable directories and all readable files with a given
+ * extension.
*/
public class FilterByFileExtension implements FileFilter {
private final String extension;
diff --git a/VectorTileMap/src/org/mapsforge/app/filefilter/ValidFileFilter.java b/TileMapApp/src/org/oscim/app/filefilter/ValidFileFilter.java
similarity index 88%
rename from VectorTileMap/src/org/mapsforge/app/filefilter/ValidFileFilter.java
rename to TileMapApp/src/org/oscim/app/filefilter/ValidFileFilter.java
index 6c6ee69..9ca6067 100644
--- a/VectorTileMap/src/org/mapsforge/app/filefilter/ValidFileFilter.java
+++ b/TileMapApp/src/org/oscim/app/filefilter/ValidFileFilter.java
@@ -12,11 +12,11 @@
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see .
*/
-package org.mapsforge.app.filefilter;
+package org.oscim.app.filefilter;
import java.io.FileFilter;
-import org.mapsforge.database.FileOpenResult;
+import org.oscim.database.OpenResult;
/**
* An extension of the {@link FileFilter} interface.
@@ -25,5 +25,5 @@ public interface ValidFileFilter extends FileFilter {
/**
* @return the result of the last {@link #accept} call (might be null).
*/
- FileOpenResult getFileOpenResult();
+ OpenResult getFileOpenResult();
}
diff --git a/VectorTileMap/src/org/mapsforge/app/filefilter/ValidMapFile.java b/TileMapApp/src/org/oscim/app/filefilter/ValidMapFile.java
similarity index 63%
rename from VectorTileMap/src/org/mapsforge/app/filefilter/ValidMapFile.java
rename to TileMapApp/src/org/oscim/app/filefilter/ValidMapFile.java
index d7a44e8..9bb20ae 100644
--- a/VectorTileMap/src/org/mapsforge/app/filefilter/ValidMapFile.java
+++ b/TileMapApp/src/org/oscim/app/filefilter/ValidMapFile.java
@@ -12,30 +12,36 @@
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see .
*/
-package org.mapsforge.app.filefilter;
+package org.oscim.app.filefilter;
import java.io.File;
-import org.mapsforge.database.FileOpenResult;
-import org.mapsforge.database.IMapDatabase;
-import org.mapsforge.database.mapfile.MapDatabase;
+import org.oscim.database.IMapDatabase;
+import org.oscim.database.MapDatabases;
+import org.oscim.database.OpenResult;
+import org.oscim.database.mapfile.MapDatabase;
+import org.oscim.database.MapOptions;
/**
* Accepts all valid map files.
*/
public final class ValidMapFile implements ValidFileFilter {
- private FileOpenResult fileOpenResult;
+ private OpenResult openResult;
@Override
public boolean accept(File file) {
IMapDatabase mapDatabase = new MapDatabase();
- this.fileOpenResult = mapDatabase.openFile(file);
- mapDatabase.closeFile();
- return this.fileOpenResult.isSuccess();
+ MapOptions options = new MapOptions(MapDatabases.MAP_READER);
+ options.put("file", file.getAbsolutePath());
+
+ this.openResult = mapDatabase.open(options);
+
+ mapDatabase.close();
+ return this.openResult.isSuccess();
}
@Override
- public FileOpenResult getFileOpenResult() {
- return this.fileOpenResult;
+ public OpenResult getFileOpenResult() {
+ return this.openResult;
}
}
diff --git a/VectorTileMap/src/org/mapsforge/app/filefilter/ValidRenderTheme.java b/TileMapApp/src/org/oscim/app/filefilter/ValidRenderTheme.java
similarity index 74%
rename from VectorTileMap/src/org/mapsforge/app/filefilter/ValidRenderTheme.java
rename to TileMapApp/src/org/oscim/app/filefilter/ValidRenderTheme.java
index d9158f4..98b3575 100644
--- a/VectorTileMap/src/org/mapsforge/app/filefilter/ValidRenderTheme.java
+++ b/TileMapApp/src/org/oscim/app/filefilter/ValidRenderTheme.java
@@ -12,7 +12,7 @@
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see .
*/
-package org.mapsforge.app.filefilter;
+package org.oscim.app.filefilter;
import java.io.File;
import java.io.FileInputStream;
@@ -22,8 +22,8 @@
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
-import org.mapsforge.android.rendertheme.RenderThemeHandler;
-import org.mapsforge.database.FileOpenResult;
+import org.oscim.database.OpenResult;
+import org.oscim.theme.RenderThemeHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
@@ -32,7 +32,7 @@
* Accepts all valid render theme XML files.
*/
public final class ValidRenderTheme implements ValidFileFilter {
- private FileOpenResult fileOpenResult;
+ private OpenResult openResult;
@Override
public boolean accept(File file) {
@@ -44,28 +44,28 @@ public boolean accept(File file) {
XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
xmlReader.setContentHandler(renderThemeHandler);
xmlReader.parse(new InputSource(inputStream));
- this.fileOpenResult = FileOpenResult.SUCCESS;
+ this.openResult = OpenResult.SUCCESS;
} catch (ParserConfigurationException e) {
- this.fileOpenResult = new FileOpenResult(e.getMessage());
+ this.openResult = new OpenResult(e.getMessage());
} catch (SAXException e) {
- this.fileOpenResult = new FileOpenResult(e.getMessage());
+ this.openResult = new OpenResult(e.getMessage());
} catch (IOException e) {
- this.fileOpenResult = new FileOpenResult(e.getMessage());
+ this.openResult = new OpenResult(e.getMessage());
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
- this.fileOpenResult = new FileOpenResult(e.getMessage());
+ this.openResult = new OpenResult(e.getMessage());
}
}
- return this.fileOpenResult.isSuccess();
+ return this.openResult.isSuccess();
}
@Override
- public FileOpenResult getFileOpenResult() {
- return this.fileOpenResult;
+ public OpenResult getFileOpenResult() {
+ return this.openResult;
}
}
diff --git a/VectorTileMap/src/org/mapsforge/app/filepicker/FilePicker.java b/TileMapApp/src/org/oscim/app/filepicker/FilePicker.java
similarity index 79%
rename from VectorTileMap/src/org/mapsforge/app/filepicker/FilePicker.java
rename to TileMapApp/src/org/oscim/app/filepicker/FilePicker.java
index 7c997da..2f7400e 100755
--- a/VectorTileMap/src/org/mapsforge/app/filepicker/FilePicker.java
+++ b/TileMapApp/src/org/oscim/app/filepicker/FilePicker.java
@@ -12,38 +12,46 @@
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see .
*/
-package org.mapsforge.app.filepicker;
+package org.oscim.app.filepicker;
import java.io.File;
import java.io.FileFilter;
import java.util.Arrays;
import java.util.Comparator;
-import org.mapsforge.app.R;
-import org.mapsforge.app.filefilter.ValidFileFilter;
+import org.oscim.app.R;
+import org.oscim.app.filefilter.ValidFileFilter;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
+import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.GridView;
/**
- * A FilePicker displays the contents of directories. The user can navigate within the file system and select a single
- * file whose path is then returned to the calling activity. The ordering of directory contents can be specified via
- * {@link #setFileComparator(Comparator)}. By default subfolders and files are grouped and each group is ordered
+ * A FilePicker displays the contents of directories. The user can navigate
+ * within the file system and select a single
+ * file whose path is then returned to the calling activity. The ordering of
+ * directory contents can be specified via
+ * {@link #setFileComparator(Comparator)}. By default subfolders and files are
+ * grouped and each group is ordered
* alphabetically.
*
- * A {@link FileFilter} can be activated via {@link #setFileDisplayFilter(FileFilter)} to restrict the displayed files
- * and folders. By default all files and folders are visible.
+ * A {@link FileFilter} can be activated via
+ * {@link #setFileDisplayFilter(FileFilter)} to restrict the displayed files and
+ * folders. By default all files and folders are visible.
*
- * Another FileFilter can be applied via {@link #setFileSelectFilter(ValidFileFilter)} to check if a
- * selected file is valid before its path is returned. By default all files are considered as valid and can be selected.
+ * Another FileFilter can be applied via
+ * {@link #setFileSelectFilter(ValidFileFilter)} to check if a selected file is
+ * valid before its path is returned. By default all files are considered as
+ * valid and can be selected.
*/
public class FilePicker extends Activity implements AdapterView.OnItemClickListener {
/**
@@ -61,9 +69,9 @@ public class FilePicker extends Activity implements AdapterView.OnItemClickListe
private static final String PREFERENCES_FILE = "FilePicker";
/**
- * Sets the file comparator which is used to order the contents of all directories before displaying them. If set to
+ * Sets the file comparator which is used to order the contents of all
+ * directories before displaying them. If set to
* null, subfolders and files will not be ordered.
- *
* @param fileComparator
* the file comparator (may be null).
*/
@@ -72,9 +80,9 @@ public static void setFileComparator(Comparator fileComparator) {
}
/**
- * Sets the file display filter. This filter is used to determine which files and subfolders of directories will be
+ * Sets the file display filter. This filter is used to determine which
+ * files and subfolders of directories will be
* displayed. If set to null, all files and subfolders are shown.
- *
* @param fileDisplayFilter
* the file display filter (may be null).
*/
@@ -83,9 +91,9 @@ public static void setFileDisplayFilter(FileFilter fileDisplayFilter) {
}
/**
- * Sets the file select filter. This filter is used when the user selects a file to determine if it is valid. If set
+ * Sets the file select filter. This filter is used when the user selects a
+ * file to determine if it is valid. If set
* to null, all files are considered as valid.
- *
* @param fileSelectFilter
* the file selection filter (may be null).
*/
@@ -95,7 +103,6 @@ public static void setFileSelectFilter(ValidFileFilter fileSelectFilter) {
/**
* Creates the default file comparator.
- *
* @return the default file comparator.
*/
private static Comparator getDefaultFileComparator() {
@@ -119,6 +126,7 @@ public int compare(File file1, File file2) {
private File[] files;
private File[] filesWithParentFolder;
+ @SuppressWarnings("deprecation")
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
File selectedFile = this.files[(int) id];
@@ -188,26 +196,26 @@ protected void onCreate(Bundle savedInstanceState) {
protected Dialog onCreateDialog(int id) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
switch (id) {
- case DIALOG_FILE_INVALID:
- builder.setIcon(android.R.drawable.ic_menu_info_details);
- builder.setTitle(R.string.error);
-
- StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.append(getString(R.string.file_invalid));
- stringBuilder.append("\n\n");
- stringBuilder.append(FilePicker.fileSelectFilter.getFileOpenResult()
- .getErrorMessage());
-
- builder.setMessage(stringBuilder.toString());
- builder.setPositiveButton(R.string.ok, null);
- return builder.create();
- // case DIALOG_FILE_SELECT:
- // builder.setMessage(R.string.file_select);
- // builder.setPositiveButton(R.string.ok, null);
- // return builder.create();
- default:
- // do dialog will be created
- return null;
+ case DIALOG_FILE_INVALID:
+ builder.setIcon(android.R.drawable.ic_menu_info_details);
+ builder.setTitle(R.string.error);
+
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append(getString(R.string.file_invalid));
+ stringBuilder.append("\n\n");
+ stringBuilder.append(FilePicker.fileSelectFilter.getFileOpenResult()
+ .getErrorMessage());
+
+ builder.setMessage(stringBuilder.toString());
+ builder.setPositiveButton(R.string.ok, null);
+ return builder.create();
+ // case DIALOG_FILE_SELECT:
+ // builder.setMessage(R.string.file_select);
+ // builder.setPositiveButton(R.string.ok, null);
+ // return builder.create();
+ default:
+ // do dialog will be created
+ return null;
}
}
@@ -223,13 +231,16 @@ protected void onPause() {
editor.commit();
}
+ @TargetApi(11)
@Override
protected void onResume() {
super.onResume();
- // getActionBar().hide();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
+ getActionBar().hide();
// check if the full screen mode should be activated
- // if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("fullscreen", false)) {
+ // if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("fullscreen",
+ // false)) {
// getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
// getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
// } else {
diff --git a/VectorTileMap/src/org/mapsforge/app/filepicker/FilePickerIconAdapter.java b/TileMapApp/src/org/oscim/app/filepicker/FilePickerIconAdapter.java
similarity index 95%
rename from VectorTileMap/src/org/mapsforge/app/filepicker/FilePickerIconAdapter.java
rename to TileMapApp/src/org/oscim/app/filepicker/FilePickerIconAdapter.java
index f27dd40..3e291c6 100755
--- a/VectorTileMap/src/org/mapsforge/app/filepicker/FilePickerIconAdapter.java
+++ b/TileMapApp/src/org/oscim/app/filepicker/FilePickerIconAdapter.java
@@ -12,11 +12,11 @@
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see .
*/
-package org.mapsforge.app.filepicker;
+package org.oscim.app.filepicker;
import java.io.File;
-import org.mapsforge.app.R;
+import org.oscim.app.R;
import android.content.Context;
import android.view.Gravity;
@@ -37,9 +37,9 @@ class FilePickerIconAdapter extends BaseAdapter {
/**
* Creates a new FilePickerIconAdapter with the given context.
- *
* @param context
- * the context of this adapter, through which new Views are created.
+ * the context of this adapter, through which new Views are
+ * created.
*/
FilePickerIconAdapter(Context context) {
super();
@@ -98,11 +98,11 @@ public View getView(int index, View convertView, ViewGroup parent) {
/**
* Sets the data of this adapter.
- *
* @param files
* the new files for this adapter.
* @param newHasParentFolder
- * true if the file array has a parent folder at index 0, false otherwise.
+ * true if the file array has a parent folder at index 0, false
+ * otherwise.
*/
void setFiles(File[] files, boolean newHasParentFolder) {
this.files = files.clone();
diff --git a/VectorTileMap/src/org/mapsforge/app/preferences/EditPreferences.java b/TileMapApp/src/org/oscim/app/preferences/EditPreferences.java
similarity index 90%
rename from VectorTileMap/src/org/mapsforge/app/preferences/EditPreferences.java
rename to TileMapApp/src/org/oscim/app/preferences/EditPreferences.java
index 4b919aa..9df8927 100644
--- a/VectorTileMap/src/org/mapsforge/app/preferences/EditPreferences.java
+++ b/TileMapApp/src/org/oscim/app/preferences/EditPreferences.java
@@ -12,9 +12,9 @@
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see .
*/
-package org.mapsforge.app.preferences;
+package org.oscim.app.preferences;
-import org.mapsforge.app.R;
+import org.oscim.app.R;
import android.os.Bundle;
import android.preference.PreferenceActivity;
@@ -23,15 +23,19 @@
* Activity to edit the application preferences.
*/
public class EditPreferences extends PreferenceActivity {
+ @SuppressWarnings("deprecation")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
+ // @TargetApi(11)
@Override
protected void onResume() {
super.onResume();
+
+ // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
// getActionBar().hide();
// check if the full screen mode should be activated
diff --git a/VectorTileMap/src/org/mapsforge/app/preferences/SeekBarPreference.java b/TileMapApp/src/org/oscim/app/preferences/SeekBarPreference.java
similarity index 71%
rename from VectorTileMap/src/org/mapsforge/app/preferences/SeekBarPreference.java
rename to TileMapApp/src/org/oscim/app/preferences/SeekBarPreference.java
index 777e716..3e060e9 100644
--- a/VectorTileMap/src/org/mapsforge/app/preferences/SeekBarPreference.java
+++ b/TileMapApp/src/org/oscim/app/preferences/SeekBarPreference.java
@@ -12,7 +12,7 @@
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see .
*/
-package org.mapsforge.app.preferences;
+package org.oscim.app.preferences;
import android.content.Context;
import android.content.DialogInterface;
@@ -29,14 +29,16 @@
import android.widget.TextView;
/**
- * This abstract class provides all code for a seek bar preference. Deriving classes only need to set the current and
- * maximum value of the seek bar. An optional text message above the seek bar is also supported as well as an optional
+ * This abstract class provides all code for a seek bar preference. Deriving
+ * classes only need to set the current and
+ * maximum value of the seek bar. An optional text message above the seek bar is
+ * also supported as well as an optional
* current value message below the seek bar.
*/
abstract class SeekBarPreference extends DialogPreference implements OnSeekBarChangeListener {
- private TextView currentValueTextView;
- private Editor editor;
- private SeekBar preferenceSeekBar;
+ private TextView mCurrentValueTextView;
+ private Editor mEditor;
+ private SeekBar mPreferenceSeekBar;
/**
* How much the value should increase when the seek bar is moved.
@@ -65,7 +67,6 @@ abstract class SeekBarPreference extends DialogPreference implements OnSeekBarCh
/**
* Create a new seek bar preference.
- *
* @param context
* the context of the seek bar preferences activity.
* @param attrs
@@ -73,26 +74,26 @@ abstract class SeekBarPreference extends DialogPreference implements OnSeekBarCh
*/
SeekBarPreference(Context context, AttributeSet attrs) {
super(context, attrs);
- this.preferencesDefault = PreferenceManager.getDefaultSharedPreferences(context);
+ preferencesDefault = PreferenceManager.getDefaultSharedPreferences(context);
}
@Override
public void onClick(DialogInterface dialog, int which) {
// check if the "OK" button was pressed and the seek bar value has changed
if (which == DialogInterface.BUTTON_POSITIVE
- && this.seekBarCurrentValue != this.preferenceSeekBar.getProgress()) {
+ && seekBarCurrentValue != mPreferenceSeekBar.getProgress()) {
// get the value of the seek bar and save it in the preferences
- this.seekBarCurrentValue = this.preferenceSeekBar.getProgress();
- this.editor = this.preferencesDefault.edit();
- this.editor.putInt(this.getKey(), this.seekBarCurrentValue);
- this.editor.commit();
+ seekBarCurrentValue = mPreferenceSeekBar.getProgress();
+ mEditor = preferencesDefault.edit();
+ mEditor.putInt(getKey(), seekBarCurrentValue);
+ mEditor.commit();
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- if (this.currentValueTextView != null) {
- this.currentValueTextView.setText(getCurrentValueText(progress));
+ if (mCurrentValueTextView != null) {
+ mCurrentValueTextView.setText(getCurrentValueText(progress));
}
}
@@ -114,38 +115,37 @@ protected View onCreateDialogView() {
linearLayout.setPadding(20, 10, 20, 10);
// check if a text message should appear above the seek bar
- if (this.messageText != null) {
+ if (messageText != null) {
// create a text view for the text messageText
TextView messageTextView = new TextView(getContext());
- messageTextView.setText(this.messageText);
+ messageTextView.setText(messageText);
messageTextView.setPadding(0, 0, 0, 20);
// add the text message view to the layout
linearLayout.addView(messageTextView);
}
// create the seek bar and set the maximum and current value
- this.preferenceSeekBar = new SeekBar(getContext());
- this.preferenceSeekBar.setOnSeekBarChangeListener(this);
- this.preferenceSeekBar.setMax(this.max);
- this.preferenceSeekBar.setProgress(Math.min(this.seekBarCurrentValue, this.max));
- this.preferenceSeekBar.setKeyProgressIncrement(this.increment);
- this.preferenceSeekBar.setPadding(0, 0, 0, 10);
+ mPreferenceSeekBar = new SeekBar(getContext());
+ mPreferenceSeekBar.setOnSeekBarChangeListener(this);
+ mPreferenceSeekBar.setMax(max);
+ mPreferenceSeekBar.setProgress(Math.min(seekBarCurrentValue, max));
+ mPreferenceSeekBar.setKeyProgressIncrement(increment);
+ mPreferenceSeekBar.setPadding(0, 0, 0, 10);
// add the seek bar to the layout
- linearLayout.addView(this.preferenceSeekBar);
+ linearLayout.addView(mPreferenceSeekBar);
// create the text view for the current value below the seek bar
- this.currentValueTextView = new TextView(getContext());
- this.currentValueTextView.setText(getCurrentValueText(this.preferenceSeekBar.getProgress()));
- this.currentValueTextView.setGravity(Gravity.CENTER_HORIZONTAL);
+ mCurrentValueTextView = new TextView(getContext());
+ mCurrentValueTextView.setText(getCurrentValueText(mPreferenceSeekBar.getProgress()));
+ mCurrentValueTextView.setGravity(Gravity.CENTER_HORIZONTAL);
// add the current value text view to the layout
- linearLayout.addView(this.currentValueTextView);
+ linearLayout.addView(mCurrentValueTextView);
return linearLayout;
}
/**
* Get the current value text.
- *
* @param progress
* the current progress level of the seek bar.
* @return the new current value text
diff --git a/TileMapApp/src/org/osmdroid/location/FlickrPOIProvider.java b/TileMapApp/src/org/osmdroid/location/FlickrPOIProvider.java
new file mode 100644
index 0000000..bf74e41
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/location/FlickrPOIProvider.java
@@ -0,0 +1,165 @@
+package org.osmdroid.location;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.oscim.core.BoundingBox;
+import org.oscim.core.GeoPoint;
+import org.osmdroid.utils.BonusPackHelper;
+
+import android.util.Log;
+
+/**
+ * POI Provider using Flickr service to get geolocalized photos.
+ * @see "http://www.flickr.com/services/api/flickr.photos.search.html"
+ * @author M.Kergall
+ */
+public class FlickrPOIProvider implements POIProvider {
+
+ protected String mApiKey;
+ private final static String PHOTO_URL = "http://www.flickr.com/photos/%s/%s/sizes/o/in/photostream/";
+
+ /**
+ * @param apiKey
+ * the registered API key to give to Flickr service.
+ * @see "http://www.flickr.com/help/api/"
+ */
+ public FlickrPOIProvider(String apiKey) {
+ mApiKey = apiKey;
+ }
+
+ private String getUrlInside(BoundingBox boundingBox, int maxResults) {
+ StringBuffer url = new StringBuffer(
+ "http://api.flickr.com/services/rest/?method=flickr.photos.search");
+ url.append("&api_key=" + mApiKey);
+ url.append("&bbox=" + boundingBox.getMinLongitude());
+ url.append("," + boundingBox.getMinLatitude());
+ url.append("," + boundingBox.getMaxLongitude());
+ url.append("," + boundingBox.getMaxLatitude());
+ url.append("&has_geo=1");
+ // url.append("&geo_context=2");
+ // url.append("&is_commons=true");
+ url.append("&format=json&nojsoncallback=1");
+ url.append("&per_page=" + maxResults);
+ // From Flickr doc:
+ // "Geo queries require some sort of limiting agent in order to prevent the database from crying."
+ // And min_date_upload is considered as a limiting agent. So:
+ url.append("&min_upload_date=2005/01/01");
+
+ // Ask to provide some additional attributes we will need:
+ url.append("&extras=geo,url_sq");
+ url.append("&sort=interestingness-desc");
+ return url.toString();
+ }
+
+ /* public POI getPhoto(String photoId){ String url =
+ * "http://api.flickr.com/services/rest/?method=flickr.photos.getInfo"
+ * +
+ * "&api_key=" + mApiKey + "&photo_id=" + photo Id +
+ * "&format=json&nojsoncallback=1"; Log.d(BonusPackHelper.LOG_TAG,
+ * "getPhoto:"+url); String jString =
+ * BonusPackHelper.requestStringFromUrl(url); if (jString == null)
+ * {
+ * Log.e(BonusPackHelper.LOG_TAG,
+ * "FlickrPOIProvider: request failed.");
+ * return null; } try { POI poi = new POI(POI.POI_SERVICE_FLICKR);
+ * JSONObject jRoot = new JSONObject(jString); JSONObject jPhoto =
+ * jRoot.getJSONObject("photo"); JSONObject jLocation =
+ * jPhoto.getJSONObject("location"); poi.mLocation = new GeoPoint(
+ * jLocation.getDouble("latitude"),
+ * jLocation.getDouble("longitude"));
+ * poi.mId = Long.parseLong(photoId); JSONObject jTitle =
+ * jPhoto.getJSONObject("title"); poi.mType =
+ * jTitle.getString("_content");
+ * JSONObject jDescription = jPhoto.getJSONObject("description");
+ * poi.mDescription = jDescription.getString("_content");
+ * //truncate
+ * description if too long: if (poi.mDescription.length() > 300){
+ * poi.mDescription = poi.mDescription.substring(0, 300) +
+ * " (...)"; }
+ * String farm = jPhoto.getString("farm"); String server =
+ * jPhoto.getString("server"); String secret =
+ * jPhoto.getString("secret");
+ * JSONObject jOwner = jPhoto.getJSONObject("owner"); String nsid
+ * =
+ * jOwner.getString("nsid"); poi.mThumbnailPath =
+ * "http://farm"+farm+".staticflickr.com/"
+ * +server+"/"+photoId+"_"+secret+"_s.jpg"; poi.mUrl =
+ * "http://www.flickr.com/photos/"+nsid+"/"+photoId; return poi;
+ * }catch
+ * (JSONException e) { e.printStackTrace(); return null; } } */
+
+ /**
+ * @param fullUrl
+ * ...
+ * @return the list of POI
+ */
+ public ArrayList getThem(String fullUrl) {
+ // for local debug: fullUrl = "http://10.0.2.2/flickr_mockup.json";
+ Log.d(BonusPackHelper.LOG_TAG, "FlickrPOIProvider:get:" + fullUrl);
+ String jString = BonusPackHelper.requestStringFromUrl(fullUrl);
+ if (jString == null) {
+ Log.e(BonusPackHelper.LOG_TAG, "FlickrPOIProvider: request failed.");
+ return null;
+ }
+ try {
+ JSONObject jRoot = new JSONObject(jString);
+ JSONObject jPhotos = jRoot.getJSONObject("photos");
+ JSONArray jPhotoArray = jPhotos.getJSONArray("photo");
+ int n = jPhotoArray.length();
+ ArrayList pois = new ArrayList(n);
+ for (int i = 0; i < n; i++) {
+ JSONObject jPhoto = jPhotoArray.getJSONObject(i);
+
+ String photoId = jPhoto.getString("id");
+ if (mPrevious != null && mPrevious.containsKey(photoId))
+ continue;
+
+ POI poi = new POI(POI.POI_SERVICE_FLICKR);
+ poi.location = new GeoPoint(
+ jPhoto.getDouble("latitude"),
+ jPhoto.getDouble("longitude"));
+ poi.id = photoId; //Long.parseLong(photoId);
+ poi.type = jPhoto.getString("title");
+ poi.thumbnailPath = jPhoto.getString("url_sq");
+ String owner = jPhoto.getString("owner");
+ // the default flickr link viewer doesnt work with mobile browsers...
+ // poi.url = "http://www.flickr.com/photos/" + owner + "/" + photoId + "/sizes/o/in/photostream/";
+
+ poi.url = String.format(PHOTO_URL, owner, photoId);
+
+ pois.add(poi);
+ }
+ // int total = jPhotos.getInt("total");
+ // Log.d(BonusPackHelper.LOG_TAG, "done:" + n + " got, on a total of:" + total);
+ return pois;
+ } catch (JSONException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * @param boundingBox
+ * ...
+ * @param maxResults
+ * ...
+ * @return list of POI, Flickr photos inside the bounding box.
+ * Null if
+ * technical issue.
+ */
+ public ArrayList getPOIInside(BoundingBox boundingBox, String query, int maxResults) {
+ String url = getUrlInside(boundingBox, maxResults);
+ return getThem(url);
+ }
+
+ HashMap mPrevious;
+
+ public void setPrevious(HashMap previous) {
+ mPrevious = previous;
+ }
+
+}
diff --git a/TileMapApp/src/org/osmdroid/location/FourSquareProvider.java b/TileMapApp/src/org/osmdroid/location/FourSquareProvider.java
new file mode 100644
index 0000000..3c56b93
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/location/FourSquareProvider.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2012 Hannes Janetzek
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see .
+ */
+package org.osmdroid.location;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.oscim.core.BoundingBox;
+import org.oscim.core.GeoPoint;
+import org.osmdroid.utils.BonusPackHelper;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.util.Log;
+
+public class FourSquareProvider {
+
+ // https://developer.foursquare.com/docs/venues/search
+ // https://developer.foursquare.com/docs/responses/venue
+ // https://apigee.com/console/foursquare
+
+ protected String mApiKey;
+
+ // private static HashMap mIcons =
+ // (HashMap)Collections.synchronizedMap(new HashMap());
+
+ /**
+ * @param apiKey
+ * the registered API key to give to Flickr service.
+ * @see "http://www.flickr.com/help/api/"
+ */
+ public FourSquareProvider(String clientId, String clientSecret) {
+ mApiKey = "client_id=" + clientId + "&client_secret=" + clientSecret;
+ }
+
+ //"https://api.foursquare.com/v2/venues/search?v=20120321&intent=checkin&ll=53.06,8.8&client_id=ZUN4ZMNZUFT3Z5QQZNMQ3ACPL4OJMBFGO15TYX51D5MHCIL3&client_secret=X1RXCVF4VVSG1Y2FUDQJLKQUC1WF4XXKIMK2STXKACLPDGLY
+ private String getUrlInside(BoundingBox boundingBox, String query, int maxResults) {
+ StringBuffer url = new StringBuffer(
+ "https://api.foursquare.com/v2/venues/search?v=20120321"
+ + "&intent=browse"
+ + "&client_id=ZUN4ZMNZUFT3Z5QQZNMQ3ACPL4OJMBFGO15TYX51D5MHCIL3"
+ + "&client_secret=X1RXCVF4VVSG1Y2FUDQJLKQUC1WF4XXKIMK2STXKACLPDGLY");
+ url.append("&sw=");
+ url.append(boundingBox.getMinLatitude());
+ url.append(',');
+ url.append(boundingBox.getMinLongitude());
+ url.append("&ne=");
+ url.append(boundingBox.getMaxLatitude());
+ url.append(',');
+ url.append(boundingBox.getMaxLongitude());
+ url.append("&limit=");
+ url.append(maxResults);
+ if (query != null)
+ url.append("&query=" + URLEncoder.encode(query));
+ return url.toString();
+ }
+
+ /**
+ * @param fullUrl
+ * ...
+ * @return the list of POI
+ */
+ public ArrayList getThem(String fullUrl) {
+ // for local debug: fullUrl = "http://10.0.2.2/flickr_mockup.json";
+ Log.d(BonusPackHelper.LOG_TAG, "FlickrPOIProvider:get:" + fullUrl);
+ String jString = BonusPackHelper.requestStringFromUrl(fullUrl);
+ if (jString == null) {
+ Log.e(BonusPackHelper.LOG_TAG, "FlickrPOIProvider: request failed.");
+ return null;
+ }
+ try {
+ JSONObject jRoot = new JSONObject(jString);
+
+ JSONObject jResponse = jRoot.getJSONObject("response");
+ JSONArray jVenueArray = jResponse.getJSONArray("venues");
+ int n = jVenueArray.length();
+ ArrayList pois = new ArrayList(n);
+ for (int i = 0; i < n; i++) {
+ JSONObject jVenue = jVenueArray.getJSONObject(i);
+
+ POI poi = new POI(POI.POI_SERVICE_4SQUARE);
+ poi.id = jVenue.getString("id");
+ poi.type = jVenue.getString("name");
+ // poi.url = jVenue.optString("url", null);
+ poi.url = "https://foursquare.com/v/" + poi.id;
+
+ JSONObject jLocation = jVenue.getJSONObject("location");
+ poi.location = new GeoPoint(
+ jLocation.getDouble("lat"),
+ jLocation.getDouble("lng"));
+ poi.description = jLocation.optString("address", null);
+
+ JSONArray jCategories = jVenue.getJSONArray("categories");
+ if (jCategories.length() > 0) {
+ JSONObject jCategory = jCategories.getJSONObject(0);
+ String icon = jCategory.getJSONObject("icon").getString("prefix");
+ poi.thumbnailPath = icon + 44 + ".png";
+ poi.category = jCategory.optString("name");
+ }
+ pois.add(poi);
+ }
+
+ return pois;
+ } catch (JSONException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * @param boundingBox
+ * ...
+ * @param maxResults
+ * ...
+ * @return list of POI, Flickr photos inside the bounding box.
+ * Null if
+ * technical issue.
+ */
+ public ArrayList getPOIInside(BoundingBox boundingBox, String query, int maxResults) {
+ String url = getUrlInside(boundingBox, query, maxResults);
+ return getThem(url);
+ }
+
+ public static void browse(final Context context, POI poi) {
+ // get the right url from redirect, could also parse the result from querying venueid...
+ new AsyncTask() {
+
+ @Override
+ protected String doInBackground(POI... params) {
+ POI poi = params[0];
+ if (poi == null)
+ return null;
+ try {
+ URL url = new URL(poi.url);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setInstanceFollowRedirects(false);
+
+ String redirect = conn.getHeaderField("Location");
+ if (redirect != null) {
+ Log.d(BonusPackHelper.LOG_TAG, redirect);
+ return redirect;
+ }
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(String result) {
+ if (result == null)
+ return;
+
+ Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://foursquare.com"
+ + result));
+ context.startActivity(myIntent);
+
+ }
+ }.execute(poi);
+
+ }
+}
diff --git a/TileMapApp/src/org/osmdroid/location/GeoNamesPOIProvider.java b/TileMapApp/src/org/osmdroid/location/GeoNamesPOIProvider.java
new file mode 100644
index 0000000..bd3eada
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/location/GeoNamesPOIProvider.java
@@ -0,0 +1,219 @@
+package org.osmdroid.location;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Locale;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.oscim.core.BoundingBox;
+import org.oscim.core.GeoPoint;
+import org.osmdroid.utils.BonusPackHelper;
+import org.osmdroid.utils.HttpConnection;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import android.util.Log;
+
+/**
+ * POI Provider using GeoNames services. Currently, "find Nearby Wikipedia" and
+ * "Wikipedia Articles in Bounding Box" services.
+ * @see "http://www.geonames.org"
+ * @author M.Kergall
+ */
+public class GeoNamesPOIProvider {
+
+ protected String mUserName;
+
+ /**
+ * @param account
+ * the registered "username" to give to GeoNames service.
+ * @see "http://www.geonames.org/login"
+ */
+ public GeoNamesPOIProvider(String account) {
+ mUserName = account;
+ }
+
+ private String getUrlCloseTo(GeoPoint p, int maxResults, double maxDistance) {
+ StringBuffer url = new StringBuffer("http://api.geonames.org/findNearbyWikipediaJSON?");
+ url.append("lat=" + p.getLatitude());
+ url.append("&lng=" + p.getLongitude());
+ url.append("&maxRows=" + maxResults);
+ url.append("&radius=" + maxDistance); //km
+ url.append("&lang=" + Locale.getDefault().getLanguage());
+ url.append("&username=" + mUserName);
+ return url.toString();
+ }
+
+ private String getUrlInside(BoundingBox boundingBox, int maxResults) {
+ StringBuffer url = new StringBuffer("http://api.geonames.org/wikipediaBoundingBoxJSON?");
+ url.append("south=" + boundingBox.getMinLatitude());
+ url.append("&north=" + boundingBox.getMaxLatitude());
+ url.append("&west=" + boundingBox.getMinLongitude());
+ url.append("&east=" + boundingBox.getMaxLongitude());
+ url.append("&maxRows=" + maxResults);
+ url.append("&lang=" + Locale.getDefault().getLanguage());
+ url.append("&username=" + mUserName);
+ return url.toString();
+ }
+
+ /**
+ * @param fullUrl
+ * ...
+ * @return the list of POI
+ */
+ public ArrayList getThem(String fullUrl) {
+ Log.d(BonusPackHelper.LOG_TAG, "GeoNamesPOIProvider:get:" + fullUrl);
+ String jString = BonusPackHelper.requestStringFromUrl(fullUrl);
+ if (jString == null) {
+ Log.e(BonusPackHelper.LOG_TAG, "GeoNamesPOIProvider: request failed.");
+ return null;
+ }
+ try {
+ JSONObject jRoot = new JSONObject(jString);
+ JSONArray jPlaceIds = jRoot.getJSONArray("geonames");
+ int n = jPlaceIds.length();
+ ArrayList pois = new ArrayList(n);
+ for (int i = 0; i < n; i++) {
+ JSONObject jPlace = jPlaceIds.getJSONObject(i);
+ POI poi = new POI(POI.POI_SERVICE_GEONAMES_WIKIPEDIA);
+ poi.location = new GeoPoint(jPlace.getDouble("lat"),
+ jPlace.getDouble("lng"));
+ poi.category = jPlace.optString("feature");
+ poi.type = jPlace.getString("title");
+ poi.description = jPlace.optString("summary");
+ poi.thumbnailPath = jPlace.optString("thumbnailImg", null);
+ /* This makes loading too long. Thumbnail loading will be done
+ * only when needed, with POI.getThumbnail() if
+ * (poi.mThumbnailPath != null){ poi.mThumbnail =
+ * BonusPackHelper.loadBitmap(poi.mThumbnailPath); } */
+ poi.url = jPlace.optString("wikipediaUrl", null);
+ if (poi.url != null)
+ poi.url = "http://" + poi.url;
+ poi.rank = jPlace.optInt("rank", 0);
+ //other attributes: distance?
+ pois.add(poi);
+ }
+ Log.d(BonusPackHelper.LOG_TAG, "done");
+ return pois;
+ } catch (JSONException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ //XML parsing seems 2 times slower than JSON parsing
+ public ArrayList getThemXML(String fullUrl) {
+ Log.d(BonusPackHelper.LOG_TAG, "GeoNamesPOIProvider:get:" + fullUrl);
+ HttpConnection connection = new HttpConnection();
+ connection.doGet(fullUrl);
+ InputStream stream = connection.getStream();
+ if (stream == null) {
+ return null;
+ }
+ GeoNamesXMLHandler handler = new GeoNamesXMLHandler();
+ try {
+ SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
+ parser.parse(stream, handler);
+ } catch (ParserConfigurationException e) {
+ e.printStackTrace();
+ } catch (SAXException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ connection.close();
+ Log.d(BonusPackHelper.LOG_TAG, "done");
+ return handler.mPOIs;
+ }
+
+ /**
+ * @param position
+ * ...
+ * @param maxResults
+ * ...
+ * @param maxDistance
+ * ... in km. 20 km max for the free service.
+ * @return list of POI, Wikipedia entries close to the position. Null if
+ * technical issue.
+ */
+ public ArrayList getPOICloseTo(GeoPoint position,
+ int maxResults, double maxDistance) {
+ String url = getUrlCloseTo(position, maxResults, maxDistance);
+ return getThem(url);
+ }
+
+ /**
+ * @param boundingBox
+ * ...
+ * @param maxResults
+ * ...
+ * @return list of POI, Wikipedia entries inside the bounding box. Null if
+ * technical issue.
+ */
+ public ArrayList getPOIInside(BoundingBox boundingBox, int maxResults) {
+ String url = getUrlInside(boundingBox, maxResults);
+ return getThem(url);
+ }
+}
+
+class GeoNamesXMLHandler extends DefaultHandler {
+
+ private String mString;
+ double mLat, mLng;
+ POI mPOI;
+ ArrayList mPOIs;
+
+ public GeoNamesXMLHandler() {
+ mPOIs = new ArrayList();
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String name,
+ Attributes attributes) {
+ if (localName.equals("entry")) {
+ mPOI = new POI(POI.POI_SERVICE_GEONAMES_WIKIPEDIA);
+ }
+ mString = new String();
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) {
+ String chars = new String(ch, start, length);
+ mString = mString.concat(chars);
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String name) {
+ if (localName.equals("lat")) {
+ mLat = Double.parseDouble(mString);
+ } else if (localName.equals("lng")) {
+ mLng = Double.parseDouble(mString);
+ } else if (localName.equals("feature")) {
+ mPOI.category = mString;
+ } else if (localName.equals("title")) {
+ mPOI.type = mString;
+ } else if (localName.equals("summary")) {
+ mPOI.description = mString;
+ } else if (localName.equals("thumbnailImg")) {
+ if (mString != null && !mString.equals(""))
+ mPOI.thumbnailPath = mString;
+ } else if (localName.equals("wikipediaUrl")) {
+ if (mString != null && !mString.equals(""))
+ mPOI.url = "http://" + mString;
+ } else if (localName.equals("rank")) {
+ mPOI.rank = Integer.parseInt(mString);
+ } else if (localName.equals("entry")) {
+ mPOI.location = new GeoPoint(mLat, mLng);
+ mPOIs.add(mPOI);
+ }
+ }
+
+}
diff --git a/TileMapApp/src/org/osmdroid/location/GeocoderNominatim.java b/TileMapApp/src/org/osmdroid/location/GeocoderNominatim.java
new file mode 100644
index 0000000..1249f8b
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/location/GeocoderNominatim.java
@@ -0,0 +1,210 @@
+package org.osmdroid.location;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.osmdroid.utils.BonusPackHelper;
+
+import android.content.Context;
+import android.location.Address;
+import android.util.Log;
+
+/**
+ * Implements an equivalent to Android Geocoder class, based on OpenStreetMap
+ * data and Nominatim API.
+ * See http://wiki.openstreetmap.org/wiki/Nominatim or
+ * http://open.mapquestapi.com/nominatim/
+ * @author M.Kergall
+ */
+public class GeocoderNominatim {
+ public static final String NOMINATIM_SERVICE_URL = "http://nominatim.openstreetmap.org/";
+ public static final String MAPQUEST_SERVICE_URL = "http://open.mapquestapi.com/nominatim/v1/";
+
+ protected Locale mLocale;
+ protected String mServiceUrl;
+
+ /**
+ * @param context
+ * ...
+ * @param locale
+ * ...
+ */
+ protected void init(Context context, Locale locale) {
+ mLocale = locale;
+ setService(NOMINATIM_SERVICE_URL); //default service
+ }
+
+ public GeocoderNominatim(Context context, Locale locale) {
+ init(context, locale);
+ }
+
+ public GeocoderNominatim(Context context) {
+ init(context, Locale.getDefault());
+ }
+
+ static public boolean isPresent() {
+ return true;
+ }
+
+ /**
+ * Specify the url of the Nominatim service provider to use. Can be one of
+ * the predefined (NOMINATIM_SERVICE_URL or MAPQUEST_SERVICE_URL), or
+ * another one, your local instance of Nominatim for instance.
+ * @param serviceUrl
+ * ...
+ */
+ public void setService(String serviceUrl) {
+ mServiceUrl = serviceUrl;
+ }
+
+ /**
+ * Build an Android Address object from the Nominatim address in JSON
+ * format. Current implementation is mainly targeting french addresses, and
+ * will be quite basic on other countries.
+ * @param jResult
+ * ...
+ * @return ...
+ * @throws JSONException
+ * ...
+ */
+ protected Address buildAndroidAddress(JSONObject jResult) throws JSONException {
+ Address gAddress = new Address(mLocale);
+ gAddress.setLatitude(jResult.getDouble("lat"));
+ gAddress.setLongitude(jResult.getDouble("lon"));
+
+ JSONObject jAddress = jResult.getJSONObject("address");
+
+ int addressIndex = 0;
+ if (jAddress.has("road")) {
+ gAddress.setAddressLine(addressIndex++, jAddress.getString("road"));
+ gAddress.setThoroughfare(jAddress.getString("road"));
+ }
+ if (jAddress.has("suburb")) {
+ //gAddress.setAddressLine(addressIndex++, jAddress.getString("suburb"));
+ //not kept => often introduce "noise" in the address.
+ gAddress.setSubLocality(jAddress.getString("suburb"));
+ }
+ if (jAddress.has("postcode")) {
+ gAddress.setAddressLine(addressIndex++, jAddress.getString("postcode"));
+ gAddress.setPostalCode(jAddress.getString("postcode"));
+ }
+
+ if (jAddress.has("city")) {
+ gAddress.setAddressLine(addressIndex++, jAddress.getString("city"));
+ gAddress.setLocality(jAddress.getString("city"));
+ } else if (jAddress.has("town")) {
+ gAddress.setAddressLine(addressIndex++, jAddress.getString("town"));
+ gAddress.setLocality(jAddress.getString("town"));
+ } else if (jAddress.has("village")) {
+ gAddress.setAddressLine(addressIndex++, jAddress.getString("village"));
+ gAddress.setLocality(jAddress.getString("village"));
+ }
+
+ if (jAddress.has("county")) { //France: departement
+ gAddress.setSubAdminArea(jAddress.getString("county"));
+ }
+ if (jAddress.has("state")) { //France: region
+ gAddress.setAdminArea(jAddress.getString("state"));
+ }
+ if (jAddress.has("country")) {
+ gAddress.setAddressLine(addressIndex++, jAddress.getString("country"));
+ gAddress.setCountryName(jAddress.getString("country"));
+ }
+ if (jAddress.has("country_code"))
+ gAddress.setCountryCode(jAddress.getString("country_code"));
+
+ /* Other possible OSM tags in Nominatim results not handled yet: subway,
+ * golf_course, bus_stop, parking,... house, house_number, building
+ * city_district (13e Arrondissement) road => or highway, ... sub-city
+ * (like suburb) => locality, isolated_dwelling, hamlet ...
+ * state_district */
+
+ return gAddress;
+ }
+
+ /**
+ * @param latitude
+ * ...
+ * @param longitude
+ * ...
+ * @param maxResults
+ * ...
+ * @return ...
+ * @throws IOException
+ * ...
+ */
+ public List getFromLocation(double latitude, double longitude, int maxResults)
+ throws IOException {
+ String url = mServiceUrl
+ + "reverse?"
+ + "format=json"
+ + "&accept-language=" + mLocale.getLanguage()
+ //+ "&addressdetails=1"
+ + "&lat=" + latitude
+ + "&lon=" + longitude;
+ Log.d(BonusPackHelper.LOG_TAG, "GeocoderNominatim::getFromLocation:" + url);
+ String result = BonusPackHelper.requestStringFromUrl(url);
+ //Log.d("NOMINATIM", result);
+ if (result == null)
+ throw new IOException();
+ try {
+ JSONObject jResult = new JSONObject(result);
+ Address gAddress = buildAndroidAddress(jResult);
+ List list = new ArrayList();
+ list.add(gAddress);
+ return list;
+ } catch (JSONException e) {
+ throw new IOException();
+ }
+ }
+
+ public List getFromLocationName(String locationName, int maxResults,
+ double lowerLeftLatitude, double lowerLeftLongitude,
+ double upperRightLatitude, double upperRightLongitude)
+ throws IOException {
+ String url = mServiceUrl
+ + "search?"
+ + "format=json"
+ + "&accept-language=" + mLocale.getLanguage()
+ + "&addressdetails=1"
+ + "&limit=" + maxResults
+ + "&q=" + URLEncoder.encode(locationName);
+ if (lowerLeftLatitude != 0.0 && lowerLeftLongitude != 0.0) {
+ //viewbox = left, top, right, bottom:
+ url += "&viewbox=" + lowerLeftLongitude
+ + "," + upperRightLatitude
+ + "," + upperRightLongitude
+ + "," + lowerLeftLatitude
+ + "&bounded=1";
+ }
+ Log.d(BonusPackHelper.LOG_TAG, "GeocoderNominatim::getFromLocationName:" + url);
+ String result = BonusPackHelper.requestStringFromUrl(url);
+ //Log.d(BonusPackHelper.LOG_TAG, result);
+ if (result == null)
+ throw new IOException();
+ try {
+ JSONArray jResults = new JSONArray(result);
+ List list = new ArrayList();
+ for (int i = 0; i < jResults.length(); i++) {
+ JSONObject jResult = jResults.getJSONObject(i);
+ Address gAddress = buildAndroidAddress(jResult);
+ list.add(gAddress);
+ }
+ return list;
+ } catch (JSONException e) {
+ throw new IOException();
+ }
+ }
+
+ public List getFromLocationName(String locationName, int maxResults)
+ throws IOException {
+ return getFromLocationName(locationName, maxResults, 0.0, 0.0, 0.0, 0.0);
+ }
+
+}
diff --git a/TileMapApp/src/org/osmdroid/location/NominatimPOIProvider.java b/TileMapApp/src/org/osmdroid/location/NominatimPOIProvider.java
new file mode 100644
index 0000000..2d6856f
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/location/NominatimPOIProvider.java
@@ -0,0 +1,199 @@
+package org.osmdroid.location;
+
+import java.net.URLEncoder;
+import java.util.ArrayList;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.oscim.core.BoundingBox;
+import org.oscim.core.GeoPoint;
+import org.osmdroid.utils.BonusPackHelper;
+
+import android.graphics.Bitmap;
+import android.util.Log;
+
+/**
+ * POI Provider using Nominatim service.
+ * See https://wiki.openstreetmap.org/wiki/Nominatim
+ * and http://open.mapquestapi.com/nominatim/
+ * @author M.Kergall
+ */
+public class NominatimPOIProvider implements POIProvider {
+ /* As the doc lacks a lot of features, source code may help:
+ * https://trac.openstreetmap
+ * .org/browser/applications/utils/nominatim/website/search.php featuretype=
+ * to select on feature type (country, city, state, settlement)
+ * format=jsonv2 to get a place_rank offset= to offset the result ?...
+ * polygon=1 to get the border of the poi as a polygon nearlat &
+ * nearlon = ??? routewidth/69 and routewidth/30 ??? */
+ public static final String MAPQUEST_POI_SERVICE = "http://open.mapquestapi.com/nominatim/v1/";
+ public static final String NOMINATIM_POI_SERVICE = "http://nominatim.openstreetmap.org/";
+ protected String mService;
+
+ public NominatimPOIProvider() {
+ mService = NOMINATIM_POI_SERVICE;
+ }
+
+ public void setService(String serviceUrl) {
+ mService = serviceUrl;
+ }
+
+ @SuppressWarnings("deprecation")
+ private StringBuffer getCommonUrl(String type, int maxResults) {
+ StringBuffer urlString = new StringBuffer(mService);
+ urlString.append("search?");
+ urlString.append("format=json");
+ urlString.append("&q=" + URLEncoder.encode(type));
+ urlString.append("&limit=" + maxResults);
+ //urlString.append("&bounded=1");
+ // urlString.append("&addressdetails=0");
+ return urlString;
+ }
+
+ private String getUrlInside(BoundingBox bb, String type, int maxResults) {
+ StringBuffer urlString = getCommonUrl(type, maxResults);
+ urlString.append("&viewbox=" + bb.getMaxLongitude() + ","
+ + bb.getMaxLatitude() + ","
+ + bb.getMinLongitude() + ","
+ + bb.getMinLatitude());
+ return urlString.toString();
+ }
+
+ private String getUrlCloseTo(GeoPoint p, String type,
+ int maxResults, double maxDistance) {
+ int maxD = (int) (maxDistance * 1E6);
+ BoundingBox bb = new BoundingBox(p.latitudeE6 + maxD,
+ p.longitudeE6 + maxD,
+ p.latitudeE6 - maxD,
+ p.longitudeE6 - maxD);
+ return getUrlInside(bb, type, maxResults);
+ }
+
+ /**
+ * @param url
+ * full URL request
+ * @return the list of POI, of null if technical issue.
+ */
+ public ArrayList getThem(String url) {
+ Log.d(BonusPackHelper.LOG_TAG, "NominatimPOIProvider:get:" + url);
+ String jString = BonusPackHelper.requestStringFromUrl(url);
+ if (jString == null) {
+ Log.e(BonusPackHelper.LOG_TAG, "NominatimPOIProvider: request failed.");
+ return null;
+ }
+ try {
+ JSONArray jPlaceIds = new JSONArray(jString);
+ int n = jPlaceIds.length();
+ ArrayList pois = new ArrayList(n);
+ Bitmap thumbnail = null;
+ for (int i = 0; i < n; i++) {
+ JSONObject jPlace = jPlaceIds.getJSONObject(i);
+ POI poi = new POI(POI.POI_SERVICE_NOMINATIM);
+ poi.id = jPlace.getString("osm_id");
+ // jPlace.optLong("osm_id");
+ poi.location = new GeoPoint(jPlace.getDouble("lat"), jPlace.getDouble("lon"));
+ JSONArray bbox = jPlace.optJSONArray("boundingbox");
+ if (bbox != null) {
+ try {
+ poi.bbox = new BoundingBox(bbox.getDouble(0), bbox.getDouble(2),
+ bbox.getDouble(1), bbox.getDouble(3));
+ } catch (Exception e) {
+ Log.d("NominatimPOIProvider", "could not parse " + bbox);
+ }
+ //Log.d("NominatimPOIProvider", "bbox " + poi.bbox);
+ }
+ poi.category = jPlace.optString("class");
+ poi.type = jPlace.getString("type");
+ poi.description = jPlace.optString("display_name");
+ poi.thumbnailPath = jPlace.optString("icon", null);
+
+ if (i == 0 && poi.thumbnailPath != null) {
+ //first POI, and we have a thumbnail: load it
+ thumbnail = BonusPackHelper.loadBitmap(poi.thumbnailPath);
+ }
+ poi.thumbnail = thumbnail;
+ pois.add(poi);
+ }
+ return pois;
+ } catch (JSONException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * @param position
+ * ...
+ * @param type
+ * an OpenStreetMap feature. See
+ * http://wiki.openstreetmap.org/wiki/Map_Features or
+ * http://code.google.com/p/osmbonuspack/source/browse/trunk/
+ * OSMBonusPackDemo/res/values/poi_tags.xml
+ * @param maxResults
+ * the maximum number of POI returned. Note that in any case,
+ * Nominatim will have an absolute maximum of 100.
+ * @param maxDistance
+ * to the position, in degrees. Note that it is used to build a
+ * bounding box around the position, not a circle.
+ * @return the list of POI, null if technical issue.
+ */
+ public ArrayList getPOICloseTo(GeoPoint position, String type,
+ int maxResults, double maxDistance) {
+ String url = getUrlCloseTo(position, type, maxResults, maxDistance);
+ return getThem(url);
+ }
+
+ /**
+ * @param boundingBox
+ * ...
+ * @param type
+ * OpenStreetMap feature
+ * @param maxResults
+ * ...
+ * @return list of POIs, null if technical issue.
+ */
+ public ArrayList getPOIInside(BoundingBox boundingBox, String type, int maxResults) {
+ String url = getUrlInside(boundingBox, type, maxResults);
+ return getThem(url);
+ }
+
+ public ArrayList getPOI(String query, int maxResults) {
+ String url = getCommonUrl(query, maxResults).toString();
+ return getThem(url);
+ }
+
+ /**
+ * @param path
+ * Warning: a long path may cause a failure due to the url to be
+ * too long. Using a simplified route may help (see
+ * Road.getRouteLow()).
+ * @param type
+ * OpenStreetMap feature
+ * @param maxResults
+ * ...
+ * @param maxWidth
+ * to the path. Certainly not in degrees. Probably in km.
+ * @return list of POIs, null if technical issue.
+ */
+ public ArrayList getPOIAlong(ArrayList path, String type,
+ int maxResults, double maxWidth) {
+ StringBuffer urlString = getCommonUrl(type, maxResults);
+ urlString.append("&routewidth=" + maxWidth);
+ urlString.append("&route=");
+ boolean isFirst = true;
+ for (GeoPoint p : path) {
+ if (isFirst)
+ isFirst = false;
+ else
+ urlString.append(",");
+ String lat = Double.toString(p.getLatitude());
+ lat = lat.substring(0, Math.min(lat.length(), 7));
+ String lon = Double.toString(p.getLongitude());
+ lon = lon.substring(0, Math.min(lon.length(), 7));
+ urlString.append(lat + "," + lon);
+ //limit the size of url as much as possible, as post method is not supported.
+ }
+ return getThem(urlString.toString());
+ }
+}
diff --git a/TileMapApp/src/org/osmdroid/location/POI.java b/TileMapApp/src/org/osmdroid/location/POI.java
new file mode 100644
index 0000000..43811e1
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/location/POI.java
@@ -0,0 +1,184 @@
+package org.osmdroid.location;
+
+import org.oscim.app.R;
+import org.oscim.core.BoundingBox;
+import org.oscim.core.GeoPoint;
+import org.osmdroid.utils.BonusPackHelper;
+
+import android.graphics.Bitmap;
+import android.os.AsyncTask;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+
+/**
+ * Point of Interest. Exact content may depend of the POI provider used.
+ * @see NominatimPOIProvider
+ * @see GeoNamesPOIProvider
+ * @author M.Kergall
+ */
+public class POI implements Parcelable {
+ /** IDs of POI services */
+ public static int POI_SERVICE_NOMINATIM = 100;
+ public static int POI_SERVICE_GEONAMES_WIKIPEDIA = 200;
+ public static int POI_SERVICE_FLICKR = 300;
+ public static int POI_SERVICE_PICASA = 400;
+ public static int POI_SERVICE_4SQUARE = 500;
+
+ /** Identifies the service provider of this POI. */
+ public int serviceId;
+ /** Nominatim: OSM ID. GeoNames: 0 */
+ public String id;
+ /** location of the POI */
+ public GeoPoint location;
+ public BoundingBox bbox;
+ /** Nominatim "class", GeoNames "feature" */
+ public String category;
+ /** type or title */
+ public String type;
+ /** can be the name, the address, a short description */
+ public String description;
+ /** url of the thumbnail. Null if none */
+ public String thumbnailPath;
+ /** the thumbnail itself. Null if none */
+ public Bitmap thumbnail;
+ /** url to a more detailed information page about this POI. Null if none */
+ public String url;
+ /**
+ * popularity of this POI, from 1 (lowest) to 100 (highest). 0 if not
+ * defined.
+ */
+ public int rank;
+
+ /** number of attempts to load the thumbnail that have failed */
+ protected int mThumbnailLoadingFailures;
+
+ public POI(int serviceId) {
+ this.serviceId = serviceId;
+ // lets all other fields empty or null. That's fine.
+ }
+
+ protected static int MAX_LOADING_ATTEMPTS = 2;
+
+ /**
+ * @return the POI thumbnail as a Bitmap, if any. If not done yet, it will
+ * load the POI thumbnail from its url (in thumbnailPath field).
+ */
+ public Bitmap getThumbnail() {
+ if (thumbnail == null && thumbnailPath != null) {
+ Log.d(BonusPackHelper.LOG_TAG, "POI:load thumbnail:" + thumbnailPath);
+ thumbnail = BonusPackHelper.loadBitmap(thumbnailPath);
+ if (thumbnail == null) {
+ mThumbnailLoadingFailures++;
+ if (mThumbnailLoadingFailures >= MAX_LOADING_ATTEMPTS) {
+ // this path really doesn't work, "kill" it for next calls:
+ thumbnailPath = null;
+ }
+ }
+ }
+ return thumbnail;
+ }
+
+ // http://stackoverflow.com/questions/7729133/using-asynctask-to-load-images-in-listview
+ // TODO see link, there might be a better solution
+ /**
+ * Fetch the thumbnail from its url on a thread.
+ * @param imageView
+ * to update once the thumbnail is retrieved, or to hide if no
+ * thumbnail.
+ */
+ public void fetchThumbnail(final ImageView imageView) {
+ if (thumbnail != null) {
+ imageView.setImageBitmap(thumbnail);
+ imageView.setVisibility(View.VISIBLE);
+ } else if (thumbnailPath != null) {
+ imageView.setImageResource(R.drawable.ic_empty);
+ imageView.setVisibility(View.VISIBLE);
+ new ThumbnailTask(imageView).execute(imageView);
+ } else {
+ imageView.setVisibility(View.GONE);
+ }
+ }
+
+ class ThumbnailTask extends AsyncTask {
+
+ public ThumbnailTask(ImageView iv) {
+ iv.setTag(thumbnailPath);
+ }
+
+ @Override
+ protected ImageView doInBackground(ImageView... params) {
+ getThumbnail();
+ return params[0];
+ }
+
+ @Override
+ protected void onPostExecute(ImageView iv) {
+ if (iv == null || thumbnail == null)
+ return;
+ if (thumbnailPath.equals(iv.getTag().toString()))
+ iv.setImageBitmap(thumbnail);
+ }
+ }
+
+ // --- Parcelable implementation
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(serviceId);
+ out.writeString(id);
+ out.writeParcelable(location, 0);
+ out.writeString(category);
+ out.writeString(type);
+ out.writeString(description);
+ out.writeString(thumbnailPath);
+ out.writeParcelable(thumbnail, 0);
+ out.writeString(url);
+ out.writeInt(rank);
+ out.writeInt(mThumbnailLoadingFailures);
+ }
+
+ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+ @Override
+ public POI createFromParcel(Parcel in) {
+ POI poi = new POI(in.readInt());
+ poi.id = in.readString();
+ poi.location = in.readParcelable(GeoPoint.class.getClassLoader());
+ poi.category = in.readString();
+ poi.type = in.readString();
+ poi.description = in.readString();
+ poi.thumbnailPath = in.readString();
+ poi.thumbnail = in.readParcelable(Bitmap.class.getClassLoader());
+ poi.url = in.readString();
+ poi.rank = in.readInt();
+ poi.mThumbnailLoadingFailures = in.readInt();
+ return poi;
+ }
+
+ @Override
+ public POI[] newArray(int size) {
+ return new POI[size];
+ }
+ };
+
+ // private POI(Parcel in) {
+ // serviceId = in.readInt();
+ // id = in.readLong();
+ // location = in.readParcelable(GeoPoint.class.getClassLoader());
+ // category = in.readString();
+ // type = in.readString();
+ // description = in.readString();
+ // thumbnailPath = in.readString();
+ // thumbnail = in.readParcelable(Bitmap.class.getClassLoader());
+ // url = in.readString();
+ // rank = in.readInt();
+ // mThumbnailLoadingFailures = in.readInt();
+ // }
+}
diff --git a/TileMapApp/src/org/osmdroid/location/POIProvider.java b/TileMapApp/src/org/osmdroid/location/POIProvider.java
new file mode 100644
index 0000000..51b98de
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/location/POIProvider.java
@@ -0,0 +1,9 @@
+package org.osmdroid.location;
+
+import java.util.List;
+
+import org.oscim.core.BoundingBox;
+
+public interface POIProvider {
+ public List getPOIInside(BoundingBox boundingBox, String query, int maxResults);
+}
diff --git a/TileMapApp/src/org/osmdroid/location/PicasaPOIProvider.java b/TileMapApp/src/org/osmdroid/location/PicasaPOIProvider.java
new file mode 100644
index 0000000..6509fa3
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/location/PicasaPOIProvider.java
@@ -0,0 +1,163 @@
+package org.osmdroid.location;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.oscim.core.BoundingBox;
+import org.oscim.core.GeoPoint;
+import org.osmdroid.utils.BonusPackHelper;
+import org.osmdroid.utils.HttpConnection;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import android.util.Log;
+
+/**
+ * POI Provider using Picasa service.
+ * @see "https://developers.google.com/picasa-web/docs/2.0/reference"
+ * @author M.Kergall
+ */
+public class PicasaPOIProvider implements POIProvider {
+
+ String mAccessToken;
+
+ /**
+ * @param accessToken
+ * the account to give to the service. Null for public access.
+ * @see "https://developers.google.com/picasa-web/docs/2.0/developers_guide_protocol#CreatingAccount"
+ */
+ public PicasaPOIProvider(String accessToken) {
+ mAccessToken = accessToken;
+ }
+
+ @SuppressWarnings("deprecation")
+ private String getUrlInside(BoundingBox boundingBox, int maxResults, String query) {
+ StringBuffer url = new StringBuffer("http://picasaweb.google.com/data/feed/api/all?");
+ url.append("bbox=" + boundingBox.getMinLongitude());
+ url.append("," + boundingBox.getMinLatitude());
+ url.append("," + boundingBox.getMaxLongitude());
+ url.append("," + boundingBox.getMaxLatitude());
+ url.append("&max-results=" + maxResults);
+ url.append("&thumbsize=64c"); //thumbnail size: 64, cropped.
+ url.append("&fields=openSearch:totalResults,entry(summary,media:group/media:thumbnail,media:group/media:title,gphoto:*,georss:where,link)");
+ if (query != null)
+ url.append("&q=" + URLEncoder.encode(query));
+ if (mAccessToken != null) {
+ //TODO: warning: not tested...
+ url.append("&access_token=" + mAccessToken);
+ }
+ return url.toString();
+ }
+
+ public ArrayList getThem(String fullUrl) {
+ Log.d(BonusPackHelper.LOG_TAG, "PicasaPOIProvider:get:" + fullUrl);
+ HttpConnection connection = new HttpConnection();
+ connection.doGet(fullUrl);
+ InputStream stream = connection.getStream();
+ if (stream == null) {
+ return null;
+ }
+ PicasaXMLHandler handler = new PicasaXMLHandler();
+ try {
+ SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
+ parser.getXMLReader().setFeature("http://xml.org/sax/features/namespaces", false);
+ parser.getXMLReader()
+ .setFeature("http://xml.org/sax/features/namespace-prefixes", true);
+ parser.parse(stream, handler);
+ } catch (ParserConfigurationException e) {
+ e.printStackTrace();
+ } catch (SAXException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ connection.close();
+ if (handler.mPOIs != null)
+ Log.d(BonusPackHelper.LOG_TAG, "done:" + handler.mPOIs.size() + " got, on a total of:"
+ + handler.mTotalResults);
+ return handler.mPOIs;
+ }
+
+ /**
+ * @param boundingBox
+ * ...
+ * @param maxResults
+ * ...
+ * @param query
+ * - optional - full-text query string. Searches the title,
+ * caption and tags for the specified string value.
+ * @return list of POI, Picasa photos inside the bounding box. Null if
+ * technical issue.
+ */
+ public List getPOIInside(BoundingBox boundingBox, String query, int maxResults) {
+ String url = getUrlInside(boundingBox, maxResults, query);
+ return getThem(url);
+ }
+}
+
+class PicasaXMLHandler extends DefaultHandler {
+
+ private String mString;
+ double mLat, mLng;
+ POI mPOI;
+ ArrayList mPOIs;
+ int mTotalResults;
+
+ public PicasaXMLHandler() {
+ mPOIs = new ArrayList();
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName,
+ Attributes attributes) {
+ if (qName.equals("entry")) {
+ mPOI = new POI(POI.POI_SERVICE_PICASA);
+ } else if (qName.equals("media:thumbnail")) {
+ mPOI.thumbnailPath = attributes.getValue("url");
+ } else if (qName.equals("link")) {
+ String rel = attributes.getValue("rel");
+ if ("http://schemas.google.com/photos/2007#canonical".equals(rel)) {
+ mPOI.url = attributes.getValue("href");
+ }
+ }
+ mString = new String();
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) {
+ String chars = new String(ch, start, length);
+ mString = mString.concat(chars);
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) {
+ if (qName.equals("gml:pos")) {
+ String[] coords = mString.split(" ");
+ mLat = Double.parseDouble(coords[0]);
+ mLng = Double.parseDouble(coords[1]);
+ } else if (qName.equals("gphoto:id")) {
+ mPOI.id = mString;
+ } else if (qName.equals("media:title")) {
+ mPOI.type = mString;
+ } else if (qName.equals("summary")) {
+ mPOI.description = mString;
+ } else if (qName.equals("gphoto:albumtitle")) {
+ mPOI.category = mString;
+ } else if (qName.equals("entry")) {
+ mPOI.location = new GeoPoint(mLat, mLng);
+ mPOIs.add(mPOI);
+ mPOI = null;
+ } else if (qName.equals("openSearch:totalResults")) {
+ mTotalResults = Integer.parseInt(mString);
+ }
+ }
+
+}
diff --git a/TileMapApp/src/org/osmdroid/overlays/DefaultInfoWindow.java b/TileMapApp/src/org/osmdroid/overlays/DefaultInfoWindow.java
new file mode 100644
index 0000000..1186d45
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/overlays/DefaultInfoWindow.java
@@ -0,0 +1,99 @@
+package org.osmdroid.overlays;
+
+import org.oscim.view.MapView;
+import org.osmdroid.utils.BonusPackHelper;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+/**
+ * Default implementation of InfoWindow. It handles a text and a description. It
+ * also handles optionally a sub-description and an image. Clicking on the
+ * bubble will close it.
+ * @author M.Kergall
+ */
+public class DefaultInfoWindow extends InfoWindow {
+
+ // resource ids
+ private static int mTitleId = 0, mDescriptionId = 0, mSubDescriptionId = 0, mImageId = 0;
+
+ private static void setResIds(Context context) {
+ // get application package name
+ String packageName = context.getPackageName();
+ mTitleId = context.getResources().getIdentifier("id/bubble_title", null, packageName);
+
+ mDescriptionId = context.getResources().getIdentifier("id/bubble_description", null,
+ packageName);
+
+ mSubDescriptionId = context.getResources().getIdentifier("id/bubble_subdescription", null,
+ packageName);
+
+ mImageId = context.getResources().getIdentifier("id/bubble_image", null, packageName);
+
+ if (mTitleId == 0 || mDescriptionId == 0) {
+ Log.e(BonusPackHelper.LOG_TAG, "DefaultInfoWindow: unable to get res ids in "
+ + packageName);
+ }
+ }
+
+ public DefaultInfoWindow(int layoutResId, MapView mapView) {
+ super(layoutResId, mapView);
+
+ if (mTitleId == 0)
+ setResIds(mapView.getContext());
+
+ // default behaviour: close it when clicking on the bubble:
+ mView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ close();
+ }
+ });
+ }
+
+ @Override
+ public void onOpen(ExtendedOverlayItem item) {
+ String title = item.getTitle();
+ if (title == null)
+ title = "";
+
+ ((TextView) mView.findViewById(mTitleId)).setText(title);
+
+ String snippet = item.getDescription();
+ if (snippet == null)
+ snippet = "";
+
+ ((TextView) mView.findViewById(mDescriptionId)).setText(snippet);
+
+ // handle sub-description, hidding or showing the text view:
+ TextView subDescText = (TextView) mView.findViewById(mSubDescriptionId);
+ String subDesc = item.getSubDescription();
+ if (subDesc != null && !("".equals(subDesc))) {
+ subDescText.setText(subDesc);
+ subDescText.setVisibility(View.VISIBLE);
+ } else {
+ subDescText.setVisibility(View.GONE);
+ }
+
+ // handle image
+ ImageView imageView = (ImageView) mView.findViewById(mImageId);
+ Drawable image = item.getImage();
+ if (image != null) {
+ // or setBackgroundDrawable(image)?
+ imageView.setImageDrawable(image);
+ imageView.setVisibility(View.VISIBLE);
+ } else
+ imageView.setVisibility(View.GONE);
+
+ }
+
+ @Override
+ public void onClose() {
+ // by default, do nothing
+ }
+
+}
diff --git a/TileMapApp/src/org/osmdroid/overlays/ExtendedOverlayItem.java b/TileMapApp/src/org/osmdroid/overlays/ExtendedOverlayItem.java
new file mode 100644
index 0000000..89f00e6
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/overlays/ExtendedOverlayItem.java
@@ -0,0 +1,168 @@
+package org.osmdroid.overlays;
+
+import org.oscim.core.GeoPoint;
+import org.oscim.overlay.OverlayItem;
+import org.oscim.view.MapView;
+
+import android.graphics.Point;
+import android.graphics.drawable.Drawable;
+
+/**
+ * An OverlayItem to use in ItemizedOverlayWithBubble
+ * - more complete: can contain an image and a sub-description that will be
+ * displayed in the bubble,
+ * - and flexible: attributes are modifiable
+ * Known Issues:
+ * - Bubble offset is not perfect on h&xhdpi resolutions, due to an osmdroid
+ * issue on marker drawing
+ * - Bubble offset is at 0 when using the default marker => set the marker on
+ * each item!
+ * @see ItemizedOverlayWithBubble
+ * @author M.Kergall
+ */
+public class ExtendedOverlayItem extends OverlayItem {
+
+ // now, they are modifiable
+ private String mTitle, mDescription;
+ // now, they are modifiable
+ // a third field that can be displayed in
+ // the infowindow, on a third line
+ // that will be shown in the infowindow.
+ //unfortunately, this is not so simple...
+ private String mSubDescription;
+ private Drawable mImage;
+ private Object mRelatedObject; // reference to an object (of any kind)
+ // linked to this item.
+
+ public ExtendedOverlayItem(String aTitle, String aDescription, GeoPoint aGeoPoint) {
+ super(aTitle, aDescription, aGeoPoint);
+ mTitle = aTitle;
+ mDescription = aDescription;
+ mSubDescription = null;
+ mImage = null;
+ mRelatedObject = null;
+ }
+
+ public void setTitle(String aTitle) {
+ mTitle = aTitle;
+ }
+
+ public void setDescription(String aDescription) {
+ mDescription = aDescription;
+ }
+
+ public void setSubDescription(String aSubDescription) {
+ mSubDescription = aSubDescription;
+ }
+
+ public void setImage(Drawable anImage) {
+ mImage = anImage;
+ }
+
+ public void setRelatedObject(Object o) {
+ mRelatedObject = o;
+ }
+
+ @Override
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public String getDescription() {
+ return mDescription;
+ }
+
+ public String getSubDescription() {
+ return mSubDescription;
+ }
+
+ public Drawable getImage() {
+ return mImage;
+ }
+
+ public Object getRelatedObject() {
+ return mRelatedObject;
+ }
+
+ /**
+ * From a HotspotPlace and drawable dimensions (width, height), return the
+ * hotspot position. Could be a public method of HotspotPlace or
+ * OverlayItem...
+ * @param place
+ * ...
+ * @param w
+ * ...
+ * @param h
+ * ...
+ * @return ...
+ */
+ public Point getHotspot(HotspotPlace place, int w, int h) {
+ Point hp = new Point();
+ if (place == null)
+ place = HotspotPlace.BOTTOM_CENTER; // use same default than in
+ // osmdroid.
+ switch (place) {
+ case NONE:
+ hp.set(0, 0);
+ break;
+ case BOTTOM_CENTER:
+ hp.set(w / 2, 0);
+ break;
+ case LOWER_LEFT_CORNER:
+ hp.set(0, 0);
+ break;
+ case LOWER_RIGHT_CORNER:
+ hp.set(w, 0);
+ break;
+ case CENTER:
+ hp.set(w / 2, -h / 2);
+ break;
+ case LEFT_CENTER:
+ hp.set(0, -h / 2);
+ break;
+ case RIGHT_CENTER:
+ hp.set(w, -h / 2);
+ break;
+ case TOP_CENTER:
+ hp.set(w / 2, -h);
+ break;
+ case UPPER_LEFT_CORNER:
+ hp.set(0, -h);
+ break;
+ case UPPER_RIGHT_CORNER:
+ hp.set(w, -h);
+ break;
+ }
+ return hp;
+ }
+
+ /**
+ * Populates this bubble with all item info:
+ *
+ * title and description in any case,
+ *
+ *
+ * image and sub-description if any.
+ *
+ * and centers the map on the item.
+ * @param bubble
+ * ...
+ * @param mapView
+ * ...
+ */
+ public void showBubble(InfoWindow bubble, MapView mapView) {
+ // offset the bubble to be top-centered on the marker:
+ Drawable marker = getMarker(0 /* OverlayItem.ITEM_STATE_FOCUSED_MASK */);
+ int markerWidth = 0, markerHeight = 0;
+ if (marker != null) {
+ markerWidth = marker.getIntrinsicWidth();
+ markerHeight = marker.getIntrinsicHeight();
+ } // else... we don't have the default marker size => don't user default
+ // markers!!!
+ Point markerH = getHotspot(getMarkerHotspot(), markerWidth, markerHeight);
+ Point bubbleH = getHotspot(HotspotPlace.TOP_CENTER, markerWidth, markerHeight);
+ bubbleH.offset(-markerH.x, -markerH.y);
+
+ bubble.open(this, bubbleH.x, bubbleH.y);
+ }
+}
diff --git a/TileMapApp/src/org/osmdroid/overlays/InfoWindow.java b/TileMapApp/src/org/osmdroid/overlays/InfoWindow.java
new file mode 100644
index 0000000..d2516aa
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/overlays/InfoWindow.java
@@ -0,0 +1,140 @@
+package org.osmdroid.overlays;
+
+// TODO composite view as texture overlay and only allow one bubble at a time.
+
+import org.oscim.view.MapView;
+
+import android.content.Context;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RelativeLayout;
+
+/**
+ * View that can be displayed on an OSMDroid map, associated to a GeoPoint.
+ * Typical usage: cartoon-like bubbles displayed when clicking an overlay item.
+ * It mimics the InfoWindow class of Google Maps JavaScript API V3. Main
+ * differences are:
+ *
+ * Structure and content of the view is let to the responsibility of the
+ * caller.
+ * The same InfoWindow can be associated to many items.
+ *
+ * Known issues:
+ *
+ * It disappears when zooming in/out (osmdroid issue #259 on osmdroid 3.0.8,
+ * should be fixed in next version).
+ * The window is displayed "above" the marker, so the queue of the bubble
+ * can hide the marker.
+ *
+ * This is an abstract class.
+ * @see DefaultInfoWindow
+ * @author M.Kergall
+ */
+public abstract class InfoWindow {
+
+ protected View mView;
+ protected boolean mIsVisible = false;
+ protected MapView mMapView;
+ protected RelativeLayout mLayout;
+ private android.widget.RelativeLayout.LayoutParams mLayoutPos;
+
+ /**
+ * @param layoutResId
+ * the id of the view resource.
+ * @param mapView
+ * the mapview on which is hooked the view
+ */
+ public InfoWindow(int layoutResId, MapView mapView) {
+ mMapView = mapView;
+
+ mIsVisible = false;
+ ViewGroup parent = (ViewGroup) mapView.getParent();
+ Context context = mapView.getContext();
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mView = inflater.inflate(layoutResId, parent, false);
+
+ RelativeLayout.LayoutParams rlp =
+ new RelativeLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT);
+
+ mLayout = new RelativeLayout(context);
+ mLayout.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
+ mLayout.setLayoutParams(rlp);
+ mLayoutPos = rlp;
+
+ // not so sure about this. why is just blitting the bitmap on glview so slow?...
+ mView.setDrawingCacheEnabled(true);
+ // mLayout.setDrawingCacheEnabled(true);
+ // mLayout.setPersistentDrawingCache(ViewGroup.PERSISTENT_ALL_CACHES);
+ // mLayout.setAlwaysDrawnWithCacheEnabled(true); // call this method
+ mLayout.setWillNotDraw(true);
+ mLayout.addView(mView);
+ }
+
+ /**
+ * Returns the Android view. This allows to set its content.
+ * @return the Android view
+ */
+ public View getView() {
+ return (mView);
+ }
+
+ /**
+ * open the window at the specified position.
+ * @param item
+ * the item on which is hooked the view
+ * @param offsetX
+ * (&offsetY) the offset of the view to the position, in pixels.
+ * This allows to offset the view from the marker position.
+ * @param offsetY
+ * ...
+ */
+ public void open(ExtendedOverlayItem item, int offsetX, int offsetY) {
+ onOpen(item);
+
+ close(); // if it was already opened
+ // mView.requestLayout();
+ mView.buildDrawingCache();
+ mMapView.addView(mLayout);
+ mIsVisible = true;
+ }
+
+ public void position(int x, int y) {
+ // if this isnt madness...
+ RelativeLayout.LayoutParams rlp = mLayoutPos;
+ rlp.leftMargin = x;
+ rlp.rightMargin = -x;
+ rlp.topMargin = -y;
+ rlp.bottomMargin = y + mMapView.getHeight() / 2;
+ mLayout.setLayoutParams(rlp);
+
+ //mMapView.requestLayout();
+ mLayout.requestLayout();
+
+ // using scrollTo the bubble somehow does not appear when it
+ // is not already in viewport...
+ // mLayout.scrollTo(-x, y + mMapView.getHeight() / 2);
+ }
+
+ public void close() {
+ if (mIsVisible) {
+ mIsVisible = false;
+ ((ViewGroup) mLayout.getParent()).removeView(mLayout);
+ onClose();
+ }
+ }
+
+ public boolean isOpen() {
+ return mIsVisible;
+ }
+
+ // Abstract methods to implement:
+ public abstract void onOpen(ExtendedOverlayItem item);
+
+ public abstract void onClose();
+
+}
diff --git a/TileMapApp/src/org/osmdroid/overlays/ItemizedOverlayWithBubble.java b/TileMapApp/src/org/osmdroid/overlays/ItemizedOverlayWithBubble.java
new file mode 100644
index 0000000..d9ac5a2
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/overlays/ItemizedOverlayWithBubble.java
@@ -0,0 +1,198 @@
+package org.osmdroid.overlays;
+
+import java.util.List;
+
+import org.oscim.core.GeoPoint;
+import org.oscim.core.MapPosition;
+import org.oscim.overlay.ItemizedIconOverlay;
+import org.oscim.overlay.OverlayItem;
+import org.oscim.view.MapView;
+import org.osmdroid.utils.BonusPackHelper;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.util.Log;
+
+/**
+ * An itemized overlay with an InfoWindow or "bubble" which opens when the user
+ * taps on an overlay item, and displays item attributes.
+ * Items must be ExtendedOverlayItem.
+ * @see ExtendedOverlayItem
+ * @see InfoWindow
+ * @author M.Kergall
+ * @param -
+ * ...
+ */
+public class ItemizedOverlayWithBubble
- extends ItemizedIconOverlay
-
+ implements ItemizedIconOverlay.OnItemGestureListener
-
+{
+
+ protected List
- mItemsList;
+
+ // only one for all items of this overlay => one at a time
+ protected InfoWindow mBubble;
+
+ // the item currently showing the bubble. Null if none.
+ protected OverlayItem mItemWithBubble;
+
+ static int layoutResId = 0;
+
+ @Override
+ public boolean onItemLongPress(final int index, final OverlayItem item) {
+ if (mBubble.isOpen())
+ hideBubble();
+ else
+ showBubble(index);
+ return false;
+ }
+
+ @Override
+ public boolean onItemSingleTapUp(final int index, final OverlayItem item) {
+ showBubble(index);
+ return false;
+ }
+
+ private Point mTmpPoint = new Point();
+
+ @Override
+ public void onUpdate(MapPosition mapPosition, boolean changed) {
+ if (mBubble.isOpen()) {
+ GeoPoint gp = mItemWithBubble.getPoint();
+
+ Point p = mTmpPoint;
+ mMapView.getMapViewPosition().project(gp, p);
+
+ mBubble.position(p.x, p.y);
+ }
+ }
+
+ public ItemizedOverlayWithBubble(final MapView mapView, final Context context,
+ final List
- aList, final InfoWindow bubble) {
+ super(mapView, context, aList, null);
+
+ mItemsList = aList;
+ if (bubble != null) {
+ mBubble = bubble;
+ } else {
+ // build default bubble:
+ String packageName = context.getPackageName();
+ if (layoutResId == 0) {
+ layoutResId = context.getResources().getIdentifier(
+ "layout/bonuspack_bubble", null,
+ packageName);
+ if (layoutResId == 0)
+ Log.e(BonusPackHelper.LOG_TAG,
+ "ItemizedOverlayWithBubble: layout/bonuspack_bubble not found in "
+ + packageName);
+ }
+ mBubble = new DefaultInfoWindow(layoutResId, mapView);
+ }
+ mItemWithBubble = null;
+
+ mOnItemGestureListener = this;
+ }
+
+ public ItemizedOverlayWithBubble(final Context context, final List
- aList,
+ final MapView mapView) {
+ this(mapView, context, aList, null);
+ }
+
+ void showBubble(int index) {
+ showBubbleOnItem(index, mMapView);
+ }
+
+ /**
+ * Opens the bubble on the item. For each ItemizedOverlay, only one bubble
+ * is opened at a time. If you want more bubbles opened simultaneously, use
+ * many ItemizedOverlays.
+ * @param index
+ * of the overlay item to show
+ * @param mapView
+ * ...
+ */
+ public void showBubbleOnItem(final int index, final MapView mapView) {
+ ExtendedOverlayItem eItem = (ExtendedOverlayItem) (getItem(index));
+ mItemWithBubble = eItem;
+ if (eItem != null) {
+ eItem.showBubble(mBubble, mapView);
+ // mMapView.getMapViewPosition().animateTo(eItem.mGeoPoint, 0);
+
+ mapView.redrawMap(true);
+ setFocus((Item) eItem);
+ }
+ }
+
+ /** Close the bubble (if it's opened). */
+ public void hideBubble() {
+ mBubble.close();
+ mItemWithBubble = null;
+ }
+
+ @Override
+ protected boolean onSingleTapUpHelper(final int index, final Item item, final MapView mapView) {
+ showBubbleOnItem(index, mapView);
+ return true;
+ }
+
+ /** @return the item currenty showing the bubble, or null if none. */
+ public OverlayItem getBubbledItem() {
+ if (mBubble.isOpen())
+ return mItemWithBubble;
+
+ return null;
+ }
+
+ /** @return the index of the item currenty showing the bubble, or -1 if none. */
+ public int getBubbledItemId() {
+ OverlayItem item = getBubbledItem();
+ if (item == null)
+ return -1;
+
+ return mItemsList.indexOf(item);
+ }
+
+ @Override
+ public boolean removeItem(final Item item) {
+ boolean result = super.removeItem(item);
+ if (mItemWithBubble == item) {
+ hideBubble();
+ }
+ return result;
+ }
+
+ @Override
+ public void removeAllItems() {
+ super.removeAllItems();
+ hideBubble();
+ }
+
+ // FIXME @Override
+ // public void draw(final Canvas canvas, final MapView mapView, final boolean shadow) {
+ // // 1. Fixing drawing focused item on top in ItemizedOverlay (osmdroid
+ // // issue 354):
+ // if (shadow) {
+ // return;
+ // }
+ // final Projection pj = mapView.getProjection();
+ // final int size = mItemsList.size() - 1;
+ // final Point mCurScreenCoords = new Point();
+ //
+ // /*
+ // * Draw in backward cycle, so the items with the least index are on the
+ // * front.
+ // */
+ // for (int i = size; i >= 0; i--) {
+ // final Item item = getItem(i);
+ // if (item != mItemWithBubble) {
+ // pj.toMapPixels(item.mGeoPoint, mCurScreenCoords);
+ // onDrawItem(canvas, item, mCurScreenCoords);
+ // }
+ // }
+ // // draw focused item last:
+ // if (mItemWithBubble != null) {
+ // pj.toMapPixels(mItemWithBubble.mGeoPoint, mCurScreenCoords);
+ // onDrawItem(canvas, (Item) mItemWithBubble, mCurScreenCoords);
+ // }
+ // }
+
+}
diff --git a/TileMapApp/src/org/osmdroid/overlays/MapEventsOverlay.java b/TileMapApp/src/org/osmdroid/overlays/MapEventsOverlay.java
new file mode 100644
index 0000000..e4063f0
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/overlays/MapEventsOverlay.java
@@ -0,0 +1,48 @@
+package org.osmdroid.overlays;
+
+import org.oscim.core.GeoPoint;
+import org.oscim.overlay.Overlay;
+import org.oscim.view.MapView;
+
+import android.content.Context;
+import android.view.MotionEvent;
+
+/**
+ * Empty overlay than can be used to detect events on the map, and to throw them
+ * to a MapEventsReceiver.
+ * @see MapEventsReceiver
+ * @author M.Kergall
+ */
+public class MapEventsOverlay extends Overlay {
+
+ private MapEventsReceiver mReceiver;
+
+ /**
+ * @param mapView
+ * the MapView
+ * @param receiver
+ * the object that will receive/handle the events. It must
+ * implement MapEventsReceiver interface.
+ */
+ public MapEventsOverlay(MapView mapView, MapEventsReceiver receiver) {
+ super(mapView);
+ mReceiver = receiver;
+ }
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ GeoPoint p = mMapView.getMapViewPosition().fromScreenPixels(e.getX(), e.getY());
+
+ return mReceiver.singleTapUpHelper(p);
+ }
+
+ @Override
+ public boolean onLongPress(MotionEvent e) {
+
+ GeoPoint p = mMapView.getMapViewPosition().fromScreenPixels(e.getX(), e.getY());
+
+ // throw event to the receiver:
+ return mReceiver.longPressHelper(p);
+ }
+
+}
diff --git a/TileMapApp/src/org/osmdroid/overlays/MapEventsReceiver.java b/TileMapApp/src/org/osmdroid/overlays/MapEventsReceiver.java
new file mode 100644
index 0000000..017bef7
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/overlays/MapEventsReceiver.java
@@ -0,0 +1,28 @@
+package org.osmdroid.overlays;
+
+import org.oscim.core.GeoPoint;
+
+/**
+ * Interface for objects that need to handle map events thrown by a
+ * MapEventsOverlay.
+ * @see MapEventsOverlay
+ * @author M.Kergall
+ */
+public interface MapEventsReceiver {
+
+ /**
+ * @param p
+ * the position where the event occurred.
+ * @return true if the event has been "consumed" and should not be handled
+ * by other objects.
+ */
+ boolean singleTapUpHelper(GeoPoint p);
+
+ /**
+ * @param p
+ * the position where the event occurred.
+ * @return true if the event has been "consumed" and should not be handled
+ * by other objects.
+ */
+ boolean longPressHelper(GeoPoint p);
+}
diff --git a/TileMapApp/src/org/osmdroid/overlays/POIOverlay.java b/TileMapApp/src/org/osmdroid/overlays/POIOverlay.java
new file mode 100644
index 0000000..a351a9d
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/overlays/POIOverlay.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2012 Hannes Janetzek
+ *
+ * This program is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with
+ * this program. If not, see
.
+ */
+package org.osmdroid.overlays;
+
+import java.util.HashMap;
+import java.util.List;
+
+import org.oscim.app.R;
+import org.oscim.core.BoundingBox;
+import org.oscim.core.MapPosition;
+import org.oscim.overlay.OverlayItem;
+import org.oscim.view.MapView;
+import org.osmdroid.location.FlickrPOIProvider;
+import org.osmdroid.location.POI;
+import org.osmdroid.location.POIProvider;
+import org.osmdroid.utils.BonusPackHelper;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+
+public class POIOverlay extends ItemizedOverlayWithBubble {
+
+ POIProvider mPoiProvider;
+ UpdateTask mUpdateTask;
+ boolean mTaskRunning;
+ Drawable mMarker;
+ BoundingBox mBoundingBox;
+
+ public POIOverlay(MapView mapView, Context context, List aList,
+ InfoWindow bubble) {
+ super(mapView, context, aList, bubble);
+
+ mUpdateTask = new UpdateTask();
+ FlickrPOIProvider provider = new FlickrPOIProvider("c39be46304a6c6efda8bc066c185cd7e");
+ provider.setPrevious(mPOIMap);
+
+ mPoiProvider = provider;
+ mMarker = context.getResources().getDrawable(R.drawable.marker_poi_flickr);
+ }
+
+ public void setPoiProvider(POIProvider poiProvider) {
+ mPoiProvider = poiProvider;
+ }
+
+ @Override
+ public void onUpdate(MapPosition mapPosition, boolean changed) {
+ super.onUpdate(mapPosition, changed);
+
+ if (changed && !mTaskRunning) {
+ mMapView.postDelayed(mUpdateTask, 1000);
+ mTaskRunning = true;
+ }
+ }
+
+ class UpdateTask implements Runnable {
+
+ @Override
+ public void run() {
+ mTaskRunning = false;
+
+ BoundingBox bb = mMapView.getBoundingBox();
+
+ if (mBoundingBox == null || !mBoundingBox.equals(bb)) {
+ // synchronized (mBoundingBox) {
+ mBoundingBox = bb;
+ // }
+
+ // check bounding box
+ Log.d(BonusPackHelper.LOG_TAG, " update pois");
+
+ new POITask().execute();
+ }
+ }
+ }
+
+ HashMap mPOIMap = new HashMap(100);
+
+ class POITask extends AsyncTask> {
+
+ @Override
+ protected List doInBackground(Object... params) {
+
+ return mPoiProvider.getPOIInside(mBoundingBox, "", 20);
+ }
+
+ @Override
+ protected void onPostExecute(List pois) {
+ // removeAllItems();
+
+ if (pois != null) {
+ for (POI poi : pois) {
+ ExtendedOverlayItem poiMarker = new ExtendedOverlayItem(poi.type,
+ poi.description, poi.location);
+
+ poiMarker.setMarker(mMarker);
+ poiMarker.setMarkerHotspot(OverlayItem.HotspotPlace.CENTER);
+ //thumbnail loading moved in POIInfoWindow.onOpen for better performances.
+ poiMarker.setRelatedObject(poi);
+
+ addItem(poiMarker);
+
+ mPOIMap.put(poi.id, poi);
+ }
+ }
+
+ mMapView.redrawMap(true);
+ }
+ }
+
+ public static class POIInfoWindow extends DefaultInfoWindow {
+
+ private Button mButton;
+ private ImageView mImage;
+
+ public POIInfoWindow(MapView mapView) {
+ super(R.layout.bonuspack_bubble, mapView);
+
+ mButton = (Button) mView.findViewById(R.id.bubble_moreinfo);
+ mImage = (ImageView) mView.findViewById(R.id.bubble_image);
+
+ //bonuspack_bubble layouts already contain a "more info" button.
+ mButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ POI poi = (POI) view.getTag();
+
+ if (poi != null && poi.url != null) {
+ Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(poi.url));
+ view.getContext().startActivity(myIntent);
+ }
+ }
+ });
+
+ // getView().setOnClickListener(new View.OnClickListener() {
+ // @Override
+ // public void onClick(View view) {
+ // POI poi = (POI) view.getTag();
+ //
+ // if (poi != null) {
+ // Intent intent = new Intent(tileMap, POIActivity.class);
+ // intent.putExtra("ID", poiMarkers.getBubbledItemId());
+ // tileMap.startActivityForResult(intent, TileMap.POIS_REQUEST);
+ // }
+ // }
+ // });
+ }
+
+ @Override
+ public void onOpen(ExtendedOverlayItem item) {
+ POI poi = (POI) item.getRelatedObject();
+
+ super.onOpen(item);
+
+ poi.fetchThumbnail(mImage);
+
+ //Show or hide "more info" button:
+ if (poi.url != null)
+ mButton.setVisibility(View.VISIBLE);
+ else
+ mButton.setVisibility(View.GONE);
+
+ mButton.setTag(poi);
+ getView().setTag(poi);
+ }
+ }
+
+}
diff --git a/TileMapApp/src/org/osmdroid/routing/Road.java b/TileMapApp/src/org/osmdroid/routing/Road.java
new file mode 100644
index 0000000..508cea4
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/routing/Road.java
@@ -0,0 +1,243 @@
+package org.osmdroid.routing;
+
+import java.util.ArrayList;
+
+import org.oscim.core.BoundingBox;
+import org.oscim.core.GeoPoint;
+import org.osmdroid.routing.provider.GoogleRoadManager;
+import org.osmdroid.routing.provider.MapQuestRoadManager;
+import org.osmdroid.routing.provider.OSRMRoadManager;
+import org.osmdroid.utils.BonusPackHelper;
+import org.osmdroid.utils.DouglasPeuckerReducer;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * describes the way to go from a position to an other. Normally returned by a
+ * call to a Directions API (from MapQuest, GoogleMaps or other)
+ * @see MapQuestRoadManager
+ * @see GoogleRoadManager
+ * @see OSRMRoadManager
+ * @author M.Kergall
+ */
+public class Road implements Parcelable {
+ /**
+ * @see #STATUS_INVALID STATUS_INVALID
+ * @see #STATUS_OK STATUS_OK
+ * @see #STATUS_DEFAULT STATUS_DEFAULT
+ */
+ public int status;
+
+ /** length of the whole route in km. */
+ public double length;
+ /** duration of the whole trip in sec. */
+ public double duration;
+ public ArrayList nodes;
+ /** */
+ /** there is one leg between each waypoint */
+ public ArrayList legs;
+ /** full shape: polyline, as an array of GeoPoints */
+ public ArrayList routeHigh;
+ /** the same, in low resolution (less points) */
+ private ArrayList routeLow;
+ /** road bounding box */
+ public BoundingBox boundingBox;
+
+ /** STATUS_INVALID = road not built */
+ public static final int STATUS_INVALID = 0;
+ /** STATUS_OK = road properly retrieved and built */
+ public static final int STATUS_OK = 1;
+ /**
+ * STATUS_DEFAULT = any issue (technical issue, or no possible route) led to
+ * build a default road
+ */
+ public static final int STATUS_DEFAULT = 2;
+
+ private void init() {
+ status = STATUS_INVALID;
+ length = 0.0;
+ duration = 0.0;
+ nodes = new ArrayList();
+ routeHigh = new ArrayList();
+ routeLow = null;
+ legs = new ArrayList();
+ boundingBox = null;
+ }
+
+ public Road() {
+ init();
+ }
+
+ /**
+ * default constructor when normal loading failed: the road shape only
+ * contains the waypoints; All distances and times are at 0; there is no
+ * node; status equals DEFAULT.
+ * @param waypoints
+ * ...
+ */
+ public Road(ArrayList waypoints) {
+ init();
+ int n = waypoints.size();
+ for (int i = 0; i < n; i++) {
+ GeoPoint p = waypoints.get(i);
+ routeHigh.add(p);
+ }
+ for (int i = 0; i < n - 1; i++) {
+ RoadLeg leg = new RoadLeg(/* i, i+1, mLinks */);
+ legs.add(leg);
+ }
+ boundingBox = BoundingBox.fromGeoPoints(routeHigh);
+ status = STATUS_DEFAULT;
+ }
+
+ /**
+ * @return the road shape in "low resolution" = simplified by around 10
+ * factor.
+ */
+ public ArrayList getRouteLow() {
+ if (routeLow == null) {
+ // Simplify the route (divide number of points by around 10):
+ int n = routeHigh.size();
+ routeLow = DouglasPeuckerReducer.reduceWithTolerance(routeHigh, 1500.0);
+ Log.d(BonusPackHelper.LOG_TAG, "Road reduced from " + n + " to " + routeLow.size()
+ + " points");
+ }
+ return routeLow;
+ }
+
+ public void setRouteLow(ArrayList route) {
+ routeLow = route;
+ }
+
+ /**
+ * @param pLength
+ * in km
+ * @param pDuration
+ * in sec
+ * @return a human-readable length&duration text.
+ */
+ public String getLengthDurationText(double pLength, double pDuration) {
+ String result;
+ if (pLength >= 100.0) {
+ result = (int) (pLength) + " km, ";
+ } else if (pLength >= 1.0) {
+ result = Math.round(pLength * 10) / 10.0 + " km, ";
+ } else {
+ result = (int) (pLength * 1000) + " m, ";
+ }
+ int totalSeconds = (int) pDuration;
+ int hours = totalSeconds / 3600;
+ int minutes = (totalSeconds / 60) - (hours * 60);
+ int seconds = (totalSeconds % 60);
+ if (hours != 0) {
+ result += hours + " h ";
+ }
+ if (minutes != 0) {
+ result += minutes + " min";
+ }
+ if (hours == 0 && minutes == 0) {
+ result += seconds + " s";
+ }
+ return result;
+ }
+
+ /**
+ * @return length and duration of the whole road, or of a leg of the road,
+ * as a String, in a readable format.
+ * @param leg
+ * leg index, starting from 0. -1 for the whole road
+ */
+ public String getLengthDurationText(int leg) {
+ double len = (leg == -1 ? this.length : legs.get(leg).length);
+ double dur = (leg == -1 ? this.duration : legs.get(leg).duration);
+ return getLengthDurationText(len, dur);
+ }
+
+ protected double distanceLLSquared(GeoPoint p1, GeoPoint p2) {
+ double deltaLat = p2.latitudeE6 - p1.latitudeE6;
+ double deltaLon = p2.longitudeE6 - p1.longitudeE6;
+ return (deltaLat * deltaLat + deltaLon * deltaLon);
+ }
+
+ /**
+ * As MapQuest and OSRM doesn't provide legs information, we have to rebuild
+ * it, using the waypoints and the road nodes.
+ * Note that MapQuest legs fit well with waypoints, as there is a
+ * "dedicated" node for each waypoint. But OSRM legs are not precise, as
+ * there is no node "dedicated" to waypoints.
+ * @param waypoints
+ * ...
+ */
+ public void buildLegs(ArrayList waypoints) {
+ legs = new ArrayList();
+ int firstNodeIndex = 0;
+ // For all intermediate waypoints, search the node closest to the
+ // waypoint
+ int w = waypoints.size();
+ int n = nodes.size();
+ for (int i = 1; i < w - 1; i++) {
+ GeoPoint waypoint = waypoints.get(i);
+ double distanceMin = -1.0;
+ int nodeIndexMin = -1;
+ for (int j = firstNodeIndex; j < n; j++) {
+ GeoPoint roadPoint = nodes.get(j).location;
+ double dSquared = distanceLLSquared(roadPoint, waypoint);
+ if (nodeIndexMin == -1 || dSquared < distanceMin) {
+ distanceMin = dSquared;
+ nodeIndexMin = j;
+ }
+ }
+ // Build the leg as ending with this closest node:
+ RoadLeg leg = new RoadLeg(firstNodeIndex, nodeIndexMin, nodes);
+ legs.add(leg);
+ firstNodeIndex = nodeIndexMin + 1; // restart next leg from end
+ }
+ // Build last leg ending with last node:
+ RoadLeg lastLeg = new RoadLeg(firstNodeIndex, n - 1, nodes);
+ legs.add(lastLeg);
+ }
+
+ // --- Parcelable implementation
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(status);
+ out.writeDouble(length);
+ out.writeDouble(duration);
+ out.writeList(nodes);
+ out.writeList(legs);
+ out.writeList(routeHigh);
+ out.writeParcelable(boundingBox, 0);
+ }
+
+ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+ @Override
+ public Road createFromParcel(Parcel source) {
+ return new Road(source);
+ }
+
+ @Override
+ public Road[] newArray(int size) {
+ return new Road[size];
+ }
+ };
+
+ @SuppressWarnings("unchecked")
+ private Road(Parcel in) {
+ status = in.readInt();
+ length = in.readDouble();
+ duration = in.readDouble();
+
+ nodes = in.readArrayList(RoadNode.class.getClassLoader());
+ legs = in.readArrayList(RoadLeg.class.getClassLoader());
+ routeHigh = in.readArrayList(GeoPoint.class.getClassLoader());
+ boundingBox = in.readParcelable(BoundingBox.class.getClassLoader());
+ }
+}
diff --git a/TileMapApp/src/org/osmdroid/routing/RoadLeg.java b/TileMapApp/src/org/osmdroid/routing/RoadLeg.java
new file mode 100644
index 0000000..a3ec66c
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/routing/RoadLeg.java
@@ -0,0 +1,77 @@
+package org.osmdroid.routing;
+
+import java.util.ArrayList;
+
+import org.osmdroid.utils.BonusPackHelper;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * Road Leg is the portion of the road between 2 waypoints (intermediate points
+ * requested)
+ * @author M.Kergall
+ */
+public class RoadLeg implements Parcelable {
+ /** in km */
+ public double length;
+ /** in sec */
+ public double duration;
+ /** starting node of the leg, as index in nodes array */
+ public int startNodeIndex;
+ /** and ending node */
+ public int endNodeIndex;
+
+ public RoadLeg() {
+ length = duration = 0.0;
+ startNodeIndex = endNodeIndex = 0;
+ }
+
+ public RoadLeg(int startNodeIndex, int endNodeIndex,
+ ArrayList nodes) {
+ this.startNodeIndex = startNodeIndex;
+ this.endNodeIndex = endNodeIndex;
+ length = duration = 0.0;
+
+ for (int i = startNodeIndex; i <= endNodeIndex; i++) {
+ RoadNode node = nodes.get(i);
+ length += node.length;
+ duration += node.duration;
+ }
+ Log.d(BonusPackHelper.LOG_TAG, "Leg: " + startNodeIndex + "-" + endNodeIndex
+ + ", length=" + length + "km, duration=" + duration + "s");
+ }
+
+ //--- Parcelable implementation
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeDouble(length);
+ out.writeDouble(duration);
+ out.writeInt(startNodeIndex);
+ out.writeInt(endNodeIndex);
+ }
+
+ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+ @Override
+ public RoadLeg createFromParcel(Parcel in) {
+ RoadLeg rl = new RoadLeg();
+ rl.length = in.readDouble();
+ rl.duration = in.readDouble();
+ rl.startNodeIndex = in.readInt();
+ rl.endNodeIndex = in.readInt();
+ return rl;
+ }
+
+ @Override
+ public RoadLeg[] newArray(int size) {
+ return new RoadLeg[size];
+ }
+ };
+}
diff --git a/TileMapApp/src/org/osmdroid/routing/RoadManager.java b/TileMapApp/src/org/osmdroid/routing/RoadManager.java
new file mode 100644
index 0000000..d6f0265
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/routing/RoadManager.java
@@ -0,0 +1,83 @@
+package org.osmdroid.routing;
+
+import java.util.ArrayList;
+
+import org.oscim.core.GeoPoint;
+import org.oscim.overlay.PathOverlay;
+import org.oscim.view.MapView;
+import org.osmdroid.routing.provider.GoogleRoadManager;
+import org.osmdroid.routing.provider.MapQuestRoadManager;
+import org.osmdroid.routing.provider.OSRMRoadManager;
+
+import android.content.Context;
+import android.graphics.Paint;
+
+/**
+ * Generic class to get a route between a start and a destination point, going
+ * through a list of waypoints.
+ * @see MapQuestRoadManager
+ * @see GoogleRoadManager
+ * @see OSRMRoadManager
+ * @author M.Kergall
+ */
+public abstract class RoadManager {
+
+ protected String mOptions;
+
+ public abstract Road getRoad(ArrayList waypoints);
+
+ public RoadManager() {
+ mOptions = "";
+ }
+
+ /**
+ * Add an option that will be used in the route request. Note that some
+ * options are set in the request in all cases.
+ * @param requestOption
+ * see provider documentation. Just one example:
+ * "routeType=bicycle" for MapQuest; "mode=bicycling" for Google.
+ */
+ public void addRequestOption(String requestOption) {
+ mOptions += "&" + requestOption;
+ }
+
+ protected String geoPointAsString(GeoPoint p) {
+ StringBuffer result = new StringBuffer();
+ double d = p.getLatitude();
+ result.append(Double.toString(d));
+ d = p.getLongitude();
+ result.append("," + Double.toString(d));
+ return result.toString();
+ }
+
+ public static PathOverlay buildRoadOverlay(MapView mapView, Road road, Paint paint) {
+ PathOverlay roadOverlay = new PathOverlay(mapView, 0);
+ roadOverlay.setPaint(paint);
+ if (road != null) {
+ ArrayList polyline = road.routeHigh;
+ for (GeoPoint p : polyline) {
+ roadOverlay.addPoint(p);
+ }
+ }
+ return roadOverlay;
+ }
+
+ /**
+ * Builds an overlay for the road shape with a default (and nice!) color.
+ * @param mapView
+ * ..
+ * @param road
+ * ..
+ * @param context
+ * ..
+ * @return route shape overlay
+ */
+ public static PathOverlay buildRoadOverlay(MapView mapView, Road road) {
+ Paint paint = new Paint();
+ paint.setColor(0x800000FF);
+ paint.setStyle(Paint.Style.STROKE);
+ paint.setStrokeWidth(5);
+ return buildRoadOverlay(mapView, road, paint);
+ }
+
+}
diff --git a/TileMapApp/src/org/osmdroid/routing/RoadNode.java b/TileMapApp/src/org/osmdroid/routing/RoadNode.java
new file mode 100644
index 0000000..3a8256e
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/routing/RoadNode.java
@@ -0,0 +1,71 @@
+package org.osmdroid.routing;
+
+import org.oscim.core.GeoPoint;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Road intersection, with instructions to continue.
+ * @author M.Kergall
+ */
+public class RoadNode implements Parcelable {
+ /**
+ * @see Maneuver
+ * Types
+ */
+ public int maneuverType;
+ /** textual information on what to do at this intersection */
+ public String instructions;
+ /** index in road links array - internal use only, for MapQuest directions */
+ public int nextRoadLink;
+ /** in km to the next node */
+ public double length;
+ /** in seconds to the next node */
+ public double duration;
+ /** position of the node */
+ public GeoPoint location;
+
+ public RoadNode() {
+ maneuverType = 0;
+ nextRoadLink = -1;
+ length = duration = 0.0;
+ }
+
+ // --- Parcelable implementation
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(maneuverType);
+ out.writeString(instructions);
+ out.writeDouble(length);
+ out.writeDouble(duration);
+ out.writeParcelable(location, 0);
+ }
+
+ public static final Parcelable.Creator CREATOR = new
+ Parcelable.Creator() {
+ @Override
+ public RoadNode createFromParcel(Parcel in) {
+ RoadNode rn = new RoadNode();
+ rn.maneuverType = in.readInt();
+ rn.instructions = in.readString();
+ rn.length = in.readDouble();
+ rn.duration = in.readDouble();
+ rn.location = in.readParcelable(GeoPoint.class.getClassLoader());
+ return rn;
+ }
+
+ @Override
+ public RoadNode[] newArray(int size) {
+ return new RoadNode[size];
+ }
+ };
+
+}
diff --git a/TileMapApp/src/org/osmdroid/routing/provider/GoogleRoadManager.java b/TileMapApp/src/org/osmdroid/routing/provider/GoogleRoadManager.java
new file mode 100644
index 0000000..924f3b2
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/routing/provider/GoogleRoadManager.java
@@ -0,0 +1,232 @@
+package org.osmdroid.routing.provider;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Locale;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.oscim.core.BoundingBox;
+import org.oscim.core.GeoPoint;
+import org.osmdroid.routing.Road;
+import org.osmdroid.routing.RoadLeg;
+import org.osmdroid.routing.RoadManager;
+import org.osmdroid.routing.RoadNode;
+import org.osmdroid.utils.BonusPackHelper;
+import org.osmdroid.utils.HttpConnection;
+import org.osmdroid.utils.PolylineEncoder;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import android.util.Log;
+
+/**
+ * class to get a route between a start and a destination point, going through a
+ * list of waypoints.
+ * https://developers.google.com/maps/documentation/directions/
+ * Note that displaying a route provided by Google on a non-Google map (like
+ * OSM) is not allowed by Google T&C.
+ * @author M.Kergall
+ */
+public class GoogleRoadManager extends RoadManager {
+
+ static final String GOOGLE_DIRECTIONS_SERVICE = "http://maps.googleapis.com/maps/api/directions/xml?";
+
+ /**
+ * Build the URL to Google Directions service returning a route in XML
+ * format
+ * @param waypoints
+ * ...
+ * @return ...
+ */
+ protected String getUrl(ArrayList waypoints) {
+ StringBuffer urlString = new StringBuffer(GOOGLE_DIRECTIONS_SERVICE);
+ urlString.append("origin=");
+ GeoPoint p = waypoints.get(0);
+ urlString.append(geoPointAsString(p));
+ urlString.append("&destination=");
+ int destinationIndex = waypoints.size() - 1;
+ p = waypoints.get(destinationIndex);
+ urlString.append(geoPointAsString(p));
+
+ for (int i = 1; i < destinationIndex; i++) {
+ if (i == 1)
+ urlString.append("&waypoints=");
+ else
+ urlString.append("%7C"); // the pipe (|), url-encoded
+ p = waypoints.get(i);
+ urlString.append(geoPointAsString(p));
+ }
+ urlString.append("&units=metric&sensor=false");
+ Locale locale = Locale.getDefault();
+ urlString.append("&language=" + locale.getLanguage());
+ urlString.append(mOptions);
+ return urlString.toString();
+ }
+
+ /**
+ * @param waypoints
+ * : list of GeoPoints. Must have at least 2 entries, start and
+ * end points.
+ * @return the road
+ */
+ @Override
+ public Road getRoad(ArrayList waypoints) {
+ String url = getUrl(waypoints);
+ Log.d(BonusPackHelper.LOG_TAG, "GoogleRoadManager.getRoad:" + url);
+ Road road = null;
+ HttpConnection connection = new HttpConnection();
+ connection.doGet(url);
+ InputStream stream = connection.getStream();
+ if (stream != null)
+ road = getRoadXML(stream);
+ connection.close();
+ if (road == null || road.routeHigh.size() == 0) {
+ //Create default road:
+ road = new Road(waypoints);
+ } else {
+ //finalize road data update:
+ for (RoadLeg leg : road.legs) {
+ road.duration += leg.duration;
+ road.length += leg.length;
+ }
+ road.status = Road.STATUS_OK;
+ }
+ Log.d(BonusPackHelper.LOG_TAG, "GoogleRoadManager.getRoad - finished");
+ return road;
+ }
+
+ protected Road getRoadXML(InputStream is) {
+ GoogleDirectionsHandler handler = new GoogleDirectionsHandler();
+ try {
+ SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
+ parser.parse(is, handler);
+ } catch (ParserConfigurationException e) {
+ e.printStackTrace();
+ } catch (SAXException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return handler.mRoad;
+ }
+
+}
+
+class GoogleDirectionsHandler extends DefaultHandler {
+ Road mRoad;
+ RoadLeg mLeg;
+ RoadNode mNode;
+ boolean isPolyline, isOverviewPolyline, isLeg, isStep, isDuration, isDistance, isBB;
+ int mValue;
+ double mLat, mLng;
+ double mNorth, mWest, mSouth, mEast;
+ private String mString;
+
+ public GoogleDirectionsHandler() {
+ isOverviewPolyline = isBB = isPolyline = isLeg = isStep = isDuration = isDistance = false;
+ mRoad = new Road();
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String name,
+ Attributes attributes) {
+ if (localName.equals("polyline")) {
+ isPolyline = true;
+ } else if (localName.equals("overview_polyline")) {
+ isOverviewPolyline = true;
+ } else if (localName.equals("leg")) {
+ mLeg = new RoadLeg();
+ isLeg = true;
+ } else if (localName.equals("step")) {
+ mNode = new RoadNode();
+ isStep = true;
+ } else if (localName.equals("duration")) {
+ isDuration = true;
+ } else if (localName.equals("distance")) {
+ isDistance = true;
+ } else if (localName.equals("bounds")) {
+ isBB = true;
+ }
+ mString = new String();
+ }
+
+ /**
+ * Overrides org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
+ */
+ public @Override
+ void characters(char[] ch, int start, int length) {
+ String chars = new String(ch, start, length);
+ mString = mString.concat(chars);
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String name) {
+ if (localName.equals("points")) {
+ if (isPolyline) {
+ //detailed piece of road for the step, to add:
+ ArrayList polyLine = PolylineEncoder.decode(mString, 10);
+ mRoad.routeHigh.addAll(polyLine);
+ } else if (isOverviewPolyline) {
+ //low-def polyline for the whole road:
+ mRoad.setRouteLow(PolylineEncoder.decode(mString, 10));
+ }
+ } else if (localName.equals("polyline")) {
+ isPolyline = false;
+ } else if (localName.equals("overview_polyline")) {
+ isOverviewPolyline = false;
+ } else if (localName.equals("value")) {
+ mValue = Integer.parseInt(mString);
+ } else if (localName.equals("duration")) {
+ if (isStep)
+ mNode.duration = mValue;
+ else
+ mLeg.duration = mValue;
+ isDuration = false;
+ } else if (localName.equals("distance")) {
+ if (isStep)
+ mNode.length = mValue / 1000.0;
+ else
+ mLeg.length = mValue / 1000.0;
+ isDistance = false;
+ } else if (localName.equals("html_instructions")) {
+ if (isStep) {
+ mString = mString.replaceAll("<[^>]*>", " "); //remove everything in <...>
+ mString = mString.replaceAll(" ", " ");
+ mNode.instructions = mString;
+ //Log.d(BonusPackHelper.LOG_TAG, mString);
+ }
+ } else if (localName.equals("start_location")) {
+ if (isStep)
+ mNode.location = new GeoPoint(mLat, mLng);
+ } else if (localName.equals("step")) {
+ mRoad.nodes.add(mNode);
+ isStep = false;
+ } else if (localName.equals("leg")) {
+ mRoad.legs.add(mLeg);
+ isLeg = false;
+ } else if (localName.equals("lat")) {
+ mLat = Double.parseDouble(mString);
+ } else if (localName.equals("lng")) {
+ mLng = Double.parseDouble(mString);
+ } else if (localName.equals("northeast")) {
+ if (isBB) {
+ mNorth = mLat;
+ mEast = mLng;
+ }
+ } else if (localName.equals("southwest")) {
+ if (isBB) {
+ mSouth = mLat;
+ mWest = mLng;
+ }
+ } else if (localName.equals("bounds")) {
+ mRoad.boundingBox = new BoundingBox(mNorth, mEast, mSouth, mWest);
+ isBB = false;
+ }
+ }
+
+}
diff --git a/TileMapApp/src/org/osmdroid/routing/provider/MapQuestRoadManager.java b/TileMapApp/src/org/osmdroid/routing/provider/MapQuestRoadManager.java
new file mode 100644
index 0000000..e536275
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/routing/provider/MapQuestRoadManager.java
@@ -0,0 +1,278 @@
+package org.osmdroid.routing.provider;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.oscim.core.BoundingBox;
+import org.oscim.core.GeoPoint;
+import org.osmdroid.routing.Road;
+import org.osmdroid.routing.RoadManager;
+import org.osmdroid.routing.RoadNode;
+import org.osmdroid.utils.BonusPackHelper;
+import org.osmdroid.utils.HttpConnection;
+import org.osmdroid.utils.PolylineEncoder;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import android.util.Log;
+
+/**
+ * class to get a route between a start and a destination point, going through a
+ * list of waypoints. It uses MapQuest open, public and free API, based on
+ * OpenStreetMap data.
+ * See http://open.mapquestapi.com/guidance
+ * @author M.Kergall
+ */
+public class MapQuestRoadManager extends RoadManager {
+
+ static final String MAPQUEST_GUIDANCE_SERVICE = "http://open.mapquestapi.com/guidance/v0/route?";
+
+ /**
+ * Build the URL to MapQuest service returning a route in XML format
+ * @param waypoints
+ * : array of waypoints, as [lat, lng], from start point to end
+ * point.
+ * @return ...
+ */
+ protected String getUrl(ArrayList waypoints) {
+ StringBuffer urlString = new StringBuffer(MAPQUEST_GUIDANCE_SERVICE);
+ urlString.append("from=");
+ GeoPoint p = waypoints.get(0);
+ urlString.append(geoPointAsString(p));
+
+ for (int i = 1; i < waypoints.size(); i++) {
+ p = waypoints.get(i);
+ urlString.append("&to=" + geoPointAsString(p));
+ }
+
+ urlString.append("&outFormat=xml");
+ urlString.append("&shapeFormat=cmp"); // encoded polyline, much faster
+
+ urlString.append("&narrativeType=text"); // or "none"
+ // Locale locale = Locale.getDefault();
+ // urlString.append("&locale="+locale.getLanguage()+"_"+locale.getCountry());
+
+ urlString.append("&unit=k&fishbone=false");
+
+ // urlString.append("&generalizeAfter=500" /*+&generalize=2"*/);
+ // 500 points max, 2 meters tolerance
+
+ // Warning: MapQuest Open API doc is sometimes WRONG:
+ // - use unit, not units
+ // - use fishbone, not enableFishbone
+ // - locale (fr_FR, en_US) is supported but not documented.
+ // - generalize and generalizeAfter are not properly implemented
+ urlString.append(mOptions);
+ return urlString.toString();
+ }
+
+ /**
+ * @param waypoints
+ * : list of GeoPoints. Must have at least 2 entries, start and
+ * end points.
+ * @return the road
+ */
+ @Override
+ public Road getRoad(ArrayList waypoints) {
+ String url = getUrl(waypoints);
+ Log.d(BonusPackHelper.LOG_TAG, "MapQuestRoadManager.getRoute:" + url);
+ Road road = null;
+ HttpConnection connection = new HttpConnection();
+ connection.doGet(url);
+ InputStream stream = connection.getStream();
+ if (stream != null)
+ road = getRoadXML(stream, waypoints);
+ if (road == null || road.routeHigh.size() == 0) {
+ // Create default road:
+ road = new Road(waypoints);
+ }
+ connection.close();
+ Log.d(BonusPackHelper.LOG_TAG, "MapQuestRoadManager.getRoute - finished");
+ return road;
+ }
+
+ /**
+ * XML implementation
+ * @param is
+ * : input stream to parse
+ * @param waypoints
+ * ...
+ * @return the road ...
+ */
+ protected Road getRoadXML(InputStream is, ArrayList waypoints) {
+ XMLHandler handler = new XMLHandler();
+ try {
+ SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
+ parser.parse(is, handler);
+ } catch (ParserConfigurationException e) {
+ e.printStackTrace();
+ } catch (SAXException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ Road road = handler.mRoad;
+ if (road != null && road.routeHigh.size() > 0) {
+ road.nodes = finalizeNodes(road.nodes, handler.mLinks, road.routeHigh);
+ road.buildLegs(waypoints);
+ road.status = Road.STATUS_OK;
+ }
+ return road;
+ }
+
+ protected ArrayList finalizeNodes(ArrayList mNodes,
+ ArrayList mLinks, ArrayList polyline) {
+ int n = mNodes.size();
+ if (n == 0)
+ return mNodes;
+ ArrayList newNodes = new ArrayList(n);
+ RoadNode lastNode = null;
+ for (int i = 1; i < n - 1; i++) { // 1, n-1 => first and last MapQuest
+ // nodes are irrelevant.
+ RoadNode node = mNodes.get(i);
+ RoadLink link = mLinks.get(node.nextRoadLink);
+ if (lastNode != null && (node.instructions == null || node.maneuverType == 0)) {
+ // this node is irrelevant, don't keep it,
+ // but update values of last node:
+ lastNode.length += link.mLength;
+ lastNode.duration += (node.duration + link.mDuration);
+ } else {
+ node.length = link.mLength;
+ node.duration += link.mDuration;
+ int locationIndex = link.mShapeIndex;
+ node.location = polyline.get(locationIndex);
+ newNodes.add(node);
+ lastNode = node;
+ }
+ }
+ // switch to the new array of nodes:
+ return newNodes;
+ }
+}
+
+/** Road Link is a portion of road between 2 "nodes" or intersections */
+class RoadLink {
+ /** in km/h */
+ public double mSpeed;
+ /** in km */
+ public double mLength;
+ /** in sec */
+ public double mDuration;
+ /** starting point of the link, as index in initial polyline */
+ public int mShapeIndex;
+}
+
+/**
+ * XMLHandler: class to handle XML generated by MapQuest "guidance" open API.
+ */
+class XMLHandler extends DefaultHandler {
+ public Road mRoad;
+ public ArrayList mLinks;
+
+ boolean isBB;
+ boolean isGuidanceNodeCollection;
+ private String mString;
+ double mLat, mLng;
+ double mNorth, mWest, mSouth, mEast;
+ RoadLink mLink;
+ RoadNode mNode;
+
+ public XMLHandler() {
+ isBB = isGuidanceNodeCollection = false;
+ mRoad = new Road();
+ mLinks = new ArrayList();
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String name,
+ Attributes attributes) {
+ if (localName.equals("boundingBox"))
+ isBB = true;
+ else if (localName.equals("link"))
+ mLink = new RoadLink();
+ else if (localName.equals("node"))
+ mNode = new RoadNode();
+ else if (localName.equals("GuidanceNodeCollection"))
+ isGuidanceNodeCollection = true;
+ mString = new String();
+ }
+
+ /**
+ * Overrides org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
+ */
+ @Override
+ public void characters(char[] ch, int start, int length) {
+ String chars = new String(ch, start, length);
+ mString = mString.concat(chars);
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String name) {
+ if (localName.equals("lat")) {
+ mLat = Double.parseDouble(mString);
+ } else if (localName.equals("lng")) {
+ mLng = Double.parseDouble(mString);
+ } else if (localName.equals("shapePoints")) {
+ mRoad.routeHigh = PolylineEncoder.decode(mString, 10);
+ // Log.d("DD", "High="+mRoad.mRouteHigh.size());
+ } else if (localName.equals("generalizedShape")) {
+ mRoad.setRouteLow(PolylineEncoder.decode(mString, 10));
+ // Log.d("DD", "Low="+mRoad.mRouteLow.size());
+ } else if (localName.equals("length")) {
+ mLink.mLength = Double.parseDouble(mString);
+ } else if (localName.equals("speed")) {
+ mLink.mSpeed = Double.parseDouble(mString);
+ } else if (localName.equals("shapeIndex")) {
+ mLink.mShapeIndex = Integer.parseInt(mString);
+ } else if (localName.equals("link")) {
+ // End of a link: update road attributes:
+ // GuidanceLinkCollection could in theory contain additional unused
+ // links,
+ // but normally not with fishbone set to false.
+ mLink.mDuration = mLink.mLength / mLink.mSpeed * 3600.0;
+ mLinks.add(mLink);
+ mRoad.length += mLink.mLength;
+ mRoad.duration += mLink.mDuration;
+ mLink = null;
+ } else if (localName.equals("turnCost")) {
+ int turnCost = Integer.parseInt(mString);
+ mNode.duration += turnCost;
+ mRoad.duration += turnCost;
+ } else if (localName.equals("maneuverType")) {
+ mNode.maneuverType = Integer.parseInt(mString);
+ } else if (localName.equals("info")) {
+ if (isGuidanceNodeCollection) {
+ if (mNode.instructions == null)
+ // this is first "info" value for this node, keep it:
+ mNode.instructions = mString;
+ }
+ } else if (localName.equals("linkId")) {
+ if (isGuidanceNodeCollection)
+ mNode.nextRoadLink = Integer.parseInt(mString);
+ } else if (localName.equals("node")) {
+ mRoad.nodes.add(mNode);
+ mNode = null;
+ } else if (localName.equals("GuidanceNodeCollection")) {
+ isGuidanceNodeCollection = false;
+ } else if (localName.equals("ul")) {
+ if (isBB) {
+ mNorth = mLat;
+ mWest = mLng;
+ }
+ } else if (localName.equals("lr")) {
+ if (isBB) {
+ mSouth = mLat;
+ mEast = mLng;
+ }
+ } else if (localName.equals("boundingBox")) {
+ mRoad.boundingBox = new BoundingBox(mNorth, mEast, mSouth, mWest);
+ isBB = false;
+ }
+ }
+}
diff --git a/TileMapApp/src/org/osmdroid/routing/provider/OSRMRoadManager.java b/TileMapApp/src/org/osmdroid/routing/provider/OSRMRoadManager.java
new file mode 100644
index 0000000..830ecde
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/routing/provider/OSRMRoadManager.java
@@ -0,0 +1,281 @@
+package org.osmdroid.routing.provider;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Locale;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.oscim.core.BoundingBox;
+import org.oscim.core.GeoPoint;
+import org.osmdroid.routing.Road;
+import org.osmdroid.routing.RoadManager;
+import org.osmdroid.routing.RoadNode;
+import org.osmdroid.utils.BonusPackHelper;
+import org.osmdroid.utils.HttpConnection;
+import org.osmdroid.utils.PolylineEncoder;
+
+import android.util.Log;
+
+/**
+ * get a route between a start and a destination point. It uses OSRM, a free
+ * open source routing service based on OpenSteetMap data.
+ * See https://github.com/DennisOSRM/Project-OSRM/wiki/Server-api
+ * It requests by default the OSRM demo site. Use setService() to request an
+ * other (for instance your own) OSRM service.
+ * TODO: improve internationalization of instructions
+ * @author M.Kergall
+ */
+public class OSRMRoadManager extends RoadManager {
+
+ static final String OSRM_SERVICE = "http://router.project-osrm.org/viaroute?";
+ //Note that the result of OSRM is quite close to Cloudmade NavEngine format:
+ //http://developers.cloudmade.com/wiki/navengine/JSON_format
+
+ protected String mServiceUrl;
+ protected String mUserAgent;
+
+ /** mapping from OSRM directions to MapQuest maneuver IDs: */
+ static final HashMap MANEUVERS;
+ static {
+ MANEUVERS = new HashMap();
+ MANEUVERS.put("0", Integer.valueOf(0)); //No instruction
+ MANEUVERS.put("1", Integer.valueOf(1)); //Continue
+ MANEUVERS.put("2", Integer.valueOf(6)); //Slight right
+ MANEUVERS.put("3", Integer.valueOf(7)); //Right
+ MANEUVERS.put("4", Integer.valueOf(8)); //Sharp right
+ MANEUVERS.put("5", Integer.valueOf(12)); //U-turn
+ MANEUVERS.put("6", Integer.valueOf(5)); //Sharp left
+ MANEUVERS.put("7", Integer.valueOf(4)); //Left
+ MANEUVERS.put("8", Integer.valueOf(3)); //Slight left
+ MANEUVERS.put("9", Integer.valueOf(24)); //Arrived (at waypoint)
+ //MANEUVERS.put("10", Integer.valueOf(0)); //"Head" => used by OSRM as the start node
+ MANEUVERS.put("11-1", Integer.valueOf(27)); //Round-about, 1st exit
+ MANEUVERS.put("11-2", Integer.valueOf(28)); //2nd exit, etc ...
+ MANEUVERS.put("11-3", Integer.valueOf(29));
+ MANEUVERS.put("11-4", Integer.valueOf(30));
+ MANEUVERS.put("11-5", Integer.valueOf(31));
+ MANEUVERS.put("11-6", Integer.valueOf(32));
+ MANEUVERS.put("11-7", Integer.valueOf(33));
+ MANEUVERS.put("11-8", Integer.valueOf(34)); //Round-about, 8th exit
+ MANEUVERS.put("15", Integer.valueOf(24)); //Arrived
+ }
+
+ //From: Project-OSRM-Web / WebContent / localization / OSRM.Locale.en.js
+ // driving directions
+ // %s: road name
+ // %d: direction => removed
+ // <*>: will only be printed when there actually is a road name
+ static final HashMap> DIRECTIONS;
+ static {
+ DIRECTIONS = new HashMap>();
+ HashMap directions;
+
+ directions = new HashMap();
+ DIRECTIONS.put("en", directions);
+ directions.put("0", "Unknown instruction< on %s>");
+ directions.put("1", "Continue< on %s>");
+ directions.put("2", "Turn slight right< on %s>");
+ directions.put("3", "Turn right< on %s>");
+ directions.put("4", "Turn sharp right< on %s>");
+ directions.put("5", "U-Turn< on %s>");
+ directions.put("6", "Turn sharp left< on %s>");
+ directions.put("7", "Turn left< on %s>");
+ directions.put("8", "Turn slight left< on %s>");
+ directions.put("9", "You have reached a waypoint of your trip");
+ directions.put("10", "");
+ directions.put("11-1", "Enter roundabout and leave at first exit< on %s>");
+ directions.put("11-2", "Enter roundabout and leave at second exit< on %s>");
+ directions.put("11-3", "Enter roundabout and leave at third exit< on %s>");
+ directions.put("11-4", "Enter roundabout and leave at fourth exit< on %s>");
+ directions.put("11-5", "Enter roundabout and leave at fifth exit< on %s>");
+ directions.put("11-6", "Enter roundabout and leave at sixth exit< on %s>");
+ directions.put("11-7", "Enter roundabout and leave at seventh exit< on %s>");
+ directions.put("11-8", "Enter roundabout and leave at eighth exit< on %s>");
+ directions.put("11-9", "Enter roundabout and leave at nineth exit< on %s>");
+ directions.put("15", "You have reached your destination");
+
+ directions = new HashMap();
+ DIRECTIONS.put("fr", directions);
+ directions.put("0", "Instruction inconnue< sur %s>");
+ directions.put("1", "Continuez< sur %s>");
+ directions.put("2", "Tournez légèrement à droite< sur %s>");
+ directions.put("3", "Tournez à droite< sur %s>");
+ directions.put("4", "Tournez fortement à droite< sur %s>");
+ directions.put("5", "Faites demi-tour< sur %s>");
+ directions.put("6", "Tournez fortement à gauche< sur %s>");
+ directions.put("7", "Tournez à gauche< sur %s>");
+ directions.put("8", "Tournez légèrement à gauche< sur %s>");
+ directions.put("9", "Vous êtes arrivé à une étape de votre voyage");
+ directions.put("10", "");
+ directions.put("11-1", "Au rond-point, prenez la première sortie< sur %s>");
+ directions.put("11-2", "Au rond-point, prenez la deuxième sortie< sur %s>");
+ directions.put("11-3", "Au rond-point, prenez la troisième sortie< sur %s>");
+ directions.put("11-4", "Au rond-point, prenez la quatrième sortie< sur %s>");
+ directions.put("11-5", "Au rond-point, prenez la cinquième sortie< sur %s>");
+ directions.put("11-6", "Au rond-point, prenez la sixième sortie< sur %s>");
+ directions.put("11-7", "Au rond-point, prenez la septième sortie< sur %s>");
+ directions.put("11-8", "Au rond-point, prenez la huitième sortie< sur %s>");
+ directions.put("11-9", "Au rond-point, prenez la neuvième sortie< sur %s>");
+ directions.put("15", "Vous êtes arrivé");
+
+ directions = new HashMap();
+ DIRECTIONS.put("pl", directions);
+ directions.put("0", "Nieznana instrukcja");
+ directions.put("1", "Kontynuuj jazdę");
+ directions.put("2", "Skręć lekko w prawo");
+ directions.put("3", "Skręć w prawo");
+ directions.put("4", "Skręć ostro w prawo");
+ directions.put("5", "Zawróć");
+ directions.put("6", "Skręć ostro w lewo");
+ directions.put("7", "Skręć w lewo");
+ directions.put("8", "Skręć lekko w lewo");
+ directions.put("9", "Dotarłeś do punktu pośredniego");
+ directions.put("10", "");
+ directions.put("11-1", "Wjedź na rondo i opuść je pierwszym zjazdem");
+ directions.put("11-2", "Wjedź na rondo i opuść je drugim zjazdem");
+ directions.put("11-3", "Wjedź na rondo i opuść je trzecim zjazdem");
+ directions.put("11-4", "Wjedź na rondo i opuść je czwartym zjazdem");
+ directions.put("11-5", "Wjedź na rondo i opuść je piątym zjazdem");
+ directions.put("11-6", "Wjedź na rondo i opuść je szóstym zjazdem");
+ directions.put("11-7", "Wjedź na rondo i opuść je siódmym zjazdem");
+ directions.put("11-8", "Wjedź na rondo i opuść je ósmym zjazdem");
+ directions.put("11-9", "Wjedź na rondo i opuść je dziewiątym zjazdem");
+ directions.put("15", "Dotarłeś do celu podróży");
+ }
+
+ public OSRMRoadManager() {
+ super();
+ mServiceUrl = OSRM_SERVICE;
+ mUserAgent = BonusPackHelper.DEFAULT_USER_AGENT; //set user agent to the default one.
+ }
+
+ /**
+ * allows to request on an other site than OSRM demo site
+ * @param serviceUrl
+ * ...
+ */
+ public void setService(String serviceUrl) {
+ mServiceUrl = serviceUrl;
+ }
+
+ /**
+ * allows to send to OSRM service a user agent specific to the app, instead
+ * of the default user agent of OSMBonusPack lib.
+ * @param userAgent
+ * ...
+ */
+ public void setUserAgent(String userAgent) {
+ mUserAgent = userAgent;
+ }
+
+ protected String getUrl(ArrayList waypoints) {
+ StringBuffer urlString = new StringBuffer(mServiceUrl);
+ for (int i = 0; i < waypoints.size(); i++) {
+ GeoPoint p = waypoints.get(i);
+ urlString.append("&loc=" + geoPointAsString(p));
+ }
+ urlString.append(mOptions);
+ return urlString.toString();
+ }
+
+ @Override
+ public Road getRoad(ArrayList waypoints) {
+ String url = getUrl(waypoints);
+ Log.d(BonusPackHelper.LOG_TAG, "OSRMRoadManager.getRoad:" + url);
+
+ //String jString = BonusPackHelper.requestStringFromUrl(url);
+ HttpConnection connection = new HttpConnection();
+ connection.setUserAgent(mUserAgent);
+ connection.doGet(url);
+ String jString = connection.getContentAsString();
+ connection.close();
+
+ if (jString == null) {
+ Log.e(BonusPackHelper.LOG_TAG, "OSRMRoadManager::getRoad: request failed.");
+ return new Road(waypoints);
+ }
+ Locale l = Locale.getDefault();
+ HashMap directions = DIRECTIONS.get(l.getLanguage());
+ if (directions == null)
+ directions = DIRECTIONS.get("en");
+ Road road = new Road();
+ try {
+ JSONObject jObject = new JSONObject(jString);
+ String route_geometry = jObject.getString("route_geometry");
+ road.routeHigh = PolylineEncoder.decode(route_geometry, 10);
+ JSONArray jInstructions = jObject.getJSONArray("route_instructions");
+ int n = jInstructions.length();
+ RoadNode lastNode = null;
+ for (int i = 0; i < n; i++) {
+ JSONArray jInstruction = jInstructions.getJSONArray(i);
+ RoadNode node = new RoadNode();
+ int positionIndex = jInstruction.getInt(3);
+ node.location = road.routeHigh.get(positionIndex);
+ node.length = jInstruction.getInt(2) / 1000.0;
+ node.duration = jInstruction.getInt(4); //Segment duration in seconds.
+ String direction = jInstruction.getString(0);
+ String roadName = jInstruction.getString(1);
+ if (lastNode != null && "1".equals(direction) && "".equals(roadName)) {
+ //node "Continue" with no road name is useless, don't add it
+ lastNode.length += node.length;
+ lastNode.duration += node.duration;
+ } else {
+ node.maneuverType = getManeuverCode(direction);
+ node.instructions = buildInstructions(direction, roadName, directions);
+ //Log.d(BonusPackHelper.LOG_TAG, direction+"=>"+node.mManeuverType+"; "+node.mInstructions);
+ road.nodes.add(node);
+ lastNode = node;
+ }
+ }
+ JSONObject jSummary = jObject.getJSONObject("route_summary");
+ road.length = jSummary.getInt("total_distance") / 1000.0;
+ road.duration = jSummary.getInt("total_time");
+ } catch (JSONException e) {
+ e.printStackTrace();
+ return new Road(waypoints);
+ }
+ if (road.routeHigh.size() == 0) {
+ //Create default road:
+ road = new Road(waypoints);
+ } else {
+ road.buildLegs(waypoints);
+ BoundingBox bb = BoundingBox.fromGeoPoints(road.routeHigh);
+ //Correcting osmdroid bug #359:
+ road.boundingBox = bb;
+ // new BoundingBox(
+ // bb.getLatSouthE6(), bb.getLonWestE6(), bb.getLatNorthE6(), bb.getLonEastE6());
+ road.status = Road.STATUS_OK;
+ }
+ Log.d(BonusPackHelper.LOG_TAG, "OSRMRoadManager.getRoad - finished");
+ return road;
+ }
+
+ protected int getManeuverCode(String direction) {
+ Integer code = MANEUVERS.get(direction);
+ if (code != null)
+ return code.intValue();
+
+ return 0;
+ }
+
+ protected String buildInstructions(String direction, String roadName,
+ HashMap directions) {
+ if (directions == null)
+ return null;
+ direction = directions.get(direction);
+ if (direction == null)
+ return null;
+ String instructions = null;
+ if (roadName.equals(""))
+ //remove "<*>"
+ instructions = direction.replaceFirst("<[^>]*>", "");
+ else {
+ direction = direction.replace('<', ' ');
+ direction = direction.replace('>', ' ');
+ instructions = String.format(direction, roadName);
+ }
+ return instructions;
+ }
+}
diff --git a/TileMapApp/src/org/osmdroid/utils/BonusPackHelper.java b/TileMapApp/src/org/osmdroid/utils/BonusPackHelper.java
new file mode 100644
index 0000000..54afb46
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/utils/BonusPackHelper.java
@@ -0,0 +1,129 @@
+package org.osmdroid.utils;
+
+import java.io.FileNotFoundException;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.List;
+
+import org.apache.http.NameValuePair;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Build;
+import android.util.Log;
+
+/**
+ * Useful functions and common constants.
+ * @author M.Kergall
+ */
+public class BonusPackHelper {
+
+ /** Log tag. */
+ public static final String LOG_TAG = "BONUSPACK";
+
+ /** User agent sent to services by default */
+ public static final String DEFAULT_USER_AGENT = "OsmBonusPack/1";
+
+ /**
+ * @return true if the device is the emulator, false if actual device.
+ */
+ public static boolean isEmulator() {
+ //return Build.MANUFACTURER.equals("unknown");
+ return ("google_sdk".equals(Build.PRODUCT) || "sdk".equals(Build.PRODUCT));
+ }
+
+ /**
+ * @param connection
+ * ...
+ * @return the whole content of the http request, as a string
+ */
+ private static String readStream(HttpConnection connection) {
+ String result = connection.getContentAsString();
+ return result;
+ }
+
+ /**
+ * sends an http request, and returns the whole content result in a String.
+ * @param url
+ * ...
+ * @return the whole content, or null if any issue.
+ */
+ public static String requestStringFromUrl(String url) {
+ HttpConnection connection = new HttpConnection();
+ connection.doGet(url);
+ String result = readStream(connection);
+ connection.close();
+ return result;
+ }
+
+ /**
+ * requestStringFromPost: do a post request to a url with name-value pairs,
+ * and returns the whole content result in a String.
+ * @param url
+ * ...
+ * @param nameValuePairs
+ * ...
+ * @return the content, or null if any issue.
+ */
+ public static String requestStringFromPost(String url, List nameValuePairs) {
+ HttpConnection connection = new HttpConnection();
+ connection.doPost(url, nameValuePairs);
+ String result = readStream(connection);
+ connection.close();
+ return result;
+ }
+
+ /**
+ * Loads a bitmap from a url.
+ * @param url
+ * ...
+ * @return the bitmap, or null if any issue.
+ */
+ public static Bitmap loadBitmap(String url) {
+ Bitmap bitmap = null;
+ try {
+ InputStream is = (InputStream) new URL(url).getContent();
+ bitmap = BitmapFactory.decodeStream(new FlushedInputStream(is));
+ //Alternative providing better handling on loading errors?
+ /* Drawable d = Drawable.createFromStream(new
+ * FlushedInputStream(is), null); if (is != null) is.close(); if (d
+ * != null) bitmap = ((BitmapDrawable)d).getBitmap(); */
+ } catch (FileNotFoundException e) {
+ Log.d(BonusPackHelper.LOG_TAG, "image not available: " + url);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ return bitmap;
+ }
+
+ /**
+ * Workaround on Android issue see
+ * http://stackoverflow.com/questions/4601352
+ * /createfromstream-in-android-returning-null-for-certain-url
+ */
+ static class FlushedInputStream extends FilterInputStream {
+ public FlushedInputStream(InputStream inputStream) {
+ super(inputStream);
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ long totalBytesSkipped = 0L;
+ while (totalBytesSkipped < n) {
+ long bytesSkipped = in.skip(n - totalBytesSkipped);
+ if (bytesSkipped == 0L) {
+ int byteValue = read();
+ if (byteValue < 0)
+ break; // we reached EOF
+
+ bytesSkipped = 1; // we read one byte
+ }
+ totalBytesSkipped += bytesSkipped;
+ }
+ return totalBytesSkipped;
+ }
+ }
+}
diff --git a/TileMapApp/src/org/osmdroid/utils/DouglasPeuckerReducer.java b/TileMapApp/src/org/osmdroid/utils/DouglasPeuckerReducer.java
new file mode 100644
index 0000000..e8bd490
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/utils/DouglasPeuckerReducer.java
@@ -0,0 +1,149 @@
+package org.osmdroid.utils;
+
+import java.util.ArrayList;
+
+import org.oscim.core.GeoPoint;
+
+/**
+ * Reduces the number of points in a shape using the Douglas-Peucker algorithm.
+ * From:
+ * http://www.phpriot.com/articles/reducing-map-path-douglas-peucker-algorithm/4
+ * Ported from PHP to Java. "marked" array added to optimize.
+ * @author M.Kergall
+ */
+public class DouglasPeuckerReducer {
+
+ /**
+ * Reduce the number of points in a shape using the Douglas-Peucker
+ * algorithm
+ * @param shape
+ * The shape to reduce
+ * @param tolerance
+ * The tolerance to decide whether or not to keep a point, in the
+ * coordinate system of the points (micro-degrees here)
+ * @return the reduced shape
+ */
+ public static ArrayList reduceWithTolerance(ArrayList shape,
+ double tolerance)
+ {
+ int n = shape.size();
+ // if a shape has 2 or less points it cannot be reduced
+ if (tolerance <= 0 || n < 3) {
+ return shape;
+ }
+
+ boolean[] marked = new boolean[n]; //vertex indexes to keep will be marked as "true"
+ for (int i = 1; i < n - 1; i++)
+ marked[i] = false;
+ // automatically add the first and last point to the returned shape
+ marked[0] = marked[n - 1] = true;
+
+ // the first and last points in the original shape are
+ // used as the entry point to the algorithm.
+ douglasPeuckerReduction(
+ shape, // original shape
+ marked, // reduced shape
+ tolerance, // tolerance
+ 0, // index of first point
+ n - 1 // index of last point
+ );
+
+ // all done, return the reduced shape
+ ArrayList newShape = new ArrayList(n); // the new shape to return
+ for (int i = 0; i < n; i++) {
+ if (marked[i])
+ newShape.add(shape.get(i));
+ }
+ return newShape;
+ }
+
+ /**
+ * Reduce the points in shape between the specified first and last index.
+ * Mark the points to keep in marked[]
+ * @param shape
+ * The original shape
+ * @param marked
+ * The points to keep (marked as true)
+ * @param tolerance
+ * The tolerance to determine if a point is kept
+ * @param firstIdx
+ * The index in original shape's point of the starting point for
+ * this line segment
+ * @param lastIdx
+ * The index in original shape's point of the ending point for
+ * this line segment
+ */
+ private static void douglasPeuckerReduction(ArrayList shape, boolean[] marked,
+ double tolerance, int firstIdx, int lastIdx)
+ {
+ if (lastIdx <= firstIdx + 1) {
+ // overlapping indexes, just return
+ return;
+ }
+
+ // loop over the points between the first and last points
+ // and find the point that is the farthest away
+
+ double maxDistance = 0.0;
+ int indexFarthest = 0;
+
+ GeoPoint firstPoint = shape.get(firstIdx);
+ GeoPoint lastPoint = shape.get(lastIdx);
+
+ for (int idx = firstIdx + 1; idx < lastIdx; idx++) {
+ GeoPoint point = shape.get(idx);
+
+ double distance = orthogonalDistance(point, firstPoint, lastPoint);
+
+ // keep the point with the greatest distance
+ if (distance > maxDistance) {
+ maxDistance = distance;
+ indexFarthest = idx;
+ }
+ }
+
+ if (maxDistance > tolerance) {
+ //The farthest point is outside the tolerance: it is marked and the algorithm continues.
+ marked[indexFarthest] = true;
+
+ // reduce the shape between the starting point to newly found point
+ douglasPeuckerReduction(shape, marked, tolerance, firstIdx, indexFarthest);
+
+ // reduce the shape between the newly found point and the finishing point
+ douglasPeuckerReduction(shape, marked, tolerance, indexFarthest, lastIdx);
+ }
+ //else: the farthest point is within the tolerance, the whole segment is discarded.
+ }
+
+ /**
+ * Calculate the orthogonal distance from the line joining the lineStart and
+ * lineEnd points to point
+ * @param point
+ * The point the distance is being calculated for
+ * @param lineStart
+ * The point that starts the line
+ * @param lineEnd
+ * The point that ends the line
+ * @return The distance in points coordinate system
+ */
+ public static double orthogonalDistance(GeoPoint point, GeoPoint lineStart, GeoPoint lineEnd)
+ {
+ double area = Math.abs(
+ (
+ 1.0 * lineStart.latitudeE6 * lineEnd.longitudeE6
+ + 1.0 * lineEnd.latitudeE6 * point.longitudeE6
+ + 1.0 * point.latitudeE6 * lineStart.longitudeE6
+ - 1.0 * lineEnd.latitudeE6 * lineStart.longitudeE6
+ - 1.0 * point.latitudeE6 * lineEnd.longitudeE6
+ - 1.0 * lineStart.latitudeE6 * point.longitudeE6
+ ) / 2.0
+ );
+
+ double bottom = Math.hypot(
+ lineStart.latitudeE6 - lineEnd.latitudeE6,
+ lineStart.longitudeE6 - lineEnd.longitudeE6
+ );
+
+ return (area / bottom * 2.0);
+ }
+}
diff --git a/TileMapApp/src/org/osmdroid/utils/HttpConnection.java b/TileMapApp/src/org/osmdroid/utils/HttpConnection.java
new file mode 100644
index 0000000..cfae636
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/utils/HttpConnection.java
@@ -0,0 +1,168 @@
+package org.osmdroid.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.StatusLine;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.EntityUtils;
+
+import android.util.Log;
+
+/**
+ * A "very very simple to use" class for performing http get and post requests.
+ * So many ways to do that, and potential subtle issues. If complexity should be
+ * added to handle even more issues, complexity should be put here and only
+ * here. Typical usage:
+ *
+ *
+ * HttpConnection connection = new HttpConnection();
+ * connection.doGet("http://www.google.com");
+ * InputStream stream = connection.getStream();
+ * if (stream != null) {
+ * //use this stream, for buffer reading, or XML SAX parsing, or whatever...
+ * }
+ * connection.close();
+ *
+ */
+public class HttpConnection {
+
+ private DefaultHttpClient client;
+ private InputStream stream;
+ private HttpEntity entity;
+ private String mUserAgent;
+
+ private final static int TIMEOUT_CONNECTION = 3000; //ms
+ private final static int TIMEOUT_SOCKET = 8000; //ms
+
+ /**
+ * Constructor. Opens the url with an HttpURLConnection, then opens a stream
+ * on it. param sUrl : url to open
+ */
+ public HttpConnection() {
+ stream = null;
+ entity = null;
+ HttpParams httpParameters = new BasicHttpParams();
+ /* useful? HttpProtocolParams.setContentCharset(httpParameters,
+ * "UTF-8"); HttpProtocolParams.setHttpElementCharset(httpParameters,
+ * "UTF-8"); */
+ // Set the timeout in milliseconds until a connection is established.
+ HttpConnectionParams.setConnectionTimeout(httpParameters, TIMEOUT_CONNECTION);
+ // Set the default socket timeout (SO_TIMEOUT)
+ // in milliseconds which is the timeout for waiting for data.
+ HttpConnectionParams.setSoTimeout(httpParameters, TIMEOUT_SOCKET);
+ client = new DefaultHttpClient(httpParameters);
+ //TODO: created here. Reuse to do for better perfs???...
+ }
+
+ public void setUserAgent(String userAgent) {
+ mUserAgent = userAgent;
+ }
+
+ public void doGet(String sUrl) {
+ HttpGet request = new HttpGet(sUrl);
+ if (mUserAgent != null)
+ request.setHeader("User-Agent", mUserAgent);
+ try {
+ HttpResponse response = client.execute(request);
+ StatusLine status = response.getStatusLine();
+ if (status.getStatusCode() != 200) {
+ Log.e(BonusPackHelper.LOG_TAG, "Invalid response from server: " + status.toString());
+ } else {
+ entity = response.getEntity();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void doPost(String sUrl, List nameValuePairs) {
+ HttpPost request = new HttpPost(sUrl);
+ if (mUserAgent != null)
+ request.setHeader("User-Agent", mUserAgent);
+ try {
+ request.setEntity(new UrlEncodedFormEntity(nameValuePairs));
+ HttpResponse response = client.execute(request);
+ StatusLine status = response.getStatusLine();
+ if (status.getStatusCode() != 200) {
+ Log.e(BonusPackHelper.LOG_TAG, "Invalid response from server: " + status.toString());
+ } else {
+ entity = response.getEntity();
+ }
+ } catch (ClientProtocolException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * @return the opened InputStream, or null if creation failed for any
+ * reason.
+ */
+ public InputStream getStream() {
+ try {
+ if (entity != null)
+ stream = entity.getContent();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return stream;
+ }
+
+ /**
+ * @return the whole content as a String, or null if creation failed for any
+ * reason.
+ */
+ public String getContentAsString() {
+ try {
+ if (entity != null) {
+ return EntityUtils.toString(entity, "UTF-8");
+ //setting the charset is important if none found in the entity.
+ }
+ return null;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * Calling close once is mandatory.
+ */
+ public void close() {
+ if (stream != null) {
+ try {
+ stream.close();
+ stream = null;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if (entity != null) {
+ try {
+ entity.consumeContent();
+ //"finish". Important if we want to reuse the client object one day...
+ entity = null;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if (client != null) {
+ client.getConnectionManager().shutdown();
+ client = null;
+ }
+ }
+
+}
diff --git a/VectorTileMap/src/org/mapsforge/android/swrenderer/ShapeType.java b/TileMapApp/src/org/osmdroid/utils/MathConstants.java
similarity index 80%
rename from VectorTileMap/src/org/mapsforge/android/swrenderer/ShapeType.java
rename to TileMapApp/src/org/osmdroid/utils/MathConstants.java
index e7679c9..b4e5b95 100644
--- a/VectorTileMap/src/org/mapsforge/android/swrenderer/ShapeType.java
+++ b/TileMapApp/src/org/osmdroid/utils/MathConstants.java
@@ -12,8 +12,11 @@
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see .
*/
-package org.mapsforge.android.swrenderer;
+package org.osmdroid.utils;
+
+public class MathConstants {
+
+ public static final double PI180E6 = (Math.PI / 180) / 1000000.0;
+ public static final double PIx4 = Math.PI * 4;
-enum ShapeType {
- CIRCLE, WAY;
}
diff --git a/TileMapApp/src/org/osmdroid/utils/PolylineEncoder.java b/TileMapApp/src/org/osmdroid/utils/PolylineEncoder.java
new file mode 100644
index 0000000..53a1ecf
--- /dev/null
+++ b/TileMapApp/src/org/osmdroid/utils/PolylineEncoder.java
@@ -0,0 +1,96 @@
+package org.osmdroid.utils;
+
+import java.util.ArrayList;
+
+import org.oscim.core.GeoPoint;
+
+/**
+ * Methods to encode and decode a polyline with Google polyline
+ * encoding/decoding scheme. See
+ * https://developers.google.com/maps/documentation/utilities/polylinealgorithm
+ */
+public class PolylineEncoder {
+
+ private static StringBuffer encodeSignedNumber(int num) {
+ int sgn_num = num << 1;
+ if (num < 0) {
+ sgn_num = ~(sgn_num);
+ }
+ return (encodeNumber(sgn_num));
+ }
+
+ private static StringBuffer encodeNumber(int num) {
+ StringBuffer encodeString = new StringBuffer();
+ while (num >= 0x20) {
+ int nextValue = (0x20 | (num & 0x1f)) + 63;
+ encodeString.append((char) (nextValue));
+ num >>= 5;
+ }
+ num += 63;
+ encodeString.append((char) (num));
+ return encodeString;
+ }
+
+ /**
+ * Encode a polyline with Google polyline encoding method
+ * @param polyline
+ * the polyline
+ * @param precision
+ * 1 for a 6 digits encoding, 10 for a 5 digits encoding.
+ * @return the encoded polyline, as a String
+ */
+ public static String encode(ArrayList polyline, int precision) {
+ StringBuffer encodedPoints = new StringBuffer();
+ int prev_lat = 0, prev_lng = 0;
+ for (GeoPoint trackpoint : polyline) {
+ int lat = trackpoint.latitudeE6 / precision;
+ int lng = trackpoint.longitudeE6 / precision;
+ encodedPoints.append(encodeSignedNumber(lat - prev_lat));
+ encodedPoints.append(encodeSignedNumber(lng - prev_lng));
+ prev_lat = lat;
+ prev_lng = lng;
+ }
+ return encodedPoints.toString();
+ }
+
+ /**
+ * Decode a "Google-encoded" polyline
+ * @param encodedString
+ * ...
+ * @param precision
+ * 1 for a 6 digits encoding, 10 for a 5 digits encoding.
+ * @return the polyline.
+ */
+ public static ArrayList decode(String encodedString, int precision) {
+ ArrayList polyline = new ArrayList();
+ int index = 0;
+ int len = encodedString.length();
+ int lat = 0, lng = 0;
+
+ while (index < len) {
+ int b, shift = 0, result = 0;
+ do {
+ b = encodedString.charAt(index++) - 63;
+ result |= (b & 0x1f) << shift;
+ shift += 5;
+ } while (b >= 0x20);
+ int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
+ lat += dlat;
+
+ shift = 0;
+ result = 0;
+ do {
+ b = encodedString.charAt(index++) - 63;
+ result |= (b & 0x1f) << shift;
+ shift += 5;
+ } while (b >= 0x20);
+ int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
+ lng += dlng;
+
+ GeoPoint p = new GeoPoint(lat * precision, lng * precision);
+ polyline.add(p);
+ }
+
+ return polyline;
+ }
+}
diff --git a/TileMapExample/.classpath b/TileMapExample/.classpath
new file mode 100644
index 0000000..a4763d1
--- /dev/null
+++ b/TileMapExample/.classpath
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/TileMapExample/.project b/TileMapExample/.project
new file mode 100644
index 0000000..9d43efd
--- /dev/null
+++ b/TileMapExample/.project
@@ -0,0 +1,33 @@
+
+
+ TileMapExample
+
+
+
+
+
+ com.android.ide.eclipse.adt.ResourceManagerBuilder
+
+
+
+
+ com.android.ide.eclipse.adt.PreCompilerBuilder
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ com.android.ide.eclipse.adt.ApkBuilder
+
+
+
+
+
+ com.android.ide.eclipse.adt.AndroidNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/TileMapExample/AndroidManifest.xml b/TileMapExample/AndroidManifest.xml
new file mode 100644
index 0000000..9fd8136
--- /dev/null
+++ b/TileMapExample/AndroidManifest.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TileMapExample/ic_launcher-web.png b/TileMapExample/ic_launcher-web.png
new file mode 100644
index 0000000..6496c5a
Binary files /dev/null and b/TileMapExample/ic_launcher-web.png differ
diff --git a/TileMapExample/libs/android-support-v4.jar b/TileMapExample/libs/android-support-v4.jar
new file mode 100644
index 0000000..018c127
Binary files /dev/null and b/TileMapExample/libs/android-support-v4.jar differ
diff --git a/TileMapExample/proguard-project.txt b/TileMapExample/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/TileMapExample/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/TileMapExample/project.properties b/TileMapExample/project.properties
new file mode 100644
index 0000000..33d9759
--- /dev/null
+++ b/TileMapExample/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17
+android.library.reference.1=../VectorTileMap
diff --git a/TileMapExample/res/drawable-hdpi/ic_action_search.png b/TileMapExample/res/drawable-hdpi/ic_action_search.png
new file mode 100644
index 0000000..67de12d
Binary files /dev/null and b/TileMapExample/res/drawable-hdpi/ic_action_search.png differ
diff --git a/TileMapExample/res/drawable-hdpi/ic_launcher.png b/TileMapExample/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..6a0db8b
Binary files /dev/null and b/TileMapExample/res/drawable-hdpi/ic_launcher.png differ
diff --git a/TileMapExample/res/drawable-ldpi/ic_launcher.png b/TileMapExample/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..80ebd90
Binary files /dev/null and b/TileMapExample/res/drawable-ldpi/ic_launcher.png differ
diff --git a/TileMapExample/res/drawable-mdpi/ic_action_search.png b/TileMapExample/res/drawable-mdpi/ic_action_search.png
new file mode 100644
index 0000000..134d549
Binary files /dev/null and b/TileMapExample/res/drawable-mdpi/ic_action_search.png differ
diff --git a/TileMapExample/res/drawable-mdpi/ic_launcher.png b/TileMapExample/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..4337a92
Binary files /dev/null and b/TileMapExample/res/drawable-mdpi/ic_launcher.png differ
diff --git a/TileMapExample/res/drawable-xhdpi/ic_action_search.png b/TileMapExample/res/drawable-xhdpi/ic_action_search.png
new file mode 100644
index 0000000..d699c6b
Binary files /dev/null and b/TileMapExample/res/drawable-xhdpi/ic_action_search.png differ
diff --git a/TileMapExample/res/drawable-xhdpi/ic_launcher.png b/TileMapExample/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..436ec1c
Binary files /dev/null and b/TileMapExample/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/TileMapExample/res/layout/activity_map.xml b/TileMapExample/res/layout/activity_map.xml
new file mode 100644
index 0000000..bda1caa
--- /dev/null
+++ b/TileMapExample/res/layout/activity_map.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/TileMapExample/res/menu/activity_map.xml b/TileMapExample/res/menu/activity_map.xml
new file mode 100644
index 0000000..cfc10fd
--- /dev/null
+++ b/TileMapExample/res/menu/activity_map.xml
@@ -0,0 +1,6 @@
+
+
+
diff --git a/TileMapExample/res/values-v11/styles.xml b/TileMapExample/res/values-v11/styles.xml
new file mode 100644
index 0000000..d408cbc
--- /dev/null
+++ b/TileMapExample/res/values-v11/styles.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/TileMapExample/res/values-v14/styles.xml b/TileMapExample/res/values-v14/styles.xml
new file mode 100644
index 0000000..1c089a7
--- /dev/null
+++ b/TileMapExample/res/values-v14/styles.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/TileMapExample/res/values/strings.xml b/TileMapExample/res/values/strings.xml
new file mode 100644
index 0000000..82ce024
--- /dev/null
+++ b/TileMapExample/res/values/strings.xml
@@ -0,0 +1,8 @@
+
+
+ TileMapExample
+ Hello world!
+ Settings
+ MapActivity
+
+
\ No newline at end of file
diff --git a/TileMapExample/res/values/styles.xml b/TileMapExample/res/values/styles.xml
new file mode 100644
index 0000000..8e0a229
--- /dev/null
+++ b/TileMapExample/res/values/styles.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/TileMapExample/src/org/ocsim/test/EventsOverlay.java b/TileMapExample/src/org/ocsim/test/EventsOverlay.java
new file mode 100644
index 0000000..edb5e9d
--- /dev/null
+++ b/TileMapExample/src/org/ocsim/test/EventsOverlay.java
@@ -0,0 +1,18 @@
+package org.ocsim.test;
+
+import org.oscim.overlay.Overlay;
+import org.oscim.view.MapView;
+
+import android.util.Log;
+import android.view.MotionEvent;
+
+public class EventsOverlay extends Overlay {
+ public EventsOverlay(MapView mapView) {
+ super(mapView);
+ }
+ @Override
+ public boolean onTouchEvent(MotionEvent e) {
+ Log.d("app", e.toString());
+ return false;
+ }
+}
diff --git a/TileMapExample/src/org/ocsim/test/MapActivity.java b/TileMapExample/src/org/ocsim/test/MapActivity.java
new file mode 100644
index 0000000..18d5fca
--- /dev/null
+++ b/TileMapExample/src/org/ocsim/test/MapActivity.java
@@ -0,0 +1,60 @@
+package org.ocsim.test;
+
+import org.oscim.core.GeoPoint;
+import org.oscim.core.MapPosition;
+import org.oscim.database.MapDatabases;
+import org.oscim.database.MapOptions;
+import org.oscim.view.MapView;
+
+import android.os.Bundle;
+import android.os.Environment;
+import android.view.Menu;
+
+public class MapActivity extends org.oscim.view.MapActivity {
+
+ private MapView mMap;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_map);
+
+ mMap = (MapView) findViewById(R.id.mapView);
+
+ // MapOptions options = new MapOptions(MapDatabases.PBMAP_READER);
+ // options.put("url",
+ // "http://city.informatik.uni-bremen.de:80/osmstache/test/");
+
+ // MapOptions options = new MapOptions(MapDatabases.OSCIMAP_READER);
+ // options.put("url",
+ // "http://city.informatik.uni-bremen.de:80/osci/map-live/");
+
+ MapOptions options = new MapOptions(MapDatabases.MAP_READER);
+ // options.put("file",
+ // Environment.getExternalStorageDirectory().getPath() +"/bremen.map");
+ options.put("file", Environment.getExternalStorageDirectory().getPath()
+ + "/Download/berlin.map");
+
+ mMap.setMapDatabase(options);
+
+ // configure the MapView and activate the zoomLevel buttons
+ mMap.setClickable(true);
+ mMap.setFocusable(true);
+
+ MapPosition mapCenter = mMap.getMapFileCenter();
+ if (mapCenter == null)
+ mMap.setMapCenter(new MapPosition(new GeoPoint(53.1f, 8.8f),
+ (byte) 12, 1));
+ else
+ mMap.setMapCenter(mapCenter);
+
+ // mMap.getOverlayManager().add(new GenericOverlay(mMap,new
+ // GridOverlay(mMap)));
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.activity_map, menu);
+ return true;
+ }
+}
diff --git a/VectorTileMap/.checkstyle_rules.xml b/VectorTileMap/.checkstyle_rules.xml
index e19e97f..160ec13 100644
--- a/VectorTileMap/.checkstyle_rules.xml
+++ b/VectorTileMap/.checkstyle_rules.xml
@@ -1,97 +1,87 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/VectorTileMap/.classpath b/VectorTileMap/.classpath
index 471fa79..e4889bb 100755
--- a/VectorTileMap/.classpath
+++ b/VectorTileMap/.classpath
@@ -3,7 +3,7 @@
-
+
diff --git a/VectorTileMap/.settings/de.loskutov.anyedit.AnyEditTools.prefs b/VectorTileMap/.settings/de.loskutov.anyedit.AnyEditTools.prefs
new file mode 100644
index 0000000..4c38705
--- /dev/null
+++ b/VectorTileMap/.settings/de.loskutov.anyedit.AnyEditTools.prefs
@@ -0,0 +1,16 @@
+activeContentFilterList=*.makefile,makefile,*.Makefile,Makefile,Makefile.*,*.mk,MANIFEST.MF
+addNewLine=true
+convertActionOnSaave=AnyEdit.CnvrtTabToSpaces
+eclipse.preferences.version=1
+ignoreBlankLinesWhenTrimming=false
+inActiveContentFilterList=
+javaTabWidthForJava=true
+org.eclipse.jdt.ui.editor.tab.width=2
+projectPropsEnabled=true
+removeTrailingSpaces=true
+replaceAllSpaces=false
+replaceAllTabs=false
+saveAndAddLine=false
+saveAndConvert=false
+saveAndTrim=true
+useModulo4Tabs=false
diff --git a/VectorTileMap/.settings/edu.umd.cs.findbugs.core.prefs b/VectorTileMap/.settings/edu.umd.cs.findbugs.core.prefs
new file mode 100644
index 0000000..a374267
--- /dev/null
+++ b/VectorTileMap/.settings/edu.umd.cs.findbugs.core.prefs
@@ -0,0 +1,132 @@
+#FindBugs User Preferences
+#Fri Oct 12 01:06:57 CEST 2012
+cloud_id=edu.umd.cs.findbugs.cloud.doNothingCloud
+detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true
+detectorAtomicityProblem=AtomicityProblem|true
+detectorBadAppletConstructor=BadAppletConstructor|false
+detectorBadResultSetAccess=BadResultSetAccess|true
+detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true
+detectorBadUseOfReturnValue=BadUseOfReturnValue|true
+detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true
+detectorBooleanReturnNull=BooleanReturnNull|true
+detectorCallToUnsupportedMethod=CallToUnsupportedMethod|false
+detectorCheckExpectedWarnings=CheckExpectedWarnings|false
+detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true
+detectorCheckTypeQualifiers=CheckTypeQualifiers|true
+detectorCloneIdiom=CloneIdiom|true
+detectorComparatorIdiom=ComparatorIdiom|true
+detectorConfusedInheritance=ConfusedInheritance|true
+detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true
+detectorCrossSiteScripting=CrossSiteScripting|true
+detectorDefaultEncodingDetector=DefaultEncodingDetector|true
+detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true
+detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true
+detectorDontIgnoreResultOfPutIfAbsent=DontIgnoreResultOfPutIfAbsent|true
+detectorDontUseEnum=DontUseEnum|true
+detectorDroppedException=DroppedException|true
+detectorDumbMethodInvocations=DumbMethodInvocations|true
+detectorDumbMethods=DumbMethods|true
+detectorDuplicateBranches=DuplicateBranches|true
+detectorEmptyZipFileEntry=EmptyZipFileEntry|true
+detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true
+detectorExplicitSerialization=ExplicitSerialization|true
+detectorFinalizerNullsFields=FinalizerNullsFields|true
+detectorFindBadCast2=FindBadCast2|true
+detectorFindBadForLoop=FindBadForLoop|true
+detectorFindCircularDependencies=FindCircularDependencies|false
+detectorFindDeadLocalStores=FindDeadLocalStores|true
+detectorFindDoubleCheck=FindDoubleCheck|true
+detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true
+detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true
+detectorFindFinalizeInvocations=FindFinalizeInvocations|true
+detectorFindFloatEquality=FindFloatEquality|true
+detectorFindHEmismatch=FindHEmismatch|true
+detectorFindInconsistentSync2=FindInconsistentSync2|true
+detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true
+detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true
+detectorFindMaskedFields=FindMaskedFields|true
+detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true
+detectorFindNakedNotify=FindNakedNotify|true
+detectorFindNonShortCircuit=FindNonShortCircuit|true
+detectorFindNullDeref=FindNullDeref|true
+detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true
+detectorFindOpenStream=FindOpenStream|true
+detectorFindPuzzlers=FindPuzzlers|true
+detectorFindRefComparison=FindRefComparison|true
+detectorFindReturnRef=FindReturnRef|true
+detectorFindRunInvocations=FindRunInvocations|true
+detectorFindSelfComparison=FindSelfComparison|true
+detectorFindSelfComparison2=FindSelfComparison2|true
+detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true
+detectorFindSpinLoop=FindSpinLoop|true
+detectorFindSqlInjection=FindSqlInjection|true
+detectorFindTwoLockWait=FindTwoLockWait|true
+detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true
+detectorFindUnconditionalWait=FindUnconditionalWait|true
+detectorFindUninitializedGet=FindUninitializedGet|true
+detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true
+detectorFindUnreleasedLock=FindUnreleasedLock|true
+detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true
+detectorFindUnsyncGet=FindUnsyncGet|true
+detectorFindUseOfNonSerializableValue=FindUseOfNonSerializableValue|true
+detectorFindUselessControlFlow=FindUselessControlFlow|true
+detectorFormatStringChecker=FormatStringChecker|true
+detectorHugeSharedStringConstants=HugeSharedStringConstants|true
+detectorIDivResultCastToDouble=IDivResultCastToDouble|true
+detectorIncompatMask=IncompatMask|true
+detectorInconsistentAnnotations=InconsistentAnnotations|true
+detectorInefficientMemberAccess=InefficientMemberAccess|false
+detectorInefficientToArray=InefficientToArray|true
+detectorInfiniteLoop=InfiniteLoop|true
+detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true
+detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true
+detectorInitializationChain=InitializationChain|true
+detectorInitializeNonnullFieldsInConstructor=InitializeNonnullFieldsInConstructor|true
+detectorInstantiateStaticClass=InstantiateStaticClass|true
+detectorIntCast2LongAsInstant=IntCast2LongAsInstant|true
+detectorInvalidJUnitTest=InvalidJUnitTest|true
+detectorIteratorIdioms=IteratorIdioms|true
+detectorLazyInit=LazyInit|true
+detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true
+detectorLostLoggerDueToWeakReference=LostLoggerDueToWeakReference|true
+detectorMethodReturnCheck=MethodReturnCheck|true
+detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true
+detectorMutableLock=MutableLock|true
+detectorMutableStaticFields=MutableStaticFields|true
+detectorNaming=Naming|true
+detectorNoteUnconditionalParamDerefs=NoteUnconditionalParamDerefs|true
+detectorNumberConstructor=NumberConstructor|true
+detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true
+detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true
+detectorPublicSemaphores=PublicSemaphores|false
+detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true
+detectorReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass=ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass|true
+detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true
+detectorRedundantInterfaces=RedundantInterfaces|true
+detectorRepeatedConditionals=RepeatedConditionals|true
+detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true
+detectorSerializableIdiom=SerializableIdiom|true
+detectorStartInConstructor=StartInConstructor|true
+detectorStaticCalendarDetector=StaticCalendarDetector|true
+detectorStringConcatenation=StringConcatenation|true
+detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true
+detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true
+detectorSwitchFallthrough=SwitchFallthrough|true
+detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true
+detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true
+detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true
+detectorURLProblems=URLProblems|true
+detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true
+detectorUnnecessaryMath=UnnecessaryMath|true
+detectorUnreadFields=UnreadFields|true
+detectorUselessSubclassMethod=UselessSubclassMethod|false
+detectorVarArgsProblems=VarArgsProblems|true
+detectorVolatileUsage=VolatileUsage|true
+detectorWaitInLoop=WaitInLoop|true
+detectorWrongMapIterator=WrongMapIterator|true
+detectorXMLFactoryBypass=XMLFactoryBypass|true
+detector_threshold=2
+effort=max
+filter_settings=Medium|BAD_PRACTICE,CORRECTNESS,MT_CORRECTNESS,PERFORMANCE,STYLE|false|15
+filter_settings_neg=MALICIOUS_CODE,NOISE,I18N,SECURITY,EXPERIMENTAL|
+run_at_full_build=false
diff --git a/VectorTileMap/.settings/org.eclipse.jdt.core.prefs b/VectorTileMap/.settings/org.eclipse.jdt.core.prefs
index e1fc21a..760a293 100755
--- a/VectorTileMap/.settings/org.eclipse.jdt.core.prefs
+++ b/VectorTileMap/.settings/org.eclipse.jdt.core.prefs
@@ -55,7 +55,7 @@ org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
org.eclipse.jdt.core.compiler.problem.nullReference=warning
org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
-org.eclipse.jdt.core.compiler.problem.parameterAssignment=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
@@ -146,23 +146,23 @@ org.eclipse.jdt.core.formatter.comment.format_block_comments=true
org.eclipse.jdt.core.formatter.comment.format_header=false
org.eclipse.jdt.core.formatter.comment.format_html=true
org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
-org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=false
org.eclipse.jdt.core.formatter.comment.format_source_code=true
org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
-org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
-org.eclipse.jdt.core.formatter.comment.line_length=120
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
-org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=true
org.eclipse.jdt.core.formatter.compact_else_if=true
org.eclipse.jdt.core.formatter.continuation_indentation=2
org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
-org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
@@ -355,13 +355,13 @@ org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_lines_in_comments=false
org.eclipse.jdt.core.formatter.join_wrapped_lines=false
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
-org.eclipse.jdt.core.formatter.lineSplit=90
+org.eclipse.jdt.core.formatter.lineSplit=100
org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
diff --git a/VectorTileMap/.settings/org.eclipse.jdt.ui.prefs b/VectorTileMap/.settings/org.eclipse.jdt.ui.prefs
index c8d2b54..34f4253 100755
--- a/VectorTileMap/.settings/org.eclipse.jdt.ui.prefs
+++ b/VectorTileMap/.settings/org.eclipse.jdt.ui.prefs
@@ -13,7 +13,7 @@ cleanup.always_use_this_for_non_static_field_access=false
cleanup.always_use_this_for_non_static_method_access=false
cleanup.convert_to_enhanced_for_loop=false
cleanup.correct_indentation=false
-cleanup.format_source_code=false
+cleanup.format_source_code=true
cleanup.format_source_code_changes_only=false
cleanup.make_local_variable_final=true
cleanup.make_parameters_final=false
@@ -29,7 +29,7 @@ cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=tru
cleanup.qualify_static_member_accesses_with_declaring_class=true
cleanup.qualify_static_method_accesses_with_declaring_class=false
cleanup.remove_private_constructors=true
-cleanup.remove_trailing_whitespaces=false
+cleanup.remove_trailing_whitespaces=true
cleanup.remove_trailing_whitespaces_all=true
cleanup.remove_trailing_whitespaces_ignore_empty=false
cleanup.remove_unnecessary_casts=true
@@ -53,7 +53,7 @@ cleanup_profile=_eclipse-cs VectorTileMap
cleanup_settings_version=2
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
-formatter_profile=_eclipse-cs VectorTileMap
+formatter_profile=_vtm
formatter_settings_version=12
org.eclipse.jdt.ui.ignorelowercasenames=true
org.eclipse.jdt.ui.importorder=java;javax;org;com;
@@ -76,7 +76,7 @@ sp_cleanup.always_use_this_for_non_static_field_access=false
sp_cleanup.always_use_this_for_non_static_method_access=false
sp_cleanup.convert_to_enhanced_for_loop=false
sp_cleanup.correct_indentation=false
-sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code=false
sp_cleanup.format_source_code_changes_only=false
sp_cleanup.make_local_variable_final=false
sp_cleanup.make_parameters_final=false
@@ -85,7 +85,7 @@ sp_cleanup.make_type_abstract_if_missing_method=false
sp_cleanup.make_variable_declarations_final=true
sp_cleanup.never_use_blocks=false
sp_cleanup.never_use_parentheses_in_expressions=true
-sp_cleanup.on_save_use_additional_actions=false
+sp_cleanup.on_save_use_additional_actions=true
sp_cleanup.organize_imports=true
sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
@@ -93,7 +93,7 @@ sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=
sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
sp_cleanup.remove_private_constructors=true
-sp_cleanup.remove_trailing_whitespaces=false
+sp_cleanup.remove_trailing_whitespaces=true
sp_cleanup.remove_trailing_whitespaces_all=true
sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
sp_cleanup.remove_unnecessary_casts=true
diff --git a/VectorTileMap/AndroidManifest.xml b/VectorTileMap/AndroidManifest.xml
old mode 100755
new mode 100644
index a4905fc..c087c12
--- a/VectorTileMap/AndroidManifest.xml
+++ b/VectorTileMap/AndroidManifest.xml
@@ -1,34 +1,16 @@
+ android:versionName="0.4" >
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:targetSdkVersion="10" />
\ No newline at end of file
diff --git a/VectorTileMap/TODO b/VectorTileMap/TODO
new file mode 100644
index 0000000..bc922d1
--- /dev/null
+++ b/VectorTileMap/TODO
@@ -0,0 +1,21 @@
+- refactor projection functions which are currently spread all over the place
+
+- rewrite TouchHandler, implement gesture recognition for rotation/tilt/scaling
+
+- cache:
+ - collect tiles from 4 zoomlevels into one cache-tile (GEM-File?)
+
+- labeling
+ - use relevance tag, sort labels by relevance for faster dynamic placement
+ - throw out overlapping labels with less relevance!
+ - use separate datasource?
+ this would allow for reuse in multiple zoomlevels and
+ reducing the problem of tile clipped ways
+ - text renderer for paths
+ - interface for different placement algorithms
+ - support icon+text items
+
+- zoom to bounding box animator
+
+- gl-compositing of overlay item bubble
+
diff --git a/VectorTileMap/TileData.proto b/VectorTileMap/TileData.proto
deleted file mode 100644
index 757bad4..0000000
--- a/VectorTileMap/TileData.proto
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.mapsforge.database.pbmap;
-
-option java_package = "org.mapsforge.database.pbmap";
-option optimize_for = LITE_RUNTIME;
-
-message Data {
- message Way {
- repeated uint32 tags = 1 [packed = true];
- repeated uint32 index = 2 [packed = true];
- repeated sint32 coordinates = 3 [packed = true];
- }
-
- message Node {
- repeated uint32 tags = 1 [packed = true];
- repeated sint32 coordinates = 2 [packed = true];
- }
-
- repeated string tags = 1;
- repeated Way ways = 2;
- repeated Node nodes = 3;
-}
-
diff --git a/VectorTileMap/ant.properties b/VectorTileMap/ant.properties
index cd202d9..0fb6c49 100644
--- a/VectorTileMap/ant.properties
+++ b/VectorTileMap/ant.properties
@@ -15,4 +15,4 @@
# 'key.alias' for the name of the key to use.
# The password will be asked during the build when you use the 'release' target.
-jar.libs.dir=lib
+#jar.libs.dir=
diff --git a/VectorTileMap/assets/info.xml b/VectorTileMap/assets/info.xml
deleted file mode 100644
index 6a03b6e..0000000
--- a/VectorTileMap/assets/info.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
- About this software
-
-
-
- Version 0.2.4
- This software is a part of the mapsforge project and distributed under the LGPL3 license .
- Map data © OpenStreetMap contributors, CC-BY-SA .
- Please report all bugs and feature requests either via our issue tracker or our public mapsforge-dev mailing list .
-
-
\ No newline at end of file
diff --git a/VectorTileMap/assets/mapsforge_logo.png b/VectorTileMap/assets/mapsforge_logo.png
deleted file mode 100644
index 7e05486..0000000
Binary files a/VectorTileMap/assets/mapsforge_logo.png and /dev/null differ
diff --git a/VectorTileMap/gen/org/mapsforge/app/BuildConfig.java b/VectorTileMap/gen/org/mapsforge/app/BuildConfig.java
deleted file mode 100644
index 780923a..0000000
--- a/VectorTileMap/gen/org/mapsforge/app/BuildConfig.java
+++ /dev/null
@@ -1,6 +0,0 @@
-/** Automatically generated file. DO NOT MODIFY */
-package org.mapsforge.app;
-
-public final class BuildConfig {
- public final static boolean DEBUG = true;
-}
\ No newline at end of file
diff --git a/VectorTileMap/jni/Android.mk b/VectorTileMap/jni/Android.mk
new file mode 100644
index 0000000..0ac5314
--- /dev/null
+++ b/VectorTileMap/jni/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+# APP_OPTIM := debug
+
+include $(CLEAR_VARS)
+
+# TRILIBDEFS = -DTRILIBRARY -DREDUCED -DCDT_ONLY
+LOCAL_CFLAGS := -O -DTRILIBRARY -DREDUCED -DCDT_ONLY -DNO_TIMER -Werror -std=c99
+# -DLINUX -> no fpu_control in bionic, needed ?
+
+LOCAL_MODULE := triangle-jni
+LOCAL_SRC_FILES := TriangleJni.c triangle.c
+LOCAL_LDLIBS := -llog
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/VectorTileMap/jni/README b/VectorTileMap/jni/README
new file mode 100644
index 0000000..b33ea00
--- /dev/null
+++ b/VectorTileMap/jni/README
@@ -0,0 +1,198 @@
+Triangle
+A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator.
+Version 1.6
+
+Show Me
+A Display Program for Meshes and More.
+Version 1.6
+
+Copyright 1993, 1995, 1997, 1998, 2002, 2005 Jonathan Richard Shewchuk
+2360 Woolsey #H
+Berkeley, California 94705-1927
+Please send bugs and comments to jrs@cs.berkeley.edu
+
+Created as part of the Quake project (tools for earthquake simulation).
+Supported in part by NSF Grant CMS-9318163 and an NSERC 1967 Scholarship.
+There is no warranty whatsoever. Use at your own risk.
+
+
+Triangle generates exact Delaunay triangulations, constrained Delaunay
+triangulations, conforming Delaunay triangulations, Voronoi diagrams, and
+high-quality triangular meshes. The latter can be generated with no small
+or large angles, and are thus suitable for finite element analysis.
+Show Me graphically displays the contents of the geometric files used by
+Triangle. Show Me can also write images in PostScript form.
+
+Information on the algorithms used by Triangle, including complete
+references, can be found in the comments at the beginning of the triangle.c
+source file. Another listing of these references, with PostScript copies
+of some of the papers, is available from the Web page
+
+ http://www.cs.cmu.edu/~quake/triangle.research.html
+
+------------------------------------------------------------------------------
+
+These programs may be freely redistributed under the condition that the
+copyright notices (including the copy of this notice in the code comments
+and the copyright notice printed when the `-h' switch is selected) are
+not removed, and no compensation is received. Private, research, and
+institutional use is free. You may distribute modified versions of this
+code UNDER THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT
+IN THE SAME FILE REMAIN UNDER COPYRIGHT OF THE ORIGINAL AUTHOR, BOTH
+SOURCE AND OBJECT CODE ARE MADE FREELY AVAILABLE WITHOUT CHARGE, AND
+CLEAR NOTICE IS GIVEN OF THE MODIFICATIONS. Distribution of this code as
+part of a commercial system is permissible ONLY BY DIRECT ARRANGEMENT
+WITH THE AUTHOR. (If you are not directly supplying this code to a
+customer, and you are instead telling them how they can obtain it for
+free, then you are not required to make any arrangement with me.)
+
+------------------------------------------------------------------------------
+
+The files included in this distribution are:
+
+ README The file you're reading now.
+ triangle.c Complete C source code for Triangle.
+ showme.c Complete C source code for Show Me.
+ triangle.h Include file for calling Triangle from another program.
+ tricall.c Sample program that calls Triangle.
+ makefile Makefile for compiling Triangle and Show Me.
+ A.poly A sample input file.
+
+Each of Triangle and Show Me is a single portable C file. The easiest way
+to compile them is to edit and use the included makefile. Before
+compiling, read the makefile, which describes your options, and edit it
+accordingly. You should specify:
+
+ The source and binary directories.
+
+ The C compiler and level of optimization.
+
+ The "correct" directories for include files (especially X include files),
+ if necessary.
+
+ Do you want single precision or double? (The default is double.) Do you
+ want to leave out some of Triangle's features to reduce the size of the
+ executable file? Investigate the SINGLE, REDUCED, and CDT_ONLY symbols.
+
+ If yours is not a Unix system, define the NO_TIMER symbol to remove the
+ Unix-specific timing code. Also, don't try to compile Show Me; it only
+ works with X Windows.
+
+ If you are compiling on an Intel x86 CPU and using gcc w/Linux or
+ Microsoft C, be sure to define the LINUX or CPU86 (for Microsoft) symbol
+ during compilation so that the exact arithmetic works right.
+
+Once you've done this, type "make" to compile the programs. Alternatively,
+the files are usually easy to compile without a makefile:
+
+ cc -O -o triangle triangle.c -lm
+ cc -O -o showme showme.c -lX11
+
+On some systems, the C compiler won't be able to find the X include files
+or libraries, and you'll need to specify an include path or library path:
+
+ cc -O -I/usr/local/include -o showme showme.c -L/usr/local/lib -lX11
+
+Some processors, including Intel x86 family and possibly Motorola 68xxx
+family chips, are IEEE conformant but have extended length internal
+floating-point registers that may defeat Triangle's exact arithmetic
+routines by failing to cause enough roundoff error! Typically, there is a
+way to set these internal registers so that they are rounded off to IEEE
+single or double precision format. I believe (but I'm not certain) that
+Triangle has the right incantations for x86 chips, if you have gcc running
+under Linux (define the LINUX compiler symbol) or Microsoft C (define the
+CPU86 compiler symbol).
+
+If you have a different processor or operating system, or if I got the
+incantations wrong, you should check your C compiler or system manuals to
+find out how to configure these internal registers to the precision you are
+using. Otherwise, the exact arithmetic routines won't be exact at all.
+See http://www.cs.cmu.edu/~quake/robust.pc.html for details. Triangle's
+exact arithmetic hasn't a hope of working on machines like the Cray C90 or
+Y-MP, which are not IEEE conformant and have inaccurate rounding.
+
+Triangle and Show Me have both text and HTML documentation. The latter is
+illustrated. Find it on the Web at
+
+ http://www.cs.cmu.edu/~quake/triangle.html
+ http://www.cs.cmu.edu/~quake/showme.html
+
+Complete text instructions are printed by invoking each program with the
+`-h' switch:
+
+ triangle -h
+ showme -h
+
+The instructions are long; you'll probably want to pipe the output to
+`more' or `lpr' or redirect it to a file.
+
+Both programs give a short list of command line options if they are invoked
+without arguments (that is, just type `triangle' or `showme').
+
+Try out Triangle on the enclosed sample file, A.poly:
+
+ triangle -p A
+ showme A.poly &
+
+Triangle will read the Planar Straight Line Graph defined by A.poly, and
+write its constrained Delaunay triangulation to A.1.node and A.1.ele.
+Show Me will display the figure defined by A.poly. There are two buttons
+marked "ele" in the Show Me window; click on the top one. This will cause
+Show Me to load and display the triangulation.
+
+For contrast, try running
+
+ triangle -pq A
+
+Now, click on the same "ele" button. A new triangulation will be loaded;
+this one having no angles smaller than 20 degrees.
+
+To see a Voronoi diagram, try this:
+
+ cp A.poly A.node
+ triangle -v A
+
+Click the "ele" button again. You will see the Delaunay triangulation of
+the points in A.poly, without the segments. Now click the top "voro" button.
+You will see the Voronoi diagram corresponding to that Delaunay triangulation.
+Click the "Reset" button to see the full extent of the diagram.
+
+------------------------------------------------------------------------------
+
+If you wish to call Triangle from another program, instructions for doing
+so are contained in the file `triangle.h' (but read Triangle's regular
+instructions first!). Also look at `tricall.c', which provides an example
+of how to call Triangle.
+
+Type "make trilibrary" to create triangle.o, a callable object file.
+Alternatively, the object file is usually easy to compile without a
+makefile:
+
+ cc -DTRILIBRARY -O -c triangle.c
+
+Type "make distclean" to remove all the object and executable files created
+by make.
+
+------------------------------------------------------------------------------
+
+If you use Triangle, and especially if you use it to accomplish real work,
+I would like very much to hear from you. A short letter or email (to
+jrs@cs.berkeley.edu) describing how you use Triangle will mean a lot to me.
+The more people I know are using this program, the more easily I can
+justify spending time on improvements and on the three-dimensional
+successor to Triangle, which in turn will benefit you. Also, I can put you
+on a list to receive email whenever a new version of Triangle is available.
+
+If you use a mesh generated by Triangle or plotted by Show Me in a
+publication, please include an acknowledgment as well. And please spell
+Triangle with a capital `T'! If you want to include a citation, use
+`Jonathan Richard Shewchuk, ``Triangle: Engineering a 2D Quality Mesh
+Generator and Delaunay Triangulator,'' in Applied Computational Geometry:
+Towards Geometric Engineering (Ming C. Lin and Dinesh Manocha, editors),
+volume 1148 of Lecture Notes in Computer Science, pages 203-222,
+Springer-Verlag, Berlin, May 1996. (From the First ACM Workshop on Applied
+Computational Geometry.)'
+
+
+Jonathan Richard Shewchuk
+July 27, 2005
diff --git a/VectorTileMap/jni/TriangleJni.c b/VectorTileMap/jni/TriangleJni.c
new file mode 100644
index 0000000..24d03d4
--- /dev/null
+++ b/VectorTileMap/jni/TriangleJni.c
@@ -0,0 +1,363 @@
+#include
+#include
+#include
+#include
+#include
+#include "triangle.h"
+
+#include
+#define printf(...) __android_log_print(ANDROID_LOG_DEBUG, "Triangle", __VA_ARGS__)
+
+// from www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
+#if 0
+int pnpoly(int nvert, float *vert, float testx, float testy)
+{
+ int i, j, c = 0;
+ for (i = 0, j = (nvert-1)*2; i < nvert * 2; j = i++) {
+ if ( ((vert[i*2+1] > testy) != (vert[j*j+1] > testy)) &&
+ (testx < (vert[j*2]-vert[i*2])
+ * (testy - vert[i*2+1])
+ / (vert[j*2+1]-vert[i*2+1]) + vert[i*2]) )
+ c = !c;
+ }
+ return c;
+}
+#endif
+
+//#define TESTING
+
+int compare_dups(const void *a, const void *b) {
+ int da = *((const int*) a);
+ int db = *((const int*) b);
+
+ return (da > db) - (da < db);
+}
+
+typedef struct triangulateio TriangleIO;
+
+jint Java_org_oscim_jni_TriangleJNI_triangulate(JNIEnv *env, jclass c,
+ jobject point_buf, jint num_rings, jobject indice_buf, jint offset) {
+ jfloat* points = (jfloat*) (*env)->GetDirectBufferAddress(env, point_buf);
+ jshort* indices = (jshort*) (*env)->GetDirectBufferAddress(env, indice_buf);
+
+ TriangleIO in, out;
+ char buf[128];
+
+ memset(&in, 0, sizeof(TriangleIO));
+
+ //int num_points = (indices[0])>>1;
+ in.numberofpoints = (indices[0]) >> 1;
+ in.pointlist = (float *) points;
+
+ // check if explicitly closed
+ if (in.pointlist[0] == in.pointlist[indices[1] - 2]
+ && in.pointlist[1] == in.pointlist[indices[1] - 1]) {
+ int point = 0;
+ for (int i = 0; i < num_rings; i++) {
+ // remove last point in ring
+ indices[i + 1] -= 2;
+ int last = point + (indices[i + 1] >> 1);
+
+ //sprintf(buf, "drop closing point at %d, %d:\n", point, last);
+ //mylog(buf);
+
+ if (in.numberofpoints - last > 1)
+ memmove(in.pointlist + (last * 2),
+ in.pointlist + ((last + 1) * 2),
+ (in.numberofpoints - last - 1) * 2 * sizeof(float));
+
+ in.numberofpoints--;
+ point = last;
+ }
+ }
+
+
+ int dups = 0;
+
+ float *i_points = points;
+ int *skip_list = NULL;
+
+ // check for duplicate vertices and keep a list
+ // of dups and the first occurence
+ for (int i = 0; i < in.numberofpoints - 1; i++) {
+ float x = *i_points++;
+ float y = *i_points++;
+ float *j_points = i_points;
+
+ for (int j = i + 1; j < in.numberofpoints; j++, j_points += 2) {
+ if ((*j_points == x) && (*(j_points + 1) == y)) {
+ skip_list = realloc(skip_list, (dups + 2) * 2 * sizeof(int));
+ skip_list[dups * 2 + 0] = j;
+ skip_list[dups * 2 + 1] = i;
+ dups++;
+ }
+ }
+ }
+
+#ifdef TESTING
+ for (i = 0; i < in.numberofpoints; i++) {
+ printf("point: %f, %f\n", points[i*2], points[i*2+1]);
+ }
+ printf("points: %d, rings: %d\n", in.numberofpoints, num_rings);
+#endif
+ in.segmentlist = (int *) malloc(in.numberofpoints * 2 * sizeof(int));
+ in.numberofsegments = in.numberofpoints;
+ in.numberofholes = num_rings - 1;
+
+ int *rings = NULL;
+ if (in.numberofholes > 0) {
+ in.holelist = (float *) malloc(in.numberofholes * 2 * sizeof(float));
+ rings = (int*) malloc(num_rings * sizeof(int));
+ }
+
+ int *seg = in.segmentlist;
+ float *hole = in.holelist;
+
+ // counter going through all points
+ int point;
+ // counter going through all rings
+ int ring;
+
+ // assign all points to segments for each ring
+ for (ring = 0, point = 0; ring < num_rings; ring++, point++) {
+ int len;
+ int num_points = indices[ring + 1] >> 1;
+
+ if (rings)
+ rings[ring] = num_points;
+
+ // add holes: we need a point inside the hole...
+ // this is just a heuristic, assuming that two
+ // 'parallel' lines have a distance of at least
+ // 1 unit. you'll notice when things went wrong
+ // when the hole is rendered instead of the poly
+ if (ring > 0) {
+ int k = point * 2;
+
+ float nx = in.pointlist[k++];
+ float ny = in.pointlist[k++];
+
+ float cx, cy, vx, vy;
+
+ // try to find a large enough segment
+ for (len = (point + num_points) * 2; k < len;) {
+ cx = nx;
+ cy = ny;
+
+ nx = in.pointlist[k++];
+ ny = in.pointlist[k++];
+
+ vx = nx - cx;
+ vy = ny - cy;
+
+ if (vx > 4 || vx < -4 || vy > 4 || vy < -4)
+ break;
+ }
+
+ float a = sqrt(vx * vx + vy * vy);
+
+ float ux = -vy / a;
+ float uy = vx / a;
+
+ float centerx = cx + vx / 2 - ux;
+ float centery = cy + vy / 2 - uy;
+
+ *hole++ = centerx;
+ *hole++ = centery;
+ }
+
+ // close ring
+ int last = point + (num_points - 1);
+ *seg++ = last;
+ *seg++ = point;
+
+ for (len = point + num_points - 1; point < len; point++) {
+ *seg++ = point;
+ *seg++ = point + 1;
+ }
+ }
+
+ if (dups) {
+
+ for (int i = 0; i < dups; i++) {
+ printf("duplicate points at %d, %d: %f,%f\n",
+ skip_list[i*2], skip_list[i*2+1],
+ in.pointlist[skip_list[i*2+1]*2],
+ in.pointlist[skip_list[i*2+1]*2+1]);
+ }
+ // print poly format to check with triangle/showme
+ for (int j = 0; j < in.numberofpoints; j++)
+ printf("%d %f %f\n", j, in.pointlist[j*2], in.pointlist[j*2+1]);
+
+ seg = in.segmentlist;
+ for (int j = 0; j < in.numberofsegments; j++, seg += 2)
+ printf("%d %d %d\n", j, *seg, *(seg+1));
+
+ for (int j = 0; j < in.numberofholes; j++) {
+ printf("%d %f %f\n", j, in.holelist[j*2], in.holelist[j*2+1]);
+ }
+
+ if (0) {
+ free(in.segmentlist);
+ free(in.holelist);
+ free(rings);
+ free(skip_list);
+ return 0;
+ }
+ if (dups == 2) {
+ if (skip_list[0] > skip_list[2]) {
+ int tmp = skip_list[0];
+ skip_list[0] = skip_list[2];
+ skip_list[2] = tmp;
+
+ tmp = skip_list[1];
+ skip_list[1] = skip_list[3];
+ skip_list[3] = tmp;
+
+ printf("flip items\n");
+ }
+ } else if (dups > 2) {
+ printf("sort dups\n");
+
+ qsort(skip_list, dups, 2 * sizeof(float), compare_dups);
+ }
+
+ // shift segment indices while removing duplicate points
+ for (int i = 0; i < dups; i++) {
+ // position of the duplicate vertex
+ int pos = skip_list[i * 2] - i;
+ // first vertex
+ int replacement = skip_list[i * 2 + 1];
+
+ printf("add offset: %d, from pos %d\n", i, pos);
+ seg = in.segmentlist;
+ for (int j = 0; j < in.numberofsegments * 2;
+ j++, seg++) {
+ if (*seg == pos) {
+ if (replacement >= pos)
+ *seg = replacement - i;
+ else
+ *seg = replacement;
+ } else if (*seg > pos)
+ *seg -= 1;
+ }
+
+ printf("move %d %d %d\n", pos, pos + 1, in.numberofpoints - pos - 1);
+
+ if (in.numberofpoints - pos > 1)
+ memmove(in.pointlist + (pos * 2),
+ in.pointlist + ((pos + 1) * 2),
+ (in.numberofpoints - pos - 1) * 2 * sizeof(float));
+
+ in.numberofpoints--;
+ }
+ }
+
+#ifdef TESTING
+ for (i = 0; i < in.numberofsegments; i++)
+ {
+ printf("segment: %d, %d\n",
+ in.segmentlist[i*2], in.segmentlist[i*2+1]);
+ }
+
+ for (i = 0; i < in.numberofholes; i++)
+ {
+ printf("hole: %f, %f\n",
+ in.holelist[i*2], in.holelist[i*2+1]);
+ }
+#endif
+
+ memset(&out, 0, sizeof(TriangleIO));
+ out.trianglelist = (INDICE*) indices;
+
+ // p - use polygon input, for CDT
+ // z - zero offset array offsets...
+ // P - no poly output
+ // N - no node output
+ // B - no bound output
+ // Q - be quiet!
+ triangulate("pzPNBQ", &in, &out, (TriangleIO *) NULL);
+
+ if (in.numberofpoints < out.numberofpoints) {
+ printf(
+ "polygon input is bad! points in:%d out%d\n", in.numberofpoints, out.numberofpoints);
+
+ for (int j = 0; j < in.numberofpoints; j++) {
+ printf("%d %f %f\n", j, in.pointlist[j*2], in.pointlist[j*2+1]);
+ }
+
+ seg = in.segmentlist;
+ for (int j = 0; j < in.numberofsegments; j++, seg += 2) {
+ printf("%d %d %d\n", j, *seg, *(seg+1));
+ }
+
+ free(in.segmentlist);
+ free(in.holelist);
+ free(rings);
+ return 0;
+ }
+
+#ifdef TESTING
+ printf( "triangles: %d\n", out.numberoftriangles);
+
+ for (int i = 0; i < out.numberoftriangles; i++)
+ {
+ printf("> %d, %d, %d\n",out.trianglelist[i*3],
+ out.trianglelist[i*3+1],
+ out.trianglelist[i*3+2]);
+ }
+#endif
+
+ INDICE *tri;
+
+ /* shift back indices from removed duplicates */
+ for (int i = 0; i < dups; i++) {
+ int pos = skip_list[i * 2] + i;
+
+ tri = out.trianglelist;
+ int n = out.numberoftriangles * 3;
+
+ for (; n-- > 0; tri++)
+ if (*tri >= pos)
+ *tri += 1;
+ }
+
+ /* fix offset to vertex buffer indices */
+
+ // scale to stride and add offset
+ short stride = 2;
+
+ if (offset < 0)
+ offset = 0;
+
+ tri = out.trianglelist;
+ for (int n = out.numberoftriangles * 3; n > 0; n--)
+ *tri++ = *tri * stride + offset;
+
+ // when a ring has an odd number of points one (or rather two)
+ // additional vertices will be added. so the following rings
+ // needs extra offset...
+ int start = offset;
+ for (int j = 0, m = in.numberofholes; j < m; j++) {
+ start += rings[j] * stride;
+ // even number of points?
+ if (!(rings[j] & 1))
+ continue;
+
+ tri = out.trianglelist;
+ int n = out.numberoftriangles * 3;
+
+ for (; n-- > 0; tri++)
+ if (*tri >= start)
+ *tri += stride;
+
+ start += stride;
+ }
+
+ free(in.segmentlist);
+ free(in.holelist);
+ free(rings);
+ free(skip_list);
+
+ return out.numberoftriangles;
+}
diff --git a/VectorTileMap/jni/triangle.c b/VectorTileMap/jni/triangle.c
new file mode 100644
index 0000000..1c96ddb
--- /dev/null
+++ b/VectorTileMap/jni/triangle.c
@@ -0,0 +1,14302 @@
+/*****************************************************************************/
+/* */
+/* 888888888 ,o, / 888 */
+/* 888 88o88o " o8888o 88o8888o o88888o 888 o88888o */
+/* 888 888 888 88b 888 888 888 888 888 d888 88b */
+/* 888 888 888 o88^o888 888 888 "88888" 888 8888oo888 */
+/* 888 888 888 C888 888 888 888 / 888 q888 */
+/* 888 888 888 "88o^888 888 888 Cb 888 "88oooo" */
+/* "8oo8D */
+/* */
+/* A Two-Dimensional Quality Mesh Generator and Delaunay Triangulator. */
+/* (triangle.c) */
+/* */
+/* Version 1.6 */
+/* July 28, 2005 */
+/* */
+/* Copyright 1993, 1995, 1997, 1998, 2002, 2005 */
+/* Jonathan Richard Shewchuk */
+/* 2360 Woolsey #H */
+/* Berkeley, California 94705-1927 */
+/* jrs@cs.berkeley.edu */
+/* */
+/* This program may be freely redistributed under the condition that the */
+/* copyright notices (including this entire header and the copyright */
+/* notice printed when the `-h' switch is selected) are not removed, and */
+/* no compensation is received. Private, research, and institutional */
+/* use is free. You may distribute modified versions of this code UNDER */
+/* THE CONDITION THAT THIS CODE AND ANY MODIFICATIONS MADE TO IT IN THE */
+/* SAME FILE REMAIN UNDER COPYRIGHT OF THE ORIGINAL AUTHOR, BOTH SOURCE */
+/* AND OBJECT CODE ARE MADE FREELY AVAILABLE WITHOUT CHARGE, AND CLEAR */
+/* NOTICE IS GIVEN OF THE MODIFICATIONS. Distribution of this code as */
+/* part of a commercial system is permissible ONLY BY DIRECT ARRANGEMENT */
+/* WITH THE AUTHOR. (If you are not directly supplying this code to a */
+/* customer, and you are instead telling them how they can obtain it for */
+/* free, then you are not required to make any arrangement with me.) */
+/* */
+/* Hypertext instructions for Triangle are available on the Web at */
+/* */
+/* http://www.cs.cmu.edu/~quake/triangle.html */
+/* */
+/* Disclaimer: Neither I nor Carnegie Mellon warrant this code in any way */
+/* whatsoever. This code is provided "as-is". Use at your own risk. */
+/* */
+/* Some of the references listed below are marked with an asterisk. [*] */
+/* These references are available for downloading from the Web page */
+/* */
+/* http://www.cs.cmu.edu/~quake/triangle.research.html */
+/* */
+/* Three papers discussing aspects of Triangle are available. A short */
+/* overview appears in "Triangle: Engineering a 2D Quality Mesh */
+/* Generator and Delaunay Triangulator," in Applied Computational */
+/* Geometry: Towards Geometric Engineering, Ming C. Lin and Dinesh */
+/* Manocha, editors, Lecture Notes in Computer Science volume 1148, */
+/* pages 203-222, Springer-Verlag, Berlin, May 1996 (from the First ACM */
+/* Workshop on Applied Computational Geometry). [*] */
+/* */
+/* The algorithms are discussed in the greatest detail in "Delaunay */
+/* Refinement Algorithms for Triangular Mesh Generation," Computational */
+/* Geometry: Theory and Applications 22(1-3):21-74, May 2002. [*] */
+/* */
+/* More detail about the data structures may be found in my dissertation: */
+/* "Delaunay Refinement Mesh Generation," Ph.D. thesis, Technical Report */
+/* CMU-CS-97-137, School of Computer Science, Carnegie Mellon University, */
+/* Pittsburgh, Pennsylvania, 18 May 1997. [*] */
+/* */
+/* Triangle was created as part of the Quake Project in the School of */
+/* Computer Science at Carnegie Mellon University. For further */
+/* information, see Hesheng Bao, Jacobo Bielak, Omar Ghattas, Loukas F. */
+/* Kallivokas, David R. O'Hallaron, Jonathan R. Shewchuk, and Jifeng Xu, */
+/* "Large-scale Simulation of Elastic Wave Propagation in Heterogeneous */
+/* Media on Parallel Computers," Computer Methods in Applied Mechanics */
+/* and Engineering 152(1-2):85-102, 22 January 1998. */
+/* */
+/* Triangle's Delaunay refinement algorithm for quality mesh generation is */
+/* a hybrid of one due to Jim Ruppert, "A Delaunay Refinement Algorithm */
+/* for Quality 2-Dimensional Mesh Generation," Journal of Algorithms */
+/* 18(3):548-585, May 1995 [*], and one due to L. Paul Chew, "Guaranteed- */
+/* Quality Mesh Generation for Curved Surfaces," Proceedings of the Ninth */
+/* Annual Symposium on Computational Geometry (San Diego, California), */
+/* pages 274-280, Association for Computing Machinery, May 1993, */
+/* http://portal.acm.org/citation.cfm?id=161150 . */
+/* */
+/* The Delaunay refinement algorithm has been modified so that it meshes */
+/* domains with small input angles well, as described in Gary L. Miller, */
+/* Steven E. Pav, and Noel J. Walkington, "When and Why Ruppert's */
+/* Algorithm Works," Twelfth International Meshing Roundtable, pages */
+/* 91-102, Sandia National Laboratories, September 2003. [*] */
+/* */
+/* My implementation of the divide-and-conquer and incremental Delaunay */
+/* triangulation algorithms follows closely the presentation of Guibas */
+/* and Stolfi, even though I use a triangle-based data structure instead */
+/* of their quad-edge data structure. (In fact, I originally implemented */
+/* Triangle using the quad-edge data structure, but the switch to a */
+/* triangle-based data structure sped Triangle by a factor of two.) The */
+/* mesh manipulation primitives and the two aforementioned Delaunay */
+/* triangulation algorithms are described by Leonidas J. Guibas and Jorge */
+/* Stolfi, "Primitives for the Manipulation of General Subdivisions and */
+/* the Computation of Voronoi Diagrams," ACM Transactions on Graphics */
+/* 4(2):74-123, April 1985, http://portal.acm.org/citation.cfm?id=282923 .*/
+/* */
+/* Their O(n log n) divide-and-conquer algorithm is adapted from Der-Tsai */
+/* Lee and Bruce J. Schachter, "Two Algorithms for Constructing the */
+/* Delaunay Triangulation," International Journal of Computer and */
+/* Information Science 9(3):219-242, 1980. Triangle's improvement of the */
+/* divide-and-conquer algorithm by alternating between vertical and */
+/* horizontal cuts was introduced by Rex A. Dwyer, "A Faster Divide-and- */
+/* Conquer Algorithm for Constructing Delaunay Triangulations," */
+/* Algorithmica 2(2):137-151, 1987. */
+/* */
+/* The incremental insertion algorithm was first proposed by C. L. Lawson, */
+/* "Software for C1 Surface Interpolation," in Mathematical Software III, */
+/* John R. Rice, editor, Academic Press, New York, pp. 161-194, 1977. */
+/* For point location, I use the algorithm of Ernst P. Mucke, Isaac */
+/* Saias, and Binhai Zhu, "Fast Randomized Point Location Without */
+/* Preprocessing in Two- and Three-Dimensional Delaunay Triangulations," */
+/* Proceedings of the Twelfth Annual Symposium on Computational Geometry, */
+/* ACM, May 1996. [*] If I were to randomize the order of vertex */
+/* insertion (I currently don't bother), their result combined with the */
+/* result of Kenneth L. Clarkson and Peter W. Shor, "Applications of */
+/* Random Sampling in Computational Geometry II," Discrete & */
+/* Computational Geometry 4(1):387-421, 1989, would yield an expected */
+/* O(n^{4/3}) bound on running time. */
+/* */
+/* The O(n log n) sweepline Delaunay triangulation algorithm is taken from */
+/* Steven Fortune, "A Sweepline Algorithm for Voronoi Diagrams", */
+/* Algorithmica 2(2):153-174, 1987. A random sample of edges on the */
+/* boundary of the triangulation are maintained in a splay tree for the */
+/* purpose of point location. Splay trees are described by Daniel */
+/* Dominic Sleator and Robert Endre Tarjan, "Self-Adjusting Binary Search */
+/* Trees," Journal of the ACM 32(3):652-686, July 1985, */
+/* http://portal.acm.org/citation.cfm?id=3835 . */
+/* */
+/* The algorithms for exact computation of the signs of determinants are */
+/* described in Jonathan Richard Shewchuk, "Adaptive Precision Floating- */
+/* Point Arithmetic and Fast Robust Geometric Predicates," Discrete & */
+/* Computational Geometry 18(3):305-363, October 1997. (Also available */
+/* as Technical Report CMU-CS-96-140, School of Computer Science, */
+/* Carnegie Mellon University, Pittsburgh, Pennsylvania, May 1996.) [*] */
+/* An abbreviated version appears as Jonathan Richard Shewchuk, "Robust */
+/* Adaptive Floating-Point Geometric Predicates," Proceedings of the */
+/* Twelfth Annual Symposium on Computational Geometry, ACM, May 1996. [*] */
+/* Many of the ideas for my exact arithmetic routines originate with */
+/* Douglas M. Priest, "Algorithms for Arbitrary Precision Floating Point */
+/* Arithmetic," Tenth Symposium on Computer Arithmetic, pp. 132-143, IEEE */
+/* Computer Society Press, 1991. [*] Many of the ideas for the correct */
+/* evaluation of the signs of determinants are taken from Steven Fortune */
+/* and Christopher J. Van Wyk, "Efficient Exact Arithmetic for Computa- */
+/* tional Geometry," Proceedings of the Ninth Annual Symposium on */
+/* Computational Geometry, ACM, pp. 163-172, May 1993, and from Steven */
+/* Fortune, "Numerical Stability of Algorithms for 2D Delaunay Triangu- */
+/* lations," International Journal of Computational Geometry & Applica- */
+/* tions 5(1-2):193-213, March-June 1995. */
+/* */
+/* The method of inserting new vertices off-center (not precisely at the */
+/* circumcenter of every poor-quality triangle) is from Alper Ungor, */
+/* "Off-centers: A New Type of Steiner Points for Computing Size-Optimal */
+/* Quality-Guaranteed Delaunay Triangulations," Proceedings of LATIN */
+/* 2004 (Buenos Aires, Argentina), April 2004. */
+/* */
+/* For definitions of and results involving Delaunay triangulations, */
+/* constrained and conforming versions thereof, and other aspects of */
+/* triangular mesh generation, see the excellent survey by Marshall Bern */
+/* and David Eppstein, "Mesh Generation and Optimal Triangulation," in */
+/* Computing and Euclidean Geometry, Ding-Zhu Du and Frank Hwang, */
+/* editors, World Scientific, Singapore, pp. 23-90, 1992. [*] */
+/* */
+/* The time for incrementally adding PSLG (planar straight line graph) */
+/* segments to create a constrained Delaunay triangulation is probably */
+/* O(t^2) per segment in the worst case and O(t) per segment in the */
+/* common case, where t is the number of triangles that intersect the */
+/* segment before it is inserted. This doesn't count point location, */
+/* which can be much more expensive. I could improve this to O(d log d) */
+/* time, but d is usually quite small, so it's not worth the bother. */
+/* (This note does not apply when the -s switch is used, invoking a */
+/* different method is used to insert segments.) */
+/* */
+/* The time for deleting a vertex from a Delaunay triangulation is O(d^2) */
+/* in the worst case and O(d) in the common case, where d is the degree */
+/* of the vertex being deleted. I could improve this to O(d log d) time, */
+/* but d is usually quite small, so it's not worth the bother. */
+/* */
+/* Ruppert's Delaunay refinement algorithm typically generates triangles */
+/* at a linear rate (constant time per triangle) after the initial */
+/* triangulation is formed. There may be pathological cases where */
+/* quadratic time is required, but these never arise in practice. */
+/* */
+/* The geometric predicates (circumcenter calculations, segment */
+/* intersection formulae, etc.) appear in my "Lecture Notes on Geometric */
+/* Robustness" at http://www.cs.berkeley.edu/~jrs/mesh . */
+/* */
+/* If you make any improvements to this code, please please please let me */
+/* know, so that I may obtain the improvements. Even if you don't change */
+/* the code, I'd still love to hear what it's being used for. */
+/* */
+/*****************************************************************************/
+
+/* For single precision (which will save some memory and reduce paging), */
+/* define the symbol SINGLE by using the -DSINGLE compiler switch or by */
+/* writing "#define SINGLE" below. */
+/* */
+/* For double precision (which will allow you to refine meshes to a smaller */
+/* edge length), leave SINGLE undefined. */
+/* */
+/* Double precision uses more memory, but improves the resolution of the */
+/* meshes you can generate with Triangle. It also reduces the likelihood */
+/* of a floating exception due to overflow. Finally, it is much faster */
+/* than single precision on 64-bit architectures like the DEC Alpha. I */
+/* recommend double precision unless you want to generate a mesh for which */
+/* you do not have enough memory. */
+
+/* #define SINGLE */
+
+/* #ifdef SINGLE */
+/* #define REAL float */
+/* #else /\* not SINGLE *\/ */
+/* #define REAL double */
+/* #endif /\* not SINGLE *\/ */
+
+/* If yours is not a Unix system, define the NO_TIMER compiler switch to */
+/* remove the Unix-specific timing code. */
+
+/* #define NO_TIMER */
+
+/* To insert lots of self-checks for internal errors, define the SELF_CHECK */
+/* symbol. This will slow down the program significantly. It is best to */
+/* define the symbol using the -DSELF_CHECK compiler switch, but you could */
+/* write "#define SELF_CHECK" below. If you are modifying this code, I */
+/* recommend you turn self-checks on until your work is debugged. */
+
+/* #define SELF_CHECK */
+
+/* To compile Triangle as a callable object library (triangle.o), define the */
+/* TRILIBRARY symbol. Read the file triangle.h for details on how to call */
+/* the procedure triangulate() that results. */
+
+/* #define TRILIBRARY */
+
+/* It is possible to generate a smaller version of Triangle using one or */
+/* both of the following symbols. Define the REDUCED symbol to eliminate */
+/* all features that are primarily of research interest; specifically, the */
+/* -i, -F, -s, and -C switches. Define the CDT_ONLY symbol to eliminate */
+/* all meshing algorithms above and beyond constrained Delaunay */
+/* triangulation; specifically, the -r, -q, -a, -u, -D, -S, and -s */
+/* switches. These reductions are most likely to be useful when */
+/* generating an object library (triangle.o) by defining the TRILIBRARY */
+/* symbol. */
+
+/* #define REDUCED */
+/* #define CDT_ONLY */
+
+/* On some machines, my exact arithmetic routines might be defeated by the */
+/* use of internal extended precision floating-point registers. The best */
+/* way to solve this problem is to set the floating-point registers to use */
+/* single or double precision internally. On 80x86 processors, this may */
+/* be accomplished by setting the CPU86 symbol for the Microsoft C */
+/* compiler, or the LINUX symbol for the gcc compiler running on Linux. */
+/* */
+/* An inferior solution is to declare certain values as `volatile', thus */
+/* forcing them to be stored to memory and rounded off. Unfortunately, */
+/* this solution might slow Triangle down quite a bit. To use volatile */
+/* values, write "#define INEXACT volatile" below. Normally, however, */
+/* INEXACT should be defined to be nothing. ("#define INEXACT".) */
+/* */
+/* For more discussion, see http://www.cs.cmu.edu/~quake/robust.pc.html . */
+/* For yet more discussion, see Section 5 of my paper, "Adaptive Precision */
+/* Floating-Point Arithmetic and Fast Robust Geometric Predicates" (also */
+/* available as Section 6.6 of my dissertation). */
+
+/* #define CPU86 */
+/* #define LINUX */
+
+#define INEXACT /* Nothing */
+/* #define INEXACT volatile */
+
+/* Maximum number of characters in a file name (including the null). */
+
+#define FILENAMESIZE 2048
+
+/* Maximum number of characters in a line read from a file (including the */
+/* null). */
+
+#define INPUTLINESIZE 1024
+
+/* For efficiency, a variety of data structures are allocated in bulk. The */
+/* following constants determine how many of each structure is allocated */
+/* at once. */
+
+#define TRIPERBLOCK 4092 /* Number of triangles allocated at once. */
+#define SUBSEGPERBLOCK 508 /* Number of subsegments allocated at once. */
+#define VERTEXPERBLOCK 4092 /* Number of vertices allocated at once. */
+#define VIRUSPERBLOCK 1020 /* Number of virus triangles allocated at once. */
+/* Number of encroached subsegments allocated at once. */
+#define BADSUBSEGPERBLOCK 252
+/* Number of skinny triangles allocated at once. */
+#define BADTRIPERBLOCK 4092
+/* Number of flipped triangles allocated at once. */
+#define FLIPSTACKERPERBLOCK 252
+/* Number of splay tree nodes allocated at once. */
+#define SPLAYNODEPERBLOCK 508
+
+/* The vertex types. A DEADVERTEX has been deleted entirely. An */
+/* UNDEADVERTEX is not part of the mesh, but is written to the output */
+/* .node file and affects the node indexing in the other output files. */
+
+#define INPUTVERTEX 0
+#define SEGMENTVERTEX 1
+#define FREEVERTEX 2
+#define DEADVERTEX -32768
+#define UNDEADVERTEX -32767
+
+/* The next line is used to outsmart some very stupid compilers. If your */
+/* compiler is smarter, feel free to replace the "int" with "void". */
+/* Not that it matters. */
+
+//#define VOID int
+#define VOID void
+
+/* Two constants for algorithms based on random sampling. Both constants */
+/* have been chosen empirically to optimize their respective algorithms. */
+
+/* Used for the point location scheme of Mucke, Saias, and Zhu, to decide */
+/* how large a random sample of triangles to inspect. */
+
+#define SAMPLEFACTOR 11
+
+/* Used in Fortune's sweepline Delaunay algorithm to determine what fraction */
+/* of boundary edges should be maintained in the splay tree for point */
+/* location on the front. */
+
+#define SAMPLERATE 10
+
+/* A number that speaks for itself, every kissable digit. */
+
+#define PI 3.141592653589793238462643383279502884197169399375105820974944592308
+
+/* Another fave. */
+
+#define SQUAREROOTTWO 1.4142135623730950488016887242096980785696718753769480732
+
+/* And here's one for those of you who are intimidated by math. */
+
+#define ONETHIRD 0.333333333333333333333333333333333333333333333333333333333333
+
+#include
+#include
+#include
+#include
+#ifndef NO_TIMER
+#include
+#endif /* not NO_TIMER */
+#ifdef CPU86
+#include
+#endif /* CPU86 */
+#ifdef LINUX
+#include
+#endif /* LINUX */
+#ifdef TRILIBRARY
+#include "triangle.h"
+#endif /* TRILIBRARY */
+
+#include
+#define printf(...) __android_log_print(ANDROID_LOG_DEBUG, "Triangle", __VA_ARGS__)
+
+/* A few forward declarations. */
+
+
+#ifndef TRILIBRARY
+char *readline();
+char *findfield();
+#endif /* not TRILIBRARY */
+
+/* Labels that signify the result of point location. The result of a */
+/* search indicates that the point falls in the interior of a triangle, on */
+/* an edge, on a vertex, or outside the mesh. */
+
+enum locateresult {INTRIANGLE, ONEDGE, ONVERTEX, OUTSIDE};
+
+/* Labels that signify the result of vertex insertion. The result indicates */
+/* that the vertex was inserted with complete success, was inserted but */
+/* encroaches upon a subsegment, was not inserted because it lies on a */
+/* segment, or was not inserted because another vertex occupies the same */
+/* location. */
+
+enum insertvertexresult {SUCCESSFULVERTEX, ENCROACHINGVERTEX, VIOLATINGVERTEX,
+ DUPLICATEVERTEX};
+
+/* Labels that signify the result of direction finding. The result */
+/* indicates that a segment connecting the two query points falls within */
+/* the direction triangle, along the left edge of the direction triangle, */
+/* or along the right edge of the direction triangle. */
+
+enum finddirectionresult {WITHIN, LEFTCOLLINEAR, RIGHTCOLLINEAR};
+
+/*****************************************************************************/
+/* */
+/* The basic mesh data structures */
+/* */
+/* There are three: vertices, triangles, and subsegments (abbreviated */
+/* `subseg'). These three data structures, linked by pointers, comprise */
+/* the mesh. A vertex simply represents a mesh vertex and its properties. */
+/* A triangle is a triangle. A subsegment is a special data structure used */
+/* to represent an impenetrable edge of the mesh (perhaps on the outer */
+/* boundary, on the boundary of a hole, or part of an internal boundary */
+/* separating two triangulated regions). Subsegments represent boundaries, */
+/* defined by the user, that triangles may not lie across. */
+/* */
+/* A triangle consists of a list of three vertices, a list of three */
+/* adjoining triangles, a list of three adjoining subsegments (when */
+/* segments exist), an arbitrary number of optional user-defined */
+/* floating-point attributes, and an optional area constraint. The latter */
+/* is an upper bound on the permissible area of each triangle in a region, */
+/* used for mesh refinement. */
+/* */
+/* For a triangle on a boundary of the mesh, some or all of the neighboring */
+/* triangles may not be present. For a triangle in the interior of the */
+/* mesh, often no neighboring subsegments are present. Such absent */
+/* triangles and subsegments are never represented by NULL pointers; they */
+/* are represented by two special records: `dummytri', the triangle that */
+/* fills "outer space", and `dummysub', the omnipresent subsegment. */
+/* `dummytri' and `dummysub' are used for several reasons; for instance, */
+/* they can be dereferenced and their contents examined without violating */
+/* protected memory. */
+/* */
+/* However, it is important to understand that a triangle includes other */
+/* information as well. The pointers to adjoining vertices, triangles, and */
+/* subsegments are ordered in a way that indicates their geometric relation */
+/* to each other. Furthermore, each of these pointers contains orientation */
+/* information. Each pointer to an adjoining triangle indicates which face */
+/* of that triangle is contacted. Similarly, each pointer to an adjoining */
+/* subsegment indicates which side of that subsegment is contacted, and how */
+/* the subsegment is oriented relative to the triangle. */
+/* */
+/* The data structure representing a subsegment may be thought to be */
+/* abutting the edge of one or two triangle data structures: either */
+/* sandwiched between two triangles, or resting against one triangle on an */
+/* exterior boundary or hole boundary. */
+/* */
+/* A subsegment consists of a list of four vertices--the vertices of the */
+/* subsegment, and the vertices of the segment it is a part of--a list of */
+/* two adjoining subsegments, and a list of two adjoining triangles. One */
+/* of the two adjoining triangles may not be present (though there should */
+/* always be one), and neighboring subsegments might not be present. */
+/* Subsegments also store a user-defined integer "boundary marker". */
+/* Typically, this integer is used to indicate what boundary conditions are */
+/* to be applied at that location in a finite element simulation. */
+/* */
+/* Like triangles, subsegments maintain information about the relative */
+/* orientation of neighboring objects. */
+/* */
+/* Vertices are relatively simple. A vertex is a list of floating-point */
+/* numbers, starting with the x, and y coordinates, followed by an */
+/* arbitrary number of optional user-defined floating-point attributes, */
+/* followed by an integer boundary marker. During the segment insertion */
+/* phase, there is also a pointer from each vertex to a triangle that may */
+/* contain it. Each pointer is not always correct, but when one is, it */
+/* speeds up segment insertion. These pointers are assigned values once */
+/* at the beginning of the segment insertion phase, and are not used or */
+/* updated except during this phase. Edge flipping during segment */
+/* insertion will render some of them incorrect. Hence, don't rely upon */
+/* them for anything. */
+/* */
+/* Other than the exception mentioned above, vertices have no information */
+/* about what triangles, subfacets, or subsegments they are linked to. */
+/* */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* */
+/* Handles */
+/* */
+/* The oriented triangle (`otri') and oriented subsegment (`osub') data */
+/* structures defined below do not themselves store any part of the mesh. */
+/* The mesh itself is made of `triangle's, `subseg's, and `vertex's. */
+/* */
+/* Oriented triangles and oriented subsegments will usually be referred to */
+/* as "handles." A handle is essentially a pointer into the mesh; it */
+/* allows you to "hold" one particular part of the mesh. Handles are used */
+/* to specify the regions in which one is traversing and modifying the mesh.*/
+/* A single `triangle' may be held by many handles, or none at all. (The */
+/* latter case is not a memory leak, because the triangle is still */
+/* connected to other triangles in the mesh.) */
+/* */
+/* An `otri' is a handle that holds a triangle. It holds a specific edge */
+/* of the triangle. An `osub' is a handle that holds a subsegment. It */
+/* holds either the left or right side of the subsegment. */
+/* */
+/* Navigation about the mesh is accomplished through a set of mesh */
+/* manipulation primitives, further below. Many of these primitives take */
+/* a handle and produce a new handle that holds the mesh near the first */
+/* handle. Other primitives take two handles and glue the corresponding */
+/* parts of the mesh together. The orientation of the handles is */
+/* important. For instance, when two triangles are glued together by the */
+/* bond() primitive, they are glued at the edges on which the handles lie. */
+/* */
+/* Because vertices have no information about which triangles they are */
+/* attached to, I commonly represent a vertex by use of a handle whose */
+/* origin is the vertex. A single handle can simultaneously represent a */
+/* triangle, an edge, and a vertex. */
+/* */
+/*****************************************************************************/
+
+/* The triangle data structure. Each triangle contains three pointers to */
+/* adjoining triangles, plus three pointers to vertices, plus three */
+/* pointers to subsegments (declared below; these pointers are usually */
+/* `dummysub'). It may or may not also contain user-defined attributes */
+/* and/or a floating-point "area constraint." It may also contain extra */
+/* pointers for nodes, when the user asks for high-order elements. */
+/* Because the size and structure of a `triangle' is not decided until */
+/* runtime, I haven't simply declared the type `triangle' as a struct. */
+
+typedef REAL **triangle; /* Really: typedef triangle *triangle */
+
+/* An oriented triangle: includes a pointer to a triangle and orientation. */
+/* The orientation denotes an edge of the triangle. Hence, there are */
+/* three possible orientations. By convention, each edge always points */
+/* counterclockwise about the corresponding triangle. */
+
+struct otri {
+ triangle *tri;
+ int orient; /* Ranges from 0 to 2. */
+};
+
+/* The subsegment data structure. Each subsegment contains two pointers to */
+/* adjoining subsegments, plus four pointers to vertices, plus two */
+/* pointers to adjoining triangles, plus one boundary marker, plus one */
+/* segment number. */
+
+typedef REAL **subseg; /* Really: typedef subseg *subseg */
+
+/* An oriented subsegment: includes a pointer to a subsegment and an */
+/* orientation. The orientation denotes a side of the edge. Hence, there */
+/* are two possible orientations. By convention, the edge is always */
+/* directed so that the "side" denoted is the right side of the edge. */
+
+struct osub {
+ subseg *ss;
+ int ssorient; /* Ranges from 0 to 1. */
+};
+
+/* The vertex data structure. Each vertex is actually an array of REALs. */
+/* The number of REALs is unknown until runtime. An integer boundary */
+/* marker, and sometimes a pointer to a triangle, is appended after the */
+/* REALs. */
+
+typedef REAL *vertex;
+
+/* A queue used to store encroached subsegments. Each subsegment's vertices */
+/* are stored so that we can check whether a subsegment is still the same. */
+
+struct badsubseg {
+ subseg encsubseg; /* An encroached subsegment. */
+ vertex subsegorg, subsegdest; /* Its two vertices. */
+};
+
+/* A queue used to store bad triangles. The key is the square of the cosine */
+/* of the smallest angle of the triangle. Each triangle's vertices are */
+/* stored so that one can check whether a triangle is still the same. */
+
+struct badtriang {
+ triangle poortri; /* A skinny or too-large triangle. */
+ REAL key; /* cos^2 of smallest (apical) angle. */
+ vertex triangorg, triangdest, triangapex; /* Its three vertices. */
+ struct badtriang *nexttriang; /* Pointer to next bad triangle. */
+};
+
+/* A stack of triangles flipped during the most recent vertex insertion. */
+/* The stack is used to undo the vertex insertion if the vertex encroaches */
+/* upon a subsegment. */
+
+struct flipstacker {
+ triangle flippedtri; /* A recently flipped triangle. */
+ struct flipstacker *prevflip; /* Previous flip in the stack. */
+};
+
+/* A node in a heap used to store events for the sweepline Delaunay */
+/* algorithm. Nodes do not point directly to their parents or children in */
+/* the heap. Instead, each node knows its position in the heap, and can */
+/* look up its parent and children in a separate array. The `eventptr' */
+/* points either to a `vertex' or to a triangle (in encoded format, so */
+/* that an orientation is included). In the latter case, the origin of */
+/* the oriented triangle is the apex of a "circle event" of the sweepline */
+/* algorithm. To distinguish site events from circle events, all circle */
+/* events are given an invalid (smaller than `xmin') x-coordinate `xkey'. */
+
+struct event {
+ REAL xkey, ykey; /* Coordinates of the event. */
+ VOID *eventptr; /* Can be a vertex or the location of a circle event. */
+ int heapposition; /* Marks this event's position in the heap. */
+};
+
+/* A node in the splay tree. Each node holds an oriented ghost triangle */
+/* that represents a boundary edge of the growing triangulation. When a */
+/* circle event covers two boundary edges with a triangle, so that they */
+/* are no longer boundary edges, those edges are not immediately deleted */
+/* from the tree; rather, they are lazily deleted when they are next */
+/* encountered. (Since only a random sample of boundary edges are kept */
+/* in the tree, lazy deletion is faster.) `keydest' is used to verify */
+/* that a triangle is still the same as when it entered the splay tree; if */
+/* it has been rotated (due to a circle event), it no longer represents a */
+/* boundary edge and should be deleted. */
+
+struct splaynode {
+ struct otri keyedge; /* Lprev of an edge on the front. */
+ vertex keydest; /* Used to verify that splay node is still live. */
+ struct splaynode *lchild, *rchild; /* Children in splay tree. */
+};
+
+/* A type used to allocate memory. firstblock is the first block of items. */
+/* nowblock is the block from which items are currently being allocated. */
+/* nextitem points to the next slab of free memory for an item. */
+/* deaditemstack is the head of a linked list (stack) of deallocated items */
+/* that can be recycled. unallocateditems is the number of items that */
+/* remain to be allocated from nowblock. */
+/* */
+/* Traversal is the process of walking through the entire list of items, and */
+/* is separate from allocation. Note that a traversal will visit items on */
+/* the "deaditemstack" stack as well as live items. pathblock points to */
+/* the block currently being traversed. pathitem points to the next item */
+/* to be traversed. pathitemsleft is the number of items that remain to */
+/* be traversed in pathblock. */
+/* */
+/* alignbytes determines how new records should be aligned in memory. */
+/* itembytes is the length of a record in bytes (after rounding up). */
+/* itemsperblock is the number of items allocated at once in a single */
+/* block. itemsfirstblock is the number of items in the first block, */
+/* which can vary from the others. items is the number of currently */
+/* allocated items. maxitems is the maximum number of items that have */
+/* been allocated at once; it is the current number of items plus the */
+/* number of records kept on deaditemstack. */
+
+struct memorypool {
+ VOID **firstblock, **nowblock;
+ VOID *nextitem;
+ VOID *deaditemstack;
+ VOID **pathblock;
+ VOID *pathitem;
+ int alignbytes;
+ int itembytes;
+ int itemsperblock;
+ int itemsfirstblock;
+ long items, maxitems;
+ int unallocateditems;
+ int pathitemsleft;
+};
+
+
+/* Global constants. */
+
+REAL splitter; /* Used to split REAL factors for exact multiplication. */
+REAL epsilon; /* Floating-point machine epsilon. */
+REAL resulterrbound;
+REAL ccwerrboundA, ccwerrboundB, ccwerrboundC;
+REAL iccerrboundA, iccerrboundB, iccerrboundC;
+REAL o3derrboundA, o3derrboundB, o3derrboundC;
+
+/* Random number seed is not constant, but I've made it global anyway. */
+
+unsigned long randomseed; /* Current random number seed. */
+
+
+/* Mesh data structure. Triangle operates on only one mesh, but the mesh */
+/* structure is used (instead of global variables) to allow reentrancy. */
+
+struct mesh {
+
+/* Variables used to allocate memory for triangles, subsegments, vertices, */
+/* viri (triangles being eaten), encroached segments, bad (skinny or too */
+/* large) triangles, and splay tree nodes. */
+
+ struct memorypool triangles;
+ struct memorypool subsegs;
+ struct memorypool vertices;
+ struct memorypool viri;
+ struct memorypool badsubsegs;
+ struct memorypool badtriangles;
+ struct memorypool flipstackers;
+ struct memorypool splaynodes;
+
+/* Variables that maintain the bad triangle queues. The queues are */
+/* ordered from 4095 (highest priority) to 0 (lowest priority). */
+
+ struct badtriang *queuefront[4096];
+ struct badtriang *queuetail[4096];
+ int nextnonemptyq[4096];
+ int firstnonemptyq;
+
+/* Variable that maintains the stack of recently flipped triangles. */
+
+ struct flipstacker *lastflip;
+
+/* Other variables. */
+
+ REAL xmin, xmax, ymin, ymax; /* x and y bounds. */
+ REAL xminextreme; /* Nonexistent x value used as a flag in sweepline. */
+ int invertices; /* Number of input vertices. */
+ int inelements; /* Number of input triangles. */
+ int insegments; /* Number of input segments. */
+ int holes; /* Number of input holes. */
+ int regions; /* Number of input regions. */
+ int undeads; /* Number of input vertices that don't appear in the mesh. */
+ long edges; /* Number of output edges. */
+ int mesh_dim; /* Dimension (ought to be 2). */
+ int nextras; /* Number of attributes per vertex. */
+ int eextras; /* Number of attributes per triangle. */
+ long hullsize; /* Number of edges in convex hull. */
+ int steinerleft; /* Number of Steiner points not yet used. */
+ int vertexmarkindex; /* Index to find boundary marker of a vertex. */
+ int vertex2triindex; /* Index to find a triangle adjacent to a vertex. */
+ int highorderindex; /* Index to find extra nodes for high-order elements. */
+ int elemattribindex; /* Index to find attributes of a triangle. */
+ int areaboundindex; /* Index to find area bound of a triangle. */
+ int checksegments; /* Are there segments in the triangulation yet? */
+ int checkquality; /* Has quality triangulation begun yet? */
+ int readnodefile; /* Has a .node file been read? */
+ long samples; /* Number of random samples for point location. */
+
+ long incirclecount; /* Number of incircle tests performed. */
+ long counterclockcount; /* Number of counterclockwise tests performed. */
+ long orient3dcount; /* Number of 3D orientation tests performed. */
+ long hyperbolacount; /* Number of right-of-hyperbola tests performed. */
+ long circumcentercount; /* Number of circumcenter calculations performed. */
+ long circletopcount; /* Number of circle top calculations performed. */
+
+/* Triangular bounding box vertices. */
+
+ vertex infvertex1, infvertex2, infvertex3;
+
+/* Pointer to the `triangle' that occupies all of "outer space." */
+
+ triangle *dummytri;
+ triangle *dummytribase; /* Keep base address so we can free() it later. */
+
+/* Pointer to the omnipresent subsegment. Referenced by any triangle or */
+/* subsegment that isn't really connected to a subsegment at that */
+/* location. */
+
+ subseg *dummysub;
+ subseg *dummysubbase; /* Keep base address so we can free() it later. */
+
+/* Pointer to a recently visited triangle. Improves point location if */
+/* proximate vertices are inserted sequentially. */
+
+ struct otri recenttri;
+
+}; /* End of `struct mesh'. */
+
+
+/* Data structure for command line switches and file names. This structure */
+/* is used (instead of global variables) to allow reentrancy. */
+
+struct behavior {
+
+/* Switches for the triangulator. */
+/* poly: -p switch. refine: -r switch. */
+/* quality: -q switch. */
+/* minangle: minimum angle bound, specified after -q switch. */
+/* goodangle: cosine squared of minangle. */
+/* offconstant: constant used to place off-center Steiner points. */
+/* vararea: -a switch without number. */
+/* fixedarea: -a switch with number. */
+/* maxarea: maximum area bound, specified after -a switch. */
+/* usertest: -u switch. */
+/* regionattrib: -A switch. convex: -c switch. */
+/* weighted: 1 for -w switch, 2 for -W switch. jettison: -j switch */
+/* firstnumber: inverse of -z switch. All items are numbered starting */
+/* from `firstnumber'. */
+/* edgesout: -e switch. voronoi: -v switch. */
+/* neighbors: -n switch. geomview: -g switch. */
+/* nobound: -B switch. nopolywritten: -P switch. */
+/* nonodewritten: -N switch. noelewritten: -E switch. */
+/* noiterationnum: -I switch. noholes: -O switch. */
+/* noexact: -X switch. */
+/* order: element order, specified after -o switch. */
+/* nobisect: count of how often -Y switch is selected. */
+/* steiner: maximum number of Steiner points, specified after -S switch. */
+/* incremental: -i switch. sweepline: -F switch. */
+/* dwyer: inverse of -l switch. */
+/* splitseg: -s switch. */
+/* conformdel: -D switch. docheck: -C switch. */
+/* quiet: -Q switch. verbose: count of how often -V switch is selected. */
+/* usesegments: -p, -r, -q, or -c switch; determines whether segments are */
+/* used at all. */
+/* */
+/* Read the instructions to find out the meaning of these switches. */
+
+ int poly, refine, quality, vararea, fixedarea, usertest;
+ int regionattrib, convex, weighted, jettison;
+ int firstnumber;
+ int edgesout, voronoi, neighbors, geomview;
+ int nobound, nopolywritten, nonodewritten, noelewritten, noiterationnum;
+ int noholes, noexact, conformdel;
+ int incremental, sweepline, dwyer;
+ int splitseg;
+ int docheck;
+ int quiet, verbose;
+ int usesegments;
+ int order;
+ int nobisect;
+ int steiner;
+ REAL minangle, goodangle, offconstant;
+ REAL maxarea;
+
+/* Variables for file names. */
+
+#ifndef TRILIBRARY
+ char innodefilename[FILENAMESIZE];
+ char inelefilename[FILENAMESIZE];
+ char inpolyfilename[FILENAMESIZE];
+ char areafilename[FILENAMESIZE];
+ char outnodefilename[FILENAMESIZE];
+ char outelefilename[FILENAMESIZE];
+ char outpolyfilename[FILENAMESIZE];
+ char edgefilename[FILENAMESIZE];
+ char vnodefilename[FILENAMESIZE];
+ char vedgefilename[FILENAMESIZE];
+ char neighborfilename[FILENAMESIZE];
+ char offfilename[FILENAMESIZE];
+#endif /* not TRILIBRARY */
+
+}; /* End of `struct behavior'. */
+
+
+/*****************************************************************************/
+/* */
+/* Mesh manipulation primitives. Each triangle contains three pointers to */
+/* other triangles, with orientations. Each pointer points not to the */
+/* first byte of a triangle, but to one of the first three bytes of a */
+/* triangle. It is necessary to extract both the triangle itself and the */
+/* orientation. To save memory, I keep both pieces of information in one */
+/* pointer. To make this possible, I assume that all triangles are aligned */
+/* to four-byte boundaries. The decode() routine below decodes a pointer, */
+/* extracting an orientation (in the range 0 to 2) and a pointer to the */
+/* beginning of a triangle. The encode() routine compresses a pointer to a */
+/* triangle and an orientation into a single pointer. My assumptions that */
+/* triangles are four-byte-aligned and that the `unsigned long' type is */
+/* long enough to hold a pointer are two of the few kludges in this program.*/
+/* */
+/* Subsegments are manipulated similarly. A pointer to a subsegment */
+/* carries both an address and an orientation in the range 0 to 1. */
+/* */
+/* The other primitives take an oriented triangle or oriented subsegment, */
+/* and return an oriented triangle or oriented subsegment or vertex; or */
+/* they change the connections in the data structure. */
+/* */
+/* Below, triangles and subsegments are denoted by their vertices. The */
+/* triangle abc has origin (org) a, destination (dest) b, and apex (apex) */
+/* c. These vertices occur in counterclockwise order about the triangle. */
+/* The handle abc may simultaneously denote vertex a, edge ab, and triangle */
+/* abc. */
+/* */
+/* Similarly, the subsegment ab has origin (sorg) a and destination (sdest) */
+/* b. If ab is thought to be directed upward (with b directly above a), */
+/* then the handle ab is thought to grasp the right side of ab, and may */
+/* simultaneously denote vertex a and edge ab. */
+/* */
+/* An asterisk (*) denotes a vertex whose identity is unknown. */
+/* */
+/* Given this notation, a partial list of mesh manipulation primitives */
+/* follows. */
+/* */
+/* */
+/* For triangles: */
+/* */
+/* sym: Find the abutting triangle; same edge. */
+/* sym(abc) -> ba* */
+/* */
+/* lnext: Find the next edge (counterclockwise) of a triangle. */
+/* lnext(abc) -> bca */
+/* */
+/* lprev: Find the previous edge (clockwise) of a triangle. */
+/* lprev(abc) -> cab */
+/* */
+/* onext: Find the next edge counterclockwise with the same origin. */
+/* onext(abc) -> ac* */
+/* */
+/* oprev: Find the next edge clockwise with the same origin. */
+/* oprev(abc) -> a*b */
+/* */
+/* dnext: Find the next edge counterclockwise with the same destination. */
+/* dnext(abc) -> *ba */
+/* */
+/* dprev: Find the next edge clockwise with the same destination. */
+/* dprev(abc) -> cb* */
+/* */
+/* rnext: Find the next edge (counterclockwise) of the adjacent triangle. */
+/* rnext(abc) -> *a* */
+/* */
+/* rprev: Find the previous edge (clockwise) of the adjacent triangle. */
+/* rprev(abc) -> b** */
+/* */
+/* org: Origin dest: Destination apex: Apex */
+/* org(abc) -> a dest(abc) -> b apex(abc) -> c */
+/* */
+/* bond: Bond two triangles together at the resepective handles. */
+/* bond(abc, bad) */
+/* */
+/* */
+/* For subsegments: */
+/* */
+/* ssym: Reverse the orientation of a subsegment. */
+/* ssym(ab) -> ba */
+/* */
+/* spivot: Find adjoining subsegment with the same origin. */
+/* spivot(ab) -> a* */
+/* */
+/* snext: Find next subsegment in sequence. */
+/* snext(ab) -> b* */
+/* */
+/* sorg: Origin sdest: Destination */
+/* sorg(ab) -> a sdest(ab) -> b */
+/* */
+/* sbond: Bond two subsegments together at the respective origins. */
+/* sbond(ab, ac) */
+/* */
+/* */
+/* For interacting tetrahedra and subfacets: */
+/* */
+/* tspivot: Find a subsegment abutting a triangle. */
+/* tspivot(abc) -> ba */
+/* */
+/* stpivot: Find a triangle abutting a subsegment. */
+/* stpivot(ab) -> ba* */
+/* */
+/* tsbond: Bond a triangle to a subsegment. */
+/* tsbond(abc, ba) */
+/* */
+/*****************************************************************************/
+
+/********* Mesh manipulation primitives begin here *********/
+/** **/
+/** **/
+
+/* Fast lookup arrays to speed some of the mesh manipulation primitives. */
+
+int plus1mod3[3] = {1, 2, 0};
+int minus1mod3[3] = {2, 0, 1};
+
+/********* Primitives for triangles *********/
+/* */
+/* */
+
+/* decode() converts a pointer to an oriented triangle. The orientation is */
+/* extracted from the two least significant bits of the pointer. */
+
+#define decode(ptr, otri) \
+ (otri).orient = (int) ((unsigned long) (ptr) & (unsigned long) 3l); \
+ (otri).tri = (triangle *) \
+ ((unsigned long) (ptr) ^ (unsigned long) (otri).orient)
+
+/* encode() compresses an oriented triangle into a single pointer. It */
+/* relies on the assumption that all triangles are aligned to four-byte */
+/* boundaries, so the two least significant bits of (otri).tri are zero. */
+
+#define encode(otri) \
+ (triangle) ((unsigned long) (otri).tri | (unsigned long) (otri).orient)
+
+/* The following handle manipulation primitives are all described by Guibas */
+/* and Stolfi. However, Guibas and Stolfi use an edge-based data */
+/* structure, whereas I use a triangle-based data structure. */
+
+/* sym() finds the abutting triangle, on the same edge. Note that the edge */
+/* direction is necessarily reversed, because the handle specified by an */
+/* oriented triangle is directed counterclockwise around the triangle. */
+
+#define sym(otri1, otri2) \
+ ptr = (otri1).tri[(otri1).orient]; \
+ decode(ptr, otri2);
+
+#define symself(otri) \
+ ptr = (otri).tri[(otri).orient]; \
+ decode(ptr, otri);
+
+/* lnext() finds the next edge (counterclockwise) of a triangle. */
+
+#define lnext(otri1, otri2) \
+ (otri2).tri = (otri1).tri; \
+ (otri2).orient = plus1mod3[(otri1).orient]
+
+#define lnextself(otri) \
+ (otri).orient = plus1mod3[(otri).orient]
+
+/* lprev() finds the previous edge (clockwise) of a triangle. */
+
+#define lprev(otri1, otri2) \
+ (otri2).tri = (otri1).tri; \
+ (otri2).orient = minus1mod3[(otri1).orient]
+
+#define lprevself(otri) \
+ (otri).orient = minus1mod3[(otri).orient]
+
+/* onext() spins counterclockwise around a vertex; that is, it finds the */
+/* next edge with the same origin in the counterclockwise direction. This */
+/* edge is part of a different triangle. */
+
+#define onext(otri1, otri2) \
+ lprev(otri1, otri2); \
+ symself(otri2);
+
+#define onextself(otri) \
+ lprevself(otri); \
+ symself(otri);
+
+/* oprev() spins clockwise around a vertex; that is, it finds the next edge */
+/* with the same origin in the clockwise direction. This edge is part of */
+/* a different triangle. */
+
+#define oprev(otri1, otri2) \
+ sym(otri1, otri2); \
+ lnextself(otri2);
+
+#define oprevself(otri) \
+ symself(otri); \
+ lnextself(otri);
+
+/* dnext() spins counterclockwise around a vertex; that is, it finds the */
+/* next edge with the same destination in the counterclockwise direction. */
+/* This edge is part of a different triangle. */
+
+#define dnext(otri1, otri2) \
+ sym(otri1, otri2); \
+ lprevself(otri2);
+
+#define dnextself(otri) \
+ symself(otri); \
+ lprevself(otri);
+
+/* dprev() spins clockwise around a vertex; that is, it finds the next edge */
+/* with the same destination in the clockwise direction. This edge is */
+/* part of a different triangle. */
+
+#define dprev(otri1, otri2) \
+ lnext(otri1, otri2); \
+ symself(otri2);
+
+#define dprevself(otri) \
+ lnextself(otri); \
+ symself(otri);
+
+/* rnext() moves one edge counterclockwise about the adjacent triangle. */
+/* (It's best understood by reading Guibas and Stolfi. It involves */
+/* changing triangles twice.) */
+
+#define rnext(otri1, otri2) \
+ sym(otri1, otri2); \
+ lnextself(otri2); \
+ symself(otri2);
+
+#define rnextself(otri) \
+ symself(otri); \
+ lnextself(otri); \
+ symself(otri);
+
+/* rprev() moves one edge clockwise about the adjacent triangle. */
+/* (It's best understood by reading Guibas and Stolfi. It involves */
+/* changing triangles twice.) */
+
+#define rprev(otri1, otri2) \
+ sym(otri1, otri2); \
+ lprevself(otri2); \
+ symself(otri2);
+
+#define rprevself(otri) \
+ symself(otri); \
+ lprevself(otri); \
+ symself(otri);
+
+/* These primitives determine or set the origin, destination, or apex of a */
+/* triangle. */
+
+#define org(otri, vertexptr) \
+ vertexptr = (vertex) (otri).tri[plus1mod3[(otri).orient] + 3]
+
+#define dest(otri, vertexptr) \
+ vertexptr = (vertex) (otri).tri[minus1mod3[(otri).orient] + 3]
+
+#define apex(otri, vertexptr) \
+ vertexptr = (vertex) (otri).tri[(otri).orient + 3]
+
+#define setorg(otri, vertexptr) \
+ (otri).tri[plus1mod3[(otri).orient] + 3] = (triangle) vertexptr
+
+#define setdest(otri, vertexptr) \
+ (otri).tri[minus1mod3[(otri).orient] + 3] = (triangle) vertexptr
+
+#define setapex(otri, vertexptr) \
+ (otri).tri[(otri).orient + 3] = (triangle) vertexptr
+
+/* Bond two triangles together. */
+
+#define bond(otri1, otri2) \
+ (otri1).tri[(otri1).orient] = encode(otri2); \
+ (otri2).tri[(otri2).orient] = encode(otri1)
+
+/* Dissolve a bond (from one side). Note that the other triangle will still */
+/* think it's connected to this triangle. Usually, however, the other */
+/* triangle is being deleted entirely, or bonded to another triangle, so */
+/* it doesn't matter. */
+
+#define dissolve(otri) \
+ (otri).tri[(otri).orient] = (triangle) m->dummytri
+
+/* Copy an oriented triangle. */
+
+#define otricopy(otri1, otri2) \
+ (otri2).tri = (otri1).tri; \
+ (otri2).orient = (otri1).orient
+
+/* Test for equality of oriented triangles. */
+
+#define otriequal(otri1, otri2) \
+ (((otri1).tri == (otri2).tri) && \
+ ((otri1).orient == (otri2).orient))
+
+/* Primitives to infect or cure a triangle with the virus. These rely on */
+/* the assumption that all subsegments are aligned to four-byte boundaries.*/
+
+#define infect(otri) \
+ (otri).tri[6] = (triangle) \
+ ((unsigned long) (otri).tri[6] | (unsigned long) 2l)
+
+#define uninfect(otri) \
+ (otri).tri[6] = (triangle) \
+ ((unsigned long) (otri).tri[6] & ~ (unsigned long) 2l)
+
+/* Test a triangle for viral infection. */
+
+#define infected(otri) \
+ (((unsigned long) (otri).tri[6] & (unsigned long) 2l) != 0l)
+
+/* Check or set a triangle's attributes. */
+
+#define elemattribute(otri, attnum) \
+ ((REAL *) (otri).tri)[m->elemattribindex + (attnum)]
+
+#define setelemattribute(otri, attnum, value) \
+ ((REAL *) (otri).tri)[m->elemattribindex + (attnum)] = value
+
+/* Check or set a triangle's maximum area bound. */
+
+#define areabound(otri) ((REAL *) (otri).tri)[m->areaboundindex]
+
+#define setareabound(otri, value) \
+ ((REAL *) (otri).tri)[m->areaboundindex] = value
+
+/* Check or set a triangle's deallocation. Its second pointer is set to */
+/* NULL to indicate that it is not allocated. (Its first pointer is used */
+/* for the stack of dead items.) Its fourth pointer (its first vertex) */
+/* is set to NULL in case a `badtriang' structure points to it. */
+
+#define deadtri(tria) ((tria)[1] == (triangle) NULL)
+
+#define killtri(tria) \
+ (tria)[1] = (triangle) NULL; \
+ (tria)[3] = (triangle) NULL
+
+/********* Primitives for subsegments *********/
+/* */
+/* */
+
+/* sdecode() converts a pointer to an oriented subsegment. The orientation */
+/* is extracted from the least significant bit of the pointer. The two */
+/* least significant bits (one for orientation, one for viral infection) */
+/* are masked out to produce the real pointer. */
+
+#define sdecode(sptr, osub) \
+ (osub).ssorient = (int) ((unsigned long) (sptr) & (unsigned long) 1l); \
+ (osub).ss = (subseg *) \
+ ((unsigned long) (sptr) & ~ (unsigned long) 3l)
+
+/* sencode() compresses an oriented subsegment into a single pointer. It */
+/* relies on the assumption that all subsegments are aligned to two-byte */
+/* boundaries, so the least significant bit of (osub).ss is zero. */
+
+#define sencode(osub) \
+ (subseg) ((unsigned long) (osub).ss | (unsigned long) (osub).ssorient)
+
+/* ssym() toggles the orientation of a subsegment. */
+
+#define ssym(osub1, osub2) \
+ (osub2).ss = (osub1).ss; \
+ (osub2).ssorient = 1 - (osub1).ssorient
+
+#define ssymself(osub) \
+ (osub).ssorient = 1 - (osub).ssorient
+
+/* spivot() finds the other subsegment (from the same segment) that shares */
+/* the same origin. */
+
+#define spivot(osub1, osub2) \
+ sptr = (osub1).ss[(osub1).ssorient]; \
+ sdecode(sptr, osub2)
+
+#define spivotself(osub) \
+ sptr = (osub).ss[(osub).ssorient]; \
+ sdecode(sptr, osub)
+
+/* snext() finds the next subsegment (from the same segment) in sequence; */
+/* one whose origin is the input subsegment's destination. */
+
+#define snext(osub1, osub2) \
+ sptr = (osub1).ss[1 - (osub1).ssorient]; \
+ sdecode(sptr, osub2)
+
+#define snextself(osub) \
+ sptr = (osub).ss[1 - (osub).ssorient]; \
+ sdecode(sptr, osub)
+
+/* These primitives determine or set the origin or destination of a */
+/* subsegment or the segment that includes it. */
+
+#define sorg(osub, vertexptr) \
+ vertexptr = (vertex) (osub).ss[2 + (osub).ssorient]
+
+#define sdest(osub, vertexptr) \
+ vertexptr = (vertex) (osub).ss[3 - (osub).ssorient]
+
+#define setsorg(osub, vertexptr) \
+ (osub).ss[2 + (osub).ssorient] = (subseg) vertexptr
+
+#define setsdest(osub, vertexptr) \
+ (osub).ss[3 - (osub).ssorient] = (subseg) vertexptr
+
+#define segorg(osub, vertexptr) \
+ vertexptr = (vertex) (osub).ss[4 + (osub).ssorient]
+
+#define segdest(osub, vertexptr) \
+ vertexptr = (vertex) (osub).ss[5 - (osub).ssorient]
+
+#define setsegorg(osub, vertexptr) \
+ (osub).ss[4 + (osub).ssorient] = (subseg) vertexptr
+
+#define setsegdest(osub, vertexptr) \
+ (osub).ss[5 - (osub).ssorient] = (subseg) vertexptr
+
+/* These primitives read or set a boundary marker. Boundary markers are */
+/* used to hold user-defined tags for setting boundary conditions in */
+/* finite element solvers. */
+
+#define mark(osub) (* (int *) ((osub).ss + 8))
+
+#define setmark(osub, value) \
+ * (int *) ((osub).ss + 8) = value
+
+/* Bond two subsegments together. */
+
+#define sbond(osub1, osub2) \
+ (osub1).ss[(osub1).ssorient] = sencode(osub2); \
+ (osub2).ss[(osub2).ssorient] = sencode(osub1)
+
+/* Dissolve a subsegment bond (from one side). Note that the other */
+/* subsegment will still think it's connected to this subsegment. */
+
+#define sdissolve(osub) \
+ (osub).ss[(osub).ssorient] = (subseg) m->dummysub
+
+/* Copy a subsegment. */
+
+#define subsegcopy(osub1, osub2) \
+ (osub2).ss = (osub1).ss; \
+ (osub2).ssorient = (osub1).ssorient
+
+/* Test for equality of subsegments. */
+
+#define subsegequal(osub1, osub2) \
+ (((osub1).ss == (osub2).ss) && \
+ ((osub1).ssorient == (osub2).ssorient))
+
+/* Check or set a subsegment's deallocation. Its second pointer is set to */
+/* NULL to indicate that it is not allocated. (Its first pointer is used */
+/* for the stack of dead items.) Its third pointer (its first vertex) */
+/* is set to NULL in case a `badsubseg' structure points to it. */
+
+#define deadsubseg(sub) ((sub)[1] == (subseg) NULL)
+
+#define killsubseg(sub) \
+ (sub)[1] = (subseg) NULL; \
+ (sub)[2] = (subseg) NULL
+
+/********* Primitives for interacting triangles and subsegments *********/
+/* */
+/* */
+
+/* tspivot() finds a subsegment abutting a triangle. */
+
+#define tspivot(otri, osub) \
+ sptr = (subseg) (otri).tri[6 + (otri).orient]; \
+ sdecode(sptr, osub)
+
+/* stpivot() finds a triangle abutting a subsegment. It requires that the */
+/* variable `ptr' of type `triangle' be defined. */
+
+#define stpivot(osub, otri) \
+ ptr = (triangle) (osub).ss[6 + (osub).ssorient]; \
+ decode(ptr, otri)
+
+/* Bond a triangle to a subsegment. */
+
+#define tsbond(otri, osub) \
+ (otri).tri[6 + (otri).orient] = (triangle) sencode(osub); \
+ (osub).ss[6 + (osub).ssorient] = (subseg) encode(otri)
+
+/* Dissolve a bond (from the triangle side). */
+
+#define tsdissolve(otri) \
+ (otri).tri[6 + (otri).orient] = (triangle) m->dummysub
+
+/* Dissolve a bond (from the subsegment side). */
+
+#define stdissolve(osub) \
+ (osub).ss[6 + (osub).ssorient] = (subseg) m->dummytri
+
+/********* Primitives for vertices *********/
+/* */
+/* */
+
+#define vertexmark(vx) ((int *) (vx))[m->vertexmarkindex]
+
+#define setvertexmark(vx, value) \
+ ((int *) (vx))[m->vertexmarkindex] = value
+
+#define vertextype(vx) ((int *) (vx))[m->vertexmarkindex + 1]
+
+#define setvertextype(vx, value) \
+ ((int *) (vx))[m->vertexmarkindex + 1] = value
+
+#define vertex2tri(vx) ((triangle *) (vx))[m->vertex2triindex]
+
+#define setvertex2tri(vx, value) \
+ ((triangle *) (vx))[m->vertex2triindex] = value
+
+/** **/
+/** **/
+/********* Mesh manipulation primitives end here *********/
+
+/********* User-defined triangle evaluation routine begins here *********/
+/** **/
+/** **/
+
+/*****************************************************************************/
+/* */
+/* triunsuitable() Determine if a triangle is unsuitable, and thus must */
+/* be further refined. */
+/* */
+/* You may write your own procedure that decides whether or not a selected */
+/* triangle is too big (and needs to be refined). There are two ways to do */
+/* this. */
+/* */
+/* (1) Modify the procedure `triunsuitable' below, then recompile */
+/* Triangle. */
+/* */
+/* (2) Define the symbol EXTERNAL_TEST (either by adding the definition */
+/* to this file, or by using the appropriate compiler switch). This way, */
+/* you can compile triangle.c separately from your test. Write your own */
+/* `triunsuitable' procedure in a separate C file (using the same prototype */
+/* as below). Compile it and link the object code with triangle.o. */
+/* */
+/* This procedure returns 1 if the triangle is too large and should be */
+/* refined; 0 otherwise. */
+/* */
+/*****************************************************************************/
+
+#ifdef EXTERNAL_TEST
+
+int triunsuitable();
+
+#else /* not EXTERNAL_TEST */
+
+#ifdef ANSI_DECLARATORS
+int triunsuitable(vertex triorg, vertex tridest, vertex triapex, REAL area)
+#else /* not ANSI_DECLARATORS */
+int triunsuitable(triorg, tridest, triapex, area)
+vertex triorg; /* The triangle's origin vertex. */
+vertex tridest; /* The triangle's destination vertex. */
+vertex triapex; /* The triangle's apex vertex. */
+REAL area; /* The area of the triangle. */
+#endif /* not ANSI_DECLARATORS */
+
+{
+ REAL dxoa, dxda, dxod;
+ REAL dyoa, dyda, dyod;
+ REAL oalen, dalen, odlen;
+ REAL maxlen;
+
+ dxoa = triorg[0] - triapex[0];
+ dyoa = triorg[1] - triapex[1];
+ dxda = tridest[0] - triapex[0];
+ dyda = tridest[1] - triapex[1];
+ dxod = triorg[0] - tridest[0];
+ dyod = triorg[1] - tridest[1];
+ /* Find the squares of the lengths of the triangle's three edges. */
+ oalen = dxoa * dxoa + dyoa * dyoa;
+ dalen = dxda * dxda + dyda * dyda;
+ odlen = dxod * dxod + dyod * dyod;
+ /* Find the square of the length of the longest edge. */
+ maxlen = (dalen > oalen) ? dalen : oalen;
+ maxlen = (odlen > maxlen) ? odlen : maxlen;
+
+ if (maxlen > 0.05 * (triorg[0] * triorg[0] + triorg[1] * triorg[1]) + 0.02) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+#endif /* not EXTERNAL_TEST */
+
+/** **/
+/** **/
+/********* User-defined triangle evaluation routine ends here *********/
+
+/********* Memory allocation and program exit wrappers begin here *********/
+/** **/
+/** **/
+
+#ifdef ANSI_DECLARATORS
+void triexit(int status)
+#else /* not ANSI_DECLARATORS */
+void triexit(status)
+int status;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ printf("Exit %d.\n", status);
+
+ exit(status);
+}
+
+#ifdef ANSI_DECLARATORS
+VOID *trimalloc(int size)
+#else /* not ANSI_DECLARATORS */
+VOID *trimalloc(size)
+int size;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ VOID *memptr;
+
+ memptr = (VOID *) malloc((unsigned int) size);
+ if (memptr == (VOID *) NULL) {
+ printf("Error: Out of memory.\n");
+ triexit(1);
+ }
+ return(memptr);
+}
+
+#ifdef ANSI_DECLARATORS
+void trifree(VOID *memptr)
+#else /* not ANSI_DECLARATORS */
+void trifree(memptr)
+VOID *memptr;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ free(memptr);
+}
+
+/** **/
+/** **/
+/********* Memory allocation and program exit wrappers end here *********/
+
+/********* User interaction routines begin here *********/
+/** **/
+/** **/
+
+/*****************************************************************************/
+/* */
+/* syntax() Print list of command line switches. */
+/* */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+void syntax()
+{
+#ifdef CDT_ONLY
+#ifdef REDUCED
+ printf("triangle [-pAcjevngBPNEIOXzo_lQVh] input_file\n");
+#else /* not REDUCED */
+ printf("triangle [-pAcjevngBPNEIOXzo_iFlCQVh] input_file\n");
+#endif /* not REDUCED */
+#else /* not CDT_ONLY */
+#ifdef REDUCED
+ printf("triangle [-prq__a__uAcDjevngBPNEIOXzo_YS__lQVh] input_file\n");
+#else /* not REDUCED */
+ printf("triangle [-prq__a__uAcDjevngBPNEIOXzo_YS__iFlsCQVh] input_file\n");
+#endif /* not REDUCED */
+#endif /* not CDT_ONLY */
+
+ printf(" -p Triangulates a Planar Straight Line Graph (.poly file).\n");
+#ifndef CDT_ONLY
+ printf(" -r Refines a previously generated mesh.\n");
+ printf(
+ " -q Quality mesh generation. A minimum angle may be specified.\n");
+ printf(" -a Applies a maximum triangle area constraint.\n");
+ printf(" -u Applies a user-defined triangle constraint.\n");
+#endif /* not CDT_ONLY */
+ printf(
+ " -A Applies attributes to identify triangles in certain regions.\n");
+ printf(" -c Encloses the convex hull with segments.\n");
+#ifndef CDT_ONLY
+ printf(" -D Conforming Delaunay: all triangles are truly Delaunay.\n");
+#endif /* not CDT_ONLY */
+/*
+ printf(" -w Weighted Delaunay triangulation.\n");
+ printf(" -W Regular triangulation (lower hull of a height field).\n");
+*/
+ printf(" -j Jettison unused vertices from output .node file.\n");
+ printf(" -e Generates an edge list.\n");
+ printf(" -v Generates a Voronoi diagram.\n");
+ printf(" -n Generates a list of triangle neighbors.\n");
+ printf(" -g Generates an .off file for Geomview.\n");
+ printf(" -B Suppresses output of boundary information.\n");
+ printf(" -P Suppresses output of .poly file.\n");
+ printf(" -N Suppresses output of .node file.\n");
+ printf(" -E Suppresses output of .ele file.\n");
+ printf(" -I Suppresses mesh iteration numbers.\n");
+ printf(" -O Ignores holes in .poly file.\n");
+ printf(" -X Suppresses use of exact arithmetic.\n");
+ printf(" -z Numbers all items starting from zero (rather than one).\n");
+ printf(" -o2 Generates second-order subparametric elements.\n");
+#ifndef CDT_ONLY
+ printf(" -Y Suppresses boundary segment splitting.\n");
+ printf(" -S Specifies maximum number of added Steiner points.\n");
+#endif /* not CDT_ONLY */
+#ifndef REDUCED
+ printf(" -i Uses incremental method, rather than divide-and-conquer.\n");
+ printf(" -F Uses Fortune's sweepline algorithm, rather than d-and-c.\n");
+#endif /* not REDUCED */
+ printf(" -l Uses vertical cuts only, rather than alternating cuts.\n");
+#ifndef REDUCED
+#ifndef CDT_ONLY
+ printf(
+ " -s Force segments into mesh by splitting (instead of using CDT).\n");
+#endif /* not CDT_ONLY */
+ printf(" -C Check consistency of final mesh.\n");
+#endif /* not REDUCED */
+ printf(" -Q Quiet: No terminal output except errors.\n");
+ printf(" -V Verbose: Detailed information on what I'm doing.\n");
+ printf(" -h Help: Detailed instructions for Triangle.\n");
+ triexit(0);
+}
+
+#endif /* not TRILIBRARY */
+
+
+
+/*****************************************************************************/
+/* */
+/* internalerror() Ask the user to send me the defective product. Exit. */
+/* */
+/*****************************************************************************/
+
+
+int error_set = 0;
+void internalerror()
+{
+ error_set = 1;
+ //printf(" Please report this bug to jrs@cs.berkeley.edu\n");
+ ///printf(" Include the message above, your input data set, and the exact\n");
+ //printf(" command line you used to run Triangle.\n");
+ //triexit(1);
+}
+
+/*****************************************************************************/
+/* */
+/* parsecommandline() Read the command line, identify switches, and set */
+/* up options and file names. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void parsecommandline(int argc, char **argv, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void parsecommandline(argc, argv, b)
+int argc;
+char **argv;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ error_set = 0;
+
+#ifdef TRILIBRARY
+#define STARTINDEX 0
+#else /* not TRILIBRARY */
+#define STARTINDEX 1
+ int increment;
+ int meshnumber;
+#endif /* not TRILIBRARY */
+ int i, j, k;
+ char workstring[FILENAMESIZE];
+
+ b->poly = b->refine = b->quality = 0;
+ b->vararea = b->fixedarea = b->usertest = 0;
+ b->regionattrib = b->convex = b->weighted = b->jettison = 0;
+ b->firstnumber = 1;
+ b->edgesout = b->voronoi = b->neighbors = b->geomview = 0;
+ b->nobound = b->nopolywritten = b->nonodewritten = b->noelewritten = 0;
+ b->noiterationnum = 0;
+ b->noholes = b->noexact = 0;
+ b->incremental = b->sweepline = 0;
+ b->dwyer = 1;
+ b->splitseg = 0;
+ b->docheck = 0;
+ b->nobisect = 0;
+ b->conformdel = 0;
+ b->steiner = -1;
+ b->order = 1;
+ b->minangle = 0.0;
+ b->maxarea = -1.0;
+ b->quiet = b->verbose = 0;
+#ifndef TRILIBRARY
+ b->innodefilename[0] = '\0';
+#endif /* not TRILIBRARY */
+
+ for (i = STARTINDEX; i < argc; i++) {
+#ifndef TRILIBRARY
+ if (argv[i][0] == '-') {
+#endif /* not TRILIBRARY */
+ for (j = STARTINDEX; argv[i][j] != '\0'; j++) {
+ if (argv[i][j] == 'p') {
+ b->poly = 1;
+ }
+#ifndef CDT_ONLY
+ if (argv[i][j] == 'r') {
+ b->refine = 1;
+ }
+ if (argv[i][j] == 'q') {
+ b->quality = 1;
+ if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+ (argv[i][j + 1] == '.')) {
+ k = 0;
+ while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+ (argv[i][j + 1] == '.')) {
+ j++;
+ workstring[k] = argv[i][j];
+ k++;
+ }
+ workstring[k] = '\0';
+ b->minangle = (REAL) strtod(workstring, (char **) NULL);
+ } else {
+ b->minangle = 20.0;
+ }
+ }
+ if (argv[i][j] == 'a') {
+ b->quality = 1;
+ if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+ (argv[i][j + 1] == '.')) {
+ b->fixedarea = 1;
+ k = 0;
+ while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
+ (argv[i][j + 1] == '.')) {
+ j++;
+ workstring[k] = argv[i][j];
+ k++;
+ }
+ workstring[k] = '\0';
+ b->maxarea = (REAL) strtod(workstring, (char **) NULL);
+ if (b->maxarea <= 0.0) {
+ printf("Error: Maximum area must be greater than zero.\n");
+ triexit(1);
+ }
+ } else {
+ b->vararea = 1;
+ }
+ }
+ if (argv[i][j] == 'u') {
+ b->quality = 1;
+ b->usertest = 1;
+ }
+#endif /* not CDT_ONLY */
+ if (argv[i][j] == 'A') {
+ b->regionattrib = 1;
+ }
+ if (argv[i][j] == 'c') {
+ b->convex = 1;
+ }
+ if (argv[i][j] == 'w') {
+ b->weighted = 1;
+ }
+ if (argv[i][j] == 'W') {
+ b->weighted = 2;
+ }
+ if (argv[i][j] == 'j') {
+ b->jettison = 1;
+ }
+ if (argv[i][j] == 'z') {
+ b->firstnumber = 0;
+ }
+ if (argv[i][j] == 'e') {
+ b->edgesout = 1;
+ }
+ if (argv[i][j] == 'v') {
+ b->voronoi = 1;
+ }
+ if (argv[i][j] == 'n') {
+ b->neighbors = 1;
+ }
+ if (argv[i][j] == 'g') {
+ b->geomview = 1;
+ }
+ if (argv[i][j] == 'B') {
+ b->nobound = 1;
+ }
+ if (argv[i][j] == 'P') {
+ b->nopolywritten = 1;
+ }
+ if (argv[i][j] == 'N') {
+ b->nonodewritten = 1;
+ }
+ if (argv[i][j] == 'E') {
+ b->noelewritten = 1;
+ }
+#ifndef TRILIBRARY
+ if (argv[i][j] == 'I') {
+ b->noiterationnum = 1;
+ }
+#endif /* not TRILIBRARY */
+ if (argv[i][j] == 'O') {
+ b->noholes = 1;
+ }
+ if (argv[i][j] == 'X') {
+ b->noexact = 1;
+ }
+ if (argv[i][j] == 'o') {
+ if (argv[i][j + 1] == '2') {
+ j++;
+ b->order = 2;
+ }
+ }
+#ifndef CDT_ONLY
+ if (argv[i][j] == 'Y') {
+ b->nobisect++;
+ }
+ if (argv[i][j] == 'S') {
+ b->steiner = 0;
+ while ((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) {
+ j++;
+ b->steiner = b->steiner * 10 + (int) (argv[i][j] - '0');
+ }
+ }
+#endif /* not CDT_ONLY */
+#ifndef REDUCED
+ if (argv[i][j] == 'i') {
+ b->incremental = 1;
+ }
+ if (argv[i][j] == 'F') {
+ b->sweepline = 1;
+ }
+#endif /* not REDUCED */
+ if (argv[i][j] == 'l') {
+ b->dwyer = 0;
+ }
+#ifndef REDUCED
+#ifndef CDT_ONLY
+ if (argv[i][j] == 's') {
+ b->splitseg = 1;
+ }
+ if ((argv[i][j] == 'D') || (argv[i][j] == 'L')) {
+ b->quality = 1;
+ b->conformdel = 1;
+ }
+#endif /* not CDT_ONLY */
+ if (argv[i][j] == 'C') {
+ b->docheck = 1;
+ }
+#endif /* not REDUCED */
+ if (argv[i][j] == 'Q') {
+ b->quiet = 1;
+ }
+ if (argv[i][j] == 'V') {
+ b->verbose++;
+ }
+#ifndef TRILIBRARY
+ if ((argv[i][j] == 'h') || (argv[i][j] == 'H') ||
+ (argv[i][j] == '?')) {
+ info();
+ }
+#endif /* not TRILIBRARY */
+ }
+#ifndef TRILIBRARY
+ } else {
+ strncpy(b->innodefilename, argv[i], FILENAMESIZE - 1);
+ b->innodefilename[FILENAMESIZE - 1] = '\0';
+ }
+#endif /* not TRILIBRARY */
+ }
+#ifndef TRILIBRARY
+ if (b->innodefilename[0] == '\0') {
+ syntax();
+ }
+ if (!strcmp(&b->innodefilename[strlen(b->innodefilename) - 5], ".node")) {
+ b->innodefilename[strlen(b->innodefilename) - 5] = '\0';
+ }
+ if (!strcmp(&b->innodefilename[strlen(b->innodefilename) - 5], ".poly")) {
+ b->innodefilename[strlen(b->innodefilename) - 5] = '\0';
+ b->poly = 1;
+ }
+#ifndef CDT_ONLY
+ if (!strcmp(&b->innodefilename[strlen(b->innodefilename) - 4], ".ele")) {
+ b->innodefilename[strlen(b->innodefilename) - 4] = '\0';
+ b->refine = 1;
+ }
+ if (!strcmp(&b->innodefilename[strlen(b->innodefilename) - 5], ".area")) {
+ b->innodefilename[strlen(b->innodefilename) - 5] = '\0';
+ b->refine = 1;
+ b->quality = 1;
+ b->vararea = 1;
+ }
+#endif /* not CDT_ONLY */
+#endif /* not TRILIBRARY */
+ b->usesegments = b->poly || b->refine || b->quality || b->convex;
+ b->goodangle = cos(b->minangle * PI / 180.0);
+ if (b->goodangle == 1.0) {
+ b->offconstant = 0.0;
+ } else {
+ b->offconstant = 0.475 * sqrt((1.0 + b->goodangle) / (1.0 - b->goodangle));
+ }
+ b->goodangle *= b->goodangle;
+ if (b->refine && b->noiterationnum) {
+ printf(
+ "Error: You cannot use the -I switch when refining a triangulation.\n");
+ triexit(1);
+ }
+ /* Be careful not to allocate space for element area constraints that */
+ /* will never be assigned any value (other than the default -1.0). */
+ if (!b->refine && !b->poly) {
+ b->vararea = 0;
+ }
+ /* Be careful not to add an extra attribute to each element unless the */
+ /* input supports it (PSLG in, but not refining a preexisting mesh). */
+ if (b->refine || !b->poly) {
+ b->regionattrib = 0;
+ }
+ /* Regular/weighted triangulations are incompatible with PSLGs */
+ /* and meshing. */
+ if (b->weighted && (b->poly || b->quality)) {
+ b->weighted = 0;
+ if (!b->quiet) {
+ printf("Warning: weighted triangulations (-w, -W) are incompatible\n");
+ printf(" with PSLGs (-p) and meshing (-q, -a, -u). Weights ignored.\n"
+ );
+ }
+ }
+ if (b->jettison && b->nonodewritten && !b->quiet) {
+ printf("Warning: -j and -N switches are somewhat incompatible.\n");
+ printf(" If any vertices are jettisoned, you will need the output\n");
+ printf(" .node file to reconstruct the new node indices.");
+ }
+
+#ifndef TRILIBRARY
+ strcpy(b->inpolyfilename, b->innodefilename);
+ strcpy(b->inelefilename, b->innodefilename);
+ strcpy(b->areafilename, b->innodefilename);
+ increment = 0;
+ strcpy(workstring, b->innodefilename);
+ j = 1;
+ while (workstring[j] != '\0') {
+ if ((workstring[j] == '.') && (workstring[j + 1] != '\0')) {
+ increment = j + 1;
+ }
+ j++;
+ }
+ meshnumber = 0;
+ if (increment > 0) {
+ j = increment;
+ do {
+ if ((workstring[j] >= '0') && (workstring[j] <= '9')) {
+ meshnumber = meshnumber * 10 + (int) (workstring[j] - '0');
+ } else {
+ increment = 0;
+ }
+ j++;
+ } while (workstring[j] != '\0');
+ }
+ if (b->noiterationnum) {
+ strcpy(b->outnodefilename, b->innodefilename);
+ strcpy(b->outelefilename, b->innodefilename);
+ strcpy(b->edgefilename, b->innodefilename);
+ strcpy(b->vnodefilename, b->innodefilename);
+ strcpy(b->vedgefilename, b->innodefilename);
+ strcpy(b->neighborfilename, b->innodefilename);
+ strcpy(b->offfilename, b->innodefilename);
+ strcat(b->outnodefilename, ".node");
+ strcat(b->outelefilename, ".ele");
+ strcat(b->edgefilename, ".edge");
+ strcat(b->vnodefilename, ".v.node");
+ strcat(b->vedgefilename, ".v.edge");
+ strcat(b->neighborfilename, ".neigh");
+ strcat(b->offfilename, ".off");
+ } else if (increment == 0) {
+ strcpy(b->outnodefilename, b->innodefilename);
+ strcpy(b->outpolyfilename, b->innodefilename);
+ strcpy(b->outelefilename, b->innodefilename);
+ strcpy(b->edgefilename, b->innodefilename);
+ strcpy(b->vnodefilename, b->innodefilename);
+ strcpy(b->vedgefilename, b->innodefilename);
+ strcpy(b->neighborfilename, b->innodefilename);
+ strcpy(b->offfilename, b->innodefilename);
+ strcat(b->outnodefilename, ".1.node");
+ strcat(b->outpolyfilename, ".1.poly");
+ strcat(b->outelefilename, ".1.ele");
+ strcat(b->edgefilename, ".1.edge");
+ strcat(b->vnodefilename, ".1.v.node");
+ strcat(b->vedgefilename, ".1.v.edge");
+ strcat(b->neighborfilename, ".1.neigh");
+ strcat(b->offfilename, ".1.off");
+ } else {
+ workstring[increment] = '%';
+ workstring[increment + 1] = 'd';
+ workstring[increment + 2] = '\0';
+ sprintf(b->outnodefilename, workstring, meshnumber + 1);
+ strcpy(b->outpolyfilename, b->outnodefilename);
+ strcpy(b->outelefilename, b->outnodefilename);
+ strcpy(b->edgefilename, b->outnodefilename);
+ strcpy(b->vnodefilename, b->outnodefilename);
+ strcpy(b->vedgefilename, b->outnodefilename);
+ strcpy(b->neighborfilename, b->outnodefilename);
+ strcpy(b->offfilename, b->outnodefilename);
+ strcat(b->outnodefilename, ".node");
+ strcat(b->outpolyfilename, ".poly");
+ strcat(b->outelefilename, ".ele");
+ strcat(b->edgefilename, ".edge");
+ strcat(b->vnodefilename, ".v.node");
+ strcat(b->vedgefilename, ".v.edge");
+ strcat(b->neighborfilename, ".neigh");
+ strcat(b->offfilename, ".off");
+ }
+ strcat(b->innodefilename, ".node");
+ strcat(b->inpolyfilename, ".poly");
+ strcat(b->inelefilename, ".ele");
+ strcat(b->areafilename, ".area");
+#endif /* not TRILIBRARY */
+}
+
+/** **/
+/** **/
+/********* User interaction routines begin here *********/
+
+/********* Debugging routines begin here *********/
+/** **/
+/** **/
+
+/*****************************************************************************/
+/* */
+/* printtriangle() Print out the details of an oriented triangle. */
+/* */
+/* I originally wrote this procedure to simplify debugging; it can be */
+/* called directly from the debugger, and presents information about an */
+/* oriented triangle in digestible form. It's also used when the */
+/* highest level of verbosity (`-VVV') is specified. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void printtriangle(struct mesh *m, struct behavior *b, struct otri *t)
+#else /* not ANSI_DECLARATORS */
+void printtriangle(m, b, t)
+struct mesh *m;
+struct behavior *b;
+struct otri *t;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri printtri;
+ struct osub printsh;
+ vertex printvertex;
+
+ printf("triangle x%lx with orientation %d:\n", (unsigned long) t->tri,
+ t->orient);
+ decode(t->tri[0], printtri);
+ if (printtri.tri == m->dummytri) {
+ printf(" [0] = Outer space\n");
+ } else {
+ printf(" [0] = x%lx %d\n", (unsigned long) printtri.tri,
+ printtri.orient);
+ }
+ decode(t->tri[1], printtri);
+ if (printtri.tri == m->dummytri) {
+ printf(" [1] = Outer space\n");
+ } else {
+ printf(" [1] = x%lx %d\n", (unsigned long) printtri.tri,
+ printtri.orient);
+ }
+ decode(t->tri[2], printtri);
+ if (printtri.tri == m->dummytri) {
+ printf(" [2] = Outer space\n");
+ } else {
+ printf(" [2] = x%lx %d\n", (unsigned long) printtri.tri,
+ printtri.orient);
+ }
+
+ org(*t, printvertex);
+ if (printvertex == (vertex) NULL)
+ printf(" Origin[%d] = NULL\n", (t->orient + 1) % 3 + 3);
+ else
+ printf(" Origin[%d] = x%lx (%.12g, %.12g)\n",
+ (t->orient + 1) % 3 + 3, (unsigned long) printvertex,
+ printvertex[0], printvertex[1]);
+ dest(*t, printvertex);
+ if (printvertex == (vertex) NULL)
+ printf(" Dest [%d] = NULL\n", (t->orient + 2) % 3 + 3);
+ else
+ printf(" Dest [%d] = x%lx (%.12g, %.12g)\n",
+ (t->orient + 2) % 3 + 3, (unsigned long) printvertex,
+ printvertex[0], printvertex[1]);
+ apex(*t, printvertex);
+ if (printvertex == (vertex) NULL)
+ printf(" Apex [%d] = NULL\n", t->orient + 3);
+ else
+ printf(" Apex [%d] = x%lx (%.12g, %.12g)\n",
+ t->orient + 3, (unsigned long) printvertex,
+ printvertex[0], printvertex[1]);
+
+ if (b->usesegments) {
+ sdecode(t->tri[6], printsh);
+ if (printsh.ss != m->dummysub) {
+ printf(" [6] = x%lx %d\n", (unsigned long) printsh.ss,
+ printsh.ssorient);
+ }
+ sdecode(t->tri[7], printsh);
+ if (printsh.ss != m->dummysub) {
+ printf(" [7] = x%lx %d\n", (unsigned long) printsh.ss,
+ printsh.ssorient);
+ }
+ sdecode(t->tri[8], printsh);
+ if (printsh.ss != m->dummysub) {
+ printf(" [8] = x%lx %d\n", (unsigned long) printsh.ss,
+ printsh.ssorient);
+ }
+ }
+
+ if (b->vararea) {
+ printf(" Area constraint: %.4g\n", areabound(*t));
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* printsubseg() Print out the details of an oriented subsegment. */
+/* */
+/* I originally wrote this procedure to simplify debugging; it can be */
+/* called directly from the debugger, and presents information about an */
+/* oriented subsegment in digestible form. It's also used when the highest */
+/* level of verbosity (`-VVV') is specified. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void printsubseg(struct mesh *m, struct behavior *b, struct osub *s)
+#else /* not ANSI_DECLARATORS */
+void printsubseg(m, b, s)
+struct mesh *m;
+struct behavior *b;
+struct osub *s;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct osub printsh;
+ struct otri printtri;
+ vertex printvertex;
+
+ printf("subsegment x%lx with orientation %d and mark %d:\n",
+ (unsigned long) s->ss, s->ssorient, mark(*s));
+ sdecode(s->ss[0], printsh);
+ if (printsh.ss == m->dummysub) {
+ printf(" [0] = No subsegment\n");
+ } else {
+ printf(" [0] = x%lx %d\n", (unsigned long) printsh.ss,
+ printsh.ssorient);
+ }
+ sdecode(s->ss[1], printsh);
+ if (printsh.ss == m->dummysub) {
+ printf(" [1] = No subsegment\n");
+ } else {
+ printf(" [1] = x%lx %d\n", (unsigned long) printsh.ss,
+ printsh.ssorient);
+ }
+
+ sorg(*s, printvertex);
+ if (printvertex == (vertex) NULL)
+ printf(" Origin[%d] = NULL\n", 2 + s->ssorient);
+ else
+ printf(" Origin[%d] = x%lx (%.12g, %.12g)\n",
+ 2 + s->ssorient, (unsigned long) printvertex,
+ printvertex[0], printvertex[1]);
+ sdest(*s, printvertex);
+ if (printvertex == (vertex) NULL)
+ printf(" Dest [%d] = NULL\n", 3 - s->ssorient);
+ else
+ printf(" Dest [%d] = x%lx (%.12g, %.12g)\n",
+ 3 - s->ssorient, (unsigned long) printvertex,
+ printvertex[0], printvertex[1]);
+
+ decode(s->ss[6], printtri);
+ if (printtri.tri == m->dummytri) {
+ printf(" [6] = Outer space\n");
+ } else {
+ printf(" [6] = x%lx %d\n", (unsigned long) printtri.tri,
+ printtri.orient);
+ }
+ decode(s->ss[7], printtri);
+ if (printtri.tri == m->dummytri) {
+ printf(" [7] = Outer space\n");
+ } else {
+ printf(" [7] = x%lx %d\n", (unsigned long) printtri.tri,
+ printtri.orient);
+ }
+
+ segorg(*s, printvertex);
+ if (printvertex == (vertex) NULL)
+ printf(" Segment origin[%d] = NULL\n", 4 + s->ssorient);
+ else
+ printf(" Segment origin[%d] = x%lx (%.12g, %.12g)\n",
+ 4 + s->ssorient, (unsigned long) printvertex,
+ printvertex[0], printvertex[1]);
+ segdest(*s, printvertex);
+ if (printvertex == (vertex) NULL)
+ printf(" Segment dest [%d] = NULL\n", 5 - s->ssorient);
+ else
+ printf(" Segment dest [%d] = x%lx (%.12g, %.12g)\n",
+ 5 - s->ssorient, (unsigned long) printvertex,
+ printvertex[0], printvertex[1]);
+}
+
+/** **/
+/** **/
+/********* Debugging routines end here *********/
+
+/********* Memory management routines begin here *********/
+/** **/
+/** **/
+
+/*****************************************************************************/
+/* */
+/* poolzero() Set all of a pool's fields to zero. */
+/* */
+/* This procedure should never be called on a pool that has any memory */
+/* allocated to it, as that memory would leak. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void poolzero(struct memorypool *pool)
+#else /* not ANSI_DECLARATORS */
+void poolzero(pool)
+struct memorypool *pool;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ pool->firstblock = (VOID **) NULL;
+ pool->nowblock = (VOID **) NULL;
+ pool->nextitem = (VOID *) NULL;
+ pool->deaditemstack = (VOID *) NULL;
+ pool->pathblock = (VOID **) NULL;
+ pool->pathitem = (VOID *) NULL;
+ pool->alignbytes = 0;
+ pool->itembytes = 0;
+ pool->itemsperblock = 0;
+ pool->itemsfirstblock = 0;
+ pool->items = 0;
+ pool->maxitems = 0;
+ pool->unallocateditems = 0;
+ pool->pathitemsleft = 0;
+}
+
+/*****************************************************************************/
+/* */
+/* poolrestart() Deallocate all items in a pool. */
+/* */
+/* The pool is returned to its starting state, except that no memory is */
+/* freed to the operating system. Rather, the previously allocated blocks */
+/* are ready to be reused. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void poolrestart(struct memorypool *pool)
+#else /* not ANSI_DECLARATORS */
+void poolrestart(pool)
+struct memorypool *pool;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ unsigned long alignptr;
+
+ pool->items = 0;
+ pool->maxitems = 0;
+
+ /* Set the currently active block. */
+ pool->nowblock = pool->firstblock;
+ /* Find the first item in the pool. Increment by the size of (VOID *). */
+ alignptr = (unsigned long) (pool->nowblock + 1);
+ /* Align the item on an `alignbytes'-byte boundary. */
+ pool->nextitem = (VOID *)
+ (alignptr + (unsigned long) pool->alignbytes -
+ (alignptr % (unsigned long) pool->alignbytes));
+ /* There are lots of unallocated items left in this block. */
+ pool->unallocateditems = pool->itemsfirstblock;
+ /* The stack of deallocated items is empty. */
+ pool->deaditemstack = (VOID *) NULL;
+}
+
+/*****************************************************************************/
+/* */
+/* poolinit() Initialize a pool of memory for allocation of items. */
+/* */
+/* This routine initializes the machinery for allocating items. A `pool' */
+/* is created whose records have size at least `bytecount'. Items will be */
+/* allocated in `itemcount'-item blocks. Each item is assumed to be a */
+/* collection of words, and either pointers or floating-point values are */
+/* assumed to be the "primary" word type. (The "primary" word type is used */
+/* to determine alignment of items.) If `alignment' isn't zero, all items */
+/* will be `alignment'-byte aligned in memory. `alignment' must be either */
+/* a multiple or a factor of the primary word size; powers of two are safe. */
+/* `alignment' is normally used to create a few unused bits at the bottom */
+/* of each item's pointer, in which information may be stored. */
+/* */
+/* Don't change this routine unless you understand it. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void poolinit(struct memorypool *pool, int bytecount, int itemcount,
+ int firstitemcount, int alignment)
+#else /* not ANSI_DECLARATORS */
+void poolinit(pool, bytecount, itemcount, firstitemcount, alignment)
+struct memorypool *pool;
+int bytecount;
+int itemcount;
+int firstitemcount;
+int alignment;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ /* Find the proper alignment, which must be at least as large as: */
+ /* - The parameter `alignment'. */
+ /* - sizeof(VOID *), so the stack of dead items can be maintained */
+ /* without unaligned accesses. */
+ if (alignment > sizeof(VOID *)) {
+ pool->alignbytes = alignment;
+ } else {
+ pool->alignbytes = sizeof(VOID *);
+ }
+ pool->itembytes = ((bytecount - 1) / pool->alignbytes + 1) *
+ pool->alignbytes;
+ pool->itemsperblock = itemcount;
+ if (firstitemcount == 0) {
+ pool->itemsfirstblock = itemcount;
+ } else {
+ pool->itemsfirstblock = firstitemcount;
+ }
+
+ /* Allocate a block of items. Space for `itemsfirstblock' items and one */
+ /* pointer (to point to the next block) are allocated, as well as space */
+ /* to ensure alignment of the items. */
+ pool->firstblock = (VOID **)
+ trimalloc(pool->itemsfirstblock * pool->itembytes + (int) sizeof(VOID *) +
+ pool->alignbytes);
+ /* Set the next block pointer to NULL. */
+ *(pool->firstblock) = (VOID *) NULL;
+ poolrestart(pool);
+}
+
+/*****************************************************************************/
+/* */
+/* pooldeinit() Free to the operating system all memory taken by a pool. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void pooldeinit(struct memorypool *pool)
+#else /* not ANSI_DECLARATORS */
+void pooldeinit(pool)
+struct memorypool *pool;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ while (pool->firstblock != (VOID **) NULL) {
+ pool->nowblock = (VOID **) *(pool->firstblock);
+ trifree((VOID *) pool->firstblock);
+ pool->firstblock = pool->nowblock;
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* poolalloc() Allocate space for an item. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+VOID *poolalloc(struct memorypool *pool)
+#else /* not ANSI_DECLARATORS */
+VOID *poolalloc(pool)
+struct memorypool *pool;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ VOID *newitem;
+ VOID **newblock;
+ unsigned long alignptr;
+
+ /* First check the linked list of dead items. If the list is not */
+ /* empty, allocate an item from the list rather than a fresh one. */
+ if (pool->deaditemstack != (VOID *) NULL) {
+ newitem = pool->deaditemstack; /* Take first item in list. */
+ pool->deaditemstack = * (VOID **) pool->deaditemstack;
+ } else {
+ /* Check if there are any free items left in the current block. */
+ if (pool->unallocateditems == 0) {
+ /* Check if another block must be allocated. */
+ if (*(pool->nowblock) == (VOID *) NULL) {
+ /* Allocate a new block of items, pointed to by the previous block. */
+ newblock = (VOID **) trimalloc(pool->itemsperblock * pool->itembytes +
+ (int) sizeof(VOID *) +
+ pool->alignbytes);
+ *(pool->nowblock) = (VOID *) newblock;
+ /* The next block pointer is NULL. */
+ *newblock = (VOID *) NULL;
+ }
+
+ /* Move to the new block. */
+ pool->nowblock = (VOID **) *(pool->nowblock);
+ /* Find the first item in the block. */
+ /* Increment by the size of (VOID *). */
+ alignptr = (unsigned long) (pool->nowblock + 1);
+ /* Align the item on an `alignbytes'-byte boundary. */
+ pool->nextitem = (VOID *)
+ (alignptr + (unsigned long) pool->alignbytes -
+ (alignptr % (unsigned long) pool->alignbytes));
+ /* There are lots of unallocated items left in this block. */
+ pool->unallocateditems = pool->itemsperblock;
+ }
+
+ /* Allocate a new item. */
+ newitem = pool->nextitem;
+ /* Advance `nextitem' pointer to next free item in block. */
+ pool->nextitem = (VOID *) ((char *) pool->nextitem + pool->itembytes);
+ pool->unallocateditems--;
+ pool->maxitems++;
+ }
+ pool->items++;
+ return newitem;
+}
+
+/*****************************************************************************/
+/* */
+/* pooldealloc() Deallocate space for an item. */
+/* */
+/* The deallocated space is stored in a queue for later reuse. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void pooldealloc(struct memorypool *pool, VOID *dyingitem)
+#else /* not ANSI_DECLARATORS */
+void pooldealloc(pool, dyingitem)
+struct memorypool *pool;
+VOID *dyingitem;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ /* Push freshly killed item onto stack. */
+ *((VOID **) dyingitem) = pool->deaditemstack;
+ pool->deaditemstack = dyingitem;
+ pool->items--;
+}
+
+/*****************************************************************************/
+/* */
+/* traversalinit() Prepare to traverse the entire list of items. */
+/* */
+/* This routine is used in conjunction with traverse(). */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void traversalinit(struct memorypool *pool)
+#else /* not ANSI_DECLARATORS */
+void traversalinit(pool)
+struct memorypool *pool;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ unsigned long alignptr;
+
+ /* Begin the traversal in the first block. */
+ pool->pathblock = pool->firstblock;
+ /* Find the first item in the block. Increment by the size of (VOID *). */
+ alignptr = (unsigned long) (pool->pathblock + 1);
+ /* Align with item on an `alignbytes'-byte boundary. */
+ pool->pathitem = (VOID *)
+ (alignptr + (unsigned long) pool->alignbytes -
+ (alignptr % (unsigned long) pool->alignbytes));
+ /* Set the number of items left in the current block. */
+ pool->pathitemsleft = pool->itemsfirstblock;
+}
+
+/*****************************************************************************/
+/* */
+/* traverse() Find the next item in the list. */
+/* */
+/* This routine is used in conjunction with traversalinit(). Be forewarned */
+/* that this routine successively returns all items in the list, including */
+/* deallocated ones on the deaditemqueue. It's up to you to figure out */
+/* which ones are actually dead. Why? I don't want to allocate extra */
+/* space just to demarcate dead items. It can usually be done more */
+/* space-efficiently by a routine that knows something about the structure */
+/* of the item. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+VOID *traverse(struct memorypool *pool)
+#else /* not ANSI_DECLARATORS */
+VOID *traverse(pool)
+struct memorypool *pool;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ VOID *newitem;
+ unsigned long alignptr;
+
+ /* Stop upon exhausting the list of items. */
+ if (pool->pathitem == pool->nextitem) {
+ return (VOID *) NULL;
+ }
+
+ /* Check whether any untraversed items remain in the current block. */
+ if (pool->pathitemsleft == 0) {
+ /* Find the next block. */
+ pool->pathblock = (VOID **) *(pool->pathblock);
+ /* Find the first item in the block. Increment by the size of (VOID *). */
+ alignptr = (unsigned long) (pool->pathblock + 1);
+ /* Align with item on an `alignbytes'-byte boundary. */
+ pool->pathitem = (VOID *)
+ (alignptr + (unsigned long) pool->alignbytes -
+ (alignptr % (unsigned long) pool->alignbytes));
+ /* Set the number of items left in the current block. */
+ pool->pathitemsleft = pool->itemsperblock;
+ }
+
+ newitem = pool->pathitem;
+ /* Find the next item in the block. */
+ pool->pathitem = (VOID *) ((char *) pool->pathitem + pool->itembytes);
+ pool->pathitemsleft--;
+ return newitem;
+}
+
+/*****************************************************************************/
+/* */
+/* dummyinit() Initialize the triangle that fills "outer space" and the */
+/* omnipresent subsegment. */
+/* */
+/* The triangle that fills "outer space," called `dummytri', is pointed to */
+/* by every triangle and subsegment on a boundary (be it outer or inner) of */
+/* the triangulation. Also, `dummytri' points to one of the triangles on */
+/* the convex hull (until the holes and concavities are carved), making it */
+/* possible to find a starting triangle for point location. */
+/* */
+/* The omnipresent subsegment, `dummysub', is pointed to by every triangle */
+/* or subsegment that doesn't have a full complement of real subsegments */
+/* to point to. */
+/* */
+/* `dummytri' and `dummysub' are generally required to fulfill only a few */
+/* invariants: their vertices must remain NULL and `dummytri' must always */
+/* be bonded (at offset zero) to some triangle on the convex hull of the */
+/* mesh, via a boundary edge. Otherwise, the connections of `dummytri' and */
+/* `dummysub' may change willy-nilly. This makes it possible to avoid */
+/* writing a good deal of special-case code (in the edge flip, for example) */
+/* for dealing with the boundary of the mesh, places where no subsegment is */
+/* present, and so forth. Other entities are frequently bonded to */
+/* `dummytri' and `dummysub' as if they were real mesh entities, with no */
+/* harm done. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void dummyinit(struct mesh *m, struct behavior *b, int trianglebytes,
+ int subsegbytes)
+#else /* not ANSI_DECLARATORS */
+void dummyinit(m, b, trianglebytes, subsegbytes)
+struct mesh *m;
+struct behavior *b;
+int trianglebytes;
+int subsegbytes;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ unsigned long alignptr;
+
+ /* Set up `dummytri', the `triangle' that occupies "outer space." */
+ m->dummytribase = (triangle *) trimalloc(trianglebytes +
+ m->triangles.alignbytes);
+ /* Align `dummytri' on a `triangles.alignbytes'-byte boundary. */
+ alignptr = (unsigned long) m->dummytribase;
+ m->dummytri = (triangle *)
+ (alignptr + (unsigned long) m->triangles.alignbytes -
+ (alignptr % (unsigned long) m->triangles.alignbytes));
+ /* Initialize the three adjoining triangles to be "outer space." These */
+ /* will eventually be changed by various bonding operations, but their */
+ /* values don't really matter, as long as they can legally be */
+ /* dereferenced. */
+ m->dummytri[0] = (triangle) m->dummytri;
+ m->dummytri[1] = (triangle) m->dummytri;
+ m->dummytri[2] = (triangle) m->dummytri;
+ /* Three NULL vertices. */
+ m->dummytri[3] = (triangle) NULL;
+ m->dummytri[4] = (triangle) NULL;
+ m->dummytri[5] = (triangle) NULL;
+
+ if (b->usesegments) {
+ /* Set up `dummysub', the omnipresent subsegment pointed to by any */
+ /* triangle side or subsegment end that isn't attached to a real */
+ /* subsegment. */
+ m->dummysubbase = (subseg *) trimalloc(subsegbytes +
+ m->subsegs.alignbytes);
+ /* Align `dummysub' on a `subsegs.alignbytes'-byte boundary. */
+ alignptr = (unsigned long) m->dummysubbase;
+ m->dummysub = (subseg *)
+ (alignptr + (unsigned long) m->subsegs.alignbytes -
+ (alignptr % (unsigned long) m->subsegs.alignbytes));
+ /* Initialize the two adjoining subsegments to be the omnipresent */
+ /* subsegment. These will eventually be changed by various bonding */
+ /* operations, but their values don't really matter, as long as they */
+ /* can legally be dereferenced. */
+ m->dummysub[0] = (subseg) m->dummysub;
+ m->dummysub[1] = (subseg) m->dummysub;
+ /* Four NULL vertices. */
+ m->dummysub[2] = (subseg) NULL;
+ m->dummysub[3] = (subseg) NULL;
+ m->dummysub[4] = (subseg) NULL;
+ m->dummysub[5] = (subseg) NULL;
+ /* Initialize the two adjoining triangles to be "outer space." */
+ m->dummysub[6] = (subseg) m->dummytri;
+ m->dummysub[7] = (subseg) m->dummytri;
+ /* Set the boundary marker to zero. */
+ * (int *) (m->dummysub + 8) = 0;
+
+ /* Initialize the three adjoining subsegments of `dummytri' to be */
+ /* the omnipresent subsegment. */
+ m->dummytri[6] = (triangle) m->dummysub;
+ m->dummytri[7] = (triangle) m->dummysub;
+ m->dummytri[8] = (triangle) m->dummysub;
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* initializevertexpool() Calculate the size of the vertex data structure */
+/* and initialize its memory pool. */
+/* */
+/* This routine also computes the `vertexmarkindex' and `vertex2triindex' */
+/* indices used to find values within each vertex. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void initializevertexpool(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void initializevertexpool(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ int vertexsize;
+
+ /* The index within each vertex at which the boundary marker is found, */
+ /* followed by the vertex type. Ensure the vertex marker is aligned to */
+ /* a sizeof(int)-byte address. */
+ m->vertexmarkindex = ((m->mesh_dim + m->nextras) * sizeof(REAL) +
+ sizeof(int) - 1) /
+ sizeof(int);
+ vertexsize = (m->vertexmarkindex + 2) * sizeof(int);
+ if (b->poly) {
+ /* The index within each vertex at which a triangle pointer is found. */
+ /* Ensure the pointer is aligned to a sizeof(triangle)-byte address. */
+ m->vertex2triindex = (vertexsize + sizeof(triangle) - 1) /
+ sizeof(triangle);
+ vertexsize = (m->vertex2triindex + 1) * sizeof(triangle);
+ }
+
+ /* Initialize the pool of vertices. */
+ poolinit(&m->vertices, vertexsize, VERTEXPERBLOCK,
+ m->invertices > VERTEXPERBLOCK ? m->invertices : VERTEXPERBLOCK,
+ sizeof(REAL));
+}
+
+/*****************************************************************************/
+/* */
+/* initializetrisubpools() Calculate the sizes of the triangle and */
+/* subsegment data structures and initialize */
+/* their memory pools. */
+/* */
+/* This routine also computes the `highorderindex', `elemattribindex', and */
+/* `areaboundindex' indices used to find values within each triangle. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void initializetrisubpools(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void initializetrisubpools(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ int trisize;
+
+ /* The index within each triangle at which the extra nodes (above three) */
+ /* associated with high order elements are found. There are three */
+ /* pointers to other triangles, three pointers to corners, and possibly */
+ /* three pointers to subsegments before the extra nodes. */
+ m->highorderindex = 6 + (b->usesegments * 3);
+ /* The number of bytes occupied by a triangle. */
+ trisize = ((b->order + 1) * (b->order + 2) / 2 + (m->highorderindex - 3)) *
+ sizeof(triangle);
+ /* The index within each triangle at which its attributes are found, */
+ /* where the index is measured in REALs. */
+ m->elemattribindex = (trisize + sizeof(REAL) - 1) / sizeof(REAL);
+ /* The index within each triangle at which the maximum area constraint */
+ /* is found, where the index is measured in REALs. Note that if the */
+ /* `regionattrib' flag is set, an additional attribute will be added. */
+ m->areaboundindex = m->elemattribindex + m->eextras + b->regionattrib;
+ /* If triangle attributes or an area bound are needed, increase the number */
+ /* of bytes occupied by a triangle. */
+ if (b->vararea) {
+ trisize = (m->areaboundindex + 1) * sizeof(REAL);
+ } else if (m->eextras + b->regionattrib > 0) {
+ trisize = m->areaboundindex * sizeof(REAL);
+ }
+ /* If a Voronoi diagram or triangle neighbor graph is requested, make */
+ /* sure there's room to store an integer index in each triangle. This */
+ /* integer index can occupy the same space as the subsegment pointers */
+ /* or attributes or area constraint or extra nodes. */
+ if ((b->voronoi || b->neighbors) &&
+ (trisize < 6 * sizeof(triangle) + sizeof(int))) {
+ trisize = 6 * sizeof(triangle) + sizeof(int);
+ }
+
+ /* Having determined the memory size of a triangle, initialize the pool. */
+ poolinit(&m->triangles, trisize, TRIPERBLOCK,
+ (2 * m->invertices - 2) > TRIPERBLOCK ? (2 * m->invertices - 2) :
+ TRIPERBLOCK, 4);
+
+ if (b->usesegments) {
+ /* Initialize the pool of subsegments. Take into account all eight */
+ /* pointers and one boundary marker. */
+ poolinit(&m->subsegs, 8 * sizeof(triangle) + sizeof(int),
+ SUBSEGPERBLOCK, SUBSEGPERBLOCK, 4);
+
+ /* Initialize the "outer space" triangle and omnipresent subsegment. */
+ dummyinit(m, b, m->triangles.itembytes, m->subsegs.itembytes);
+ } else {
+ /* Initialize the "outer space" triangle. */
+ dummyinit(m, b, m->triangles.itembytes, 0);
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* triangledealloc() Deallocate space for a triangle, marking it dead. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void triangledealloc(struct mesh *m, triangle *dyingtriangle)
+#else /* not ANSI_DECLARATORS */
+void triangledealloc(m, dyingtriangle)
+struct mesh *m;
+triangle *dyingtriangle;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ /* Mark the triangle as dead. This makes it possible to detect dead */
+ /* triangles when traversing the list of all triangles. */
+ killtri(dyingtriangle);
+ pooldealloc(&m->triangles, (VOID *) dyingtriangle);
+}
+
+/*****************************************************************************/
+/* */
+/* triangletraverse() Traverse the triangles, skipping dead ones. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+triangle *triangletraverse(struct mesh *m)
+#else /* not ANSI_DECLARATORS */
+triangle *triangletraverse(m)
+struct mesh *m;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ triangle *newtriangle;
+
+ do {
+ newtriangle = (triangle *) traverse(&m->triangles);
+ if (newtriangle == (triangle *) NULL) {
+ return (triangle *) NULL;
+ }
+ } while (deadtri(newtriangle)); /* Skip dead ones. */
+ return newtriangle;
+}
+
+/*****************************************************************************/
+/* */
+/* subsegdealloc() Deallocate space for a subsegment, marking it dead. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void subsegdealloc(struct mesh *m, subseg *dyingsubseg)
+#else /* not ANSI_DECLARATORS */
+void subsegdealloc(m, dyingsubseg)
+struct mesh *m;
+subseg *dyingsubseg;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ /* Mark the subsegment as dead. This makes it possible to detect dead */
+ /* subsegments when traversing the list of all subsegments. */
+ killsubseg(dyingsubseg);
+ pooldealloc(&m->subsegs, (VOID *) dyingsubseg);
+}
+
+/*****************************************************************************/
+/* */
+/* subsegtraverse() Traverse the subsegments, skipping dead ones. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+subseg *subsegtraverse(struct mesh *m)
+#else /* not ANSI_DECLARATORS */
+subseg *subsegtraverse(m)
+struct mesh *m;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ subseg *newsubseg;
+
+ do {
+ newsubseg = (subseg *) traverse(&m->subsegs);
+ if (newsubseg == (subseg *) NULL) {
+ return (subseg *) NULL;
+ }
+ } while (deadsubseg(newsubseg)); /* Skip dead ones. */
+ return newsubseg;
+}
+
+/*****************************************************************************/
+/* */
+/* vertexdealloc() Deallocate space for a vertex, marking it dead. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void vertexdealloc(struct mesh *m, vertex dyingvertex)
+#else /* not ANSI_DECLARATORS */
+void vertexdealloc(m, dyingvertex)
+struct mesh *m;
+vertex dyingvertex;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ /* Mark the vertex as dead. This makes it possible to detect dead */
+ /* vertices when traversing the list of all vertices. */
+ setvertextype(dyingvertex, DEADVERTEX);
+ pooldealloc(&m->vertices, (VOID *) dyingvertex);
+}
+
+/*****************************************************************************/
+/* */
+/* vertextraverse() Traverse the vertices, skipping dead ones. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+vertex vertextraverse(struct mesh *m)
+#else /* not ANSI_DECLARATORS */
+vertex vertextraverse(m)
+struct mesh *m;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ vertex newvertex;
+
+ do {
+ newvertex = (vertex) traverse(&m->vertices);
+ if (newvertex == (vertex) NULL) {
+ return (vertex) NULL;
+ }
+ } while (vertextype(newvertex) == DEADVERTEX); /* Skip dead ones. */
+ return newvertex;
+}
+
+/*****************************************************************************/
+/* */
+/* badsubsegdealloc() Deallocate space for a bad subsegment, marking it */
+/* dead. */
+/* */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void badsubsegdealloc(struct mesh *m, struct badsubseg *dyingseg)
+#else /* not ANSI_DECLARATORS */
+void badsubsegdealloc(m, dyingseg)
+struct mesh *m;
+struct badsubseg *dyingseg;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ /* Set subsegment's origin to NULL. This makes it possible to detect dead */
+ /* badsubsegs when traversing the list of all badsubsegs . */
+ dyingseg->subsegorg = (vertex) NULL;
+ pooldealloc(&m->badsubsegs, (VOID *) dyingseg);
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/* */
+/* badsubsegtraverse() Traverse the bad subsegments, skipping dead ones. */
+/* */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+struct badsubseg *badsubsegtraverse(struct mesh *m)
+#else /* not ANSI_DECLARATORS */
+struct badsubseg *badsubsegtraverse(m)
+struct mesh *m;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct badsubseg *newseg;
+
+ do {
+ newseg = (struct badsubseg *) traverse(&m->badsubsegs);
+ if (newseg == (struct badsubseg *) NULL) {
+ return (struct badsubseg *) NULL;
+ }
+ } while (newseg->subsegorg == (vertex) NULL); /* Skip dead ones. */
+ return newseg;
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/* */
+/* getvertex() Get a specific vertex, by number, from the list. */
+/* */
+/* The first vertex is number 'firstnumber'. */
+/* */
+/* Note that this takes O(n) time (with a small constant, if VERTEXPERBLOCK */
+/* is large). I don't care to take the trouble to make it work in constant */
+/* time. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+vertex getvertex(struct mesh *m, struct behavior *b, int number)
+#else /* not ANSI_DECLARATORS */
+vertex getvertex(m, b, number)
+struct mesh *m;
+struct behavior *b;
+int number;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ VOID **getblock;
+ char *foundvertex;
+ unsigned long alignptr;
+ int current;
+
+ getblock = m->vertices.firstblock;
+ current = b->firstnumber;
+
+ /* Find the right block. */
+ if (current + m->vertices.itemsfirstblock <= number) {
+ getblock = (VOID **) *getblock;
+ current += m->vertices.itemsfirstblock;
+ while (current + m->vertices.itemsperblock <= number) {
+ getblock = (VOID **) *getblock;
+ current += m->vertices.itemsperblock;
+ }
+ }
+
+ /* Now find the right vertex. */
+ alignptr = (unsigned long) (getblock + 1);
+ foundvertex = (char *) (alignptr + (unsigned long) m->vertices.alignbytes -
+ (alignptr % (unsigned long) m->vertices.alignbytes));
+ return (vertex) (foundvertex + m->vertices.itembytes * (number - current));
+}
+
+/*****************************************************************************/
+/* */
+/* triangledeinit() Free all remaining allocated memory. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void triangledeinit(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void triangledeinit(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ pooldeinit(&m->triangles);
+ trifree((VOID *) m->dummytribase);
+ if (b->usesegments) {
+ pooldeinit(&m->subsegs);
+ trifree((VOID *) m->dummysubbase);
+ }
+ pooldeinit(&m->vertices);
+#ifndef CDT_ONLY
+ if (b->quality) {
+ pooldeinit(&m->badsubsegs);
+ if ((b->minangle > 0.0) || b->vararea || b->fixedarea || b->usertest) {
+ pooldeinit(&m->badtriangles);
+ pooldeinit(&m->flipstackers);
+ }
+ }
+#endif /* not CDT_ONLY */
+}
+
+/** **/
+/** **/
+/********* Memory management routines end here *********/
+
+/********* Constructors begin here *********/
+/** **/
+/** **/
+
+/*****************************************************************************/
+/* */
+/* maketriangle() Create a new triangle with orientation zero. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void maketriangle(struct mesh *m, struct behavior *b, struct otri *newotri)
+#else /* not ANSI_DECLARATORS */
+void maketriangle(m, b, newotri)
+struct mesh *m;
+struct behavior *b;
+struct otri *newotri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ int i;
+
+ newotri->tri = (triangle *) poolalloc(&m->triangles);
+ /* Initialize the three adjoining triangles to be "outer space". */
+ newotri->tri[0] = (triangle) m->dummytri;
+ newotri->tri[1] = (triangle) m->dummytri;
+ newotri->tri[2] = (triangle) m->dummytri;
+ /* Three NULL vertices. */
+ newotri->tri[3] = (triangle) NULL;
+ newotri->tri[4] = (triangle) NULL;
+ newotri->tri[5] = (triangle) NULL;
+ if (b->usesegments) {
+ /* Initialize the three adjoining subsegments to be the omnipresent */
+ /* subsegment. */
+ newotri->tri[6] = (triangle) m->dummysub;
+ newotri->tri[7] = (triangle) m->dummysub;
+ newotri->tri[8] = (triangle) m->dummysub;
+ }
+ for (i = 0; i < m->eextras; i++) {
+ setelemattribute(*newotri, i, 0.0);
+ }
+ if (b->vararea) {
+ setareabound(*newotri, -1.0);
+ }
+
+ newotri->orient = 0;
+}
+
+/*****************************************************************************/
+/* */
+/* makesubseg() Create a new subsegment with orientation zero. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void makesubseg(struct mesh *m, struct osub *newsubseg)
+#else /* not ANSI_DECLARATORS */
+void makesubseg(m, newsubseg)
+struct mesh *m;
+struct osub *newsubseg;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ newsubseg->ss = (subseg *) poolalloc(&m->subsegs);
+ /* Initialize the two adjoining subsegments to be the omnipresent */
+ /* subsegment. */
+ newsubseg->ss[0] = (subseg) m->dummysub;
+ newsubseg->ss[1] = (subseg) m->dummysub;
+ /* Four NULL vertices. */
+ newsubseg->ss[2] = (subseg) NULL;
+ newsubseg->ss[3] = (subseg) NULL;
+ newsubseg->ss[4] = (subseg) NULL;
+ newsubseg->ss[5] = (subseg) NULL;
+ /* Initialize the two adjoining triangles to be "outer space." */
+ newsubseg->ss[6] = (subseg) m->dummytri;
+ newsubseg->ss[7] = (subseg) m->dummytri;
+ /* Set the boundary marker to zero. */
+ setmark(*newsubseg, 0);
+
+ newsubseg->ssorient = 0;
+}
+
+/** **/
+/** **/
+/********* Constructors end here *********/
+
+/********* Geometric primitives begin here *********/
+/** **/
+/** **/
+
+/* The adaptive exact arithmetic geometric predicates implemented herein are */
+/* described in detail in my paper, "Adaptive Precision Floating-Point */
+/* Arithmetic and Fast Robust Geometric Predicates." See the header for a */
+/* full citation. */
+
+/* Which of the following two methods of finding the absolute values is */
+/* fastest is compiler-dependent. A few compilers can inline and optimize */
+/* the fabs() call; but most will incur the overhead of a function call, */
+/* which is disastrously slow. A faster way on IEEE machines might be to */
+/* mask the appropriate bit, but that's difficult to do in C without */
+/* forcing the value to be stored to memory (rather than be kept in the */
+/* register to which the optimizer assigned it). */
+
+#define Absolute(a) ((a) >= 0.0 ? (a) : -(a))
+/* #define Absolute(a) fabs(a) */
+
+/* Many of the operations are broken up into two pieces, a main part that */
+/* performs an approximate operation, and a "tail" that computes the */
+/* roundoff error of that operation. */
+/* */
+/* The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(), */
+/* Split(), and Two_Product() are all implemented as described in the */
+/* reference. Each of these macros requires certain variables to be */
+/* defined in the calling routine. The variables `bvirt', `c', `abig', */
+/* `_i', `_j', `_k', `_l', `_m', and `_n' are declared `INEXACT' because */
+/* they store the result of an operation that may incur roundoff error. */
+/* The input parameter `x' (or the highest numbered `x_' parameter) must */
+/* also be declared `INEXACT'. */
+
+#define Fast_Two_Sum_Tail(a, b, x, y) \
+ bvirt = x - a; \
+ y = b - bvirt
+
+#define Fast_Two_Sum(a, b, x, y) \
+ x = (REAL) (a + b); \
+ Fast_Two_Sum_Tail(a, b, x, y)
+
+#define Two_Sum_Tail(a, b, x, y) \
+ bvirt = (REAL) (x - a); \
+ avirt = x - bvirt; \
+ bround = b - bvirt; \
+ around = a - avirt; \
+ y = around + bround
+
+#define Two_Sum(a, b, x, y) \
+ x = (REAL) (a + b); \
+ Two_Sum_Tail(a, b, x, y)
+
+#define Two_Diff_Tail(a, b, x, y) \
+ bvirt = (REAL) (a - x); \
+ avirt = x + bvirt; \
+ bround = bvirt - b; \
+ around = a - avirt; \
+ y = around + bround
+
+#define Two_Diff(a, b, x, y) \
+ x = (REAL) (a - b); \
+ Two_Diff_Tail(a, b, x, y)
+
+#define Split(a, ahi, alo) \
+ c = (REAL) (splitter * a); \
+ abig = (REAL) (c - a); \
+ ahi = c - abig; \
+ alo = a - ahi
+
+#define Two_Product_Tail(a, b, x, y) \
+ Split(a, ahi, alo); \
+ Split(b, bhi, blo); \
+ err1 = x - (ahi * bhi); \
+ err2 = err1 - (alo * bhi); \
+ err3 = err2 - (ahi * blo); \
+ y = (alo * blo) - err3
+
+#define Two_Product(a, b, x, y) \
+ x = (REAL) (a * b); \
+ Two_Product_Tail(a, b, x, y)
+
+/* Two_Product_Presplit() is Two_Product() where one of the inputs has */
+/* already been split. Avoids redundant splitting. */
+
+#define Two_Product_Presplit(a, b, bhi, blo, x, y) \
+ x = (REAL) (a * b); \
+ Split(a, ahi, alo); \
+ err1 = x - (ahi * bhi); \
+ err2 = err1 - (alo * bhi); \
+ err3 = err2 - (ahi * blo); \
+ y = (alo * blo) - err3
+
+/* Square() can be done more quickly than Two_Product(). */
+
+#define Square_Tail(a, x, y) \
+ Split(a, ahi, alo); \
+ err1 = x - (ahi * ahi); \
+ err3 = err1 - ((ahi + ahi) * alo); \
+ y = (alo * alo) - err3
+
+#define Square(a, x, y) \
+ x = (REAL) (a * a); \
+ Square_Tail(a, x, y)
+
+/* Macros for summing expansions of various fixed lengths. These are all */
+/* unrolled versions of Expansion_Sum(). */
+
+#define Two_One_Sum(a1, a0, b, x2, x1, x0) \
+ Two_Sum(a0, b , _i, x0); \
+ Two_Sum(a1, _i, x2, x1)
+
+#define Two_One_Diff(a1, a0, b, x2, x1, x0) \
+ Two_Diff(a0, b , _i, x0); \
+ Two_Sum( a1, _i, x2, x1)
+
+#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \
+ Two_One_Sum(a1, a0, b0, _j, _0, x0); \
+ Two_One_Sum(_j, _0, b1, x3, x2, x1)
+
+#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \
+ Two_One_Diff(a1, a0, b0, _j, _0, x0); \
+ Two_One_Diff(_j, _0, b1, x3, x2, x1)
+
+/* Macro for multiplying a two-component expansion by a single component. */
+
+#define Two_One_Product(a1, a0, b, x3, x2, x1, x0) \
+ Split(b, bhi, blo); \
+ Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \
+ Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \
+ Two_Sum(_i, _0, _k, x1); \
+ Fast_Two_Sum(_j, _k, x3, x2)
+
+/*****************************************************************************/
+/* */
+/* exactinit() Initialize the variables used for exact arithmetic. */
+/* */
+/* `epsilon' is the largest power of two such that 1.0 + epsilon = 1.0 in */
+/* floating-point arithmetic. `epsilon' bounds the relative roundoff */
+/* error. It is used for floating-point error analysis. */
+/* */
+/* `splitter' is used to split floating-point numbers into two half- */
+/* length significands for exact multiplication. */
+/* */
+/* I imagine that a highly optimizing compiler might be too smart for its */
+/* own good, and somehow cause this routine to fail, if it pretends that */
+/* floating-point arithmetic is too much like real arithmetic. */
+/* */
+/* Don't change this routine unless you fully understand it. */
+/* */
+/*****************************************************************************/
+
+void exactinit()
+{
+ REAL half;
+ REAL check, lastcheck;
+ int every_other;
+#ifdef LINUX
+ int cword;
+#endif /* LINUX */
+
+#ifdef CPU86
+#ifdef SINGLE
+ _control87(_PC_24, _MCW_PC); /* Set FPU control word for single precision. */
+#else /* not SINGLE */
+ _control87(_PC_53, _MCW_PC); /* Set FPU control word for double precision. */
+#endif /* not SINGLE */
+#endif /* CPU86 */
+#ifdef LINUX
+#ifdef SINGLE
+ /* cword = 4223; */
+ cword = 4210; /* set FPU control word for single precision */
+#else /* not SINGLE */
+ /* cword = 4735; */
+ cword = 4722; /* set FPU control word for double precision */
+#endif /* not SINGLE */
+ _FPU_SETCW(cword);
+#endif /* LINUX */
+
+ every_other = 1;
+ half = 0.5;
+ epsilon = 1.0;
+ splitter = 1.0;
+ check = 1.0;
+ /* Repeatedly divide `epsilon' by two until it is too small to add to */
+ /* one without causing roundoff. (Also check if the sum is equal to */
+ /* the previous sum, for machines that round up instead of using exact */
+ /* rounding. Not that these routines will work on such machines.) */
+ do {
+ lastcheck = check;
+ epsilon *= half;
+ if (every_other) {
+ splitter *= 2.0;
+ }
+ every_other = !every_other;
+ check = 1.0 + epsilon;
+ } while ((check != 1.0) && (check != lastcheck));
+ splitter += 1.0;
+ /* Error bounds for orientation and incircle tests. */
+ resulterrbound = (3.0 + 8.0 * epsilon) * epsilon;
+ ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon;
+ ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon;
+ ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon;
+ iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon;
+ iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon;
+ iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon;
+ o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon;
+ o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon;
+ o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon;
+}
+
+/*****************************************************************************/
+/* */
+/* fast_expansion_sum_zeroelim() Sum two expansions, eliminating zero */
+/* components from the output expansion. */
+/* */
+/* Sets h = e + f. See my Robust Predicates paper for details. */
+/* */
+/* If round-to-even is used (as with IEEE 754), maintains the strongly */
+/* nonoverlapping property. (That is, if e is strongly nonoverlapping, h */
+/* will be also.) Does NOT maintain the nonoverlapping or nonadjacent */
+/* properties. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+int fast_expansion_sum_zeroelim(int elen, REAL *e, int flen, REAL *f, REAL *h)
+#else /* not ANSI_DECLARATORS */
+int fast_expansion_sum_zeroelim(elen, e, flen, f, h) /* h cannot be e or f. */
+int elen;
+REAL *e;
+int flen;
+REAL *f;
+REAL *h;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ REAL Q;
+ INEXACT REAL Qnew;
+ INEXACT REAL hh;
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+ int eindex, findex, hindex;
+ REAL enow, fnow;
+
+ enow = e[0];
+ fnow = f[0];
+ eindex = findex = 0;
+ if ((fnow > enow) == (fnow > -enow)) {
+ Q = enow;
+ enow = e[++eindex];
+ } else {
+ Q = fnow;
+ fnow = f[++findex];
+ }
+ hindex = 0;
+ if ((eindex < elen) && (findex < flen)) {
+ if ((fnow > enow) == (fnow > -enow)) {
+ Fast_Two_Sum(enow, Q, Qnew, hh);
+ enow = e[++eindex];
+ } else {
+ Fast_Two_Sum(fnow, Q, Qnew, hh);
+ fnow = f[++findex];
+ }
+ Q = Qnew;
+ if (hh != 0.0) {
+ h[hindex++] = hh;
+ }
+ while ((eindex < elen) && (findex < flen)) {
+ if ((fnow > enow) == (fnow > -enow)) {
+ Two_Sum(Q, enow, Qnew, hh);
+ enow = e[++eindex];
+ } else {
+ Two_Sum(Q, fnow, Qnew, hh);
+ fnow = f[++findex];
+ }
+ Q = Qnew;
+ if (hh != 0.0) {
+ h[hindex++] = hh;
+ }
+ }
+ }
+ while (eindex < elen) {
+ Two_Sum(Q, enow, Qnew, hh);
+ enow = e[++eindex];
+ Q = Qnew;
+ if (hh != 0.0) {
+ h[hindex++] = hh;
+ }
+ }
+ while (findex < flen) {
+ Two_Sum(Q, fnow, Qnew, hh);
+ fnow = f[++findex];
+ Q = Qnew;
+ if (hh != 0.0) {
+ h[hindex++] = hh;
+ }
+ }
+ if ((Q != 0.0) || (hindex == 0)) {
+ h[hindex++] = Q;
+ }
+ return hindex;
+}
+
+/*****************************************************************************/
+/* */
+/* scale_expansion_zeroelim() Multiply an expansion by a scalar, */
+/* eliminating zero components from the */
+/* output expansion. */
+/* */
+/* Sets h = be. See my Robust Predicates paper for details. */
+/* */
+/* Maintains the nonoverlapping property. If round-to-even is used (as */
+/* with IEEE 754), maintains the strongly nonoverlapping and nonadjacent */
+/* properties as well. (That is, if e has one of these properties, so */
+/* will h.) */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+int scale_expansion_zeroelim(int elen, REAL *e, REAL b, REAL *h)
+#else /* not ANSI_DECLARATORS */
+int scale_expansion_zeroelim(elen, e, b, h) /* e and h cannot be the same. */
+int elen;
+REAL *e;
+REAL b;
+REAL *h;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ INEXACT REAL Q, sum;
+ REAL hh;
+ INEXACT REAL product1;
+ REAL product0;
+ int eindex, hindex;
+ REAL enow;
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+ INEXACT REAL c;
+ INEXACT REAL abig;
+ REAL ahi, alo, bhi, blo;
+ REAL err1, err2, err3;
+
+ Split(b, bhi, blo);
+ Two_Product_Presplit(e[0], b, bhi, blo, Q, hh);
+ hindex = 0;
+ if (hh != 0) {
+ h[hindex++] = hh;
+ }
+ for (eindex = 1; eindex < elen; eindex++) {
+ enow = e[eindex];
+ Two_Product_Presplit(enow, b, bhi, blo, product1, product0);
+ Two_Sum(Q, product0, sum, hh);
+ if (hh != 0) {
+ h[hindex++] = hh;
+ }
+ Fast_Two_Sum(product1, sum, Q, hh);
+ if (hh != 0) {
+ h[hindex++] = hh;
+ }
+ }
+ if ((Q != 0.0) || (hindex == 0)) {
+ h[hindex++] = Q;
+ }
+ return hindex;
+}
+
+/*****************************************************************************/
+/* */
+/* estimate() Produce a one-word estimate of an expansion's value. */
+/* */
+/* See my Robust Predicates paper for details. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+REAL estimate(int elen, REAL *e)
+#else /* not ANSI_DECLARATORS */
+REAL estimate(elen, e)
+int elen;
+REAL *e;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ REAL Q;
+ int eindex;
+
+ Q = e[0];
+ for (eindex = 1; eindex < elen; eindex++) {
+ Q += e[eindex];
+ }
+ return Q;
+}
+
+/*****************************************************************************/
+/* */
+/* counterclockwise() Return a positive value if the points pa, pb, and */
+/* pc occur in counterclockwise order; a negative */
+/* value if they occur in clockwise order; and zero */
+/* if they are collinear. The result is also a rough */
+/* approximation of twice the signed area of the */
+/* triangle defined by the three points. */
+/* */
+/* Uses exact arithmetic if necessary to ensure a correct answer. The */
+/* result returned is the determinant of a matrix. This determinant is */
+/* computed adaptively, in the sense that exact arithmetic is used only to */
+/* the degree it is needed to ensure that the returned value has the */
+/* correct sign. Hence, this function is usually quite fast, but will run */
+/* more slowly when the input points are collinear or nearly so. */
+/* */
+/* See my Robust Predicates paper for details. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+REAL counterclockwiseadapt(vertex pa, vertex pb, vertex pc, REAL detsum)
+#else /* not ANSI_DECLARATORS */
+REAL counterclockwiseadapt(pa, pb, pc, detsum)
+vertex pa;
+vertex pb;
+vertex pc;
+REAL detsum;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ INEXACT REAL acx, acy, bcx, bcy;
+ REAL acxtail, acytail, bcxtail, bcytail;
+ INEXACT REAL detleft, detright;
+ REAL detlefttail, detrighttail;
+ REAL det, errbound;
+ REAL B[4], C1[8], C2[12], D[16];
+ INEXACT REAL B3;
+ int C1length, C2length, Dlength;
+ REAL u[4];
+ INEXACT REAL u3;
+ INEXACT REAL s1, t1;
+ REAL s0, t0;
+
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+ INEXACT REAL c;
+ INEXACT REAL abig;
+ REAL ahi, alo, bhi, blo;
+ REAL err1, err2, err3;
+ INEXACT REAL _i, _j;
+ REAL _0;
+
+ acx = (REAL) (pa[0] - pc[0]);
+ bcx = (REAL) (pb[0] - pc[0]);
+ acy = (REAL) (pa[1] - pc[1]);
+ bcy = (REAL) (pb[1] - pc[1]);
+
+ Two_Product(acx, bcy, detleft, detlefttail);
+ Two_Product(acy, bcx, detright, detrighttail);
+
+ Two_Two_Diff(detleft, detlefttail, detright, detrighttail,
+ B3, B[2], B[1], B[0]);
+ B[3] = B3;
+
+ det = estimate(4, B);
+ errbound = ccwerrboundB * detsum;
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ Two_Diff_Tail(pa[0], pc[0], acx, acxtail);
+ Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail);
+ Two_Diff_Tail(pa[1], pc[1], acy, acytail);
+ Two_Diff_Tail(pb[1], pc[1], bcy, bcytail);
+
+ if ((acxtail == 0.0) && (acytail == 0.0)
+ && (bcxtail == 0.0) && (bcytail == 0.0)) {
+ return det;
+ }
+
+ errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det);
+ det += (acx * bcytail + bcy * acxtail)
+ - (acy * bcxtail + bcx * acytail);
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ Two_Product(acxtail, bcy, s1, s0);
+ Two_Product(acytail, bcx, t1, t0);
+ Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1);
+
+ Two_Product(acx, bcytail, s1, s0);
+ Two_Product(acy, bcxtail, t1, t0);
+ Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2);
+
+ Two_Product(acxtail, bcytail, s1, s0);
+ Two_Product(acytail, bcxtail, t1, t0);
+ Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D);
+
+ return(D[Dlength - 1]);
+}
+
+#ifdef ANSI_DECLARATORS
+REAL counterclockwise(struct mesh *m, struct behavior *b,
+ vertex pa, vertex pb, vertex pc)
+#else /* not ANSI_DECLARATORS */
+REAL counterclockwise(m, b, pa, pb, pc)
+struct mesh *m;
+struct behavior *b;
+vertex pa;
+vertex pb;
+vertex pc;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ REAL detleft, detright, det;
+ REAL detsum, errbound;
+
+ m->counterclockcount++;
+
+ detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]);
+ detright = (pa[1] - pc[1]) * (pb[0] - pc[0]);
+ det = detleft - detright;
+
+ if (b->noexact) {
+ return det;
+ }
+
+ if (detleft > 0.0) {
+ if (detright <= 0.0) {
+ return det;
+ } else {
+ detsum = detleft + detright;
+ }
+ } else if (detleft < 0.0) {
+ if (detright >= 0.0) {
+ return det;
+ } else {
+ detsum = -detleft - detright;
+ }
+ } else {
+ return det;
+ }
+
+ errbound = ccwerrboundA * detsum;
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ return counterclockwiseadapt(pa, pb, pc, detsum);
+}
+
+/*****************************************************************************/
+/* */
+/* incircle() Return a positive value if the point pd lies inside the */
+/* circle passing through pa, pb, and pc; a negative value if */
+/* it lies outside; and zero if the four points are cocircular.*/
+/* The points pa, pb, and pc must be in counterclockwise */
+/* order, or the sign of the result will be reversed. */
+/* */
+/* Uses exact arithmetic if necessary to ensure a correct answer. The */
+/* result returned is the determinant of a matrix. This determinant is */
+/* computed adaptively, in the sense that exact arithmetic is used only to */
+/* the degree it is needed to ensure that the returned value has the */
+/* correct sign. Hence, this function is usually quite fast, but will run */
+/* more slowly when the input points are cocircular or nearly so. */
+/* */
+/* See my Robust Predicates paper for details. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+REAL incircleadapt(vertex pa, vertex pb, vertex pc, vertex pd, REAL permanent)
+#else /* not ANSI_DECLARATORS */
+REAL incircleadapt(pa, pb, pc, pd, permanent)
+vertex pa;
+vertex pb;
+vertex pc;
+vertex pd;
+REAL permanent;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ INEXACT REAL adx, bdx, cdx, ady, bdy, cdy;
+ REAL det, errbound;
+
+ INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1;
+ REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0;
+ REAL bc[4], ca[4], ab[4];
+ INEXACT REAL bc3, ca3, ab3;
+ REAL axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32];
+ int axbclen, axxbclen, aybclen, ayybclen, alen;
+ REAL bxca[8], bxxca[16], byca[8], byyca[16], bdet[32];
+ int bxcalen, bxxcalen, bycalen, byycalen, blen;
+ REAL cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32];
+ int cxablen, cxxablen, cyablen, cyyablen, clen;
+ REAL abdet[64];
+ int ablen;
+ REAL fin1[1152], fin2[1152];
+ REAL *finnow, *finother, *finswap;
+ int finlength;
+
+ REAL adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail;
+ INEXACT REAL adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1;
+ REAL adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0;
+ REAL aa[4], bb[4], cc[4];
+ INEXACT REAL aa3, bb3, cc3;
+ INEXACT REAL ti1, tj1;
+ REAL ti0, tj0;
+ REAL u[4], v[4];
+ INEXACT REAL u3, v3;
+ REAL temp8[8], temp16a[16], temp16b[16], temp16c[16];
+ REAL temp32a[32], temp32b[32], temp48[48], temp64[64];
+ int temp8len, temp16alen, temp16blen, temp16clen;
+ int temp32alen, temp32blen, temp48len, temp64len;
+ REAL axtbb[8], axtcc[8], aytbb[8], aytcc[8];
+ int axtbblen, axtcclen, aytbblen, aytcclen;
+ REAL bxtaa[8], bxtcc[8], bytaa[8], bytcc[8];
+ int bxtaalen, bxtcclen, bytaalen, bytcclen;
+ REAL cxtaa[8], cxtbb[8], cytaa[8], cytbb[8];
+ int cxtaalen, cxtbblen, cytaalen, cytbblen;
+ REAL axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8];
+ int axtbclen, aytbclen, bxtcalen, bytcalen, cxtablen, cytablen;
+ REAL axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16];
+ int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen;
+ REAL axtbctt[8], aytbctt[8], bxtcatt[8];
+ REAL bytcatt[8], cxtabtt[8], cytabtt[8];
+ int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen;
+ REAL abt[8], bct[8], cat[8];
+ int abtlen, bctlen, catlen;
+ REAL abtt[4], bctt[4], catt[4];
+ int abttlen, bcttlen, cattlen;
+ INEXACT REAL abtt3, bctt3, catt3;
+ REAL negate;
+
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+ INEXACT REAL c;
+ INEXACT REAL abig;
+ REAL ahi, alo, bhi, blo;
+ REAL err1, err2, err3;
+ INEXACT REAL _i, _j;
+ REAL _0;
+
+ adx = (REAL) (pa[0] - pd[0]);
+ bdx = (REAL) (pb[0] - pd[0]);
+ cdx = (REAL) (pc[0] - pd[0]);
+ ady = (REAL) (pa[1] - pd[1]);
+ bdy = (REAL) (pb[1] - pd[1]);
+ cdy = (REAL) (pc[1] - pd[1]);
+
+ Two_Product(bdx, cdy, bdxcdy1, bdxcdy0);
+ Two_Product(cdx, bdy, cdxbdy1, cdxbdy0);
+ Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]);
+ bc[3] = bc3;
+ axbclen = scale_expansion_zeroelim(4, bc, adx, axbc);
+ axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc);
+ aybclen = scale_expansion_zeroelim(4, bc, ady, aybc);
+ ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc);
+ alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet);
+
+ Two_Product(cdx, ady, cdxady1, cdxady0);
+ Two_Product(adx, cdy, adxcdy1, adxcdy0);
+ Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]);
+ ca[3] = ca3;
+ bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca);
+ bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca);
+ bycalen = scale_expansion_zeroelim(4, ca, bdy, byca);
+ byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca);
+ blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet);
+
+ Two_Product(adx, bdy, adxbdy1, adxbdy0);
+ Two_Product(bdx, ady, bdxady1, bdxady0);
+ Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]);
+ ab[3] = ab3;
+ cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab);
+ cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab);
+ cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab);
+ cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab);
+ clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet);
+
+ ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+ finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1);
+
+ det = estimate(finlength, fin1);
+ errbound = iccerrboundB * permanent;
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
+ Two_Diff_Tail(pa[1], pd[1], ady, adytail);
+ Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
+ Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
+ Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
+ Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
+ if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0)
+ && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0)) {
+ return det;
+ }
+
+ errbound = iccerrboundC * permanent + resulterrbound * Absolute(det);
+ det += ((adx * adx + ady * ady) * ((bdx * cdytail + cdy * bdxtail)
+ - (bdy * cdxtail + cdx * bdytail))
+ + 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx))
+ + ((bdx * bdx + bdy * bdy) * ((cdx * adytail + ady * cdxtail)
+ - (cdy * adxtail + adx * cdytail))
+ + 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx))
+ + ((cdx * cdx + cdy * cdy) * ((adx * bdytail + bdy * adxtail)
+ - (ady * bdxtail + bdx * adytail))
+ + 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx));
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ finnow = fin1;
+ finother = fin2;
+
+ if ((bdxtail != 0.0) || (bdytail != 0.0)
+ || (cdxtail != 0.0) || (cdytail != 0.0)) {
+ Square(adx, adxadx1, adxadx0);
+ Square(ady, adyady1, adyady0);
+ Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]);
+ aa[3] = aa3;
+ }
+ if ((cdxtail != 0.0) || (cdytail != 0.0)
+ || (adxtail != 0.0) || (adytail != 0.0)) {
+ Square(bdx, bdxbdx1, bdxbdx0);
+ Square(bdy, bdybdy1, bdybdy0);
+ Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]);
+ bb[3] = bb3;
+ }
+ if ((adxtail != 0.0) || (adytail != 0.0)
+ || (bdxtail != 0.0) || (bdytail != 0.0)) {
+ Square(cdx, cdxcdx1, cdxcdx0);
+ Square(cdy, cdycdy1, cdycdy0);
+ Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]);
+ cc[3] = cc3;
+ }
+
+ if (adxtail != 0.0) {
+ axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc);
+ temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx,
+ temp16a);
+
+ axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc);
+ temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b);
+
+ axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb);
+ temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c);
+
+ temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+ temp16blen, temp16b, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+ temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+ temp48, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ if (adytail != 0.0) {
+ aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc);
+ temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady,
+ temp16a);
+
+ aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb);
+ temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b);
+
+ aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc);
+ temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c);
+
+ temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+ temp16blen, temp16b, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+ temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+ temp48, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ if (bdxtail != 0.0) {
+ bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca);
+ temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx,
+ temp16a);
+
+ bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa);
+ temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b);
+
+ bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc);
+ temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c);
+
+ temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+ temp16blen, temp16b, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+ temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+ temp48, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ if (bdytail != 0.0) {
+ bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca);
+ temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy,
+ temp16a);
+
+ bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc);
+ temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b);
+
+ bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa);
+ temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c);
+
+ temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+ temp16blen, temp16b, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+ temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+ temp48, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ if (cdxtail != 0.0) {
+ cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab);
+ temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx,
+ temp16a);
+
+ cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb);
+ temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b);
+
+ cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa);
+ temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c);
+
+ temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+ temp16blen, temp16b, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+ temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+ temp48, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ if (cdytail != 0.0) {
+ cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab);
+ temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy,
+ temp16a);
+
+ cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa);
+ temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b);
+
+ cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb);
+ temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c);
+
+ temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+ temp16blen, temp16b, temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+ temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+ temp48, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+
+ if ((adxtail != 0.0) || (adytail != 0.0)) {
+ if ((bdxtail != 0.0) || (bdytail != 0.0)
+ || (cdxtail != 0.0) || (cdytail != 0.0)) {
+ Two_Product(bdxtail, cdy, ti1, ti0);
+ Two_Product(bdx, cdytail, tj1, tj0);
+ Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ negate = -bdy;
+ Two_Product(cdxtail, negate, ti1, ti0);
+ negate = -bdytail;
+ Two_Product(cdx, negate, tj1, tj0);
+ Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+ v[3] = v3;
+ bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct);
+
+ Two_Product(bdxtail, cdytail, ti1, ti0);
+ Two_Product(cdxtail, bdytail, tj1, tj0);
+ Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]);
+ bctt[3] = bctt3;
+ bcttlen = 4;
+ } else {
+ bct[0] = 0.0;
+ bctlen = 1;
+ bctt[0] = 0.0;
+ bcttlen = 1;
+ }
+
+ if (adxtail != 0.0) {
+ temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a);
+ axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct);
+ temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx,
+ temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+ temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+ temp48, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ if (bdytail != 0.0) {
+ temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8);
+ temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail,
+ temp16a);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+ temp16a, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ if (cdytail != 0.0) {
+ temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8);
+ temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail,
+ temp16a);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+ temp16a, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+
+ temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail,
+ temp32a);
+ axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt);
+ temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx,
+ temp16a);
+ temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail,
+ temp16b);
+ temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+ temp16blen, temp16b, temp32b);
+ temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+ temp32blen, temp32b, temp64);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+ temp64, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ if (adytail != 0.0) {
+ temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a);
+ aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct);
+ temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady,
+ temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+ temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+ temp48, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+
+
+ temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail,
+ temp32a);
+ aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt);
+ temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady,
+ temp16a);
+ temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail,
+ temp16b);
+ temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+ temp16blen, temp16b, temp32b);
+ temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+ temp32blen, temp32b, temp64);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+ temp64, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ }
+ if ((bdxtail != 0.0) || (bdytail != 0.0)) {
+ if ((cdxtail != 0.0) || (cdytail != 0.0)
+ || (adxtail != 0.0) || (adytail != 0.0)) {
+ Two_Product(cdxtail, ady, ti1, ti0);
+ Two_Product(cdx, adytail, tj1, tj0);
+ Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ negate = -cdy;
+ Two_Product(adxtail, negate, ti1, ti0);
+ negate = -cdytail;
+ Two_Product(adx, negate, tj1, tj0);
+ Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+ v[3] = v3;
+ catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat);
+
+ Two_Product(cdxtail, adytail, ti1, ti0);
+ Two_Product(adxtail, cdytail, tj1, tj0);
+ Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]);
+ catt[3] = catt3;
+ cattlen = 4;
+ } else {
+ cat[0] = 0.0;
+ catlen = 1;
+ catt[0] = 0.0;
+ cattlen = 1;
+ }
+
+ if (bdxtail != 0.0) {
+ temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a);
+ bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat);
+ temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx,
+ temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+ temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+ temp48, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ if (cdytail != 0.0) {
+ temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8);
+ temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail,
+ temp16a);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+ temp16a, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ if (adytail != 0.0) {
+ temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8);
+ temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail,
+ temp16a);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+ temp16a, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+
+ temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail,
+ temp32a);
+ bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt);
+ temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx,
+ temp16a);
+ temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail,
+ temp16b);
+ temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+ temp16blen, temp16b, temp32b);
+ temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+ temp32blen, temp32b, temp64);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+ temp64, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ if (bdytail != 0.0) {
+ temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a);
+ bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat);
+ temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy,
+ temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+ temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+ temp48, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+
+
+ temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail,
+ temp32a);
+ bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt);
+ temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy,
+ temp16a);
+ temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail,
+ temp16b);
+ temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+ temp16blen, temp16b, temp32b);
+ temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+ temp32blen, temp32b, temp64);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+ temp64, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ }
+ if ((cdxtail != 0.0) || (cdytail != 0.0)) {
+ if ((adxtail != 0.0) || (adytail != 0.0)
+ || (bdxtail != 0.0) || (bdytail != 0.0)) {
+ Two_Product(adxtail, bdy, ti1, ti0);
+ Two_Product(adx, bdytail, tj1, tj0);
+ Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ negate = -ady;
+ Two_Product(bdxtail, negate, ti1, ti0);
+ negate = -adytail;
+ Two_Product(bdx, negate, tj1, tj0);
+ Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+ v[3] = v3;
+ abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt);
+
+ Two_Product(adxtail, bdytail, ti1, ti0);
+ Two_Product(bdxtail, adytail, tj1, tj0);
+ Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]);
+ abtt[3] = abtt3;
+ abttlen = 4;
+ } else {
+ abt[0] = 0.0;
+ abtlen = 1;
+ abtt[0] = 0.0;
+ abttlen = 1;
+ }
+
+ if (cdxtail != 0.0) {
+ temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a);
+ cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt);
+ temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx,
+ temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+ temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+ temp48, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ if (adytail != 0.0) {
+ temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8);
+ temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail,
+ temp16a);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+ temp16a, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ if (bdytail != 0.0) {
+ temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8);
+ temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail,
+ temp16a);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+ temp16a, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+
+ temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail,
+ temp32a);
+ cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt);
+ temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx,
+ temp16a);
+ temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail,
+ temp16b);
+ temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+ temp16blen, temp16b, temp32b);
+ temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+ temp32blen, temp32b, temp64);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+ temp64, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ if (cdytail != 0.0) {
+ temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a);
+ cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt);
+ temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy,
+ temp32a);
+ temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+ temp32alen, temp32a, temp48);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+ temp48, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+
+
+ temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail,
+ temp32a);
+ cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt);
+ temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy,
+ temp16a);
+ temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail,
+ temp16b);
+ temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+ temp16blen, temp16b, temp32b);
+ temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+ temp32blen, temp32b, temp64);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+ temp64, finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ }
+
+ return finnow[finlength - 1];
+}
+
+#ifdef ANSI_DECLARATORS
+REAL incircle(struct mesh *m, struct behavior *b,
+ vertex pa, vertex pb, vertex pc, vertex pd)
+#else /* not ANSI_DECLARATORS */
+REAL incircle(m, b, pa, pb, pc, pd)
+struct mesh *m;
+struct behavior *b;
+vertex pa;
+vertex pb;
+vertex pc;
+vertex pd;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ REAL adx, bdx, cdx, ady, bdy, cdy;
+ REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
+ REAL alift, blift, clift;
+ REAL det;
+ REAL permanent, errbound;
+
+ m->incirclecount++;
+
+ adx = pa[0] - pd[0];
+ bdx = pb[0] - pd[0];
+ cdx = pc[0] - pd[0];
+ ady = pa[1] - pd[1];
+ bdy = pb[1] - pd[1];
+ cdy = pc[1] - pd[1];
+
+ bdxcdy = bdx * cdy;
+ cdxbdy = cdx * bdy;
+ alift = adx * adx + ady * ady;
+
+ cdxady = cdx * ady;
+ adxcdy = adx * cdy;
+ blift = bdx * bdx + bdy * bdy;
+
+ adxbdy = adx * bdy;
+ bdxady = bdx * ady;
+ clift = cdx * cdx + cdy * cdy;
+
+ det = alift * (bdxcdy - cdxbdy)
+ + blift * (cdxady - adxcdy)
+ + clift * (adxbdy - bdxady);
+
+ if (b->noexact) {
+ return det;
+ }
+
+ permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift
+ + (Absolute(cdxady) + Absolute(adxcdy)) * blift
+ + (Absolute(adxbdy) + Absolute(bdxady)) * clift;
+ errbound = iccerrboundA * permanent;
+ if ((det > errbound) || (-det > errbound)) {
+ return det;
+ }
+
+ return incircleadapt(pa, pb, pc, pd, permanent);
+}
+
+/*****************************************************************************/
+/* */
+/* orient3d() Return a positive value if the point pd lies below the */
+/* plane passing through pa, pb, and pc; "below" is defined so */
+/* that pa, pb, and pc appear in counterclockwise order when */
+/* viewed from above the plane. Returns a negative value if */
+/* pd lies above the plane. Returns zero if the points are */
+/* coplanar. The result is also a rough approximation of six */
+/* times the signed volume of the tetrahedron defined by the */
+/* four points. */
+/* */
+/* Uses exact arithmetic if necessary to ensure a correct answer. The */
+/* result returned is the determinant of a matrix. This determinant is */
+/* computed adaptively, in the sense that exact arithmetic is used only to */
+/* the degree it is needed to ensure that the returned value has the */
+/* correct sign. Hence, this function is usually quite fast, but will run */
+/* more slowly when the input points are coplanar or nearly so. */
+/* */
+/* See my Robust Predicates paper for details. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+REAL orient3dadapt(vertex pa, vertex pb, vertex pc, vertex pd,
+ REAL aheight, REAL bheight, REAL cheight, REAL dheight,
+ REAL permanent)
+#else /* not ANSI_DECLARATORS */
+REAL orient3dadapt(pa, pb, pc, pd,
+ aheight, bheight, cheight, dheight, permanent)
+vertex pa;
+vertex pb;
+vertex pc;
+vertex pd;
+REAL aheight;
+REAL bheight;
+REAL cheight;
+REAL dheight;
+REAL permanent;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ INEXACT REAL adx, bdx, cdx, ady, bdy, cdy, adheight, bdheight, cdheight;
+ REAL det, errbound;
+
+ INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1;
+ REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0;
+ REAL bc[4], ca[4], ab[4];
+ INEXACT REAL bc3, ca3, ab3;
+ REAL adet[8], bdet[8], cdet[8];
+ int alen, blen, clen;
+ REAL abdet[16];
+ int ablen;
+ REAL *finnow, *finother, *finswap;
+ REAL fin1[192], fin2[192];
+ int finlength;
+
+ REAL adxtail, bdxtail, cdxtail;
+ REAL adytail, bdytail, cdytail;
+ REAL adheighttail, bdheighttail, cdheighttail;
+ INEXACT REAL at_blarge, at_clarge;
+ INEXACT REAL bt_clarge, bt_alarge;
+ INEXACT REAL ct_alarge, ct_blarge;
+ REAL at_b[4], at_c[4], bt_c[4], bt_a[4], ct_a[4], ct_b[4];
+ int at_blen, at_clen, bt_clen, bt_alen, ct_alen, ct_blen;
+ INEXACT REAL bdxt_cdy1, cdxt_bdy1, cdxt_ady1;
+ INEXACT REAL adxt_cdy1, adxt_bdy1, bdxt_ady1;
+ REAL bdxt_cdy0, cdxt_bdy0, cdxt_ady0;
+ REAL adxt_cdy0, adxt_bdy0, bdxt_ady0;
+ INEXACT REAL bdyt_cdx1, cdyt_bdx1, cdyt_adx1;
+ INEXACT REAL adyt_cdx1, adyt_bdx1, bdyt_adx1;
+ REAL bdyt_cdx0, cdyt_bdx0, cdyt_adx0;
+ REAL adyt_cdx0, adyt_bdx0, bdyt_adx0;
+ REAL bct[8], cat[8], abt[8];
+ int bctlen, catlen, abtlen;
+ INEXACT REAL bdxt_cdyt1, cdxt_bdyt1, cdxt_adyt1;
+ INEXACT REAL adxt_cdyt1, adxt_bdyt1, bdxt_adyt1;
+ REAL bdxt_cdyt0, cdxt_bdyt0, cdxt_adyt0;
+ REAL adxt_cdyt0, adxt_bdyt0, bdxt_adyt0;
+ REAL u[4], v[12], w[16];
+ INEXACT REAL u3;
+ int vlength, wlength;
+ REAL negate;
+
+ INEXACT REAL bvirt;
+ REAL avirt, bround, around;
+ INEXACT REAL c;
+ INEXACT REAL abig;
+ REAL ahi, alo, bhi, blo;
+ REAL err1, err2, err3;
+ INEXACT REAL _i, _j, _k;
+ REAL _0;
+
+ adx = (REAL) (pa[0] - pd[0]);
+ bdx = (REAL) (pb[0] - pd[0]);
+ cdx = (REAL) (pc[0] - pd[0]);
+ ady = (REAL) (pa[1] - pd[1]);
+ bdy = (REAL) (pb[1] - pd[1]);
+ cdy = (REAL) (pc[1] - pd[1]);
+ adheight = (REAL) (aheight - dheight);
+ bdheight = (REAL) (bheight - dheight);
+ cdheight = (REAL) (cheight - dheight);
+
+ Two_Product(bdx, cdy, bdxcdy1, bdxcdy0);
+ Two_Product(cdx, bdy, cdxbdy1, cdxbdy0);
+ Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]);
+ bc[3] = bc3;
+ alen = scale_expansion_zeroelim(4, bc, adheight, adet);
+
+ Two_Product(cdx, ady, cdxady1, cdxady0);
+ Two_Product(adx, cdy, adxcdy1, adxcdy0);
+ Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]);
+ ca[3] = ca3;
+ blen = scale_expansion_zeroelim(4, ca, bdheight, bdet);
+
+ Two_Product(adx, bdy, adxbdy1, adxbdy0);
+ Two_Product(bdx, ady, bdxady1, bdxady0);
+ Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]);
+ ab[3] = ab3;
+ clen = scale_expansion_zeroelim(4, ab, cdheight, cdet);
+
+ ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+ finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1);
+
+ det = estimate(finlength, fin1);
+ errbound = o3derrboundB * permanent;
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
+ Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
+ Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
+ Two_Diff_Tail(pa[1], pd[1], ady, adytail);
+ Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
+ Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
+ Two_Diff_Tail(aheight, dheight, adheight, adheighttail);
+ Two_Diff_Tail(bheight, dheight, bdheight, bdheighttail);
+ Two_Diff_Tail(cheight, dheight, cdheight, cdheighttail);
+
+ if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) &&
+ (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0) &&
+ (adheighttail == 0.0) &&
+ (bdheighttail == 0.0) &&
+ (cdheighttail == 0.0)) {
+ return det;
+ }
+
+ errbound = o3derrboundC * permanent + resulterrbound * Absolute(det);
+ det += (adheight * ((bdx * cdytail + cdy * bdxtail) -
+ (bdy * cdxtail + cdx * bdytail)) +
+ adheighttail * (bdx * cdy - bdy * cdx)) +
+ (bdheight * ((cdx * adytail + ady * cdxtail) -
+ (cdy * adxtail + adx * cdytail)) +
+ bdheighttail * (cdx * ady - cdy * adx)) +
+ (cdheight * ((adx * bdytail + bdy * adxtail) -
+ (ady * bdxtail + bdx * adytail)) +
+ cdheighttail * (adx * bdy - ady * bdx));
+ if ((det >= errbound) || (-det >= errbound)) {
+ return det;
+ }
+
+ finnow = fin1;
+ finother = fin2;
+
+ if (adxtail == 0.0) {
+ if (adytail == 0.0) {
+ at_b[0] = 0.0;
+ at_blen = 1;
+ at_c[0] = 0.0;
+ at_clen = 1;
+ } else {
+ negate = -adytail;
+ Two_Product(negate, bdx, at_blarge, at_b[0]);
+ at_b[1] = at_blarge;
+ at_blen = 2;
+ Two_Product(adytail, cdx, at_clarge, at_c[0]);
+ at_c[1] = at_clarge;
+ at_clen = 2;
+ }
+ } else {
+ if (adytail == 0.0) {
+ Two_Product(adxtail, bdy, at_blarge, at_b[0]);
+ at_b[1] = at_blarge;
+ at_blen = 2;
+ negate = -adxtail;
+ Two_Product(negate, cdy, at_clarge, at_c[0]);
+ at_c[1] = at_clarge;
+ at_clen = 2;
+ } else {
+ Two_Product(adxtail, bdy, adxt_bdy1, adxt_bdy0);
+ Two_Product(adytail, bdx, adyt_bdx1, adyt_bdx0);
+ Two_Two_Diff(adxt_bdy1, adxt_bdy0, adyt_bdx1, adyt_bdx0,
+ at_blarge, at_b[2], at_b[1], at_b[0]);
+ at_b[3] = at_blarge;
+ at_blen = 4;
+ Two_Product(adytail, cdx, adyt_cdx1, adyt_cdx0);
+ Two_Product(adxtail, cdy, adxt_cdy1, adxt_cdy0);
+ Two_Two_Diff(adyt_cdx1, adyt_cdx0, adxt_cdy1, adxt_cdy0,
+ at_clarge, at_c[2], at_c[1], at_c[0]);
+ at_c[3] = at_clarge;
+ at_clen = 4;
+ }
+ }
+ if (bdxtail == 0.0) {
+ if (bdytail == 0.0) {
+ bt_c[0] = 0.0;
+ bt_clen = 1;
+ bt_a[0] = 0.0;
+ bt_alen = 1;
+ } else {
+ negate = -bdytail;
+ Two_Product(negate, cdx, bt_clarge, bt_c[0]);
+ bt_c[1] = bt_clarge;
+ bt_clen = 2;
+ Two_Product(bdytail, adx, bt_alarge, bt_a[0]);
+ bt_a[1] = bt_alarge;
+ bt_alen = 2;
+ }
+ } else {
+ if (bdytail == 0.0) {
+ Two_Product(bdxtail, cdy, bt_clarge, bt_c[0]);
+ bt_c[1] = bt_clarge;
+ bt_clen = 2;
+ negate = -bdxtail;
+ Two_Product(negate, ady, bt_alarge, bt_a[0]);
+ bt_a[1] = bt_alarge;
+ bt_alen = 2;
+ } else {
+ Two_Product(bdxtail, cdy, bdxt_cdy1, bdxt_cdy0);
+ Two_Product(bdytail, cdx, bdyt_cdx1, bdyt_cdx0);
+ Two_Two_Diff(bdxt_cdy1, bdxt_cdy0, bdyt_cdx1, bdyt_cdx0,
+ bt_clarge, bt_c[2], bt_c[1], bt_c[0]);
+ bt_c[3] = bt_clarge;
+ bt_clen = 4;
+ Two_Product(bdytail, adx, bdyt_adx1, bdyt_adx0);
+ Two_Product(bdxtail, ady, bdxt_ady1, bdxt_ady0);
+ Two_Two_Diff(bdyt_adx1, bdyt_adx0, bdxt_ady1, bdxt_ady0,
+ bt_alarge, bt_a[2], bt_a[1], bt_a[0]);
+ bt_a[3] = bt_alarge;
+ bt_alen = 4;
+ }
+ }
+ if (cdxtail == 0.0) {
+ if (cdytail == 0.0) {
+ ct_a[0] = 0.0;
+ ct_alen = 1;
+ ct_b[0] = 0.0;
+ ct_blen = 1;
+ } else {
+ negate = -cdytail;
+ Two_Product(negate, adx, ct_alarge, ct_a[0]);
+ ct_a[1] = ct_alarge;
+ ct_alen = 2;
+ Two_Product(cdytail, bdx, ct_blarge, ct_b[0]);
+ ct_b[1] = ct_blarge;
+ ct_blen = 2;
+ }
+ } else {
+ if (cdytail == 0.0) {
+ Two_Product(cdxtail, ady, ct_alarge, ct_a[0]);
+ ct_a[1] = ct_alarge;
+ ct_alen = 2;
+ negate = -cdxtail;
+ Two_Product(negate, bdy, ct_blarge, ct_b[0]);
+ ct_b[1] = ct_blarge;
+ ct_blen = 2;
+ } else {
+ Two_Product(cdxtail, ady, cdxt_ady1, cdxt_ady0);
+ Two_Product(cdytail, adx, cdyt_adx1, cdyt_adx0);
+ Two_Two_Diff(cdxt_ady1, cdxt_ady0, cdyt_adx1, cdyt_adx0,
+ ct_alarge, ct_a[2], ct_a[1], ct_a[0]);
+ ct_a[3] = ct_alarge;
+ ct_alen = 4;
+ Two_Product(cdytail, bdx, cdyt_bdx1, cdyt_bdx0);
+ Two_Product(cdxtail, bdy, cdxt_bdy1, cdxt_bdy0);
+ Two_Two_Diff(cdyt_bdx1, cdyt_bdx0, cdxt_bdy1, cdxt_bdy0,
+ ct_blarge, ct_b[2], ct_b[1], ct_b[0]);
+ ct_b[3] = ct_blarge;
+ ct_blen = 4;
+ }
+ }
+
+ bctlen = fast_expansion_sum_zeroelim(bt_clen, bt_c, ct_blen, ct_b, bct);
+ wlength = scale_expansion_zeroelim(bctlen, bct, adheight, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+
+ catlen = fast_expansion_sum_zeroelim(ct_alen, ct_a, at_clen, at_c, cat);
+ wlength = scale_expansion_zeroelim(catlen, cat, bdheight, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+
+ abtlen = fast_expansion_sum_zeroelim(at_blen, at_b, bt_alen, bt_a, abt);
+ wlength = scale_expansion_zeroelim(abtlen, abt, cdheight, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+
+ if (adheighttail != 0.0) {
+ vlength = scale_expansion_zeroelim(4, bc, adheighttail, v);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ if (bdheighttail != 0.0) {
+ vlength = scale_expansion_zeroelim(4, ca, bdheighttail, v);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ if (cdheighttail != 0.0) {
+ vlength = scale_expansion_zeroelim(4, ab, cdheighttail, v);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+
+ if (adxtail != 0.0) {
+ if (bdytail != 0.0) {
+ Two_Product(adxtail, bdytail, adxt_bdyt1, adxt_bdyt0);
+ Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdheight, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ if (cdheighttail != 0.0) {
+ Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdheighttail,
+ u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ }
+ if (cdytail != 0.0) {
+ negate = -adxtail;
+ Two_Product(negate, cdytail, adxt_cdyt1, adxt_cdyt0);
+ Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdheight, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ if (bdheighttail != 0.0) {
+ Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdheighttail,
+ u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ }
+ }
+ if (bdxtail != 0.0) {
+ if (cdytail != 0.0) {
+ Two_Product(bdxtail, cdytail, bdxt_cdyt1, bdxt_cdyt0);
+ Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adheight, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ if (adheighttail != 0.0) {
+ Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adheighttail,
+ u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ }
+ if (adytail != 0.0) {
+ negate = -bdxtail;
+ Two_Product(negate, adytail, bdxt_adyt1, bdxt_adyt0);
+ Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdheight, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ if (cdheighttail != 0.0) {
+ Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdheighttail,
+ u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ }
+ }
+ if (cdxtail != 0.0) {
+ if (adytail != 0.0) {
+ Two_Product(cdxtail, adytail, cdxt_adyt1, cdxt_adyt0);
+ Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdheight, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ if (bdheighttail != 0.0) {
+ Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdheighttail,
+ u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ }
+ if (bdytail != 0.0) {
+ negate = -cdxtail;
+ Two_Product(negate, bdytail, cdxt_bdyt1, cdxt_bdyt0);
+ Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adheight, u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ if (adheighttail != 0.0) {
+ Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adheighttail,
+ u3, u[2], u[1], u[0]);
+ u[3] = u3;
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ }
+ }
+
+ if (adheighttail != 0.0) {
+ wlength = scale_expansion_zeroelim(bctlen, bct, adheighttail, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ if (bdheighttail != 0.0) {
+ wlength = scale_expansion_zeroelim(catlen, cat, bdheighttail, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+ if (cdheighttail != 0.0) {
+ wlength = scale_expansion_zeroelim(abtlen, abt, cdheighttail, w);
+ finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+ finother);
+ finswap = finnow; finnow = finother; finother = finswap;
+ }
+
+ return finnow[finlength - 1];
+}
+
+#ifdef ANSI_DECLARATORS
+REAL orient3d(struct mesh *m, struct behavior *b,
+ vertex pa, vertex pb, vertex pc, vertex pd,
+ REAL aheight, REAL bheight, REAL cheight, REAL dheight)
+#else /* not ANSI_DECLARATORS */
+REAL orient3d(m, b, pa, pb, pc, pd, aheight, bheight, cheight, dheight)
+struct mesh *m;
+struct behavior *b;
+vertex pa;
+vertex pb;
+vertex pc;
+vertex pd;
+REAL aheight;
+REAL bheight;
+REAL cheight;
+REAL dheight;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ REAL adx, bdx, cdx, ady, bdy, cdy, adheight, bdheight, cdheight;
+ REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
+ REAL det;
+ REAL permanent, errbound;
+
+ m->orient3dcount++;
+
+ adx = pa[0] - pd[0];
+ bdx = pb[0] - pd[0];
+ cdx = pc[0] - pd[0];
+ ady = pa[1] - pd[1];
+ bdy = pb[1] - pd[1];
+ cdy = pc[1] - pd[1];
+ adheight = aheight - dheight;
+ bdheight = bheight - dheight;
+ cdheight = cheight - dheight;
+
+ bdxcdy = bdx * cdy;
+ cdxbdy = cdx * bdy;
+
+ cdxady = cdx * ady;
+ adxcdy = adx * cdy;
+
+ adxbdy = adx * bdy;
+ bdxady = bdx * ady;
+
+ det = adheight * (bdxcdy - cdxbdy)
+ + bdheight * (cdxady - adxcdy)
+ + cdheight * (adxbdy - bdxady);
+
+ if (b->noexact) {
+ return det;
+ }
+
+ permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adheight)
+ + (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdheight)
+ + (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdheight);
+ errbound = o3derrboundA * permanent;
+ if ((det > errbound) || (-det > errbound)) {
+ return det;
+ }
+
+ return orient3dadapt(pa, pb, pc, pd, aheight, bheight, cheight, dheight,
+ permanent);
+}
+
+/*****************************************************************************/
+/* */
+/* nonregular() Return a positive value if the point pd is incompatible */
+/* with the circle or plane passing through pa, pb, and pc */
+/* (meaning that pd is inside the circle or below the */
+/* plane); a negative value if it is compatible; and zero if */
+/* the four points are cocircular/coplanar. The points pa, */
+/* pb, and pc must be in counterclockwise order, or the sign */
+/* of the result will be reversed. */
+/* */
+/* If the -w switch is used, the points are lifted onto the parabolic */
+/* lifting map, then they are dropped according to their weights, then the */
+/* 3D orientation test is applied. If the -W switch is used, the points' */
+/* heights are already provided, so the 3D orientation test is applied */
+/* directly. If neither switch is used, the incircle test is applied. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+REAL nonregular(struct mesh *m, struct behavior *b,
+ vertex pa, vertex pb, vertex pc, vertex pd)
+#else /* not ANSI_DECLARATORS */
+REAL nonregular(m, b, pa, pb, pc, pd)
+struct mesh *m;
+struct behavior *b;
+vertex pa;
+vertex pb;
+vertex pc;
+vertex pd;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ if (b->weighted == 0) {
+ return incircle(m, b, pa, pb, pc, pd);
+ } else if (b->weighted == 1) {
+ return orient3d(m, b, pa, pb, pc, pd,
+ pa[0] * pa[0] + pa[1] * pa[1] - pa[2],
+ pb[0] * pb[0] + pb[1] * pb[1] - pb[2],
+ pc[0] * pc[0] + pc[1] * pc[1] - pc[2],
+ pd[0] * pd[0] + pd[1] * pd[1] - pd[2]);
+ } else {
+ return orient3d(m, b, pa, pb, pc, pd, pa[2], pb[2], pc[2], pd[2]);
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* findcircumcenter() Find the circumcenter of a triangle. */
+/* */
+/* The result is returned both in terms of x-y coordinates and xi-eta */
+/* (barycentric) coordinates. The xi-eta coordinate system is defined in */
+/* terms of the triangle: the origin of the triangle is the origin of the */
+/* coordinate system; the destination of the triangle is one unit along the */
+/* xi axis; and the apex of the triangle is one unit along the eta axis. */
+/* This procedure also returns the square of the length of the triangle's */
+/* shortest edge. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void findcircumcenter(struct mesh *m, struct behavior *b,
+ vertex torg, vertex tdest, vertex tapex,
+ vertex circumcenter, REAL *xi, REAL *eta, int offcenter)
+#else /* not ANSI_DECLARATORS */
+void findcircumcenter(m, b, torg, tdest, tapex, circumcenter, xi, eta,
+ offcenter)
+struct mesh *m;
+struct behavior *b;
+vertex torg;
+vertex tdest;
+vertex tapex;
+vertex circumcenter;
+REAL *xi;
+REAL *eta;
+int offcenter;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ REAL xdo, ydo, xao, yao;
+ REAL dodist, aodist, dadist;
+ REAL denominator;
+ REAL dx, dy, dxoff, dyoff;
+
+ m->circumcentercount++;
+
+ /* Compute the circumcenter of the triangle. */
+ xdo = tdest[0] - torg[0];
+ ydo = tdest[1] - torg[1];
+ xao = tapex[0] - torg[0];
+ yao = tapex[1] - torg[1];
+ dodist = xdo * xdo + ydo * ydo;
+ aodist = xao * xao + yao * yao;
+ dadist = (tdest[0] - tapex[0]) * (tdest[0] - tapex[0]) +
+ (tdest[1] - tapex[1]) * (tdest[1] - tapex[1]);
+ if (b->noexact) {
+ denominator = 0.5 / (xdo * yao - xao * ydo);
+ } else {
+ /* Use the counterclockwise() routine to ensure a positive (and */
+ /* reasonably accurate) result, avoiding any possibility of */
+ /* division by zero. */
+ denominator = 0.5 / counterclockwise(m, b, tdest, tapex, torg);
+ /* Don't count the above as an orientation test. */
+ m->counterclockcount--;
+ }
+ dx = (yao * dodist - ydo * aodist) * denominator;
+ dy = (xdo * aodist - xao * dodist) * denominator;
+
+ /* Find the (squared) length of the triangle's shortest edge. This */
+ /* serves as a conservative estimate of the insertion radius of the */
+ /* circumcenter's parent. The estimate is used to ensure that */
+ /* the algorithm terminates even if very small angles appear in */
+ /* the input PSLG. */
+ if ((dodist < aodist) && (dodist < dadist)) {
+ if (offcenter && (b->offconstant > 0.0)) {
+ /* Find the position of the off-center, as described by Alper Ungor. */
+ dxoff = 0.5 * xdo - b->offconstant * ydo;
+ dyoff = 0.5 * ydo + b->offconstant * xdo;
+ /* If the off-center is closer to the origin than the */
+ /* circumcenter, use the off-center instead. */
+ if (dxoff * dxoff + dyoff * dyoff < dx * dx + dy * dy) {
+ dx = dxoff;
+ dy = dyoff;
+ }
+ }
+ } else if (aodist < dadist) {
+ if (offcenter && (b->offconstant > 0.0)) {
+ dxoff = 0.5 * xao + b->offconstant * yao;
+ dyoff = 0.5 * yao - b->offconstant * xao;
+ /* If the off-center is closer to the origin than the */
+ /* circumcenter, use the off-center instead. */
+ if (dxoff * dxoff + dyoff * dyoff < dx * dx + dy * dy) {
+ dx = dxoff;
+ dy = dyoff;
+ }
+ }
+ } else {
+ if (offcenter && (b->offconstant > 0.0)) {
+ dxoff = 0.5 * (tapex[0] - tdest[0]) -
+ b->offconstant * (tapex[1] - tdest[1]);
+ dyoff = 0.5 * (tapex[1] - tdest[1]) +
+ b->offconstant * (tapex[0] - tdest[0]);
+ /* If the off-center is closer to the destination than the */
+ /* circumcenter, use the off-center instead. */
+ if (dxoff * dxoff + dyoff * dyoff <
+ (dx - xdo) * (dx - xdo) + (dy - ydo) * (dy - ydo)) {
+ dx = xdo + dxoff;
+ dy = ydo + dyoff;
+ }
+ }
+ }
+
+ circumcenter[0] = torg[0] + dx;
+ circumcenter[1] = torg[1] + dy;
+
+ /* To interpolate vertex attributes for the new vertex inserted at */
+ /* the circumcenter, define a coordinate system with a xi-axis, */
+ /* directed from the triangle's origin to its destination, and */
+ /* an eta-axis, directed from its origin to its apex. */
+ /* Calculate the xi and eta coordinates of the circumcenter. */
+ *xi = (yao * dx - xao * dy) * (2.0 * denominator);
+ *eta = (xdo * dy - ydo * dx) * (2.0 * denominator);
+}
+
+/** **/
+/** **/
+/********* Geometric primitives end here *********/
+
+/*****************************************************************************/
+/* */
+/* triangleinit() Initialize some variables. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void triangleinit(struct mesh *m)
+#else /* not ANSI_DECLARATORS */
+void triangleinit(m)
+struct mesh *m;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ poolzero(&m->vertices);
+ poolzero(&m->triangles);
+ poolzero(&m->subsegs);
+ poolzero(&m->viri);
+ poolzero(&m->badsubsegs);
+ poolzero(&m->badtriangles);
+ poolzero(&m->flipstackers);
+ poolzero(&m->splaynodes);
+
+ m->recenttri.tri = (triangle *) NULL; /* No triangle has been visited yet. */
+ m->undeads = 0; /* No eliminated input vertices yet. */
+ m->samples = 1; /* Point location should take at least one sample. */
+ m->checksegments = 0; /* There are no segments in the triangulation yet. */
+ m->checkquality = 0; /* The quality triangulation stage has not begun. */
+ m->incirclecount = m->counterclockcount = m->orient3dcount = 0;
+ m->hyperbolacount = m->circletopcount = m->circumcentercount = 0;
+ randomseed = 1;
+
+ exactinit(); /* Initialize exact arithmetic constants. */
+}
+
+/*****************************************************************************/
+/* */
+/* randomnation() Generate a random number between 0 and `choices' - 1. */
+/* */
+/* This is a simple linear congruential random number generator. Hence, it */
+/* is a bad random number generator, but good enough for most randomized */
+/* geometric algorithms. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+unsigned long randomnation(unsigned int choices)
+#else /* not ANSI_DECLARATORS */
+unsigned long randomnation(choices)
+unsigned int choices;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ randomseed = (randomseed * 1366l + 150889l) % 714025l;
+ return randomseed / (714025l / choices + 1);
+}
+
+/********* Mesh quality testing routines begin here *********/
+/** **/
+/** **/
+
+/*****************************************************************************/
+/* */
+/* checkmesh() Test the mesh for topological consistency. */
+/* */
+/*****************************************************************************/
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void checkmesh(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void checkmesh(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri triangleloop;
+ struct otri oppotri, oppooppotri;
+ vertex triorg, tridest, triapex;
+ vertex oppoorg, oppodest;
+ int horrors;
+ int saveexact;
+ triangle ptr; /* Temporary variable used by sym(). */
+
+ /* Temporarily turn on exact arithmetic if it's off. */
+ saveexact = b->noexact;
+ b->noexact = 0;
+ if (!b->quiet) {
+ printf(" Checking consistency of mesh...\n");
+ }
+ horrors = 0;
+ /* Run through the list of triangles, checking each one. */
+ traversalinit(&m->triangles);
+ triangleloop.tri = triangletraverse(m);
+ while (triangleloop.tri != (triangle *) NULL) {
+ /* Check all three edges of the triangle. */
+ for (triangleloop.orient = 0; triangleloop.orient < 3;
+ triangleloop.orient++) {
+ org(triangleloop, triorg);
+ dest(triangleloop, tridest);
+ if (triangleloop.orient == 0) { /* Only test for inversion once. */
+ /* Test if the triangle is flat or inverted. */
+ apex(triangleloop, triapex);
+ if (counterclockwise(m, b, triorg, tridest, triapex) <= 0.0) {
+ printf(" !! !! Inverted ");
+ printtriangle(m, b, &triangleloop);
+ horrors++;
+ }
+ }
+ /* Find the neighboring triangle on this edge. */
+ sym(triangleloop, oppotri);
+ if (oppotri.tri != m->dummytri) {
+ /* Check that the triangle's neighbor knows it's a neighbor. */
+ sym(oppotri, oppooppotri);
+ if ((triangleloop.tri != oppooppotri.tri)
+ || (triangleloop.orient != oppooppotri.orient)) {
+ printf(" !! !! Asymmetric triangle-triangle bond:\n");
+ if (triangleloop.tri == oppooppotri.tri) {
+ printf(" (Right triangle, wrong orientation)\n");
+ }
+ printf(" First ");
+ printtriangle(m, b, &triangleloop);
+ printf(" Second (nonreciprocating) ");
+ printtriangle(m, b, &oppotri);
+ horrors++;
+ }
+ /* Check that both triangles agree on the identities */
+ /* of their shared vertices. */
+ org(oppotri, oppoorg);
+ dest(oppotri, oppodest);
+ if ((triorg != oppodest) || (tridest != oppoorg)) {
+ printf(" !! !! Mismatched edge coordinates between two triangles:\n"
+ );
+ printf(" First mismatched ");
+ printtriangle(m, b, &triangleloop);
+ printf(" Second mismatched ");
+ printtriangle(m, b, &oppotri);
+ horrors++;
+ }
+ }
+ }
+ triangleloop.tri = triangletraverse(m);
+ }
+ if (horrors == 0) {
+ if (!b->quiet) {
+ printf(" In my studied opinion, the mesh appears to be consistent.\n");
+ }
+ } else if (horrors == 1) {
+ printf(" !! !! !! !! Precisely one festering wound discovered.\n");
+ } else {
+ printf(" !! !! !! !! %d abominations witnessed.\n", horrors);
+ }
+ /* Restore the status of exact arithmetic. */
+ b->noexact = saveexact;
+}
+
+#endif /* not REDUCED */
+
+/*****************************************************************************/
+/* */
+/* checkdelaunay() Ensure that the mesh is (constrained) Delaunay. */
+/* */
+/*****************************************************************************/
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void checkdelaunay(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void checkdelaunay(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri triangleloop;
+ struct otri oppotri;
+ struct osub opposubseg;
+ vertex triorg, tridest, triapex;
+ vertex oppoapex;
+ int shouldbedelaunay;
+ int horrors;
+ int saveexact;
+ triangle ptr; /* Temporary variable used by sym(). */
+ subseg sptr; /* Temporary variable used by tspivot(). */
+
+ /* Temporarily turn on exact arithmetic if it's off. */
+ saveexact = b->noexact;
+ b->noexact = 0;
+ if (!b->quiet) {
+ printf(" Checking Delaunay property of mesh...\n");
+ }
+ horrors = 0;
+ /* Run through the list of triangles, checking each one. */
+ traversalinit(&m->triangles);
+ triangleloop.tri = triangletraverse(m);
+ while (triangleloop.tri != (triangle *) NULL) {
+ /* Check all three edges of the triangle. */
+ for (triangleloop.orient = 0; triangleloop.orient < 3;
+ triangleloop.orient++) {
+ org(triangleloop, triorg);
+ dest(triangleloop, tridest);
+ apex(triangleloop, triapex);
+ sym(triangleloop, oppotri);
+ apex(oppotri, oppoapex);
+ /* Only test that the edge is locally Delaunay if there is an */
+ /* adjoining triangle whose pointer is larger (to ensure that */
+ /* each pair isn't tested twice). */
+ shouldbedelaunay = (oppotri.tri != m->dummytri) &&
+ !deadtri(oppotri.tri) && (triangleloop.tri < oppotri.tri) &&
+ (triorg != m->infvertex1) && (triorg != m->infvertex2) &&
+ (triorg != m->infvertex3) &&
+ (tridest != m->infvertex1) && (tridest != m->infvertex2) &&
+ (tridest != m->infvertex3) &&
+ (triapex != m->infvertex1) && (triapex != m->infvertex2) &&
+ (triapex != m->infvertex3) &&
+ (oppoapex != m->infvertex1) && (oppoapex != m->infvertex2) &&
+ (oppoapex != m->infvertex3);
+ if (m->checksegments && shouldbedelaunay) {
+ /* If a subsegment separates the triangles, then the edge is */
+ /* constrained, so no local Delaunay test should be done. */
+ tspivot(triangleloop, opposubseg);
+ if (opposubseg.ss != m->dummysub){
+ shouldbedelaunay = 0;
+ }
+ }
+ if (shouldbedelaunay) {
+ if (nonregular(m, b, triorg, tridest, triapex, oppoapex) > 0.0) {
+ if (!b->weighted) {
+ printf(" !! !! Non-Delaunay pair of triangles:\n");
+ printf(" First non-Delaunay ");
+ printtriangle(m, b, &triangleloop);
+ printf(" Second non-Delaunay ");
+ } else {
+ printf(" !! !! Non-regular pair of triangles:\n");
+ printf(" First non-regular ");
+ printtriangle(m, b, &triangleloop);
+ printf(" Second non-regular ");
+ }
+ printtriangle(m, b, &oppotri);
+ horrors++;
+ }
+ }
+ }
+ triangleloop.tri = triangletraverse(m);
+ }
+ if (horrors == 0) {
+ if (!b->quiet) {
+ printf(
+ " By virtue of my perceptive intelligence, I declare the mesh Delaunay.\n");
+ }
+ } else if (horrors == 1) {
+ printf(
+ " !! !! !! !! Precisely one terrifying transgression identified.\n");
+ } else {
+ printf(" !! !! !! !! %d obscenities viewed with horror.\n", horrors);
+ }
+ /* Restore the status of exact arithmetic. */
+ b->noexact = saveexact;
+}
+
+#endif /* not REDUCED */
+
+/*****************************************************************************/
+/* */
+/* enqueuebadtriang() Add a bad triangle data structure to the end of a */
+/* queue. */
+/* */
+/* The queue is actually a set of 4096 queues. I use multiple queues to */
+/* give priority to smaller angles. I originally implemented a heap, but */
+/* the queues are faster by a larger margin than I'd suspected. */
+/* */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void enqueuebadtriang(struct mesh *m, struct behavior *b,
+ struct badtriang *badtri)
+#else /* not ANSI_DECLARATORS */
+void enqueuebadtriang(m, b, badtri)
+struct mesh *m;
+struct behavior *b;
+struct badtriang *badtri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ REAL length, multiplier;
+ int exponent, expincrement;
+ int queuenumber;
+ int posexponent;
+ int i;
+
+ if (b->verbose > 2) {
+ printf(" Queueing bad triangle:\n");
+ printf(" (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+ badtri->triangorg[0], badtri->triangorg[1],
+ badtri->triangdest[0], badtri->triangdest[1],
+ badtri->triangapex[0], badtri->triangapex[1]);
+ }
+
+ /* Determine the appropriate queue to put the bad triangle into. */
+ /* Recall that the key is the square of its shortest edge length. */
+ if (badtri->key >= 1.0) {
+ length = badtri->key;
+ posexponent = 1;
+ } else {
+ /* `badtri->key' is 2.0 to a negative exponent, so we'll record that */
+ /* fact and use the reciprocal of `badtri->key', which is > 1.0. */
+ length = 1.0 / badtri->key;
+ posexponent = 0;
+ }
+ /* `length' is approximately 2.0 to what exponent? The following code */
+ /* determines the answer in time logarithmic in the exponent. */
+ exponent = 0;
+ while (length > 2.0) {
+ /* Find an approximation by repeated squaring of two. */
+ expincrement = 1;
+ multiplier = 0.5;
+ while (length * multiplier * multiplier > 1.0) {
+ expincrement *= 2;
+ multiplier *= multiplier;
+ }
+ /* Reduce the value of `length', then iterate if necessary. */
+ exponent += expincrement;
+ length *= multiplier;
+ }
+ /* `length' is approximately squareroot(2.0) to what exponent? */
+ exponent = 2.0 * exponent + (length > SQUAREROOTTWO);
+ /* `exponent' is now in the range 0...2047 for IEEE double precision. */
+ /* Choose a queue in the range 0...4095. The shortest edges have the */
+ /* highest priority (queue 4095). */
+ if (posexponent) {
+ queuenumber = 2047 - exponent;
+ } else {
+ queuenumber = 2048 + exponent;
+ }
+
+ /* Are we inserting into an empty queue? */
+ if (m->queuefront[queuenumber] == (struct badtriang *) NULL) {
+ /* Yes, we are inserting into an empty queue. */
+ /* Will this become the highest-priority queue? */
+ if (queuenumber > m->firstnonemptyq) {
+ /* Yes, this is the highest-priority queue. */
+ m->nextnonemptyq[queuenumber] = m->firstnonemptyq;
+ m->firstnonemptyq = queuenumber;
+ } else {
+ /* No, this is not the highest-priority queue. */
+ /* Find the queue with next higher priority. */
+ i = queuenumber + 1;
+ while (m->queuefront[i] == (struct badtriang *) NULL) {
+ i++;
+ }
+ /* Mark the newly nonempty queue as following a higher-priority queue. */
+ m->nextnonemptyq[queuenumber] = m->nextnonemptyq[i];
+ m->nextnonemptyq[i] = queuenumber;
+ }
+ /* Put the bad triangle at the beginning of the (empty) queue. */
+ m->queuefront[queuenumber] = badtri;
+ } else {
+ /* Add the bad triangle to the end of an already nonempty queue. */
+ m->queuetail[queuenumber]->nexttriang = badtri;
+ }
+ /* Maintain a pointer to the last triangle of the queue. */
+ m->queuetail[queuenumber] = badtri;
+ /* Newly enqueued bad triangle has no successor in the queue. */
+ badtri->nexttriang = (struct badtriang *) NULL;
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/* */
+/* enqueuebadtri() Add a bad triangle to the end of a queue. */
+/* */
+/* Allocates a badtriang data structure for the triangle, then passes it to */
+/* enqueuebadtriang(). */
+/* */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void enqueuebadtri(struct mesh *m, struct behavior *b, struct otri *enqtri,
+ REAL minedge, vertex enqapex, vertex enqorg, vertex enqdest)
+#else /* not ANSI_DECLARATORS */
+void enqueuebadtri(m, b, enqtri, minedge, enqapex, enqorg, enqdest)
+struct mesh *m;
+struct behavior *b;
+struct otri *enqtri;
+REAL minedge;
+vertex enqapex;
+vertex enqorg;
+vertex enqdest;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct badtriang *newbad;
+
+ /* Allocate space for the bad triangle. */
+ newbad = (struct badtriang *) poolalloc(&m->badtriangles);
+ newbad->poortri = encode(*enqtri);
+ newbad->key = minedge;
+ newbad->triangapex = enqapex;
+ newbad->triangorg = enqorg;
+ newbad->triangdest = enqdest;
+ enqueuebadtriang(m, b, newbad);
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/* */
+/* dequeuebadtriang() Remove a triangle from the front of the queue. */
+/* */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+struct badtriang *dequeuebadtriang(struct mesh *m)
+#else /* not ANSI_DECLARATORS */
+struct badtriang *dequeuebadtriang(m)
+struct mesh *m;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct badtriang *result;
+
+ /* If no queues are nonempty, return NULL. */
+ if (m->firstnonemptyq < 0) {
+ return (struct badtriang *) NULL;
+ }
+ /* Find the first triangle of the highest-priority queue. */
+ result = m->queuefront[m->firstnonemptyq];
+ /* Remove the triangle from the queue. */
+ m->queuefront[m->firstnonemptyq] = result->nexttriang;
+ /* If this queue is now empty, note the new highest-priority */
+ /* nonempty queue. */
+ if (result == m->queuetail[m->firstnonemptyq]) {
+ m->firstnonemptyq = m->nextnonemptyq[m->firstnonemptyq];
+ }
+ return result;
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/* */
+/* checkseg4encroach() Check a subsegment to see if it is encroached; add */
+/* it to the list if it is. */
+/* */
+/* A subsegment is encroached if there is a vertex in its diametral lens. */
+/* For Ruppert's algorithm (-D switch), the "diametral lens" is the */
+/* diametral circle. For Chew's algorithm (default), the diametral lens is */
+/* just big enough to enclose two isosceles triangles whose bases are the */
+/* subsegment. Each of the two isosceles triangles has two angles equal */
+/* to `b->minangle'. */
+/* */
+/* Chew's algorithm does not require diametral lenses at all--but they save */
+/* time. Any vertex inside a subsegment's diametral lens implies that the */
+/* triangle adjoining the subsegment will be too skinny, so it's only a */
+/* matter of time before the encroaching vertex is deleted by Chew's */
+/* algorithm. It's faster to simply not insert the doomed vertex in the */
+/* first place, which is why I use diametral lenses with Chew's algorithm. */
+/* */
+/* Returns a nonzero value if the subsegment is encroached. */
+/* */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+int checkseg4encroach(struct mesh *m, struct behavior *b,
+ struct osub *testsubseg)
+#else /* not ANSI_DECLARATORS */
+int checkseg4encroach(m, b, testsubseg)
+struct mesh *m;
+struct behavior *b;
+struct osub *testsubseg;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri neighbortri;
+ struct osub testsym;
+ struct badsubseg *encroachedseg;
+ REAL dotproduct;
+ int encroached;
+ int sides;
+ vertex eorg, edest, eapex;
+ triangle ptr; /* Temporary variable used by stpivot(). */
+
+ encroached = 0;
+ sides = 0;
+
+ sorg(*testsubseg, eorg);
+ sdest(*testsubseg, edest);
+ /* Check one neighbor of the subsegment. */
+ stpivot(*testsubseg, neighbortri);
+ /* Does the neighbor exist, or is this a boundary edge? */
+ if (neighbortri.tri != m->dummytri) {
+ sides++;
+ /* Find a vertex opposite this subsegment. */
+ apex(neighbortri, eapex);
+ /* Check whether the apex is in the diametral lens of the subsegment */
+ /* (the diametral circle if `conformdel' is set). A dot product */
+ /* of two sides of the triangle is used to check whether the angle */
+ /* at the apex is greater than (180 - 2 `minangle') degrees (for */
+ /* lenses; 90 degrees for diametral circles). */
+ dotproduct = (eorg[0] - eapex[0]) * (edest[0] - eapex[0]) +
+ (eorg[1] - eapex[1]) * (edest[1] - eapex[1]);
+ if (dotproduct < 0.0) {
+ if (b->conformdel ||
+ (dotproduct * dotproduct >=
+ (2.0 * b->goodangle - 1.0) * (2.0 * b->goodangle - 1.0) *
+ ((eorg[0] - eapex[0]) * (eorg[0] - eapex[0]) +
+ (eorg[1] - eapex[1]) * (eorg[1] - eapex[1])) *
+ ((edest[0] - eapex[0]) * (edest[0] - eapex[0]) +
+ (edest[1] - eapex[1]) * (edest[1] - eapex[1])))) {
+ encroached = 1;
+ }
+ }
+ }
+ /* Check the other neighbor of the subsegment. */
+ ssym(*testsubseg, testsym);
+ stpivot(testsym, neighbortri);
+ /* Does the neighbor exist, or is this a boundary edge? */
+ if (neighbortri.tri != m->dummytri) {
+ sides++;
+ /* Find the other vertex opposite this subsegment. */
+ apex(neighbortri, eapex);
+ /* Check whether the apex is in the diametral lens of the subsegment */
+ /* (or the diametral circle, if `conformdel' is set). */
+ dotproduct = (eorg[0] - eapex[0]) * (edest[0] - eapex[0]) +
+ (eorg[1] - eapex[1]) * (edest[1] - eapex[1]);
+ if (dotproduct < 0.0) {
+ if (b->conformdel ||
+ (dotproduct * dotproduct >=
+ (2.0 * b->goodangle - 1.0) * (2.0 * b->goodangle - 1.0) *
+ ((eorg[0] - eapex[0]) * (eorg[0] - eapex[0]) +
+ (eorg[1] - eapex[1]) * (eorg[1] - eapex[1])) *
+ ((edest[0] - eapex[0]) * (edest[0] - eapex[0]) +
+ (edest[1] - eapex[1]) * (edest[1] - eapex[1])))) {
+ encroached += 2;
+ }
+ }
+ }
+
+ if (encroached && (!b->nobisect || ((b->nobisect == 1) && (sides == 2)))) {
+ if (b->verbose > 2) {
+ printf(
+ " Queueing encroached subsegment (%.12g, %.12g) (%.12g, %.12g).\n",
+ eorg[0], eorg[1], edest[0], edest[1]);
+ }
+ /* Add the subsegment to the list of encroached subsegments. */
+ /* Be sure to get the orientation right. */
+ encroachedseg = (struct badsubseg *) poolalloc(&m->badsubsegs);
+ if (encroached == 1) {
+ encroachedseg->encsubseg = sencode(*testsubseg);
+ encroachedseg->subsegorg = eorg;
+ encroachedseg->subsegdest = edest;
+ } else {
+ encroachedseg->encsubseg = sencode(testsym);
+ encroachedseg->subsegorg = edest;
+ encroachedseg->subsegdest = eorg;
+ }
+ }
+
+ return encroached;
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/* */
+/* testtriangle() Test a triangle for quality and size. */
+/* */
+/* Tests a triangle to see if it satisfies the minimum angle condition and */
+/* the maximum area condition. Triangles that aren't up to spec are added */
+/* to the bad triangle queue. */
+/* */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void testtriangle(struct mesh *m, struct behavior *b, struct otri *testtri)
+#else /* not ANSI_DECLARATORS */
+void testtriangle(m, b, testtri)
+struct mesh *m;
+struct behavior *b;
+struct otri *testtri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri tri1, tri2;
+ struct osub testsub;
+ vertex torg, tdest, tapex;
+ vertex base1, base2;
+ vertex org1, dest1, org2, dest2;
+ vertex joinvertex;
+ REAL dxod, dyod, dxda, dyda, dxao, dyao;
+ REAL dxod2, dyod2, dxda2, dyda2, dxao2, dyao2;
+ REAL apexlen, orglen, destlen, minedge;
+ REAL angle;
+ REAL area;
+ REAL dist1, dist2;
+ subseg sptr; /* Temporary variable used by tspivot(). */
+ triangle ptr; /* Temporary variable used by oprev() and dnext(). */
+
+ org(*testtri, torg);
+ dest(*testtri, tdest);
+ apex(*testtri, tapex);
+ dxod = torg[0] - tdest[0];
+ dyod = torg[1] - tdest[1];
+ dxda = tdest[0] - tapex[0];
+ dyda = tdest[1] - tapex[1];
+ dxao = tapex[0] - torg[0];
+ dyao = tapex[1] - torg[1];
+ dxod2 = dxod * dxod;
+ dyod2 = dyod * dyod;
+ dxda2 = dxda * dxda;
+ dyda2 = dyda * dyda;
+ dxao2 = dxao * dxao;
+ dyao2 = dyao * dyao;
+ /* Find the lengths of the triangle's three edges. */
+ apexlen = dxod2 + dyod2;
+ orglen = dxda2 + dyda2;
+ destlen = dxao2 + dyao2;
+
+ if ((apexlen < orglen) && (apexlen < destlen)) {
+ /* The edge opposite the apex is shortest. */
+ minedge = apexlen;
+ /* Find the square of the cosine of the angle at the apex. */
+ angle = dxda * dxao + dyda * dyao;
+ angle = angle * angle / (orglen * destlen);
+ base1 = torg;
+ base2 = tdest;
+ otricopy(*testtri, tri1);
+ } else if (orglen < destlen) {
+ /* The edge opposite the origin is shortest. */
+ minedge = orglen;
+ /* Find the square of the cosine of the angle at the origin. */
+ angle = dxod * dxao + dyod * dyao;
+ angle = angle * angle / (apexlen * destlen);
+ base1 = tdest;
+ base2 = tapex;
+ lnext(*testtri, tri1);
+ } else {
+ /* The edge opposite the destination is shortest. */
+ minedge = destlen;
+ /* Find the square of the cosine of the angle at the destination. */
+ angle = dxod * dxda + dyod * dyda;
+ angle = angle * angle / (apexlen * orglen);
+ base1 = tapex;
+ base2 = torg;
+ lprev(*testtri, tri1);
+ }
+
+ if (b->vararea || b->fixedarea || b->usertest) {
+ /* Check whether the area is larger than permitted. */
+ area = 0.5 * (dxod * dyda - dyod * dxda);
+ if (b->fixedarea && (area > b->maxarea)) {
+ /* Add this triangle to the list of bad triangles. */
+ enqueuebadtri(m, b, testtri, minedge, tapex, torg, tdest);
+ return;
+ }
+
+ /* Nonpositive area constraints are treated as unconstrained. */
+ if ((b->vararea) && (area > areabound(*testtri)) &&
+ (areabound(*testtri) > 0.0)) {
+ /* Add this triangle to the list of bad triangles. */
+ enqueuebadtri(m, b, testtri, minedge, tapex, torg, tdest);
+ return;
+ }
+
+ if (b->usertest) {
+ /* Check whether the user thinks this triangle is too large. */
+ if (triunsuitable(torg, tdest, tapex, area)) {
+ enqueuebadtri(m, b, testtri, minedge, tapex, torg, tdest);
+ return;
+ }
+ }
+ }
+
+ /* Check whether the angle is smaller than permitted. */
+ if (angle > b->goodangle) {
+ /* Use the rules of Miller, Pav, and Walkington to decide that certain */
+ /* triangles should not be split, even if they have bad angles. */
+ /* A skinny triangle is not split if its shortest edge subtends a */
+ /* small input angle, and both endpoints of the edge lie on a */
+ /* concentric circular shell. For convenience, I make a small */
+ /* adjustment to that rule: I check if the endpoints of the edge */
+ /* both lie in segment interiors, equidistant from the apex where */
+ /* the two segments meet. */
+ /* First, check if both points lie in segment interiors. */
+ if ((vertextype(base1) == SEGMENTVERTEX) &&
+ (vertextype(base2) == SEGMENTVERTEX)) {
+ /* Check if both points lie in a common segment. If they do, the */
+ /* skinny triangle is enqueued to be split as usual. */
+ tspivot(tri1, testsub);
+ if (testsub.ss == m->dummysub) {
+ /* No common segment. Find a subsegment that contains `torg'. */
+ otricopy(tri1, tri2);
+ do {
+ oprevself(tri1);
+ tspivot(tri1, testsub);
+ } while (testsub.ss == m->dummysub);
+ /* Find the endpoints of the containing segment. */
+ segorg(testsub, org1);
+ segdest(testsub, dest1);
+ /* Find a subsegment that contains `tdest'. */
+ do {
+ dnextself(tri2);
+ tspivot(tri2, testsub);
+ } while (testsub.ss == m->dummysub);
+ /* Find the endpoints of the containing segment. */
+ segorg(testsub, org2);
+ segdest(testsub, dest2);
+ /* Check if the two containing segments have an endpoint in common. */
+ joinvertex = (vertex) NULL;
+ if ((dest1[0] == org2[0]) && (dest1[1] == org2[1])) {
+ joinvertex = dest1;
+ } else if ((org1[0] == dest2[0]) && (org1[1] == dest2[1])) {
+ joinvertex = org1;
+ }
+ if (joinvertex != (vertex) NULL) {
+ /* Compute the distance from the common endpoint (of the two */
+ /* segments) to each of the endpoints of the shortest edge. */
+ dist1 = ((base1[0] - joinvertex[0]) * (base1[0] - joinvertex[0]) +
+ (base1[1] - joinvertex[1]) * (base1[1] - joinvertex[1]));
+ dist2 = ((base2[0] - joinvertex[0]) * (base2[0] - joinvertex[0]) +
+ (base2[1] - joinvertex[1]) * (base2[1] - joinvertex[1]));
+ /* If the two distances are equal, don't split the triangle. */
+ if ((dist1 < 1.001 * dist2) && (dist1 > 0.999 * dist2)) {
+ /* Return now to avoid enqueueing the bad triangle. */
+ return;
+ }
+ }
+ }
+ }
+
+ /* Add this triangle to the list of bad triangles. */
+ enqueuebadtri(m, b, testtri, minedge, tapex, torg, tdest);
+ }
+}
+
+#endif /* not CDT_ONLY */
+
+/** **/
+/** **/
+/********* Mesh quality testing routines end here *********/
+
+/********* Point location routines begin here *********/
+/** **/
+/** **/
+
+/*****************************************************************************/
+/* */
+/* makevertexmap() Construct a mapping from vertices to triangles to */
+/* improve the speed of point location for segment */
+/* insertion. */
+/* */
+/* Traverses all the triangles, and provides each corner of each triangle */
+/* with a pointer to that triangle. Of course, pointers will be */
+/* overwritten by other pointers because (almost) each vertex is a corner */
+/* of several triangles, but in the end every vertex will point to some */
+/* triangle that contains it. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void makevertexmap(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void makevertexmap(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri triangleloop;
+ vertex triorg;
+
+ if (b->verbose) {
+ printf(" Constructing mapping from vertices to triangles.\n");
+ }
+ traversalinit(&m->triangles);
+ triangleloop.tri = triangletraverse(m);
+ while (triangleloop.tri != (triangle *) NULL) {
+ /* Check all three vertices of the triangle. */
+ for (triangleloop.orient = 0; triangleloop.orient < 3;
+ triangleloop.orient++) {
+ org(triangleloop, triorg);
+ setvertex2tri(triorg, encode(triangleloop));
+ }
+ triangleloop.tri = triangletraverse(m);
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* preciselocate() Find a triangle or edge containing a given point. */
+/* */
+/* Begins its search from `searchtri'. It is important that `searchtri' */
+/* be a handle with the property that `searchpoint' is strictly to the left */
+/* of the edge denoted by `searchtri', or is collinear with that edge and */
+/* does not intersect that edge. (In particular, `searchpoint' should not */
+/* be the origin or destination of that edge.) */
+/* */
+/* These conditions are imposed because preciselocate() is normally used in */
+/* one of two situations: */
+/* */
+/* (1) To try to find the location to insert a new point. Normally, we */
+/* know an edge that the point is strictly to the left of. In the */
+/* incremental Delaunay algorithm, that edge is a bounding box edge. */
+/* In Ruppert's Delaunay refinement algorithm for quality meshing, */
+/* that edge is the shortest edge of the triangle whose circumcenter */
+/* is being inserted. */
+/* */
+/* (2) To try to find an existing point. In this case, any edge on the */
+/* convex hull is a good starting edge. You must screen out the */
+/* possibility that the vertex sought is an endpoint of the starting */
+/* edge before you call preciselocate(). */
+/* */
+/* On completion, `searchtri' is a triangle that contains `searchpoint'. */
+/* */
+/* This implementation differs from that given by Guibas and Stolfi. It */
+/* walks from triangle to triangle, crossing an edge only if `searchpoint' */
+/* is on the other side of the line containing that edge. After entering */
+/* a triangle, there are two edges by which one can leave that triangle. */
+/* If both edges are valid (`searchpoint' is on the other side of both */
+/* edges), one of the two is chosen by drawing a line perpendicular to */
+/* the entry edge (whose endpoints are `forg' and `fdest') passing through */
+/* `fapex'. Depending on which side of this perpendicular `searchpoint' */
+/* falls on, an exit edge is chosen. */
+/* */
+/* This implementation is empirically faster than the Guibas and Stolfi */
+/* point location routine (which I originally used), which tends to spiral */
+/* in toward its target. */
+/* */
+/* Returns ONVERTEX if the point lies on an existing vertex. `searchtri' */
+/* is a handle whose origin is the existing vertex. */
+/* */
+/* Returns ONEDGE if the point lies on a mesh edge. `searchtri' is a */
+/* handle whose primary edge is the edge on which the point lies. */
+/* */
+/* Returns INTRIANGLE if the point lies strictly within a triangle. */
+/* `searchtri' is a handle on the triangle that contains the point. */
+/* */
+/* Returns OUTSIDE if the point lies outside the mesh. `searchtri' is a */
+/* handle whose primary edge the point is to the right of. This might */
+/* occur when the circumcenter of a triangle falls just slightly outside */
+/* the mesh due to floating-point roundoff error. It also occurs when */
+/* seeking a hole or region point that a foolish user has placed outside */
+/* the mesh. */
+/* */
+/* If `stopatsubsegment' is nonzero, the search will stop if it tries to */
+/* walk through a subsegment, and will return OUTSIDE. */
+/* */
+/* WARNING: This routine is designed for convex triangulations, and will */
+/* not generally work after the holes and concavities have been carved. */
+/* However, it can still be used to find the circumcenter of a triangle, as */
+/* long as the search is begun from the triangle in question. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+enum locateresult preciselocate(struct mesh *m, struct behavior *b,
+ vertex searchpoint, struct otri *searchtri,
+ int stopatsubsegment)
+#else /* not ANSI_DECLARATORS */
+enum locateresult preciselocate(m, b, searchpoint, searchtri, stopatsubsegment)
+struct mesh *m;
+struct behavior *b;
+vertex searchpoint;
+struct otri *searchtri;
+int stopatsubsegment;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri backtracktri;
+ struct osub checkedge;
+ vertex forg, fdest, fapex;
+ REAL orgorient, destorient;
+ int moveleft;
+ triangle ptr; /* Temporary variable used by sym(). */
+ subseg sptr; /* Temporary variable used by tspivot(). */
+
+ if (b->verbose > 2) {
+ printf(" Searching for point (%.12g, %.12g).\n",
+ searchpoint[0], searchpoint[1]);
+ }
+ /* Where are we? */
+ org(*searchtri, forg);
+ dest(*searchtri, fdest);
+ apex(*searchtri, fapex);
+ while (1) {
+ if (b->verbose > 2) {
+ printf(" At (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+ forg[0], forg[1], fdest[0], fdest[1], fapex[0], fapex[1]);
+ }
+ /* Check whether the apex is the point we seek. */
+ if ((fapex[0] == searchpoint[0]) && (fapex[1] == searchpoint[1])) {
+ lprevself(*searchtri);
+ return ONVERTEX;
+ }
+ /* Does the point lie on the other side of the line defined by the */
+ /* triangle edge opposite the triangle's destination? */
+ destorient = counterclockwise(m, b, forg, fapex, searchpoint);
+ /* Does the point lie on the other side of the line defined by the */
+ /* triangle edge opposite the triangle's origin? */
+ orgorient = counterclockwise(m, b, fapex, fdest, searchpoint);
+ if (destorient > 0.0) {
+ if (orgorient > 0.0) {
+ /* Move left if the inner product of (fapex - searchpoint) and */
+ /* (fdest - forg) is positive. This is equivalent to drawing */
+ /* a line perpendicular to the line (forg, fdest) and passing */
+ /* through `fapex', and determining which side of this line */
+ /* `searchpoint' falls on. */
+ moveleft = (fapex[0] - searchpoint[0]) * (fdest[0] - forg[0]) +
+ (fapex[1] - searchpoint[1]) * (fdest[1] - forg[1]) > 0.0;
+ } else {
+ moveleft = 1;
+ }
+ } else {
+ if (orgorient > 0.0) {
+ moveleft = 0;
+ } else {
+ /* The point we seek must be on the boundary of or inside this */
+ /* triangle. */
+ if (destorient == 0.0) {
+ lprevself(*searchtri);
+ return ONEDGE;
+ }
+ if (orgorient == 0.0) {
+ lnextself(*searchtri);
+ return ONEDGE;
+ }
+ return INTRIANGLE;
+ }
+ }
+
+ /* Move to another triangle. Leave a trace `backtracktri' in case */
+ /* floating-point roundoff or some such bogey causes us to walk */
+ /* off a boundary of the triangulation. */
+ if (moveleft) {
+ lprev(*searchtri, backtracktri);
+ fdest = fapex;
+ } else {
+ lnext(*searchtri, backtracktri);
+ forg = fapex;
+ }
+ sym(backtracktri, *searchtri);
+
+ if (m->checksegments && stopatsubsegment) {
+ /* Check for walking through a subsegment. */
+ tspivot(backtracktri, checkedge);
+ if (checkedge.ss != m->dummysub) {
+ /* Go back to the last triangle. */
+ otricopy(backtracktri, *searchtri);
+ return OUTSIDE;
+ }
+ }
+ /* Check for walking right out of the triangulation. */
+ if (searchtri->tri == m->dummytri) {
+ /* Go back to the last triangle. */
+ otricopy(backtracktri, *searchtri);
+ return OUTSIDE;
+ }
+
+ apex(*searchtri, fapex);
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* locate() Find a triangle or edge containing a given point. */
+/* */
+/* Searching begins from one of: the input `searchtri', a recently */
+/* encountered triangle `recenttri', or from a triangle chosen from a */
+/* random sample. The choice is made by determining which triangle's */
+/* origin is closest to the point we are searching for. Normally, */
+/* `searchtri' should be a handle on the convex hull of the triangulation. */
+/* */
+/* Details on the random sampling method can be found in the Mucke, Saias, */
+/* and Zhu paper cited in the header of this code. */
+/* */
+/* On completion, `searchtri' is a triangle that contains `searchpoint'. */
+/* */
+/* Returns ONVERTEX if the point lies on an existing vertex. `searchtri' */
+/* is a handle whose origin is the existing vertex. */
+/* */
+/* Returns ONEDGE if the point lies on a mesh edge. `searchtri' is a */
+/* handle whose primary edge is the edge on which the point lies. */
+/* */
+/* Returns INTRIANGLE if the point lies strictly within a triangle. */
+/* `searchtri' is a handle on the triangle that contains the point. */
+/* */
+/* Returns OUTSIDE if the point lies outside the mesh. `searchtri' is a */
+/* handle whose primary edge the point is to the right of. This might */
+/* occur when the circumcenter of a triangle falls just slightly outside */
+/* the mesh due to floating-point roundoff error. It also occurs when */
+/* seeking a hole or region point that a foolish user has placed outside */
+/* the mesh. */
+/* */
+/* WARNING: This routine is designed for convex triangulations, and will */
+/* not generally work after the holes and concavities have been carved. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+enum locateresult locate(struct mesh *m, struct behavior *b,
+ vertex searchpoint, struct otri *searchtri)
+#else /* not ANSI_DECLARATORS */
+enum locateresult locate(m, b, searchpoint, searchtri)
+struct mesh *m;
+struct behavior *b;
+vertex searchpoint;
+struct otri *searchtri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ VOID **sampleblock;
+ char *firsttri;
+ struct otri sampletri;
+ vertex torg, tdest;
+ unsigned long alignptr;
+ REAL searchdist, dist;
+ REAL ahead;
+ long samplesperblock, totalsamplesleft, samplesleft;
+ long population, totalpopulation;
+ triangle ptr; /* Temporary variable used by sym(). */
+
+ if (b->verbose > 2) {
+ printf(" Randomly sampling for a triangle near point (%.12g, %.12g).\n",
+ searchpoint[0], searchpoint[1]);
+ }
+ /* Record the distance from the suggested starting triangle to the */
+ /* point we seek. */
+ org(*searchtri, torg);
+ searchdist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) +
+ (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]);
+ if (b->verbose > 2) {
+ printf(" Boundary triangle has origin (%.12g, %.12g).\n",
+ torg[0], torg[1]);
+ }
+
+ /* If a recently encountered triangle has been recorded and has not been */
+ /* deallocated, test it as a good starting point. */
+ if (m->recenttri.tri != (triangle *) NULL) {
+ if (!deadtri(m->recenttri.tri)) {
+ org(m->recenttri, torg);
+ if ((torg[0] == searchpoint[0]) && (torg[1] == searchpoint[1])) {
+ otricopy(m->recenttri, *searchtri);
+ return ONVERTEX;
+ }
+ dist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) +
+ (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]);
+ if (dist < searchdist) {
+ otricopy(m->recenttri, *searchtri);
+ searchdist = dist;
+ if (b->verbose > 2) {
+ printf(" Choosing recent triangle with origin (%.12g, %.12g).\n",
+ torg[0], torg[1]);
+ }
+ }
+ }
+ }
+
+ /* The number of random samples taken is proportional to the cube root of */
+ /* the number of triangles in the mesh. The next bit of code assumes */
+ /* that the number of triangles increases monotonically (or at least */
+ /* doesn't decrease enough to matter). */
+ while (SAMPLEFACTOR * m->samples * m->samples * m->samples <
+ m->triangles.items) {
+ m->samples++;
+ }
+
+ /* We'll draw ceiling(samples * TRIPERBLOCK / maxitems) random samples */
+ /* from each block of triangles (except the first)--until we meet the */
+ /* sample quota. The ceiling means that blocks at the end might be */
+ /* neglected, but I don't care. */
+ samplesperblock = (m->samples * TRIPERBLOCK - 1) / m->triangles.maxitems + 1;
+ /* We'll draw ceiling(samples * itemsfirstblock / maxitems) random samples */
+ /* from the first block of triangles. */
+ samplesleft = (m->samples * m->triangles.itemsfirstblock - 1) /
+ m->triangles.maxitems + 1;
+ totalsamplesleft = m->samples;
+ population = m->triangles.itemsfirstblock;
+ totalpopulation = m->triangles.maxitems;
+ sampleblock = m->triangles.firstblock;
+ sampletri.orient = 0;
+ while (totalsamplesleft > 0) {
+ /* If we're in the last block, `population' needs to be corrected. */
+ if (population > totalpopulation) {
+ population = totalpopulation;
+ }
+ /* Find a pointer to the first triangle in the block. */
+ alignptr = (unsigned long) (sampleblock + 1);
+ firsttri = (char *) (alignptr +
+ (unsigned long) m->triangles.alignbytes -
+ (alignptr %
+ (unsigned long) m->triangles.alignbytes));
+
+ /* Choose `samplesleft' randomly sampled triangles in this block. */
+ do {
+ sampletri.tri = (triangle *) (firsttri +
+ (randomnation((unsigned int) population) *
+ m->triangles.itembytes));
+ if (!deadtri(sampletri.tri)) {
+ org(sampletri, torg);
+ dist = (searchpoint[0] - torg[0]) * (searchpoint[0] - torg[0]) +
+ (searchpoint[1] - torg[1]) * (searchpoint[1] - torg[1]);
+ if (dist < searchdist) {
+ otricopy(sampletri, *searchtri);
+ searchdist = dist;
+ if (b->verbose > 2) {
+ printf(" Choosing triangle with origin (%.12g, %.12g).\n",
+ torg[0], torg[1]);
+ }
+ }
+ }
+
+ samplesleft--;
+ totalsamplesleft--;
+ } while ((samplesleft > 0) && (totalsamplesleft > 0));
+
+ if (totalsamplesleft > 0) {
+ sampleblock = (VOID **) *sampleblock;
+ samplesleft = samplesperblock;
+ totalpopulation -= population;
+ population = TRIPERBLOCK;
+ }
+ }
+
+ /* Where are we? */
+ org(*searchtri, torg);
+ dest(*searchtri, tdest);
+ /* Check the starting triangle's vertices. */
+ if ((torg[0] == searchpoint[0]) && (torg[1] == searchpoint[1])) {
+ return ONVERTEX;
+ }
+ if ((tdest[0] == searchpoint[0]) && (tdest[1] == searchpoint[1])) {
+ lnextself(*searchtri);
+ return ONVERTEX;
+ }
+ /* Orient `searchtri' to fit the preconditions of calling preciselocate(). */
+ ahead = counterclockwise(m, b, torg, tdest, searchpoint);
+ if (ahead < 0.0) {
+ /* Turn around so that `searchpoint' is to the left of the */
+ /* edge specified by `searchtri'. */
+ symself(*searchtri);
+ } else if (ahead == 0.0) {
+ /* Check if `searchpoint' is between `torg' and `tdest'. */
+ if (((torg[0] < searchpoint[0]) == (searchpoint[0] < tdest[0])) &&
+ ((torg[1] < searchpoint[1]) == (searchpoint[1] < tdest[1]))) {
+ return ONEDGE;
+ }
+ }
+ return preciselocate(m, b, searchpoint, searchtri, 0);
+}
+
+/** **/
+/** **/
+/********* Point location routines end here *********/
+
+/********* Mesh transformation routines begin here *********/
+/** **/
+/** **/
+
+/*****************************************************************************/
+/* */
+/* insertsubseg() Create a new subsegment and insert it between two */
+/* triangles. */
+/* */
+/* The new subsegment is inserted at the edge described by the handle */
+/* `tri'. Its vertices are properly initialized. The marker `subsegmark' */
+/* is applied to the subsegment and, if appropriate, its vertices. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void insertsubseg(struct mesh *m, struct behavior *b, struct otri *tri,
+ int subsegmark)
+#else /* not ANSI_DECLARATORS */
+void insertsubseg(m, b, tri, subsegmark)
+struct mesh *m;
+struct behavior *b;
+struct otri *tri; /* Edge at which to insert the new subsegment. */
+int subsegmark; /* Marker for the new subsegment. */
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri oppotri;
+ struct osub newsubseg;
+ vertex triorg, tridest;
+ triangle ptr; /* Temporary variable used by sym(). */
+ subseg sptr; /* Temporary variable used by tspivot(). */
+
+ org(*tri, triorg);
+ dest(*tri, tridest);
+ /* Mark vertices if possible. */
+ if (vertexmark(triorg) == 0) {
+ setvertexmark(triorg, subsegmark);
+ }
+ if (vertexmark(tridest) == 0) {
+ setvertexmark(tridest, subsegmark);
+ }
+ /* Check if there's already a subsegment here. */
+ tspivot(*tri, newsubseg);
+ if (newsubseg.ss == m->dummysub) {
+ /* Make new subsegment and initialize its vertices. */
+ makesubseg(m, &newsubseg);
+ setsorg(newsubseg, tridest);
+ setsdest(newsubseg, triorg);
+ setsegorg(newsubseg, tridest);
+ setsegdest(newsubseg, triorg);
+ /* Bond new subsegment to the two triangles it is sandwiched between. */
+ /* Note that the facing triangle `oppotri' might be equal to */
+ /* `dummytri' (outer space), but the new subsegment is bonded to it */
+ /* all the same. */
+ tsbond(*tri, newsubseg);
+ sym(*tri, oppotri);
+ ssymself(newsubseg);
+ tsbond(oppotri, newsubseg);
+ setmark(newsubseg, subsegmark);
+ if (b->verbose > 2) {
+ printf(" Inserting new ");
+ printsubseg(m, b, &newsubseg);
+ }
+ } else {
+ if (mark(newsubseg) == 0) {
+ setmark(newsubseg, subsegmark);
+ }
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* Terminology */
+/* */
+/* A "local transformation" replaces a small set of triangles with another */
+/* set of triangles. This may or may not involve inserting or deleting a */
+/* vertex. */
+/* */
+/* The term "casing" is used to describe the set of triangles that are */
+/* attached to the triangles being transformed, but are not transformed */
+/* themselves. Think of the casing as a fixed hollow structure inside */
+/* which all the action happens. A "casing" is only defined relative to */
+/* a single transformation; each occurrence of a transformation will */
+/* involve a different casing. */
+/* */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* */
+/* flip() Transform two triangles to two different triangles by flipping */
+/* an edge counterclockwise within a quadrilateral. */
+/* */
+/* Imagine the original triangles, abc and bad, oriented so that the */
+/* shared edge ab lies in a horizontal plane, with the vertex b on the left */
+/* and the vertex a on the right. The vertex c lies below the edge, and */
+/* the vertex d lies above the edge. The `flipedge' handle holds the edge */
+/* ab of triangle abc, and is directed left, from vertex a to vertex b. */
+/* */
+/* The triangles abc and bad are deleted and replaced by the triangles cdb */
+/* and dca. The triangles that represent abc and bad are NOT deallocated; */
+/* they are reused for dca and cdb, respectively. Hence, any handles that */
+/* may have held the original triangles are still valid, although not */
+/* directed as they were before. */
+/* */
+/* Upon completion of this routine, the `flipedge' handle holds the edge */
+/* dc of triangle dca, and is directed down, from vertex d to vertex c. */
+/* (Hence, the two triangles have rotated counterclockwise.) */
+/* */
+/* WARNING: This transformation is geometrically valid only if the */
+/* quadrilateral adbc is convex. Furthermore, this transformation is */
+/* valid only if there is not a subsegment between the triangles abc and */
+/* bad. This routine does not check either of these preconditions, and */
+/* it is the responsibility of the calling routine to ensure that they are */
+/* met. If they are not, the streets shall be filled with wailing and */
+/* gnashing of teeth. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void flip(struct mesh *m, struct behavior *b, struct otri *flipedge)
+#else /* not ANSI_DECLARATORS */
+void flip(m, b, flipedge)
+struct mesh *m;
+struct behavior *b;
+struct otri *flipedge; /* Handle for the triangle abc. */
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri botleft, botright;
+ struct otri topleft, topright;
+ struct otri top;
+ struct otri botlcasing, botrcasing;
+ struct otri toplcasing, toprcasing;
+ struct osub botlsubseg, botrsubseg;
+ struct osub toplsubseg, toprsubseg;
+ vertex leftvertex, rightvertex, botvertex;
+ vertex farvertex;
+ triangle ptr; /* Temporary variable used by sym(). */
+ subseg sptr; /* Temporary variable used by tspivot(). */
+
+ /* Identify the vertices of the quadrilateral. */
+ org(*flipedge, rightvertex);
+ dest(*flipedge, leftvertex);
+ apex(*flipedge, botvertex);
+ sym(*flipedge, top);
+#ifdef SELF_CHECK
+ if (top.tri == m->dummytri) {
+ printf("Internal error in flip(): Attempt to flip on boundary.\n");
+ lnextself(*flipedge);
+ return;
+ }
+ if (m->checksegments) {
+ tspivot(*flipedge, toplsubseg);
+ if (toplsubseg.ss != m->dummysub) {
+ printf("Internal error in flip(): Attempt to flip a segment.\n");
+ lnextself(*flipedge);
+ return;
+ }
+ }
+#endif /* SELF_CHECK */
+ apex(top, farvertex);
+
+ /* Identify the casing of the quadrilateral. */
+ lprev(top, topleft);
+ sym(topleft, toplcasing);
+ lnext(top, topright);
+ sym(topright, toprcasing);
+ lnext(*flipedge, botleft);
+ sym(botleft, botlcasing);
+ lprev(*flipedge, botright);
+ sym(botright, botrcasing);
+ /* Rotate the quadrilateral one-quarter turn counterclockwise. */
+ bond(topleft, botlcasing);
+ bond(botleft, botrcasing);
+ bond(botright, toprcasing);
+ bond(topright, toplcasing);
+
+ if (m->checksegments) {
+ /* Check for subsegments and rebond them to the quadrilateral. */
+ tspivot(topleft, toplsubseg);
+ tspivot(botleft, botlsubseg);
+ tspivot(botright, botrsubseg);
+ tspivot(topright, toprsubseg);
+ if (toplsubseg.ss == m->dummysub) {
+ tsdissolve(topright);
+ } else {
+ tsbond(topright, toplsubseg);
+ }
+ if (botlsubseg.ss == m->dummysub) {
+ tsdissolve(topleft);
+ } else {
+ tsbond(topleft, botlsubseg);
+ }
+ if (botrsubseg.ss == m->dummysub) {
+ tsdissolve(botleft);
+ } else {
+ tsbond(botleft, botrsubseg);
+ }
+ if (toprsubseg.ss == m->dummysub) {
+ tsdissolve(botright);
+ } else {
+ tsbond(botright, toprsubseg);
+ }
+ }
+
+ /* New vertex assignments for the rotated quadrilateral. */
+ setorg(*flipedge, farvertex);
+ setdest(*flipedge, botvertex);
+ setapex(*flipedge, rightvertex);
+ setorg(top, botvertex);
+ setdest(top, farvertex);
+ setapex(top, leftvertex);
+ if (b->verbose > 2) {
+ printf(" Edge flip results in left ");
+ printtriangle(m, b, &top);
+ printf(" and right ");
+ printtriangle(m, b, flipedge);
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* unflip() Transform two triangles to two different triangles by */
+/* flipping an edge clockwise within a quadrilateral. Reverses */
+/* the flip() operation so that the data structures representing */
+/* the triangles are back where they were before the flip(). */
+/* */
+/* Imagine the original triangles, abc and bad, oriented so that the */
+/* shared edge ab lies in a horizontal plane, with the vertex b on the left */
+/* and the vertex a on the right. The vertex c lies below the edge, and */
+/* the vertex d lies above the edge. The `flipedge' handle holds the edge */
+/* ab of triangle abc, and is directed left, from vertex a to vertex b. */
+/* */
+/* The triangles abc and bad are deleted and replaced by the triangles cdb */
+/* and dca. The triangles that represent abc and bad are NOT deallocated; */
+/* they are reused for cdb and dca, respectively. Hence, any handles that */
+/* may have held the original triangles are still valid, although not */
+/* directed as they were before. */
+/* */
+/* Upon completion of this routine, the `flipedge' handle holds the edge */
+/* cd of triangle cdb, and is directed up, from vertex c to vertex d. */
+/* (Hence, the two triangles have rotated clockwise.) */
+/* */
+/* WARNING: This transformation is geometrically valid only if the */
+/* quadrilateral adbc is convex. Furthermore, this transformation is */
+/* valid only if there is not a subsegment between the triangles abc and */
+/* bad. This routine does not check either of these preconditions, and */
+/* it is the responsibility of the calling routine to ensure that they are */
+/* met. If they are not, the streets shall be filled with wailing and */
+/* gnashing of teeth. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void unflip(struct mesh *m, struct behavior *b, struct otri *flipedge)
+#else /* not ANSI_DECLARATORS */
+void unflip(m, b, flipedge)
+struct mesh *m;
+struct behavior *b;
+struct otri *flipedge; /* Handle for the triangle abc. */
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri botleft, botright;
+ struct otri topleft, topright;
+ struct otri top;
+ struct otri botlcasing, botrcasing;
+ struct otri toplcasing, toprcasing;
+ struct osub botlsubseg, botrsubseg;
+ struct osub toplsubseg, toprsubseg;
+ vertex leftvertex, rightvertex, botvertex;
+ vertex farvertex;
+ triangle ptr; /* Temporary variable used by sym(). */
+ subseg sptr; /* Temporary variable used by tspivot(). */
+
+ /* Identify the vertices of the quadrilateral. */
+ org(*flipedge, rightvertex);
+ dest(*flipedge, leftvertex);
+ apex(*flipedge, botvertex);
+ sym(*flipedge, top);
+#ifdef SELF_CHECK
+ if (top.tri == m->dummytri) {
+ printf("Internal error in unflip(): Attempt to flip on boundary.\n");
+ lnextself(*flipedge);
+ return;
+ }
+ if (m->checksegments) {
+ tspivot(*flipedge, toplsubseg);
+ if (toplsubseg.ss != m->dummysub) {
+ printf("Internal error in unflip(): Attempt to flip a subsegment.\n");
+ lnextself(*flipedge);
+ return;
+ }
+ }
+#endif /* SELF_CHECK */
+ apex(top, farvertex);
+
+ /* Identify the casing of the quadrilateral. */
+ lprev(top, topleft);
+ sym(topleft, toplcasing);
+ lnext(top, topright);
+ sym(topright, toprcasing);
+ lnext(*flipedge, botleft);
+ sym(botleft, botlcasing);
+ lprev(*flipedge, botright);
+ sym(botright, botrcasing);
+ /* Rotate the quadrilateral one-quarter turn clockwise. */
+ bond(topleft, toprcasing);
+ bond(botleft, toplcasing);
+ bond(botright, botlcasing);
+ bond(topright, botrcasing);
+
+ if (m->checksegments) {
+ /* Check for subsegments and rebond them to the quadrilateral. */
+ tspivot(topleft, toplsubseg);
+ tspivot(botleft, botlsubseg);
+ tspivot(botright, botrsubseg);
+ tspivot(topright, toprsubseg);
+ if (toplsubseg.ss == m->dummysub) {
+ tsdissolve(botleft);
+ } else {
+ tsbond(botleft, toplsubseg);
+ }
+ if (botlsubseg.ss == m->dummysub) {
+ tsdissolve(botright);
+ } else {
+ tsbond(botright, botlsubseg);
+ }
+ if (botrsubseg.ss == m->dummysub) {
+ tsdissolve(topright);
+ } else {
+ tsbond(topright, botrsubseg);
+ }
+ if (toprsubseg.ss == m->dummysub) {
+ tsdissolve(topleft);
+ } else {
+ tsbond(topleft, toprsubseg);
+ }
+ }
+
+ /* New vertex assignments for the rotated quadrilateral. */
+ setorg(*flipedge, botvertex);
+ setdest(*flipedge, farvertex);
+ setapex(*flipedge, leftvertex);
+ setorg(top, farvertex);
+ setdest(top, botvertex);
+ setapex(top, rightvertex);
+ if (b->verbose > 2) {
+ printf(" Edge unflip results in left ");
+ printtriangle(m, b, flipedge);
+ printf(" and right ");
+ printtriangle(m, b, &top);
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* insertvertex() Insert a vertex into a Delaunay triangulation, */
+/* performing flips as necessary to maintain the Delaunay */
+/* property. */
+/* */
+/* The point `insertvertex' is located. If `searchtri.tri' is not NULL, */
+/* the search for the containing triangle begins from `searchtri'. If */
+/* `searchtri.tri' is NULL, a full point location procedure is called. */
+/* If `insertvertex' is found inside a triangle, the triangle is split into */
+/* three; if `insertvertex' lies on an edge, the edge is split in two, */
+/* thereby splitting the two adjacent triangles into four. Edge flips are */
+/* used to restore the Delaunay property. If `insertvertex' lies on an */
+/* existing vertex, no action is taken, and the value DUPLICATEVERTEX is */
+/* returned. On return, `searchtri' is set to a handle whose origin is the */
+/* existing vertex. */
+/* */
+/* Normally, the parameter `splitseg' is set to NULL, implying that no */
+/* subsegment should be split. In this case, if `insertvertex' is found to */
+/* lie on a segment, no action is taken, and the value VIOLATINGVERTEX is */
+/* returned. On return, `searchtri' is set to a handle whose primary edge */
+/* is the violated subsegment. */
+/* */
+/* If the calling routine wishes to split a subsegment by inserting a */
+/* vertex in it, the parameter `splitseg' should be that subsegment. In */
+/* this case, `searchtri' MUST be the triangle handle reached by pivoting */
+/* from that subsegment; no point location is done. */
+/* */
+/* `segmentflaws' and `triflaws' are flags that indicate whether or not */
+/* there should be checks for the creation of encroached subsegments or bad */
+/* quality triangles. If a newly inserted vertex encroaches upon */
+/* subsegments, these subsegments are added to the list of subsegments to */
+/* be split if `segmentflaws' is set. If bad triangles are created, these */
+/* are added to the queue if `triflaws' is set. */
+/* */
+/* If a duplicate vertex or violated segment does not prevent the vertex */
+/* from being inserted, the return value will be ENCROACHINGVERTEX if the */
+/* vertex encroaches upon a subsegment (and checking is enabled), or */
+/* SUCCESSFULVERTEX otherwise. In either case, `searchtri' is set to a */
+/* handle whose origin is the newly inserted vertex. */
+/* */
+/* insertvertex() does not use flip() for reasons of speed; some */
+/* information can be reused from edge flip to edge flip, like the */
+/* locations of subsegments. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+enum insertvertexresult insertvertex(struct mesh *m, struct behavior *b,
+ vertex newvertex, struct otri *searchtri,
+ struct osub *splitseg,
+ int segmentflaws, int triflaws)
+#else /* not ANSI_DECLARATORS */
+enum insertvertexresult insertvertex(m, b, newvertex, searchtri, splitseg,
+ segmentflaws, triflaws)
+struct mesh *m;
+struct behavior *b;
+vertex newvertex;
+struct otri *searchtri;
+struct osub *splitseg;
+int segmentflaws;
+int triflaws;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri horiz;
+ struct otri top;
+ struct otri botleft, botright;
+ struct otri topleft, topright;
+ struct otri newbotleft, newbotright;
+ struct otri newtopright;
+ struct otri botlcasing, botrcasing;
+ struct otri toplcasing, toprcasing;
+ struct otri testtri;
+ struct osub botlsubseg, botrsubseg;
+ struct osub toplsubseg, toprsubseg;
+ struct osub brokensubseg;
+ struct osub checksubseg;
+ struct osub rightsubseg;
+ struct osub newsubseg;
+ struct badsubseg *encroached;
+ struct flipstacker *newflip;
+ vertex first;
+ vertex leftvertex, rightvertex, botvertex, topvertex, farvertex;
+ vertex segmentorg, segmentdest;
+ REAL attrib;
+ REAL area;
+ enum insertvertexresult success;
+ enum locateresult intersect;
+ int doflip;
+ int mirrorflag;
+ int enq;
+ int i;
+ triangle ptr; /* Temporary variable used by sym(). */
+ subseg sptr; /* Temporary variable used by spivot() and tspivot(). */
+
+ if (b->verbose > 1) {
+ printf(" Inserting (%.12g, %.12g).\n", newvertex[0], newvertex[1]);
+ }
+
+ if (splitseg == (struct osub *) NULL) {
+ /* Find the location of the vertex to be inserted. Check if a good */
+ /* starting triangle has already been provided by the caller. */
+ if (searchtri->tri == m->dummytri) {
+ /* Find a boundary triangle. */
+ horiz.tri = m->dummytri;
+ horiz.orient = 0;
+ symself(horiz);
+ /* Search for a triangle containing `newvertex'. */
+ intersect = locate(m, b, newvertex, &horiz);
+ } else {
+ /* Start searching from the triangle provided by the caller. */
+ otricopy(*searchtri, horiz);
+ intersect = preciselocate(m, b, newvertex, &horiz, 1);
+ }
+ } else {
+ /* The calling routine provides the subsegment in which */
+ /* the vertex is inserted. */
+ otricopy(*searchtri, horiz);
+ intersect = ONEDGE;
+ }
+
+ if (intersect == ONVERTEX) {
+ /* There's already a vertex there. Return in `searchtri' a triangle */
+ /* whose origin is the existing vertex. */
+ otricopy(horiz, *searchtri);
+ otricopy(horiz, m->recenttri);
+ return DUPLICATEVERTEX;
+ }
+ if ((intersect == ONEDGE) || (intersect == OUTSIDE)) {
+ /* The vertex falls on an edge or boundary. */
+ if (m->checksegments && (splitseg == (struct osub *) NULL)) {
+ /* Check whether the vertex falls on a subsegment. */
+ tspivot(horiz, brokensubseg);
+ if (brokensubseg.ss != m->dummysub) {
+ /* The vertex falls on a subsegment, and hence will not be inserted. */
+ if (segmentflaws) {
+ enq = b->nobisect != 2;
+ if (enq && (b->nobisect == 1)) {
+ /* This subsegment may be split only if it is an */
+ /* internal boundary. */
+ sym(horiz, testtri);
+ enq = testtri.tri != m->dummytri;
+ }
+ if (enq) {
+ /* Add the subsegment to the list of encroached subsegments. */
+ encroached = (struct badsubseg *) poolalloc(&m->badsubsegs);
+ encroached->encsubseg = sencode(brokensubseg);
+ sorg(brokensubseg, encroached->subsegorg);
+ sdest(brokensubseg, encroached->subsegdest);
+ if (b->verbose > 2) {
+ printf(
+ " Queueing encroached subsegment (%.12g, %.12g) (%.12g, %.12g).\n",
+ encroached->subsegorg[0], encroached->subsegorg[1],
+ encroached->subsegdest[0], encroached->subsegdest[1]);
+ }
+ }
+ }
+ /* Return a handle whose primary edge contains the vertex, */
+ /* which has not been inserted. */
+ otricopy(horiz, *searchtri);
+ otricopy(horiz, m->recenttri);
+ return VIOLATINGVERTEX;
+ }
+ }
+
+ /* Insert the vertex on an edge, dividing one triangle into two (if */
+ /* the edge lies on a boundary) or two triangles into four. */
+ lprev(horiz, botright);
+ sym(botright, botrcasing);
+ sym(horiz, topright);
+ /* Is there a second triangle? (Or does this edge lie on a boundary?) */
+ mirrorflag = topright.tri != m->dummytri;
+ if (mirrorflag) {
+ lnextself(topright);
+ sym(topright, toprcasing);
+ maketriangle(m, b, &newtopright);
+ } else {
+ /* Splitting a boundary edge increases the number of boundary edges. */
+ m->hullsize++;
+ }
+ maketriangle(m, b, &newbotright);
+
+ /* Set the vertices of changed and new triangles. */
+ org(horiz, rightvertex);
+ dest(horiz, leftvertex);
+ apex(horiz, botvertex);
+ setorg(newbotright, botvertex);
+ setdest(newbotright, rightvertex);
+ setapex(newbotright, newvertex);
+ setorg(horiz, newvertex);
+ for (i = 0; i < m->eextras; i++) {
+ /* Set the element attributes of a new triangle. */
+ setelemattribute(newbotright, i, elemattribute(botright, i));
+ }
+ if (b->vararea) {
+ /* Set the area constraint of a new triangle. */
+ setareabound(newbotright, areabound(botright));
+ }
+ if (mirrorflag) {
+ dest(topright, topvertex);
+ setorg(newtopright, rightvertex);
+ setdest(newtopright, topvertex);
+ setapex(newtopright, newvertex);
+ setorg(topright, newvertex);
+ for (i = 0; i < m->eextras; i++) {
+ /* Set the element attributes of another new triangle. */
+ setelemattribute(newtopright, i, elemattribute(topright, i));
+ }
+ if (b->vararea) {
+ /* Set the area constraint of another new triangle. */
+ setareabound(newtopright, areabound(topright));
+ }
+ }
+
+ /* There may be subsegments that need to be bonded */
+ /* to the new triangle(s). */
+ if (m->checksegments) {
+ tspivot(botright, botrsubseg);
+ if (botrsubseg.ss != m->dummysub) {
+ tsdissolve(botright);
+ tsbond(newbotright, botrsubseg);
+ }
+ if (mirrorflag) {
+ tspivot(topright, toprsubseg);
+ if (toprsubseg.ss != m->dummysub) {
+ tsdissolve(topright);
+ tsbond(newtopright, toprsubseg);
+ }
+ }
+ }
+
+ /* Bond the new triangle(s) to the surrounding triangles. */
+ bond(newbotright, botrcasing);
+ lprevself(newbotright);
+ bond(newbotright, botright);
+ lprevself(newbotright);
+ if (mirrorflag) {
+ bond(newtopright, toprcasing);
+ lnextself(newtopright);
+ bond(newtopright, topright);
+ lnextself(newtopright);
+ bond(newtopright, newbotright);
+ }
+
+ if (splitseg != (struct osub *) NULL) {
+ /* Split the subsegment into two. */
+ setsdest(*splitseg, newvertex);
+ segorg(*splitseg, segmentorg);
+ segdest(*splitseg, segmentdest);
+ ssymself(*splitseg);
+ spivot(*splitseg, rightsubseg);
+ insertsubseg(m, b, &newbotright, mark(*splitseg));
+ tspivot(newbotright, newsubseg);
+ setsegorg(newsubseg, segmentorg);
+ setsegdest(newsubseg, segmentdest);
+ sbond(*splitseg, newsubseg);
+ ssymself(newsubseg);
+ sbond(newsubseg, rightsubseg);
+ ssymself(*splitseg);
+ /* Transfer the subsegment's boundary marker to the vertex */
+ /* if required. */
+ if (vertexmark(newvertex) == 0) {
+ setvertexmark(newvertex, mark(*splitseg));
+ }
+ }
+
+ if (m->checkquality) {
+ poolrestart(&m->flipstackers);
+ m->lastflip = (struct flipstacker *) poolalloc(&m->flipstackers);
+ m->lastflip->flippedtri = encode(horiz);
+ m->lastflip->prevflip = (struct flipstacker *) &insertvertex;
+ }
+
+#ifdef SELF_CHECK
+ if (counterclockwise(m, b, rightvertex, leftvertex, botvertex) < 0.0) {
+ printf("Internal error in insertvertex():\n");
+ printf(
+ " Clockwise triangle prior to edge vertex insertion (bottom).\n");
+ }
+ if (mirrorflag) {
+ if (counterclockwise(m, b, leftvertex, rightvertex, topvertex) < 0.0) {
+ printf("Internal error in insertvertex():\n");
+ printf(" Clockwise triangle prior to edge vertex insertion (top).\n");
+ }
+ if (counterclockwise(m, b, rightvertex, topvertex, newvertex) < 0.0) {
+ printf("Internal error in insertvertex():\n");
+ printf(
+ " Clockwise triangle after edge vertex insertion (top right).\n");
+ }
+ if (counterclockwise(m, b, topvertex, leftvertex, newvertex) < 0.0) {
+ printf("Internal error in insertvertex():\n");
+ printf(
+ " Clockwise triangle after edge vertex insertion (top left).\n");
+ }
+ }
+ if (counterclockwise(m, b, leftvertex, botvertex, newvertex) < 0.0) {
+ printf("Internal error in insertvertex():\n");
+ printf(
+ " Clockwise triangle after edge vertex insertion (bottom left).\n");
+ }
+ if (counterclockwise(m, b, botvertex, rightvertex, newvertex) < 0.0) {
+ printf("Internal error in insertvertex():\n");
+ printf(
+ " Clockwise triangle after edge vertex insertion (bottom right).\n");
+ }
+#endif /* SELF_CHECK */
+ if (b->verbose > 2) {
+ printf(" Updating bottom left ");
+ printtriangle(m, b, &botright);
+ if (mirrorflag) {
+ printf(" Updating top left ");
+ printtriangle(m, b, &topright);
+ printf(" Creating top right ");
+ printtriangle(m, b, &newtopright);
+ }
+ printf(" Creating bottom right ");
+ printtriangle(m, b, &newbotright);
+ }
+
+ /* Position `horiz' on the first edge to check for */
+ /* the Delaunay property. */
+ lnextself(horiz);
+ } else {
+ /* Insert the vertex in a triangle, splitting it into three. */
+ lnext(horiz, botleft);
+ lprev(horiz, botright);
+ sym(botleft, botlcasing);
+ sym(botright, botrcasing);
+ maketriangle(m, b, &newbotleft);
+ maketriangle(m, b, &newbotright);
+
+ /* Set the vertices of changed and new triangles. */
+ org(horiz, rightvertex);
+ dest(horiz, leftvertex);
+ apex(horiz, botvertex);
+ setorg(newbotleft, leftvertex);
+ setdest(newbotleft, botvertex);
+ setapex(newbotleft, newvertex);
+ setorg(newbotright, botvertex);
+ setdest(newbotright, rightvertex);
+ setapex(newbotright, newvertex);
+ setapex(horiz, newvertex);
+ for (i = 0; i < m->eextras; i++) {
+ /* Set the element attributes of the new triangles. */
+ attrib = elemattribute(horiz, i);
+ setelemattribute(newbotleft, i, attrib);
+ setelemattribute(newbotright, i, attrib);
+ }
+ if (b->vararea) {
+ /* Set the area constraint of the new triangles. */
+ area = areabound(horiz);
+ setareabound(newbotleft, area);
+ setareabound(newbotright, area);
+ }
+
+ /* There may be subsegments that need to be bonded */
+ /* to the new triangles. */
+ if (m->checksegments) {
+ tspivot(botleft, botlsubseg);
+ if (botlsubseg.ss != m->dummysub) {
+ tsdissolve(botleft);
+ tsbond(newbotleft, botlsubseg);
+ }
+ tspivot(botright, botrsubseg);
+ if (botrsubseg.ss != m->dummysub) {
+ tsdissolve(botright);
+ tsbond(newbotright, botrsubseg);
+ }
+ }
+
+ /* Bond the new triangles to the surrounding triangles. */
+ bond(newbotleft, botlcasing);
+ bond(newbotright, botrcasing);
+ lnextself(newbotleft);
+ lprevself(newbotright);
+ bond(newbotleft, newbotright);
+ lnextself(newbotleft);
+ bond(botleft, newbotleft);
+ lprevself(newbotright);
+ bond(botright, newbotright);
+
+ if (m->checkquality) {
+ poolrestart(&m->flipstackers);
+ m->lastflip = (struct flipstacker *) poolalloc(&m->flipstackers);
+ m->lastflip->flippedtri = encode(horiz);
+ m->lastflip->prevflip = (struct flipstacker *) NULL;
+ }
+
+#ifdef SELF_CHECK
+ if (counterclockwise(m, b, rightvertex, leftvertex, botvertex) < 0.0) {
+ printf("Internal error in insertvertex():\n");
+ printf(" Clockwise triangle prior to vertex insertion.\n");
+ }
+ if (counterclockwise(m, b, rightvertex, leftvertex, newvertex) < 0.0) {
+ printf("Internal error in insertvertex():\n");
+ printf(" Clockwise triangle after vertex insertion (top).\n");
+ }
+ if (counterclockwise(m, b, leftvertex, botvertex, newvertex) < 0.0) {
+ printf("Internal error in insertvertex():\n");
+ printf(" Clockwise triangle after vertex insertion (left).\n");
+ }
+ if (counterclockwise(m, b, botvertex, rightvertex, newvertex) < 0.0) {
+ printf("Internal error in insertvertex():\n");
+ printf(" Clockwise triangle after vertex insertion (right).\n");
+ }
+#endif /* SELF_CHECK */
+ if (b->verbose > 2) {
+ printf(" Updating top ");
+ printtriangle(m, b, &horiz);
+ printf(" Creating left ");
+ printtriangle(m, b, &newbotleft);
+ printf(" Creating right ");
+ printtriangle(m, b, &newbotright);
+ }
+ }
+
+ /* The insertion is successful by default, unless an encroached */
+ /* subsegment is found. */
+ success = SUCCESSFULVERTEX;
+ /* Circle around the newly inserted vertex, checking each edge opposite */
+ /* it for the Delaunay property. Non-Delaunay edges are flipped. */
+ /* `horiz' is always the edge being checked. `first' marks where to */
+ /* stop circling. */
+ org(horiz, first);
+ rightvertex = first;
+ dest(horiz, leftvertex);
+ /* Circle until finished. */
+ while (1) {
+ /* By default, the edge will be flipped. */
+ doflip = 1;
+
+ if (m->checksegments) {
+ /* Check for a subsegment, which cannot be flipped. */
+ tspivot(horiz, checksubseg);
+ if (checksubseg.ss != m->dummysub) {
+ /* The edge is a subsegment and cannot be flipped. */
+ doflip = 0;
+#ifndef CDT_ONLY
+ if (segmentflaws) {
+ /* Does the new vertex encroach upon this subsegment? */
+ if (checkseg4encroach(m, b, &checksubseg)) {
+ success = ENCROACHINGVERTEX;
+ }
+ }
+#endif /* not CDT_ONLY */
+ }
+ }
+
+ if (doflip) {
+ /* Check if the edge is a boundary edge. */
+ sym(horiz, top);
+ if (top.tri == m->dummytri) {
+ /* The edge is a boundary edge and cannot be flipped. */
+ doflip = 0;
+ } else {
+ /* Find the vertex on the other side of the edge. */
+ apex(top, farvertex);
+ /* In the incremental Delaunay triangulation algorithm, any of */
+ /* `leftvertex', `rightvertex', and `farvertex' could be vertices */
+ /* of the triangular bounding box. These vertices must be */
+ /* treated as if they are infinitely distant, even though their */
+ /* "coordinates" are not. */
+ if ((leftvertex == m->infvertex1) || (leftvertex == m->infvertex2) ||
+ (leftvertex == m->infvertex3)) {
+ /* `leftvertex' is infinitely distant. Check the convexity of */
+ /* the boundary of the triangulation. 'farvertex' might be */
+ /* infinite as well, but trust me, this same condition should */
+ /* be applied. */
+ doflip = counterclockwise(m, b, newvertex, rightvertex, farvertex)
+ > 0.0;
+ } else if ((rightvertex == m->infvertex1) ||
+ (rightvertex == m->infvertex2) ||
+ (rightvertex == m->infvertex3)) {
+ /* `rightvertex' is infinitely distant. Check the convexity of */
+ /* the boundary of the triangulation. 'farvertex' might be */
+ /* infinite as well, but trust me, this same condition should */
+ /* be applied. */
+ doflip = counterclockwise(m, b, farvertex, leftvertex, newvertex)
+ > 0.0;
+ } else if ((farvertex == m->infvertex1) ||
+ (farvertex == m->infvertex2) ||
+ (farvertex == m->infvertex3)) {
+ /* `farvertex' is infinitely distant and cannot be inside */
+ /* the circumcircle of the triangle `horiz'. */
+ doflip = 0;
+ } else {
+ /* Test whether the edge is locally Delaunay. */
+ doflip = incircle(m, b, leftvertex, newvertex, rightvertex,
+ farvertex) > 0.0;
+ }
+ if (doflip) {
+ /* We made it! Flip the edge `horiz' by rotating its containing */
+ /* quadrilateral (the two triangles adjacent to `horiz'). */
+ /* Identify the casing of the quadrilateral. */
+ lprev(top, topleft);
+ sym(topleft, toplcasing);
+ lnext(top, topright);
+ sym(topright, toprcasing);
+ lnext(horiz, botleft);
+ sym(botleft, botlcasing);
+ lprev(horiz, botright);
+ sym(botright, botrcasing);
+ /* Rotate the quadrilateral one-quarter turn counterclockwise. */
+ bond(topleft, botlcasing);
+ bond(botleft, botrcasing);
+ bond(botright, toprcasing);
+ bond(topright, toplcasing);
+ if (m->checksegments) {
+ /* Check for subsegments and rebond them to the quadrilateral. */
+ tspivot(topleft, toplsubseg);
+ tspivot(botleft, botlsubseg);
+ tspivot(botright, botrsubseg);
+ tspivot(topright, toprsubseg);
+ if (toplsubseg.ss == m->dummysub) {
+ tsdissolve(topright);
+ } else {
+ tsbond(topright, toplsubseg);
+ }
+ if (botlsubseg.ss == m->dummysub) {
+ tsdissolve(topleft);
+ } else {
+ tsbond(topleft, botlsubseg);
+ }
+ if (botrsubseg.ss == m->dummysub) {
+ tsdissolve(botleft);
+ } else {
+ tsbond(botleft, botrsubseg);
+ }
+ if (toprsubseg.ss == m->dummysub) {
+ tsdissolve(botright);
+ } else {
+ tsbond(botright, toprsubseg);
+ }
+ }
+ /* New vertex assignments for the rotated quadrilateral. */
+ setorg(horiz, farvertex);
+ setdest(horiz, newvertex);
+ setapex(horiz, rightvertex);
+ setorg(top, newvertex);
+ setdest(top, farvertex);
+ setapex(top, leftvertex);
+ for (i = 0; i < m->eextras; i++) {
+ /* Take the average of the two triangles' attributes. */
+ attrib = 0.5 * (elemattribute(top, i) + elemattribute(horiz, i));
+ setelemattribute(top, i, attrib);
+ setelemattribute(horiz, i, attrib);
+ }
+ if (b->vararea) {
+ if ((areabound(top) <= 0.0) || (areabound(horiz) <= 0.0)) {
+ area = -1.0;
+ } else {
+ /* Take the average of the two triangles' area constraints. */
+ /* This prevents small area constraints from migrating a */
+ /* long, long way from their original location due to flips. */
+ area = 0.5 * (areabound(top) + areabound(horiz));
+ }
+ setareabound(top, area);
+ setareabound(horiz, area);
+ }
+
+ if (m->checkquality) {
+ newflip = (struct flipstacker *) poolalloc(&m->flipstackers);
+ newflip->flippedtri = encode(horiz);
+ newflip->prevflip = m->lastflip;
+ m->lastflip = newflip;
+ }
+
+#ifdef SELF_CHECK
+ if (newvertex != (vertex) NULL) {
+ if (counterclockwise(m, b, leftvertex, newvertex, rightvertex) <
+ 0.0) {
+ printf("Internal error in insertvertex():\n");
+ printf(" Clockwise triangle prior to edge flip (bottom).\n");
+ }
+ /* The following test has been removed because constrainededge() */
+ /* sometimes generates inverted triangles that insertvertex() */
+ /* removes. */
+/*
+ if (counterclockwise(m, b, rightvertex, farvertex, leftvertex) <
+ 0.0) {
+ printf("Internal error in insertvertex():\n");
+ printf(" Clockwise triangle prior to edge flip (top).\n");
+ }
+*/
+ if (counterclockwise(m, b, farvertex, leftvertex, newvertex) <
+ 0.0) {
+ printf("Internal error in insertvertex():\n");
+ printf(" Clockwise triangle after edge flip (left).\n");
+ }
+ if (counterclockwise(m, b, newvertex, rightvertex, farvertex) <
+ 0.0) {
+ printf("Internal error in insertvertex():\n");
+ printf(" Clockwise triangle after edge flip (right).\n");
+ }
+ }
+#endif /* SELF_CHECK */
+ if (b->verbose > 2) {
+ printf(" Edge flip results in left ");
+ lnextself(topleft);
+ printtriangle(m, b, &topleft);
+ printf(" and right ");
+ printtriangle(m, b, &horiz);
+ }
+ /* On the next iterations, consider the two edges that were */
+ /* exposed (this is, are now visible to the newly inserted */
+ /* vertex) by the edge flip. */
+ lprevself(horiz);
+ leftvertex = farvertex;
+ }
+ }
+ }
+ if (!doflip) {
+ /* The handle `horiz' is accepted as locally Delaunay. */
+#ifndef CDT_ONLY
+ if (triflaws) {
+ /* Check the triangle `horiz' for quality. */
+ testtriangle(m, b, &horiz);
+ }
+#endif /* not CDT_ONLY */
+ /* Look for the next edge around the newly inserted vertex. */
+ lnextself(horiz);
+ sym(horiz, testtri);
+ /* Check for finishing a complete revolution about the new vertex, or */
+ /* falling outside of the triangulation. The latter will happen */
+ /* when a vertex is inserted at a boundary. */
+ if ((leftvertex == first) || (testtri.tri == m->dummytri)) {
+ /* We're done. Return a triangle whose origin is the new vertex. */
+ lnext(horiz, *searchtri);
+ lnext(horiz, m->recenttri);
+ return success;
+ }
+ /* Finish finding the next edge around the newly inserted vertex. */
+ lnext(testtri, horiz);
+ rightvertex = leftvertex;
+ dest(horiz, leftvertex);
+ }
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* triangulatepolygon() Find the Delaunay triangulation of a polygon that */
+/* has a certain "nice" shape. This includes the */
+/* polygons that result from deletion of a vertex or */
+/* insertion of a segment. */
+/* */
+/* This is a conceptually difficult routine. The starting assumption is */
+/* that we have a polygon with n sides. n - 1 of these sides are currently */
+/* represented as edges in the mesh. One side, called the "base", need not */
+/* be. */
+/* */
+/* Inside the polygon is a structure I call a "fan", consisting of n - 1 */
+/* triangles that share a common origin. For each of these triangles, the */
+/* edge opposite the origin is one of the sides of the polygon. The */
+/* primary edge of each triangle is the edge directed from the origin to */
+/* the destination; note that this is not the same edge that is a side of */
+/* the polygon. `firstedge' is the primary edge of the first triangle. */
+/* From there, the triangles follow in counterclockwise order about the */
+/* polygon, until `lastedge', the primary edge of the last triangle. */
+/* `firstedge' and `lastedge' are probably connected to other triangles */
+/* beyond the extremes of the fan, but their identity is not important, as */
+/* long as the fan remains connected to them. */
+/* */
+/* Imagine the polygon oriented so that its base is at the bottom. This */
+/* puts `firstedge' on the far right, and `lastedge' on the far left. */
+/* The right vertex of the base is the destination of `firstedge', and the */
+/* left vertex of the base is the apex of `lastedge'. */
+/* */
+/* The challenge now is to find the right sequence of edge flips to */
+/* transform the fan into a Delaunay triangulation of the polygon. Each */
+/* edge flip effectively removes one triangle from the fan, committing it */
+/* to the polygon. The resulting polygon has one fewer edge. If `doflip' */
+/* is set, the final flip will be performed, resulting in a fan of one */
+/* (useless?) triangle. If `doflip' is not set, the final flip is not */
+/* performed, resulting in a fan of two triangles, and an unfinished */
+/* triangular polygon that is not yet filled out with a single triangle. */
+/* On completion of the routine, `lastedge' is the last remaining triangle, */
+/* or the leftmost of the last two. */
+/* */
+/* Although the flips are performed in the order described above, the */
+/* decisions about what flips to perform are made in precisely the reverse */
+/* order. The recursive triangulatepolygon() procedure makes a decision, */
+/* uses up to two recursive calls to triangulate the "subproblems" */
+/* (polygons with fewer edges), and then performs an edge flip. */
+/* */
+/* The "decision" it makes is which vertex of the polygon should be */
+/* connected to the base. This decision is made by testing every possible */
+/* vertex. Once the best vertex is found, the two edges that connect this */
+/* vertex to the base become the bases for two smaller polygons. These */
+/* are triangulated recursively. Unfortunately, this approach can take */
+/* O(n^2) time not only in the worst case, but in many common cases. It's */
+/* rarely a big deal for vertex deletion, where n is rarely larger than */
+/* ten, but it could be a big deal for segment insertion, especially if */
+/* there's a lot of long segments that each cut many triangles. I ought to */
+/* code a faster algorithm some day. */
+/* */
+/* The `edgecount' parameter is the number of sides of the polygon, */
+/* including its base. `triflaws' is a flag that determines whether the */
+/* new triangles should be tested for quality, and enqueued if they are */
+/* bad. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void triangulatepolygon(struct mesh *m, struct behavior *b,
+ struct otri *firstedge, struct otri *lastedge,
+ int edgecount, int doflip, int triflaws)
+#else /* not ANSI_DECLARATORS */
+void triangulatepolygon(m, b, firstedge, lastedge, edgecount, doflip, triflaws)
+struct mesh *m;
+struct behavior *b;
+struct otri *firstedge;
+struct otri *lastedge;
+int edgecount;
+int doflip;
+int triflaws;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri testtri;
+ struct otri besttri;
+ struct otri tempedge;
+ vertex leftbasevertex, rightbasevertex;
+ vertex testvertex;
+ vertex bestvertex;
+ int bestnumber;
+ int i;
+ triangle ptr; /* Temporary variable used by sym(), onext(), and oprev(). */
+
+ /* Identify the base vertices. */
+ apex(*lastedge, leftbasevertex);
+ dest(*firstedge, rightbasevertex);
+ if (b->verbose > 2) {
+ printf(" Triangulating interior polygon at edge\n");
+ printf(" (%.12g, %.12g) (%.12g, %.12g)\n", leftbasevertex[0],
+ leftbasevertex[1], rightbasevertex[0], rightbasevertex[1]);
+ }
+ /* Find the best vertex to connect the base to. */
+ onext(*firstedge, besttri);
+ dest(besttri, bestvertex);
+ otricopy(besttri, testtri);
+ bestnumber = 1;
+ for (i = 2; i <= edgecount - 2; i++) {
+ onextself(testtri);
+ dest(testtri, testvertex);
+ /* Is this a better vertex? */
+ if (incircle(m, b, leftbasevertex, rightbasevertex, bestvertex,
+ testvertex) > 0.0) {
+ otricopy(testtri, besttri);
+ bestvertex = testvertex;
+ bestnumber = i;
+ }
+ }
+ if (b->verbose > 2) {
+ printf(" Connecting edge to (%.12g, %.12g)\n", bestvertex[0],
+ bestvertex[1]);
+ }
+ if (bestnumber > 1) {
+ /* Recursively triangulate the smaller polygon on the right. */
+ oprev(besttri, tempedge);
+ triangulatepolygon(m, b, firstedge, &tempedge, bestnumber + 1, 1,
+ triflaws);
+ }
+ if (bestnumber < edgecount - 2) {
+ /* Recursively triangulate the smaller polygon on the left. */
+ sym(besttri, tempedge);
+ triangulatepolygon(m, b, &besttri, lastedge, edgecount - bestnumber, 1,
+ triflaws);
+ /* Find `besttri' again; it may have been lost to edge flips. */
+ sym(tempedge, besttri);
+ }
+ if (doflip) {
+ /* Do one final edge flip. */
+ flip(m, b, &besttri);
+#ifndef CDT_ONLY
+ if (triflaws) {
+ /* Check the quality of the newly committed triangle. */
+ sym(besttri, testtri);
+ testtriangle(m, b, &testtri);
+ }
+#endif /* not CDT_ONLY */
+ }
+ /* Return the base triangle. */
+ otricopy(besttri, *lastedge);
+}
+
+/*****************************************************************************/
+/* */
+/* deletevertex() Delete a vertex from a Delaunay triangulation, ensuring */
+/* that the triangulation remains Delaunay. */
+/* */
+/* The origin of `deltri' is deleted. The union of the triangles adjacent */
+/* to this vertex is a polygon, for which the Delaunay triangulation is */
+/* found. Two triangles are removed from the mesh. */
+/* */
+/* Only interior vertices that do not lie on segments or boundaries may be */
+/* deleted. */
+/* */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void deletevertex(struct mesh *m, struct behavior *b, struct otri *deltri)
+#else /* not ANSI_DECLARATORS */
+void deletevertex(m, b, deltri)
+struct mesh *m;
+struct behavior *b;
+struct otri *deltri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri countingtri;
+ struct otri firstedge, lastedge;
+ struct otri deltriright;
+ struct otri lefttri, righttri;
+ struct otri leftcasing, rightcasing;
+ struct osub leftsubseg, rightsubseg;
+ vertex delvertex;
+ vertex neworg;
+ int edgecount;
+ triangle ptr; /* Temporary variable used by sym(), onext(), and oprev(). */
+ subseg sptr; /* Temporary variable used by tspivot(). */
+
+ org(*deltri, delvertex);
+ if (b->verbose > 1) {
+ printf(" Deleting (%.12g, %.12g).\n", delvertex[0], delvertex[1]);
+ }
+ vertexdealloc(m, delvertex);
+
+ /* Count the degree of the vertex being deleted. */
+ onext(*deltri, countingtri);
+ edgecount = 1;
+ while (!otriequal(*deltri, countingtri)) {
+#ifdef SELF_CHECK
+ if (countingtri.tri == m->dummytri) {
+ printf("Internal error in deletevertex():\n");
+ printf(" Attempt to delete boundary vertex.\n");
+ internalerror();
+ }
+#endif /* SELF_CHECK */
+ edgecount++;
+ onextself(countingtri);
+ }
+
+#ifdef SELF_CHECK
+ if (edgecount < 3) {
+ printf("Internal error in deletevertex():\n Vertex has degree %d.\n",
+ edgecount);
+ internalerror();
+ }
+#endif /* SELF_CHECK */
+ if (edgecount > 3) {
+ /* Triangulate the polygon defined by the union of all triangles */
+ /* adjacent to the vertex being deleted. Check the quality of */
+ /* the resulting triangles. */
+ onext(*deltri, firstedge);
+ oprev(*deltri, lastedge);
+ triangulatepolygon(m, b, &firstedge, &lastedge, edgecount, 0,
+ !b->nobisect);
+ }
+ /* Splice out two triangles. */
+ lprev(*deltri, deltriright);
+ dnext(*deltri, lefttri);
+ sym(lefttri, leftcasing);
+ oprev(deltriright, righttri);
+ sym(righttri, rightcasing);
+ bond(*deltri, leftcasing);
+ bond(deltriright, rightcasing);
+ tspivot(lefttri, leftsubseg);
+ if (leftsubseg.ss != m->dummysub) {
+ tsbond(*deltri, leftsubseg);
+ }
+ tspivot(righttri, rightsubseg);
+ if (rightsubseg.ss != m->dummysub) {
+ tsbond(deltriright, rightsubseg);
+ }
+
+ /* Set the new origin of `deltri' and check its quality. */
+ org(lefttri, neworg);
+ setorg(*deltri, neworg);
+ if (!b->nobisect) {
+ testtriangle(m, b, deltri);
+ }
+
+ /* Delete the two spliced-out triangles. */
+ triangledealloc(m, lefttri.tri);
+ triangledealloc(m, righttri.tri);
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/* */
+/* undovertex() Undo the most recent vertex insertion. */
+/* */
+/* Walks through the list of transformations (flips and a vertex insertion) */
+/* in the reverse of the order in which they were done, and undoes them. */
+/* The inserted vertex is removed from the triangulation and deallocated. */
+/* Two triangles (possibly just one) are also deallocated. */
+/* */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void undovertex(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void undovertex(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri fliptri;
+ struct otri botleft, botright, topright;
+ struct otri botlcasing, botrcasing, toprcasing;
+ struct otri gluetri;
+ struct osub botlsubseg, botrsubseg, toprsubseg;
+ vertex botvertex, rightvertex;
+ triangle ptr; /* Temporary variable used by sym(). */
+ subseg sptr; /* Temporary variable used by tspivot(). */
+
+ /* Walk through the list of transformations (flips and a vertex insertion) */
+ /* in the reverse of the order in which they were done, and undo them. */
+ while (m->lastflip != (struct flipstacker *) NULL) {
+ /* Find a triangle involved in the last unreversed transformation. */
+ decode(m->lastflip->flippedtri, fliptri);
+
+ /* We are reversing one of three transformations: a trisection of one */
+ /* triangle into three (by inserting a vertex in the triangle), a */
+ /* bisection of two triangles into four (by inserting a vertex in an */
+ /* edge), or an edge flip. */
+ if (m->lastflip->prevflip == (struct flipstacker *) NULL) {
+ /* Restore a triangle that was split into three triangles, */
+ /* so it is again one triangle. */
+ dprev(fliptri, botleft);
+ lnextself(botleft);
+ onext(fliptri, botright);
+ lprevself(botright);
+ sym(botleft, botlcasing);
+ sym(botright, botrcasing);
+ dest(botleft, botvertex);
+
+ setapex(fliptri, botvertex);
+ lnextself(fliptri);
+ bond(fliptri, botlcasing);
+ tspivot(botleft, botlsubseg);
+ tsbond(fliptri, botlsubseg);
+ lnextself(fliptri);
+ bond(fliptri, botrcasing);
+ tspivot(botright, botrsubseg);
+ tsbond(fliptri, botrsubseg);
+
+ /* Delete the two spliced-out triangles. */
+ triangledealloc(m, botleft.tri);
+ triangledealloc(m, botright.tri);
+ } else if (m->lastflip->prevflip == (struct flipstacker *) &insertvertex) {
+ /* Restore two triangles that were split into four triangles, */
+ /* so they are again two triangles. */
+ lprev(fliptri, gluetri);
+ sym(gluetri, botright);
+ lnextself(botright);
+ sym(botright, botrcasing);
+ dest(botright, rightvertex);
+
+ setorg(fliptri, rightvertex);
+ bond(gluetri, botrcasing);
+ tspivot(botright, botrsubseg);
+ tsbond(gluetri, botrsubseg);
+
+ /* Delete the spliced-out triangle. */
+ triangledealloc(m, botright.tri);
+
+ sym(fliptri, gluetri);
+ if (gluetri.tri != m->dummytri) {
+ lnextself(gluetri);
+ dnext(gluetri, topright);
+ sym(topright, toprcasing);
+
+ setorg(gluetri, rightvertex);
+ bond(gluetri, toprcasing);
+ tspivot(topright, toprsubseg);
+ tsbond(gluetri, toprsubseg);
+
+ /* Delete the spliced-out triangle. */
+ triangledealloc(m, topright.tri);
+ }
+
+ /* This is the end of the list, sneakily encoded. */
+ m->lastflip->prevflip = (struct flipstacker *) NULL;
+ } else {
+ /* Undo an edge flip. */
+ unflip(m, b, &fliptri);
+ }
+
+ /* Go on and process the next transformation. */
+ m->lastflip = m->lastflip->prevflip;
+ }
+}
+
+#endif /* not CDT_ONLY */
+
+/** **/
+/** **/
+/********* Mesh transformation routines end here *********/
+
+/********* Divide-and-conquer Delaunay triangulation begins here *********/
+/** **/
+/** **/
+
+/*****************************************************************************/
+/* */
+/* The divide-and-conquer bounding box */
+/* */
+/* I originally implemented the divide-and-conquer and incremental Delaunay */
+/* triangulations using the edge-based data structure presented by Guibas */
+/* and Stolfi. Switching to a triangle-based data structure doubled the */
+/* speed. However, I had to think of a few extra tricks to maintain the */
+/* elegance of the original algorithms. */
+/* */
+/* The "bounding box" used by my variant of the divide-and-conquer */
+/* algorithm uses one triangle for each edge of the convex hull of the */
+/* triangulation. These bounding triangles all share a common apical */
+/* vertex, which is represented by NULL and which represents nothing. */
+/* The bounding triangles are linked in a circular fan about this NULL */
+/* vertex, and the edges on the convex hull of the triangulation appear */
+/* opposite the NULL vertex. You might find it easiest to imagine that */
+/* the NULL vertex is a point in 3D space behind the center of the */
+/* triangulation, and that the bounding triangles form a sort of cone. */
+/* */
+/* This bounding box makes it easy to represent degenerate cases. For */
+/* instance, the triangulation of two vertices is a single edge. This edge */
+/* is represented by two bounding box triangles, one on each "side" of the */
+/* edge. These triangles are also linked together in a fan about the NULL */
+/* vertex. */
+/* */
+/* The bounding box also makes it easy to traverse the convex hull, as the */
+/* divide-and-conquer algorithm needs to do. */
+/* */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* */
+/* vertexsort() Sort an array of vertices by x-coordinate, using the */
+/* y-coordinate as a secondary key. */
+/* */
+/* Uses quicksort. Randomized O(n log n) time. No, I did not make any of */
+/* the usual quicksort mistakes. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void vertexsort(vertex *sortarray, int arraysize)
+#else /* not ANSI_DECLARATORS */
+void vertexsort(sortarray, arraysize)
+vertex *sortarray;
+int arraysize;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ int left, right;
+ int pivot;
+ REAL pivotx, pivoty;
+ vertex temp;
+
+ if (arraysize == 2) {
+ /* Recursive base case. */
+ if ((sortarray[0][0] > sortarray[1][0]) ||
+ ((sortarray[0][0] == sortarray[1][0]) &&
+ (sortarray[0][1] > sortarray[1][1]))) {
+ temp = sortarray[1];
+ sortarray[1] = sortarray[0];
+ sortarray[0] = temp;
+ }
+ return;
+ }
+ /* Choose a random pivot to split the array. */
+ pivot = (int) randomnation((unsigned int) arraysize);
+ pivotx = sortarray[pivot][0];
+ pivoty = sortarray[pivot][1];
+ /* Split the array. */
+ left = -1;
+ right = arraysize;
+ while (left < right) {
+ /* Search for a vertex whose x-coordinate is too large for the left. */
+ do {
+ left++;
+ } while ((left <= right) && ((sortarray[left][0] < pivotx) ||
+ ((sortarray[left][0] == pivotx) &&
+ (sortarray[left][1] < pivoty))));
+ /* Search for a vertex whose x-coordinate is too small for the right. */
+ do {
+ right--;
+ } while ((left <= right) && ((sortarray[right][0] > pivotx) ||
+ ((sortarray[right][0] == pivotx) &&
+ (sortarray[right][1] > pivoty))));
+ if (left < right) {
+ /* Swap the left and right vertices. */
+ temp = sortarray[left];
+ sortarray[left] = sortarray[right];
+ sortarray[right] = temp;
+ }
+ }
+ if (left > 1) {
+ /* Recursively sort the left subset. */
+ vertexsort(sortarray, left);
+ }
+ if (right < arraysize - 2) {
+ /* Recursively sort the right subset. */
+ vertexsort(&sortarray[right + 1], arraysize - right - 1);
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* vertexmedian() An order statistic algorithm, almost. Shuffles an */
+/* array of vertices so that the first `median' vertices */
+/* occur lexicographically before the remaining vertices. */
+/* */
+/* Uses the x-coordinate as the primary key if axis == 0; the y-coordinate */
+/* if axis == 1. Very similar to the vertexsort() procedure, but runs in */
+/* randomized linear time. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void vertexmedian(vertex *sortarray, int arraysize, int median, int axis)
+#else /* not ANSI_DECLARATORS */
+void vertexmedian(sortarray, arraysize, median, axis)
+vertex *sortarray;
+int arraysize;
+int median;
+int axis;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ int left, right;
+ int pivot;
+ REAL pivot1, pivot2;
+ vertex temp;
+
+ if (arraysize == 2) {
+ /* Recursive base case. */
+ if ((sortarray[0][axis] > sortarray[1][axis]) ||
+ ((sortarray[0][axis] == sortarray[1][axis]) &&
+ (sortarray[0][1 - axis] > sortarray[1][1 - axis]))) {
+ temp = sortarray[1];
+ sortarray[1] = sortarray[0];
+ sortarray[0] = temp;
+ }
+ return;
+ }
+ /* Choose a random pivot to split the array. */
+ pivot = (int) randomnation((unsigned int) arraysize);
+ pivot1 = sortarray[pivot][axis];
+ pivot2 = sortarray[pivot][1 - axis];
+ /* Split the array. */
+ left = -1;
+ right = arraysize;
+ while (left < right) {
+ /* Search for a vertex whose x-coordinate is too large for the left. */
+ do {
+ left++;
+ } while ((left <= right) && ((sortarray[left][axis] < pivot1) ||
+ ((sortarray[left][axis] == pivot1) &&
+ (sortarray[left][1 - axis] < pivot2))));
+ /* Search for a vertex whose x-coordinate is too small for the right. */
+ do {
+ right--;
+ } while ((left <= right) && ((sortarray[right][axis] > pivot1) ||
+ ((sortarray[right][axis] == pivot1) &&
+ (sortarray[right][1 - axis] > pivot2))));
+ if (left < right) {
+ /* Swap the left and right vertices. */
+ temp = sortarray[left];
+ sortarray[left] = sortarray[right];
+ sortarray[right] = temp;
+ }
+ }
+ /* Unlike in vertexsort(), at most one of the following */
+ /* conditionals is true. */
+ if (left > median) {
+ /* Recursively shuffle the left subset. */
+ vertexmedian(sortarray, left, median, axis);
+ }
+ if (right < median - 1) {
+ /* Recursively shuffle the right subset. */
+ vertexmedian(&sortarray[right + 1], arraysize - right - 1,
+ median - right - 1, axis);
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* alternateaxes() Sorts the vertices as appropriate for the divide-and- */
+/* conquer algorithm with alternating cuts. */
+/* */
+/* Partitions by x-coordinate if axis == 0; by y-coordinate if axis == 1. */
+/* For the base case, subsets containing only two or three vertices are */
+/* always sorted by x-coordinate. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void alternateaxes(vertex *sortarray, int arraysize, int axis)
+#else /* not ANSI_DECLARATORS */
+void alternateaxes(sortarray, arraysize, axis)
+vertex *sortarray;
+int arraysize;
+int axis;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ int divider;
+
+ divider = arraysize >> 1;
+ if (arraysize <= 3) {
+ /* Recursive base case: subsets of two or three vertices will be */
+ /* handled specially, and should always be sorted by x-coordinate. */
+ axis = 0;
+ }
+ /* Partition with a horizontal or vertical cut. */
+ vertexmedian(sortarray, arraysize, divider, axis);
+ /* Recursively partition the subsets with a cross cut. */
+ if (arraysize - divider >= 2) {
+ if (divider >= 2) {
+ alternateaxes(sortarray, divider, 1 - axis);
+ }
+ alternateaxes(&sortarray[divider], arraysize - divider, 1 - axis);
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* mergehulls() Merge two adjacent Delaunay triangulations into a */
+/* single Delaunay triangulation. */
+/* */
+/* This is similar to the algorithm given by Guibas and Stolfi, but uses */
+/* a triangle-based, rather than edge-based, data structure. */
+/* */
+/* The algorithm walks up the gap between the two triangulations, knitting */
+/* them together. As they are merged, some of their bounding triangles */
+/* are converted into real triangles of the triangulation. The procedure */
+/* pulls each hull's bounding triangles apart, then knits them together */
+/* like the teeth of two gears. The Delaunay property determines, at each */
+/* step, whether the next "tooth" is a bounding triangle of the left hull */
+/* or the right. When a bounding triangle becomes real, its apex is */
+/* changed from NULL to a real vertex. */
+/* */
+/* Only two new triangles need to be allocated. These become new bounding */
+/* triangles at the top and bottom of the seam. They are used to connect */
+/* the remaining bounding triangles (those that have not been converted */
+/* into real triangles) into a single fan. */
+/* */
+/* On entry, `farleft' and `innerleft' are bounding triangles of the left */
+/* triangulation. The origin of `farleft' is the leftmost vertex, and */
+/* the destination of `innerleft' is the rightmost vertex of the */
+/* triangulation. Similarly, `innerright' and `farright' are bounding */
+/* triangles of the right triangulation. The origin of `innerright' and */
+/* destination of `farright' are the leftmost and rightmost vertices. */
+/* */
+/* On completion, the origin of `farleft' is the leftmost vertex of the */
+/* merged triangulation, and the destination of `farright' is the rightmost */
+/* vertex. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void mergehulls(struct mesh *m, struct behavior *b, struct otri *farleft,
+ struct otri *innerleft, struct otri *innerright,
+ struct otri *farright, int axis)
+#else /* not ANSI_DECLARATORS */
+void mergehulls(m, b, farleft, innerleft, innerright, farright, axis)
+struct mesh *m;
+struct behavior *b;
+struct otri *farleft;
+struct otri *innerleft;
+struct otri *innerright;
+struct otri *farright;
+int axis;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri leftcand, rightcand;
+ struct otri baseedge;
+ struct otri nextedge;
+ struct otri sidecasing, topcasing, outercasing;
+ struct otri checkedge;
+ vertex innerleftdest;
+ vertex innerrightorg;
+ vertex innerleftapex, innerrightapex;
+ vertex farleftpt, farrightpt;
+ vertex farleftapex, farrightapex;
+ vertex lowerleft, lowerright;
+ vertex upperleft, upperright;
+ vertex nextapex;
+ vertex checkvertex;
+ int changemade;
+ int badedge;
+ int leftfinished, rightfinished;
+ triangle ptr; /* Temporary variable used by sym(). */
+
+ dest(*innerleft, innerleftdest);
+ apex(*innerleft, innerleftapex);
+ org(*innerright, innerrightorg);
+ apex(*innerright, innerrightapex);
+ /* Special treatment for horizontal cuts. */
+ if (b->dwyer && (axis == 1)) {
+ org(*farleft, farleftpt);
+ apex(*farleft, farleftapex);
+ dest(*farright, farrightpt);
+ apex(*farright, farrightapex);
+ /* The pointers to the extremal vertices are shifted to point to the */
+ /* topmost and bottommost vertex of each hull, rather than the */
+ /* leftmost and rightmost vertices. */
+ while (farleftapex[1] < farleftpt[1]) {
+ lnextself(*farleft);
+ symself(*farleft);
+ farleftpt = farleftapex;
+ apex(*farleft, farleftapex);
+ }
+ sym(*innerleft, checkedge);
+ apex(checkedge, checkvertex);
+ while (checkvertex[1] > innerleftdest[1]) {
+ lnext(checkedge, *innerleft);
+ innerleftapex = innerleftdest;
+ innerleftdest = checkvertex;
+ sym(*innerleft, checkedge);
+ apex(checkedge, checkvertex);
+ }
+ while (innerrightapex[1] < innerrightorg[1]) {
+ lnextself(*innerright);
+ symself(*innerright);
+ innerrightorg = innerrightapex;
+ apex(*innerright, innerrightapex);
+ }
+ sym(*farright, checkedge);
+ apex(checkedge, checkvertex);
+ while (checkvertex[1] > farrightpt[1]) {
+ lnext(checkedge, *farright);
+ farrightapex = farrightpt;
+ farrightpt = checkvertex;
+ sym(*farright, checkedge);
+ apex(checkedge, checkvertex);
+ }
+ }
+ /* Find a line tangent to and below both hulls. */
+ do {
+ changemade = 0;
+ /* Make innerleftdest the "bottommost" vertex of the left hull. */
+ if (counterclockwise(m, b, innerleftdest, innerleftapex, innerrightorg) >
+ 0.0) {
+ lprevself(*innerleft);
+ symself(*innerleft);
+ innerleftdest = innerleftapex;
+ apex(*innerleft, innerleftapex);
+ changemade = 1;
+ }
+ /* Make innerrightorg the "bottommost" vertex of the right hull. */
+ if (counterclockwise(m, b, innerrightapex, innerrightorg, innerleftdest) >
+ 0.0) {
+ lnextself(*innerright);
+ symself(*innerright);
+ innerrightorg = innerrightapex;
+ apex(*innerright, innerrightapex);
+ changemade = 1;
+ }
+ } while (changemade);
+ /* Find the two candidates to be the next "gear tooth." */
+ sym(*innerleft, leftcand);
+ sym(*innerright, rightcand);
+ /* Create the bottom new bounding triangle. */
+ maketriangle(m, b, &baseedge);
+ /* Connect it to the bounding boxes of the left and right triangulations. */
+ bond(baseedge, *innerleft);
+ lnextself(baseedge);
+ bond(baseedge, *innerright);
+ lnextself(baseedge);
+ setorg(baseedge, innerrightorg);
+ setdest(baseedge, innerleftdest);
+ /* Apex is intentionally left NULL. */
+ if (b->verbose > 2) {
+ printf(" Creating base bounding ");
+ printtriangle(m, b, &baseedge);
+ }
+ /* Fix the extreme triangles if necessary. */
+ org(*farleft, farleftpt);
+ if (innerleftdest == farleftpt) {
+ lnext(baseedge, *farleft);
+ }
+ dest(*farright, farrightpt);
+ if (innerrightorg == farrightpt) {
+ lprev(baseedge, *farright);
+ }
+ /* The vertices of the current knitting edge. */
+ lowerleft = innerleftdest;
+ lowerright = innerrightorg;
+ /* The candidate vertices for knitting. */
+ apex(leftcand, upperleft);
+ apex(rightcand, upperright);
+ /* Walk up the gap between the two triangulations, knitting them together. */
+ while (1) {
+ /* Have we reached the top? (This isn't quite the right question, */
+ /* because even though the left triangulation might seem finished now, */
+ /* moving up on the right triangulation might reveal a new vertex of */
+ /* the left triangulation. And vice-versa.) */
+ leftfinished = counterclockwise(m, b, upperleft, lowerleft, lowerright) <=
+ 0.0;
+ rightfinished = counterclockwise(m, b, upperright, lowerleft, lowerright)
+ <= 0.0;
+ if (leftfinished && rightfinished) {
+ /* Create the top new bounding triangle. */
+ maketriangle(m, b, &nextedge);
+ setorg(nextedge, lowerleft);
+ setdest(nextedge, lowerright);
+ /* Apex is intentionally left NULL. */
+ /* Connect it to the bounding boxes of the two triangulations. */
+ bond(nextedge, baseedge);
+ lnextself(nextedge);
+ bond(nextedge, rightcand);
+ lnextself(nextedge);
+ bond(nextedge, leftcand);
+ if (b->verbose > 2) {
+ printf(" Creating top bounding ");
+ printtriangle(m, b, &nextedge);
+ }
+ /* Special treatment for horizontal cuts. */
+ if (b->dwyer && (axis == 1)) {
+ org(*farleft, farleftpt);
+ apex(*farleft, farleftapex);
+ dest(*farright, farrightpt);
+ apex(*farright, farrightapex);
+ sym(*farleft, checkedge);
+ apex(checkedge, checkvertex);
+ /* The pointers to the extremal vertices are restored to the */
+ /* leftmost and rightmost vertices (rather than topmost and */
+ /* bottommost). */
+ while (checkvertex[0] < farleftpt[0]) {
+ lprev(checkedge, *farleft);
+ farleftapex = farleftpt;
+ farleftpt = checkvertex;
+ sym(*farleft, checkedge);
+ apex(checkedge, checkvertex);
+ }
+ while (farrightapex[0] > farrightpt[0]) {
+ lprevself(*farright);
+ symself(*farright);
+ farrightpt = farrightapex;
+ apex(*farright, farrightapex);
+ }
+ }
+ return;
+ }
+ /* Consider eliminating edges from the left triangulation. */
+ if (!leftfinished) {
+ /* What vertex would be exposed if an edge were deleted? */
+ lprev(leftcand, nextedge);
+ symself(nextedge);
+ apex(nextedge, nextapex);
+ /* If nextapex is NULL, then no vertex would be exposed; the */
+ /* triangulation would have been eaten right through. */
+ if (nextapex != (vertex) NULL) {
+ /* Check whether the edge is Delaunay. */
+ badedge = incircle(m, b, lowerleft, lowerright, upperleft, nextapex) >
+ 0.0;
+ while (badedge) {
+ /* Eliminate the edge with an edge flip. As a result, the */
+ /* left triangulation will have one more boundary triangle. */
+ lnextself(nextedge);
+ sym(nextedge, topcasing);
+ lnextself(nextedge);
+ sym(nextedge, sidecasing);
+ bond(nextedge, topcasing);
+ bond(leftcand, sidecasing);
+ lnextself(leftcand);
+ sym(leftcand, outercasing);
+ lprevself(nextedge);
+ bond(nextedge, outercasing);
+ /* Correct the vertices to reflect the edge flip. */
+ setorg(leftcand, lowerleft);
+ setdest(leftcand, NULL);
+ setapex(leftcand, nextapex);
+ setorg(nextedge, NULL);
+ setdest(nextedge, upperleft);
+ setapex(nextedge, nextapex);
+ /* Consider the newly exposed vertex. */
+ upperleft = nextapex;
+ /* What vertex would be exposed if another edge were deleted? */
+ otricopy(sidecasing, nextedge);
+ apex(nextedge, nextapex);
+ if (nextapex != (vertex) NULL) {
+ /* Check whether the edge is Delaunay. */
+ badedge = incircle(m, b, lowerleft, lowerright, upperleft,
+ nextapex) > 0.0;
+ } else {
+ /* Avoid eating right through the triangulation. */
+ badedge = 0;
+ }
+ }
+ }
+ }
+ /* Consider eliminating edges from the right triangulation. */
+ if (!rightfinished) {
+ /* What vertex would be exposed if an edge were deleted? */
+ lnext(rightcand, nextedge);
+ symself(nextedge);
+ apex(nextedge, nextapex);
+ /* If nextapex is NULL, then no vertex would be exposed; the */
+ /* triangulation would have been eaten right through. */
+ if (nextapex != (vertex) NULL) {
+ /* Check whether the edge is Delaunay. */
+ badedge = incircle(m, b, lowerleft, lowerright, upperright, nextapex) >
+ 0.0;
+ while (badedge) {
+ /* Eliminate the edge with an edge flip. As a result, the */
+ /* right triangulation will have one more boundary triangle. */
+ lprevself(nextedge);
+ sym(nextedge, topcasing);
+ lprevself(nextedge);
+ sym(nextedge, sidecasing);
+ bond(nextedge, topcasing);
+ bond(rightcand, sidecasing);
+ lprevself(rightcand);
+ sym(rightcand, outercasing);
+ lnextself(nextedge);
+ bond(nextedge, outercasing);
+ /* Correct the vertices to reflect the edge flip. */
+ setorg(rightcand, NULL);
+ setdest(rightcand, lowerright);
+ setapex(rightcand, nextapex);
+ setorg(nextedge, upperright);
+ setdest(nextedge, NULL);
+ setapex(nextedge, nextapex);
+ /* Consider the newly exposed vertex. */
+ upperright = nextapex;
+ /* What vertex would be exposed if another edge were deleted? */
+ otricopy(sidecasing, nextedge);
+ apex(nextedge, nextapex);
+ if (nextapex != (vertex) NULL) {
+ /* Check whether the edge is Delaunay. */
+ badedge = incircle(m, b, lowerleft, lowerright, upperright,
+ nextapex) > 0.0;
+ } else {
+ /* Avoid eating right through the triangulation. */
+ badedge = 0;
+ }
+ }
+ }
+ }
+ if (leftfinished || (!rightfinished &&
+ (incircle(m, b, upperleft, lowerleft, lowerright, upperright) >
+ 0.0))) {
+ /* Knit the triangulations, adding an edge from `lowerleft' */
+ /* to `upperright'. */
+ bond(baseedge, rightcand);
+ lprev(rightcand, baseedge);
+ setdest(baseedge, lowerleft);
+ lowerright = upperright;
+ sym(baseedge, rightcand);
+ apex(rightcand, upperright);
+ } else {
+ /* Knit the triangulations, adding an edge from `upperleft' */
+ /* to `lowerright'. */
+ bond(baseedge, leftcand);
+ lnext(leftcand, baseedge);
+ setorg(baseedge, lowerright);
+ lowerleft = upperleft;
+ sym(baseedge, leftcand);
+ apex(leftcand, upperleft);
+ }
+ if (b->verbose > 2) {
+ printf(" Connecting ");
+ printtriangle(m, b, &baseedge);
+ }
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* divconqrecurse() Recursively form a Delaunay triangulation by the */
+/* divide-and-conquer method. */
+/* */
+/* Recursively breaks down the problem into smaller pieces, which are */
+/* knitted together by mergehulls(). The base cases (problems of two or */
+/* three vertices) are handled specially here. */
+/* */
+/* On completion, `farleft' and `farright' are bounding triangles such that */
+/* the origin of `farleft' is the leftmost vertex (breaking ties by */
+/* choosing the highest leftmost vertex), and the destination of */
+/* `farright' is the rightmost vertex (breaking ties by choosing the */
+/* lowest rightmost vertex). */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void divconqrecurse(struct mesh *m, struct behavior *b, vertex *sortarray,
+ int vertices, int axis,
+ struct otri *farleft, struct otri *farright)
+#else /* not ANSI_DECLARATORS */
+void divconqrecurse(m, b, sortarray, vertices, axis, farleft, farright)
+struct mesh *m;
+struct behavior *b;
+vertex *sortarray;
+int vertices;
+int axis;
+struct otri *farleft;
+struct otri *farright;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri midtri, tri1, tri2, tri3;
+ struct otri innerleft, innerright;
+ REAL area;
+ int divider;
+
+ if (b->verbose > 2) {
+ printf(" Triangulating %d vertices.\n", vertices);
+ }
+ if (vertices == 2) {
+ /* The triangulation of two vertices is an edge. An edge is */
+ /* represented by two bounding triangles. */
+ maketriangle(m, b, farleft);
+ setorg(*farleft, sortarray[0]);
+ setdest(*farleft, sortarray[1]);
+ /* The apex is intentionally left NULL. */
+ maketriangle(m, b, farright);
+ setorg(*farright, sortarray[1]);
+ setdest(*farright, sortarray[0]);
+ /* The apex is intentionally left NULL. */
+ bond(*farleft, *farright);
+ lprevself(*farleft);
+ lnextself(*farright);
+ bond(*farleft, *farright);
+ lprevself(*farleft);
+ lnextself(*farright);
+ bond(*farleft, *farright);
+ if (b->verbose > 2) {
+ printf(" Creating ");
+ printtriangle(m, b, farleft);
+ printf(" Creating ");
+ printtriangle(m, b, farright);
+ }
+ /* Ensure that the origin of `farleft' is sortarray[0]. */
+ lprev(*farright, *farleft);
+ return;
+ } else if (vertices == 3) {
+ /* The triangulation of three vertices is either a triangle (with */
+ /* three bounding triangles) or two edges (with four bounding */
+ /* triangles). In either case, four triangles are created. */
+ maketriangle(m, b, &midtri);
+ maketriangle(m, b, &tri1);
+ maketriangle(m, b, &tri2);
+ maketriangle(m, b, &tri3);
+ area = counterclockwise(m, b, sortarray[0], sortarray[1], sortarray[2]);
+ if (area == 0.0) {
+ /* Three collinear vertices; the triangulation is two edges. */
+ setorg(midtri, sortarray[0]);
+ setdest(midtri, sortarray[1]);
+ setorg(tri1, sortarray[1]);
+ setdest(tri1, sortarray[0]);
+ setorg(tri2, sortarray[2]);
+ setdest(tri2, sortarray[1]);
+ setorg(tri3, sortarray[1]);
+ setdest(tri3, sortarray[2]);
+ /* All apices are intentionally left NULL. */
+ bond(midtri, tri1);
+ bond(tri2, tri3);
+ lnextself(midtri);
+ lprevself(tri1);
+ lnextself(tri2);
+ lprevself(tri3);
+ bond(midtri, tri3);
+ bond(tri1, tri2);
+ lnextself(midtri);
+ lprevself(tri1);
+ lnextself(tri2);
+ lprevself(tri3);
+ bond(midtri, tri1);
+ bond(tri2, tri3);
+ /* Ensure that the origin of `farleft' is sortarray[0]. */
+ otricopy(tri1, *farleft);
+ /* Ensure that the destination of `farright' is sortarray[2]. */
+ otricopy(tri2, *farright);
+ } else {
+ /* The three vertices are not collinear; the triangulation is one */
+ /* triangle, namely `midtri'. */
+ setorg(midtri, sortarray[0]);
+ setdest(tri1, sortarray[0]);
+ setorg(tri3, sortarray[0]);
+ /* Apices of tri1, tri2, and tri3 are left NULL. */
+ if (area > 0.0) {
+ /* The vertices are in counterclockwise order. */
+ setdest(midtri, sortarray[1]);
+ setorg(tri1, sortarray[1]);
+ setdest(tri2, sortarray[1]);
+ setapex(midtri, sortarray[2]);
+ setorg(tri2, sortarray[2]);
+ setdest(tri3, sortarray[2]);
+ } else {
+ /* The vertices are in clockwise order. */
+ setdest(midtri, sortarray[2]);
+ setorg(tri1, sortarray[2]);
+ setdest(tri2, sortarray[2]);
+ setapex(midtri, sortarray[1]);
+ setorg(tri2, sortarray[1]);
+ setdest(tri3, sortarray[1]);
+ }
+ /* The topology does not depend on how the vertices are ordered. */
+ bond(midtri, tri1);
+ lnextself(midtri);
+ bond(midtri, tri2);
+ lnextself(midtri);
+ bond(midtri, tri3);
+ lprevself(tri1);
+ lnextself(tri2);
+ bond(tri1, tri2);
+ lprevself(tri1);
+ lprevself(tri3);
+ bond(tri1, tri3);
+ lnextself(tri2);
+ lprevself(tri3);
+ bond(tri2, tri3);
+ /* Ensure that the origin of `farleft' is sortarray[0]. */
+ otricopy(tri1, *farleft);
+ /* Ensure that the destination of `farright' is sortarray[2]. */
+ if (area > 0.0) {
+ otricopy(tri2, *farright);
+ } else {
+ lnext(*farleft, *farright);
+ }
+ }
+ if (b->verbose > 2) {
+ printf(" Creating ");
+ printtriangle(m, b, &midtri);
+ printf(" Creating ");
+ printtriangle(m, b, &tri1);
+ printf(" Creating ");
+ printtriangle(m, b, &tri2);
+ printf(" Creating ");
+ printtriangle(m, b, &tri3);
+ }
+ return;
+ } else {
+ /* Split the vertices in half. */
+ divider = vertices >> 1;
+ /* Recursively triangulate each half. */
+ divconqrecurse(m, b, sortarray, divider, 1 - axis, farleft, &innerleft);
+ divconqrecurse(m, b, &sortarray[divider], vertices - divider, 1 - axis,
+ &innerright, farright);
+ if (b->verbose > 1) {
+ printf(" Joining triangulations with %d and %d vertices.\n", divider,
+ vertices - divider);
+ }
+ /* Merge the two triangulations into one. */
+ mergehulls(m, b, farleft, &innerleft, &innerright, farright, axis);
+ }
+}
+
+#ifdef ANSI_DECLARATORS
+long removeghosts(struct mesh *m, struct behavior *b, struct otri *startghost)
+#else /* not ANSI_DECLARATORS */
+long removeghosts(m, b, startghost)
+struct mesh *m;
+struct behavior *b;
+struct otri *startghost;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri searchedge;
+ struct otri dissolveedge;
+ struct otri deadtriangle;
+ vertex markorg;
+ long hullsize;
+ triangle ptr; /* Temporary variable used by sym(). */
+
+ if (b->verbose) {
+ printf(" Removing ghost triangles.\n");
+ }
+ /* Find an edge on the convex hull to start point location from. */
+ lprev(*startghost, searchedge);
+ symself(searchedge);
+ m->dummytri[0] = encode(searchedge);
+ /* Remove the bounding box and count the convex hull edges. */
+ otricopy(*startghost, dissolveedge);
+ hullsize = 0;
+ do {
+ hullsize++;
+ lnext(dissolveedge, deadtriangle);
+ lprevself(dissolveedge);
+ symself(dissolveedge);
+ /* If no PSLG is involved, set the boundary markers of all the vertices */
+ /* on the convex hull. If a PSLG is used, this step is done later. */
+ if (!b->poly) {
+ /* Watch out for the case where all the input vertices are collinear. */
+ if (dissolveedge.tri != m->dummytri) {
+ org(dissolveedge, markorg);
+ if (vertexmark(markorg) == 0) {
+ setvertexmark(markorg, 1);
+ }
+ }
+ }
+ /* Remove a bounding triangle from a convex hull triangle. */
+ dissolve(dissolveedge);
+ /* Find the next bounding triangle. */
+ sym(deadtriangle, dissolveedge);
+ /* Delete the bounding triangle. */
+ triangledealloc(m, deadtriangle.tri);
+ } while (!otriequal(dissolveedge, *startghost));
+ return hullsize;
+}
+
+/*****************************************************************************/
+/* */
+/* divconqdelaunay() Form a Delaunay triangulation by the divide-and- */
+/* conquer method. */
+/* */
+/* Sorts the vertices, calls a recursive procedure to triangulate them, and */
+/* removes the bounding box, setting boundary markers as appropriate. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+long divconqdelaunay(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+long divconqdelaunay(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ vertex *sortarray;
+ struct otri hullleft, hullright;
+ int divider;
+ int i, j;
+
+ if (b->verbose) {
+ printf(" Sorting vertices.\n");
+ }
+
+ /* Allocate an array of pointers to vertices for sorting. */
+ sortarray = (vertex *) trimalloc(m->invertices * (int) sizeof(vertex));
+ traversalinit(&m->vertices);
+ for (i = 0; i < m->invertices; i++) {
+ sortarray[i] = vertextraverse(m);
+ }
+ /* Sort the vertices. */
+ vertexsort(sortarray, m->invertices);
+ /* Discard duplicate vertices, which can really mess up the algorithm. */
+ i = 0;
+ for (j = 1; j < m->invertices; j++) {
+ if ((sortarray[i][0] == sortarray[j][0])
+ && (sortarray[i][1] == sortarray[j][1])) {
+ if (!b->quiet) {
+ printf(
+"Warning: A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n",
+ sortarray[j][0], sortarray[j][1]);
+ }
+ setvertextype(sortarray[j], UNDEADVERTEX);
+ m->undeads++;
+ } else {
+ i++;
+ sortarray[i] = sortarray[j];
+ }
+ }
+ i++;
+ if (b->dwyer) {
+ /* Re-sort the array of vertices to accommodate alternating cuts. */
+ divider = i >> 1;
+ if (i - divider >= 2) {
+ if (divider >= 2) {
+ alternateaxes(sortarray, divider, 1);
+ }
+ alternateaxes(&sortarray[divider], i - divider, 1);
+ }
+ }
+
+ if (b->verbose) {
+ printf(" Forming triangulation.\n");
+ }
+
+ /* Form the Delaunay triangulation. */
+ divconqrecurse(m, b, sortarray, i, 0, &hullleft, &hullright);
+ trifree((VOID *) sortarray);
+
+ return removeghosts(m, b, &hullleft);
+}
+
+/** **/
+/** **/
+/********* Divide-and-conquer Delaunay triangulation ends here *********/
+
+/********* Incremental Delaunay triangulation begins here *********/
+/** **/
+/** **/
+
+/*****************************************************************************/
+/* */
+/* boundingbox() Form an "infinite" bounding triangle to insert vertices */
+/* into. */
+/* */
+/* The vertices at "infinity" are assigned finite coordinates, which are */
+/* used by the point location routines, but (mostly) ignored by the */
+/* Delaunay edge flip routines. */
+/* */
+/*****************************************************************************/
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void boundingbox(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void boundingbox(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri inftri; /* Handle for the triangular bounding box. */
+ REAL width;
+
+ if (b->verbose) {
+ printf(" Creating triangular bounding box.\n");
+ }
+ /* Find the width (or height, whichever is larger) of the triangulation. */
+ width = m->xmax - m->xmin;
+ if (m->ymax - m->ymin > width) {
+ width = m->ymax - m->ymin;
+ }
+ if (width == 0.0) {
+ width = 1.0;
+ }
+ /* Create the vertices of the bounding box. */
+ m->infvertex1 = (vertex) trimalloc(m->vertices.itembytes);
+ m->infvertex2 = (vertex) trimalloc(m->vertices.itembytes);
+ m->infvertex3 = (vertex) trimalloc(m->vertices.itembytes);
+ m->infvertex1[0] = m->xmin - 50.0 * width;
+ m->infvertex1[1] = m->ymin - 40.0 * width;
+ m->infvertex2[0] = m->xmax + 50.0 * width;
+ m->infvertex2[1] = m->ymin - 40.0 * width;
+ m->infvertex3[0] = 0.5 * (m->xmin + m->xmax);
+ m->infvertex3[1] = m->ymax + 60.0 * width;
+
+ /* Create the bounding box. */
+ maketriangle(m, b, &inftri);
+ setorg(inftri, m->infvertex1);
+ setdest(inftri, m->infvertex2);
+ setapex(inftri, m->infvertex3);
+ /* Link dummytri to the bounding box so we can always find an */
+ /* edge to begin searching (point location) from. */
+ m->dummytri[0] = (triangle) inftri.tri;
+ if (b->verbose > 2) {
+ printf(" Creating ");
+ printtriangle(m, b, &inftri);
+ }
+}
+
+#endif /* not REDUCED */
+
+/*****************************************************************************/
+/* */
+/* removebox() Remove the "infinite" bounding triangle, setting boundary */
+/* markers as appropriate. */
+/* */
+/* The triangular bounding box has three boundary triangles (one for each */
+/* side of the bounding box), and a bunch of triangles fanning out from */
+/* the three bounding box vertices (one triangle for each edge of the */
+/* convex hull of the inner mesh). This routine removes these triangles. */
+/* */
+/* Returns the number of edges on the convex hull of the triangulation. */
+/* */
+/*****************************************************************************/
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+long removebox(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+long removebox(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri deadtriangle;
+ struct otri searchedge;
+ struct otri checkedge;
+ struct otri nextedge, finaledge, dissolveedge;
+ vertex markorg;
+ long hullsize;
+ triangle ptr; /* Temporary variable used by sym(). */
+
+ if (b->verbose) {
+ printf(" Removing triangular bounding box.\n");
+ }
+ /* Find a boundary triangle. */
+ nextedge.tri = m->dummytri;
+ nextedge.orient = 0;
+ symself(nextedge);
+ /* Mark a place to stop. */
+ lprev(nextedge, finaledge);
+ lnextself(nextedge);
+ symself(nextedge);
+ /* Find a triangle (on the boundary of the vertex set) that isn't */
+ /* a bounding box triangle. */
+ lprev(nextedge, searchedge);
+ symself(searchedge);
+ /* Check whether nextedge is another boundary triangle */
+ /* adjacent to the first one. */
+ lnext(nextedge, checkedge);
+ symself(checkedge);
+ if (checkedge.tri == m->dummytri) {
+ /* Go on to the next triangle. There are only three boundary */
+ /* triangles, and this next triangle cannot be the third one, */
+ /* so it's safe to stop here. */
+ lprevself(searchedge);
+ symself(searchedge);
+ }
+ /* Find a new boundary edge to search from, as the current search */
+ /* edge lies on a bounding box triangle and will be deleted. */
+ m->dummytri[0] = encode(searchedge);
+ hullsize = -2l;
+ while (!otriequal(nextedge, finaledge)) {
+ hullsize++;
+ lprev(nextedge, dissolveedge);
+ symself(dissolveedge);
+ /* If not using a PSLG, the vertices should be marked now. */
+ /* (If using a PSLG, markhull() will do the job.) */
+ if (!b->poly) {
+ /* Be careful! One must check for the case where all the input */
+ /* vertices are collinear, and thus all the triangles are part of */
+ /* the bounding box. Otherwise, the setvertexmark() call below */
+ /* will cause a bad pointer reference. */
+ if (dissolveedge.tri != m->dummytri) {
+ org(dissolveedge, markorg);
+ if (vertexmark(markorg) == 0) {
+ setvertexmark(markorg, 1);
+ }
+ }
+ }
+ /* Disconnect the bounding box triangle from the mesh triangle. */
+ dissolve(dissolveedge);
+ lnext(nextedge, deadtriangle);
+ sym(deadtriangle, nextedge);
+ /* Get rid of the bounding box triangle. */
+ triangledealloc(m, deadtriangle.tri);
+ /* Do we need to turn the corner? */
+ if (nextedge.tri == m->dummytri) {
+ /* Turn the corner. */
+ otricopy(dissolveedge, nextedge);
+ }
+ }
+ triangledealloc(m, finaledge.tri);
+
+ trifree((VOID *) m->infvertex1); /* Deallocate the bounding box vertices. */
+ trifree((VOID *) m->infvertex2);
+ trifree((VOID *) m->infvertex3);
+
+ return hullsize;
+}
+
+#endif /* not REDUCED */
+
+/*****************************************************************************/
+/* */
+/* incrementaldelaunay() Form a Delaunay triangulation by incrementally */
+/* inserting vertices. */
+/* */
+/* Returns the number of edges on the convex hull of the triangulation. */
+/* */
+/*****************************************************************************/
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+long incrementaldelaunay(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+long incrementaldelaunay(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri starttri;
+ vertex vertexloop;
+
+ /* Create a triangular bounding box. */
+ boundingbox(m, b);
+ if (b->verbose) {
+ printf(" Incrementally inserting vertices.\n");
+ }
+ traversalinit(&m->vertices);
+ vertexloop = vertextraverse(m);
+ while (vertexloop != (vertex) NULL) {
+ starttri.tri = m->dummytri;
+ if (insertvertex(m, b, vertexloop, &starttri, (struct osub *) NULL, 0, 0)
+ == DUPLICATEVERTEX) {
+ if (!b->quiet) {
+ printf(
+"Warning: A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n",
+ vertexloop[0], vertexloop[1]);
+ }
+ setvertextype(vertexloop, UNDEADVERTEX);
+ m->undeads++;
+ }
+ vertexloop = vertextraverse(m);
+ }
+ /* Remove the bounding box. */
+ return removebox(m, b);
+}
+
+#endif /* not REDUCED */
+
+/** **/
+/** **/
+/********* Incremental Delaunay triangulation ends here *********/
+
+/********* Sweepline Delaunay triangulation begins here *********/
+/** **/
+/** **/
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void eventheapinsert(struct event **heap, int heapsize, struct event *newevent)
+#else /* not ANSI_DECLARATORS */
+void eventheapinsert(heap, heapsize, newevent)
+struct event **heap;
+int heapsize;
+struct event *newevent;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ REAL eventx, eventy;
+ int eventnum;
+ int parent;
+ int notdone;
+
+ eventx = newevent->xkey;
+ eventy = newevent->ykey;
+ eventnum = heapsize;
+ notdone = eventnum > 0;
+ while (notdone) {
+ parent = (eventnum - 1) >> 1;
+ if ((heap[parent]->ykey < eventy) ||
+ ((heap[parent]->ykey == eventy)
+ && (heap[parent]->xkey <= eventx))) {
+ notdone = 0;
+ } else {
+ heap[eventnum] = heap[parent];
+ heap[eventnum]->heapposition = eventnum;
+
+ eventnum = parent;
+ notdone = eventnum > 0;
+ }
+ }
+ heap[eventnum] = newevent;
+ newevent->heapposition = eventnum;
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void eventheapify(struct event **heap, int heapsize, int eventnum)
+#else /* not ANSI_DECLARATORS */
+void eventheapify(heap, heapsize, eventnum)
+struct event **heap;
+int heapsize;
+int eventnum;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct event *thisevent;
+ REAL eventx, eventy;
+ int leftchild, rightchild;
+ int smallest;
+ int notdone;
+
+ thisevent = heap[eventnum];
+ eventx = thisevent->xkey;
+ eventy = thisevent->ykey;
+ leftchild = 2 * eventnum + 1;
+ notdone = leftchild < heapsize;
+ while (notdone) {
+ if ((heap[leftchild]->ykey < eventy) ||
+ ((heap[leftchild]->ykey == eventy)
+ && (heap[leftchild]->xkey < eventx))) {
+ smallest = leftchild;
+ } else {
+ smallest = eventnum;
+ }
+ rightchild = leftchild + 1;
+ if (rightchild < heapsize) {
+ if ((heap[rightchild]->ykey < heap[smallest]->ykey) ||
+ ((heap[rightchild]->ykey == heap[smallest]->ykey)
+ && (heap[rightchild]->xkey < heap[smallest]->xkey))) {
+ smallest = rightchild;
+ }
+ }
+ if (smallest == eventnum) {
+ notdone = 0;
+ } else {
+ heap[eventnum] = heap[smallest];
+ heap[eventnum]->heapposition = eventnum;
+ heap[smallest] = thisevent;
+ thisevent->heapposition = smallest;
+
+ eventnum = smallest;
+ leftchild = 2 * eventnum + 1;
+ notdone = leftchild < heapsize;
+ }
+ }
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void eventheapdelete(struct event **heap, int heapsize, int eventnum)
+#else /* not ANSI_DECLARATORS */
+void eventheapdelete(heap, heapsize, eventnum)
+struct event **heap;
+int heapsize;
+int eventnum;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct event *moveevent;
+ REAL eventx, eventy;
+ int parent;
+ int notdone;
+
+ moveevent = heap[heapsize - 1];
+ if (eventnum > 0) {
+ eventx = moveevent->xkey;
+ eventy = moveevent->ykey;
+ do {
+ parent = (eventnum - 1) >> 1;
+ if ((heap[parent]->ykey < eventy) ||
+ ((heap[parent]->ykey == eventy)
+ && (heap[parent]->xkey <= eventx))) {
+ notdone = 0;
+ } else {
+ heap[eventnum] = heap[parent];
+ heap[eventnum]->heapposition = eventnum;
+
+ eventnum = parent;
+ notdone = eventnum > 0;
+ }
+ } while (notdone);
+ }
+ heap[eventnum] = moveevent;
+ moveevent->heapposition = eventnum;
+ eventheapify(heap, heapsize - 1, eventnum);
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void createeventheap(struct mesh *m, struct event ***eventheap,
+ struct event **events, struct event **freeevents)
+#else /* not ANSI_DECLARATORS */
+void createeventheap(m, eventheap, events, freeevents)
+struct mesh *m;
+struct event ***eventheap;
+struct event **events;
+struct event **freeevents;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ vertex thisvertex;
+ int maxevents;
+ int i;
+
+ maxevents = (3 * m->invertices) / 2;
+ *eventheap = (struct event **) trimalloc(maxevents *
+ (int) sizeof(struct event *));
+ *events = (struct event *) trimalloc(maxevents * (int) sizeof(struct event));
+ traversalinit(&m->vertices);
+ for (i = 0; i < m->invertices; i++) {
+ thisvertex = vertextraverse(m);
+ (*events)[i].eventptr = (VOID *) thisvertex;
+ (*events)[i].xkey = thisvertex[0];
+ (*events)[i].ykey = thisvertex[1];
+ eventheapinsert(*eventheap, i, *events + i);
+ }
+ *freeevents = (struct event *) NULL;
+ for (i = maxevents - 1; i >= m->invertices; i--) {
+ (*events)[i].eventptr = (VOID *) *freeevents;
+ *freeevents = *events + i;
+ }
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+int rightofhyperbola(struct mesh *m, struct otri *fronttri, vertex newsite)
+#else /* not ANSI_DECLARATORS */
+int rightofhyperbola(m, fronttri, newsite)
+struct mesh *m;
+struct otri *fronttri;
+vertex newsite;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ vertex leftvertex, rightvertex;
+ REAL dxa, dya, dxb, dyb;
+
+ m->hyperbolacount++;
+
+ dest(*fronttri, leftvertex);
+ apex(*fronttri, rightvertex);
+ if ((leftvertex[1] < rightvertex[1]) ||
+ ((leftvertex[1] == rightvertex[1]) &&
+ (leftvertex[0] < rightvertex[0]))) {
+ if (newsite[0] >= rightvertex[0]) {
+ return 1;
+ }
+ } else {
+ if (newsite[0] <= leftvertex[0]) {
+ return 0;
+ }
+ }
+ dxa = leftvertex[0] - newsite[0];
+ dya = leftvertex[1] - newsite[1];
+ dxb = rightvertex[0] - newsite[0];
+ dyb = rightvertex[1] - newsite[1];
+ return dya * (dxb * dxb + dyb * dyb) > dyb * (dxa * dxa + dya * dya);
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+REAL circletop(struct mesh *m, vertex pa, vertex pb, vertex pc, REAL ccwabc)
+#else /* not ANSI_DECLARATORS */
+REAL circletop(m, pa, pb, pc, ccwabc)
+struct mesh *m;
+vertex pa;
+vertex pb;
+vertex pc;
+REAL ccwabc;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ REAL xac, yac, xbc, ybc, xab, yab;
+ REAL aclen2, bclen2, ablen2;
+
+ m->circletopcount++;
+
+ xac = pa[0] - pc[0];
+ yac = pa[1] - pc[1];
+ xbc = pb[0] - pc[0];
+ ybc = pb[1] - pc[1];
+ xab = pa[0] - pb[0];
+ yab = pa[1] - pb[1];
+ aclen2 = xac * xac + yac * yac;
+ bclen2 = xbc * xbc + ybc * ybc;
+ ablen2 = xab * xab + yab * yab;
+ return pc[1] + (xac * bclen2 - xbc * aclen2 + sqrt(aclen2 * bclen2 * ablen2))
+ / (2.0 * ccwabc);
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+void check4deadevent(struct otri *checktri, struct event **freeevents,
+ struct event **eventheap, int *heapsize)
+#else /* not ANSI_DECLARATORS */
+void check4deadevent(checktri, freeevents, eventheap, heapsize)
+struct otri *checktri;
+struct event **freeevents;
+struct event **eventheap;
+int *heapsize;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct event *deadevent;
+ vertex eventvertex;
+ int eventnum;
+
+ org(*checktri, eventvertex);
+ if (eventvertex != (vertex) NULL) {
+ deadevent = (struct event *) eventvertex;
+ eventnum = deadevent->heapposition;
+ deadevent->eventptr = (VOID *) *freeevents;
+ *freeevents = deadevent;
+ eventheapdelete(eventheap, *heapsize, eventnum);
+ (*heapsize)--;
+ setorg(*checktri, NULL);
+ }
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+struct splaynode *splay(struct mesh *m, struct splaynode *splaytree,
+ vertex searchpoint, struct otri *searchtri)
+#else /* not ANSI_DECLARATORS */
+struct splaynode *splay(m, splaytree, searchpoint, searchtri)
+struct mesh *m;
+struct splaynode *splaytree;
+vertex searchpoint;
+struct otri *searchtri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct splaynode *child, *grandchild;
+ struct splaynode *lefttree, *righttree;
+ struct splaynode *leftright;
+ vertex checkvertex;
+ int rightofroot, rightofchild;
+
+ if (splaytree == (struct splaynode *) NULL) {
+ return (struct splaynode *) NULL;
+ }
+ dest(splaytree->keyedge, checkvertex);
+ if (checkvertex == splaytree->keydest) {
+ rightofroot = rightofhyperbola(m, &splaytree->keyedge, searchpoint);
+ if (rightofroot) {
+ otricopy(splaytree->keyedge, *searchtri);
+ child = splaytree->rchild;
+ } else {
+ child = splaytree->lchild;
+ }
+ if (child == (struct splaynode *) NULL) {
+ return splaytree;
+ }
+ dest(child->keyedge, checkvertex);
+ if (checkvertex != child->keydest) {
+ child = splay(m, child, searchpoint, searchtri);
+ if (child == (struct splaynode *) NULL) {
+ if (rightofroot) {
+ splaytree->rchild = (struct splaynode *) NULL;
+ } else {
+ splaytree->lchild = (struct splaynode *) NULL;
+ }
+ return splaytree;
+ }
+ }
+ rightofchild = rightofhyperbola(m, &child->keyedge, searchpoint);
+ if (rightofchild) {
+ otricopy(child->keyedge, *searchtri);
+ grandchild = splay(m, child->rchild, searchpoint, searchtri);
+ child->rchild = grandchild;
+ } else {
+ grandchild = splay(m, child->lchild, searchpoint, searchtri);
+ child->lchild = grandchild;
+ }
+ if (grandchild == (struct splaynode *) NULL) {
+ if (rightofroot) {
+ splaytree->rchild = child->lchild;
+ child->lchild = splaytree;
+ } else {
+ splaytree->lchild = child->rchild;
+ child->rchild = splaytree;
+ }
+ return child;
+ }
+ if (rightofchild) {
+ if (rightofroot) {
+ splaytree->rchild = child->lchild;
+ child->lchild = splaytree;
+ } else {
+ splaytree->lchild = grandchild->rchild;
+ grandchild->rchild = splaytree;
+ }
+ child->rchild = grandchild->lchild;
+ grandchild->lchild = child;
+ } else {
+ if (rightofroot) {
+ splaytree->rchild = grandchild->lchild;
+ grandchild->lchild = splaytree;
+ } else {
+ splaytree->lchild = child->rchild;
+ child->rchild = splaytree;
+ }
+ child->lchild = grandchild->rchild;
+ grandchild->rchild = child;
+ }
+ return grandchild;
+ } else {
+ lefttree = splay(m, splaytree->lchild, searchpoint, searchtri);
+ righttree = splay(m, splaytree->rchild, searchpoint, searchtri);
+
+ pooldealloc(&m->splaynodes, (VOID *) splaytree);
+ if (lefttree == (struct splaynode *) NULL) {
+ return righttree;
+ } else if (righttree == (struct splaynode *) NULL) {
+ return lefttree;
+ } else if (lefttree->rchild == (struct splaynode *) NULL) {
+ lefttree->rchild = righttree->lchild;
+ righttree->lchild = lefttree;
+ return righttree;
+ } else if (righttree->lchild == (struct splaynode *) NULL) {
+ righttree->lchild = lefttree->rchild;
+ lefttree->rchild = righttree;
+ return lefttree;
+ } else {
+/* printf("Holy Toledo!!!\n"); */
+ leftright = lefttree->rchild;
+ while (leftright->rchild != (struct splaynode *) NULL) {
+ leftright = leftright->rchild;
+ }
+ leftright->rchild = righttree;
+ return lefttree;
+ }
+ }
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+struct splaynode *splayinsert(struct mesh *m, struct splaynode *splayroot,
+ struct otri *newkey, vertex searchpoint)
+#else /* not ANSI_DECLARATORS */
+struct splaynode *splayinsert(m, splayroot, newkey, searchpoint)
+struct mesh *m;
+struct splaynode *splayroot;
+struct otri *newkey;
+vertex searchpoint;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct splaynode *newsplaynode;
+
+ newsplaynode = (struct splaynode *) poolalloc(&m->splaynodes);
+ otricopy(*newkey, newsplaynode->keyedge);
+ dest(*newkey, newsplaynode->keydest);
+ if (splayroot == (struct splaynode *) NULL) {
+ newsplaynode->lchild = (struct splaynode *) NULL;
+ newsplaynode->rchild = (struct splaynode *) NULL;
+ } else if (rightofhyperbola(m, &splayroot->keyedge, searchpoint)) {
+ newsplaynode->lchild = splayroot;
+ newsplaynode->rchild = splayroot->rchild;
+ splayroot->rchild = (struct splaynode *) NULL;
+ } else {
+ newsplaynode->lchild = splayroot->lchild;
+ newsplaynode->rchild = splayroot;
+ splayroot->lchild = (struct splaynode *) NULL;
+ }
+ return newsplaynode;
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+struct splaynode *circletopinsert(struct mesh *m, struct behavior *b,
+ struct splaynode *splayroot,
+ struct otri *newkey,
+ vertex pa, vertex pb, vertex pc, REAL topy)
+#else /* not ANSI_DECLARATORS */
+struct splaynode *circletopinsert(m, b, splayroot, newkey, pa, pb, pc, topy)
+struct mesh *m;
+struct behavior *b;
+struct splaynode *splayroot;
+struct otri *newkey;
+vertex pa;
+vertex pb;
+vertex pc;
+REAL topy;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ REAL ccwabc;
+ REAL xac, yac, xbc, ybc;
+ REAL aclen2, bclen2;
+ REAL searchpoint[2];
+ struct otri dummytri;
+
+ ccwabc = counterclockwise(m, b, pa, pb, pc);
+ xac = pa[0] - pc[0];
+ yac = pa[1] - pc[1];
+ xbc = pb[0] - pc[0];
+ ybc = pb[1] - pc[1];
+ aclen2 = xac * xac + yac * yac;
+ bclen2 = xbc * xbc + ybc * ybc;
+ searchpoint[0] = pc[0] - (yac * bclen2 - ybc * aclen2) / (2.0 * ccwabc);
+ searchpoint[1] = topy;
+ return splayinsert(m, splay(m, splayroot, (vertex) searchpoint, &dummytri),
+ newkey, (vertex) searchpoint);
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+struct splaynode *frontlocate(struct mesh *m, struct splaynode *splayroot,
+ struct otri *bottommost, vertex searchvertex,
+ struct otri *searchtri, int *farright)
+#else /* not ANSI_DECLARATORS */
+struct splaynode *frontlocate(m, splayroot, bottommost, searchvertex,
+ searchtri, farright)
+struct mesh *m;
+struct splaynode *splayroot;
+struct otri *bottommost;
+vertex searchvertex;
+struct otri *searchtri;
+int *farright;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ int farrightflag;
+ triangle ptr; /* Temporary variable used by onext(). */
+
+ otricopy(*bottommost, *searchtri);
+ splayroot = splay(m, splayroot, searchvertex, searchtri);
+
+ farrightflag = 0;
+ while (!farrightflag && rightofhyperbola(m, searchtri, searchvertex)) {
+ onextself(*searchtri);
+ farrightflag = otriequal(*searchtri, *bottommost);
+ }
+ *farright = farrightflag;
+ return splayroot;
+}
+
+#endif /* not REDUCED */
+
+#ifndef REDUCED
+
+#ifdef ANSI_DECLARATORS
+long sweeplinedelaunay(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+long sweeplinedelaunay(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct event **eventheap;
+ struct event *events;
+ struct event *freeevents;
+ struct event *nextevent;
+ struct event *newevent;
+ struct splaynode *splayroot;
+ struct otri bottommost;
+ struct otri searchtri;
+ struct otri fliptri;
+ struct otri lefttri, righttri, farlefttri, farrighttri;
+ struct otri inserttri;
+ vertex firstvertex, secondvertex;
+ vertex nextvertex, lastvertex;
+ vertex connectvertex;
+ vertex leftvertex, midvertex, rightvertex;
+ REAL lefttest, righttest;
+ int heapsize;
+ int check4events, farrightflag;
+ triangle ptr; /* Temporary variable used by sym(), onext(), and oprev(). */
+
+ poolinit(&m->splaynodes, sizeof(struct splaynode), SPLAYNODEPERBLOCK,
+ SPLAYNODEPERBLOCK, 0);
+ splayroot = (struct splaynode *) NULL;
+
+ if (b->verbose) {
+ printf(" Placing vertices in event heap.\n");
+ }
+ createeventheap(m, &eventheap, &events, &freeevents);
+ heapsize = m->invertices;
+
+ if (b->verbose) {
+ printf(" Forming triangulation.\n");
+ }
+ maketriangle(m, b, &lefttri);
+ maketriangle(m, b, &righttri);
+ bond(lefttri, righttri);
+ lnextself(lefttri);
+ lprevself(righttri);
+ bond(lefttri, righttri);
+ lnextself(lefttri);
+ lprevself(righttri);
+ bond(lefttri, righttri);
+ firstvertex = (vertex) eventheap[0]->eventptr;
+ eventheap[0]->eventptr = (VOID *) freeevents;
+ freeevents = eventheap[0];
+ eventheapdelete(eventheap, heapsize, 0);
+ heapsize--;
+ do {
+ if (heapsize == 0) {
+ printf("Error: Input vertices are all identical.\n");
+ triexit(1);
+ }
+ secondvertex = (vertex) eventheap[0]->eventptr;
+ eventheap[0]->eventptr = (VOID *) freeevents;
+ freeevents = eventheap[0];
+ eventheapdelete(eventheap, heapsize, 0);
+ heapsize--;
+ if ((firstvertex[0] == secondvertex[0]) &&
+ (firstvertex[1] == secondvertex[1])) {
+ if (!b->quiet) {
+ printf(
+"Warning: A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n",
+ secondvertex[0], secondvertex[1]);
+ }
+ setvertextype(secondvertex, UNDEADVERTEX);
+ m->undeads++;
+ }
+ } while ((firstvertex[0] == secondvertex[0]) &&
+ (firstvertex[1] == secondvertex[1]));
+ setorg(lefttri, firstvertex);
+ setdest(lefttri, secondvertex);
+ setorg(righttri, secondvertex);
+ setdest(righttri, firstvertex);
+ lprev(lefttri, bottommost);
+ lastvertex = secondvertex;
+ while (heapsize > 0) {
+ nextevent = eventheap[0];
+ eventheapdelete(eventheap, heapsize, 0);
+ heapsize--;
+ check4events = 1;
+ if (nextevent->xkey < m->xmin) {
+ decode(nextevent->eventptr, fliptri);
+ oprev(fliptri, farlefttri);
+ check4deadevent(&farlefttri, &freeevents, eventheap, &heapsize);
+ onext(fliptri, farrighttri);
+ check4deadevent(&farrighttri, &freeevents, eventheap, &heapsize);
+
+ if (otriequal(farlefttri, bottommost)) {
+ lprev(fliptri, bottommost);
+ }
+ flip(m, b, &fliptri);
+ setapex(fliptri, NULL);
+ lprev(fliptri, lefttri);
+ lnext(fliptri, righttri);
+ sym(lefttri, farlefttri);
+
+ if (randomnation(SAMPLERATE) == 0) {
+ symself(fliptri);
+ dest(fliptri, leftvertex);
+ apex(fliptri, midvertex);
+ org(fliptri, rightvertex);
+ splayroot = circletopinsert(m, b, splayroot, &lefttri, leftvertex,
+ midvertex, rightvertex, nextevent->ykey);
+ }
+ } else {
+ nextvertex = (vertex) nextevent->eventptr;
+ if ((nextvertex[0] == lastvertex[0]) &&
+ (nextvertex[1] == lastvertex[1])) {
+ if (!b->quiet) {
+ printf(
+"Warning: A duplicate vertex at (%.12g, %.12g) appeared and was ignored.\n",
+ nextvertex[0], nextvertex[1]);
+ }
+ setvertextype(nextvertex, UNDEADVERTEX);
+ m->undeads++;
+ check4events = 0;
+ } else {
+ lastvertex = nextvertex;
+
+ splayroot = frontlocate(m, splayroot, &bottommost, nextvertex,
+ &searchtri, &farrightflag);
+/*
+ otricopy(bottommost, searchtri);
+ farrightflag = 0;
+ while (!farrightflag && rightofhyperbola(m, &searchtri, nextvertex)) {
+ onextself(searchtri);
+ farrightflag = otriequal(searchtri, bottommost);
+ }
+*/
+
+ check4deadevent(&searchtri, &freeevents, eventheap, &heapsize);
+
+ otricopy(searchtri, farrighttri);
+ sym(searchtri, farlefttri);
+ maketriangle(m, b, &lefttri);
+ maketriangle(m, b, &righttri);
+ dest(farrighttri, connectvertex);
+ setorg(lefttri, connectvertex);
+ setdest(lefttri, nextvertex);
+ setorg(righttri, nextvertex);
+ setdest(righttri, connectvertex);
+ bond(lefttri, righttri);
+ lnextself(lefttri);
+ lprevself(righttri);
+ bond(lefttri, righttri);
+ lnextself(lefttri);
+ lprevself(righttri);
+ bond(lefttri, farlefttri);
+ bond(righttri, farrighttri);
+ if (!farrightflag && otriequal(farrighttri, bottommost)) {
+ otricopy(lefttri, bottommost);
+ }
+
+ if (randomnation(SAMPLERATE) == 0) {
+ splayroot = splayinsert(m, splayroot, &lefttri, nextvertex);
+ } else if (randomnation(SAMPLERATE) == 0) {
+ lnext(righttri, inserttri);
+ splayroot = splayinsert(m, splayroot, &inserttri, nextvertex);
+ }
+ }
+ }
+ nextevent->eventptr = (VOID *) freeevents;
+ freeevents = nextevent;
+
+ if (check4events) {
+ apex(farlefttri, leftvertex);
+ dest(lefttri, midvertex);
+ apex(lefttri, rightvertex);
+ lefttest = counterclockwise(m, b, leftvertex, midvertex, rightvertex);
+ if (lefttest > 0.0) {
+ newevent = freeevents;
+ freeevents = (struct event *) freeevents->eventptr;
+ newevent->xkey = m->xminextreme;
+ newevent->ykey = circletop(m, leftvertex, midvertex, rightvertex,
+ lefttest);
+ newevent->eventptr = (VOID *) encode(lefttri);
+ eventheapinsert(eventheap, heapsize, newevent);
+ heapsize++;
+ setorg(lefttri, newevent);
+ }
+ apex(righttri, leftvertex);
+ org(righttri, midvertex);
+ apex(farrighttri, rightvertex);
+ righttest = counterclockwise(m, b, leftvertex, midvertex, rightvertex);
+ if (righttest > 0.0) {
+ newevent = freeevents;
+ freeevents = (struct event *) freeevents->eventptr;
+ newevent->xkey = m->xminextreme;
+ newevent->ykey = circletop(m, leftvertex, midvertex, rightvertex,
+ righttest);
+ newevent->eventptr = (VOID *) encode(farrighttri);
+ eventheapinsert(eventheap, heapsize, newevent);
+ heapsize++;
+ setorg(farrighttri, newevent);
+ }
+ }
+ }
+
+ pooldeinit(&m->splaynodes);
+ lprevself(bottommost);
+ return removeghosts(m, b, &bottommost);
+}
+
+#endif /* not REDUCED */
+
+/** **/
+/** **/
+/********* Sweepline Delaunay triangulation ends here *********/
+
+/********* General mesh construction routines begin here *********/
+/** **/
+/** **/
+
+/*****************************************************************************/
+/* */
+/* delaunay() Form a Delaunay triangulation. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+long delaunay(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+long delaunay(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ long hulledges;
+
+ m->eextras = 0;
+ initializetrisubpools(m, b);
+
+#ifdef REDUCED
+ if (!b->quiet) {
+ printf(
+ "Constructing Delaunay triangulation by divide-and-conquer method.\n");
+ }
+ hulledges = divconqdelaunay(m, b);
+#else /* not REDUCED */
+ if (!b->quiet) {
+ printf("Constructing Delaunay triangulation ");
+ if (b->incremental) {
+ printf("by incremental method.\n");
+ } else if (b->sweepline) {
+ printf("by sweepline method.\n");
+ } else {
+ printf("by divide-and-conquer method.\n");
+ }
+ }
+ if (b->incremental) {
+ hulledges = incrementaldelaunay(m, b);
+ } else if (b->sweepline) {
+ hulledges = sweeplinedelaunay(m, b);
+ } else {
+ hulledges = divconqdelaunay(m, b);
+ }
+#endif /* not REDUCED */
+
+ if (m->triangles.items == 0) {
+ /* The input vertices were all collinear, so there are no triangles. */
+ return 0l;
+ } else {
+ return hulledges;
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* reconstruct() Reconstruct a triangulation from its .ele (and possibly */
+/* .poly) file. Used when the -r switch is used. */
+/* */
+/* Reads an .ele file and reconstructs the original mesh. If the -p switch */
+/* is used, this procedure will also read a .poly file and reconstruct the */
+/* subsegments of the original mesh. If the -a switch is used, this */
+/* procedure will also read an .area file and set a maximum area constraint */
+/* on each triangle. */
+/* */
+/* Vertices that are not corners of triangles, such as nodes on edges of */
+/* subparametric elements, are discarded. */
+/* */
+/* This routine finds the adjacencies between triangles (and subsegments) */
+/* by forming one stack of triangles for each vertex. Each triangle is on */
+/* three different stacks simultaneously. Each triangle's subsegment */
+/* pointers are used to link the items in each stack. This memory-saving */
+/* feature makes the code harder to read. The most important thing to keep */
+/* in mind is that each triangle is removed from a stack precisely when */
+/* the corresponding pointer is adjusted to refer to a subsegment rather */
+/* than the next triangle of the stack. */
+/* */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+int reconstruct(struct mesh *m, struct behavior *b, int *trianglelist,
+ REAL *triangleattriblist, REAL *trianglearealist,
+ int elements, int corners, int attribs,
+ int *segmentlist,int *segmentmarkerlist, int numberofsegments)
+#else /* not ANSI_DECLARATORS */
+int reconstruct(m, b, trianglelist, triangleattriblist, trianglearealist,
+ elements, corners, attribs, segmentlist, segmentmarkerlist,
+ numberofsegments)
+struct mesh *m;
+struct behavior *b;
+int *trianglelist;
+REAL *triangleattriblist;
+REAL *trianglearealist;
+int elements;
+int corners;
+int attribs;
+int *segmentlist;
+int *segmentmarkerlist;
+int numberofsegments;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+long reconstruct(struct mesh *m, struct behavior *b, char *elefilename,
+ char *areafilename, char *polyfilename, FILE *polyfile)
+#else /* not ANSI_DECLARATORS */
+long reconstruct(m, b, elefilename, areafilename, polyfilename, polyfile)
+struct mesh *m;
+struct behavior *b;
+char *elefilename;
+char *areafilename;
+char *polyfilename;
+FILE *polyfile;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+ int vertexindex;
+ int attribindex;
+#else /* not TRILIBRARY */
+ FILE *elefile;
+ FILE *areafile;
+ char inputline[INPUTLINESIZE];
+ char *stringptr;
+ int areaelements;
+#endif /* not TRILIBRARY */
+ struct otri triangleloop;
+ struct otri triangleleft;
+ struct otri checktri;
+ struct otri checkleft;
+ struct otri checkneighbor;
+ struct osub subsegloop;
+ triangle *vertexarray;
+ triangle *prevlink;
+ triangle nexttri;
+ vertex tdest, tapex;
+ vertex checkdest, checkapex;
+ vertex shorg;
+ vertex killvertex;
+ vertex segmentorg, segmentdest;
+ REAL area;
+ int corner[3];
+ int end[2];
+ int killvertexindex;
+ int incorners;
+ int segmentmarkers;
+ int boundmarker;
+ int aroundvertex;
+ long hullsize;
+ int notfound;
+ long elementnumber, segmentnumber;
+ int i, j;
+ triangle ptr; /* Temporary variable used by sym(). */
+
+#ifdef TRILIBRARY
+ m->inelements = elements;
+ incorners = corners;
+ if (incorners < 3) {
+ printf("Error: Triangles must have at least 3 vertices.\n");
+ triexit(1);
+ }
+ m->eextras = attribs;
+#else /* not TRILIBRARY */
+ /* Read the triangles from an .ele file. */
+ if (!b->quiet) {
+ printf("Opening %s.\n", elefilename);
+ }
+ elefile = fopen(elefilename, "r");
+ if (elefile == (FILE *) NULL) {
+ printf(" Error: Cannot access file %s.\n", elefilename);
+ triexit(1);
+ }
+ /* Read number of triangles, number of vertices per triangle, and */
+ /* number of triangle attributes from .ele file. */
+ stringptr = readline(inputline, elefile, elefilename);
+ m->inelements = (int) strtol(stringptr, &stringptr, 0);
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ incorners = 3;
+ } else {
+ incorners = (int) strtol(stringptr, &stringptr, 0);
+ if (incorners < 3) {
+ printf("Error: Triangles in %s must have at least 3 vertices.\n",
+ elefilename);
+ triexit(1);
+ }
+ }
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ m->eextras = 0;
+ } else {
+ m->eextras = (int) strtol(stringptr, &stringptr, 0);
+ }
+#endif /* not TRILIBRARY */
+
+ initializetrisubpools(m, b);
+
+ /* Create the triangles. */
+ for (elementnumber = 1; elementnumber <= m->inelements; elementnumber++) {
+ maketriangle(m, b, &triangleloop);
+ /* Mark the triangle as living. */
+ triangleloop.tri[3] = (triangle) triangleloop.tri;
+ }
+
+ segmentmarkers = 0;
+ if (b->poly) {
+#ifdef TRILIBRARY
+ m->insegments = numberofsegments;
+ segmentmarkers = segmentmarkerlist != (int *) NULL;
+#else /* not TRILIBRARY */
+ /* Read number of segments and number of segment */
+ /* boundary markers from .poly file. */
+ stringptr = readline(inputline, polyfile, b->inpolyfilename);
+ m->insegments = (int) strtol(stringptr, &stringptr, 0);
+ stringptr = findfield(stringptr);
+ if (*stringptr != '\0') {
+ segmentmarkers = (int) strtol(stringptr, &stringptr, 0);
+ }
+#endif /* not TRILIBRARY */
+
+ /* Create the subsegments. */
+ for (segmentnumber = 1; segmentnumber <= m->insegments; segmentnumber++) {
+ makesubseg(m, &subsegloop);
+ /* Mark the subsegment as living. */
+ subsegloop.ss[2] = (subseg) subsegloop.ss;
+ }
+ }
+
+#ifdef TRILIBRARY
+ vertexindex = 0;
+ attribindex = 0;
+#else /* not TRILIBRARY */
+ if (b->vararea) {
+ /* Open an .area file, check for consistency with the .ele file. */
+ if (!b->quiet) {
+ printf("Opening %s.\n", areafilename);
+ }
+ areafile = fopen(areafilename, "r");
+ if (areafile == (FILE *) NULL) {
+ printf(" Error: Cannot access file %s.\n", areafilename);
+ triexit(1);
+ }
+ stringptr = readline(inputline, areafile, areafilename);
+ areaelements = (int) strtol(stringptr, &stringptr, 0);
+ if (areaelements != m->inelements) {
+ printf("Error: %s and %s disagree on number of triangles.\n",
+ elefilename, areafilename);
+ triexit(1);
+ }
+ }
+#endif /* not TRILIBRARY */
+
+ if (!b->quiet) {
+ printf("Reconstructing mesh.\n");
+ }
+ /* Allocate a temporary array that maps each vertex to some adjacent */
+ /* triangle. I took care to allocate all the permanent memory for */
+ /* triangles and subsegments first. */
+ vertexarray = (triangle *) trimalloc(m->vertices.items *
+ (int) sizeof(triangle));
+ /* Each vertex is initially unrepresented. */
+ for (i = 0; i < m->vertices.items; i++) {
+ vertexarray[i] = (triangle) m->dummytri;
+ }
+
+ if (b->verbose) {
+ printf(" Assembling triangles.\n");
+ }
+ /* Read the triangles from the .ele file, and link */
+ /* together those that share an edge. */
+ traversalinit(&m->triangles);
+ triangleloop.tri = triangletraverse(m);
+ elementnumber = b->firstnumber;
+ while (triangleloop.tri != (triangle *) NULL) {
+#ifdef TRILIBRARY
+ /* Copy the triangle's three corners. */
+ for (j = 0; j < 3; j++) {
+ corner[j] = trianglelist[vertexindex++];
+ if ((corner[j] < b->firstnumber) ||
+ (corner[j] >= b->firstnumber + m->invertices)) {
+ printf("Error: Triangle %ld has an invalid vertex index.\n",
+ elementnumber);
+ triexit(1);
+ }
+ }
+#else /* not TRILIBRARY */
+ /* Read triangle number and the triangle's three corners. */
+ stringptr = readline(inputline, elefile, elefilename);
+ for (j = 0; j < 3; j++) {
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ printf("Error: Triangle %ld is missing vertex %d in %s.\n",
+ elementnumber, j + 1, elefilename);
+ triexit(1);
+ } else {
+ corner[j] = (int) strtol(stringptr, &stringptr, 0);
+ if ((corner[j] < b->firstnumber) ||
+ (corner[j] >= b->firstnumber + m->invertices)) {
+ printf("Error: Triangle %ld has an invalid vertex index.\n",
+ elementnumber);
+ triexit(1);
+ }
+ }
+ }
+#endif /* not TRILIBRARY */
+
+ /* Find out about (and throw away) extra nodes. */
+ for (j = 3; j < incorners; j++) {
+#ifdef TRILIBRARY
+ killvertexindex = trianglelist[vertexindex++];
+#else /* not TRILIBRARY */
+ stringptr = findfield(stringptr);
+ if (*stringptr != '\0') {
+ killvertexindex = (int) strtol(stringptr, &stringptr, 0);
+#endif /* not TRILIBRARY */
+ if ((killvertexindex >= b->firstnumber) &&
+ (killvertexindex < b->firstnumber + m->invertices)) {
+ /* Delete the non-corner vertex if it's not already deleted. */
+ killvertex = getvertex(m, b, killvertexindex);
+ if (vertextype(killvertex) != DEADVERTEX) {
+ vertexdealloc(m, killvertex);
+ }
+ }
+#ifndef TRILIBRARY
+ }
+#endif /* not TRILIBRARY */
+ }
+
+ /* Read the triangle's attributes. */
+ for (j = 0; j < m->eextras; j++) {
+#ifdef TRILIBRARY
+ setelemattribute(triangleloop, j, triangleattriblist[attribindex++]);
+#else /* not TRILIBRARY */
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ setelemattribute(triangleloop, j, 0);
+ } else {
+ setelemattribute(triangleloop, j,
+ (REAL) strtod(stringptr, &stringptr));
+ }
+#endif /* not TRILIBRARY */
+ }
+
+ if (b->vararea) {
+#ifdef TRILIBRARY
+ area = trianglearealist[elementnumber - b->firstnumber];
+#else /* not TRILIBRARY */
+ /* Read an area constraint from the .area file. */
+ stringptr = readline(inputline, areafile, areafilename);
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ area = -1.0; /* No constraint on this triangle. */
+ } else {
+ area = (REAL) strtod(stringptr, &stringptr);
+ }
+#endif /* not TRILIBRARY */
+ setareabound(triangleloop, area);
+ }
+
+ /* Set the triangle's vertices. */
+ triangleloop.orient = 0;
+ setorg(triangleloop, getvertex(m, b, corner[0]));
+ setdest(triangleloop, getvertex(m, b, corner[1]));
+ setapex(triangleloop, getvertex(m, b, corner[2]));
+ /* Try linking the triangle to others that share these vertices. */
+ for (triangleloop.orient = 0; triangleloop.orient < 3;
+ triangleloop.orient++) {
+ /* Take the number for the origin of triangleloop. */
+ aroundvertex = corner[triangleloop.orient];
+ /* Look for other triangles having this vertex. */
+ nexttri = vertexarray[aroundvertex - b->firstnumber];
+ /* Link the current triangle to the next one in the stack. */
+ triangleloop.tri[6 + triangleloop.orient] = nexttri;
+ /* Push the current triangle onto the stack. */
+ vertexarray[aroundvertex - b->firstnumber] = encode(triangleloop);
+ decode(nexttri, checktri);
+ if (checktri.tri != m->dummytri) {
+ dest(triangleloop, tdest);
+ apex(triangleloop, tapex);
+ /* Look for other triangles that share an edge. */
+ do {
+ dest(checktri, checkdest);
+ apex(checktri, checkapex);
+ if (tapex == checkdest) {
+ /* The two triangles share an edge; bond them together. */
+ lprev(triangleloop, triangleleft);
+ bond(triangleleft, checktri);
+ }
+ if (tdest == checkapex) {
+ /* The two triangles share an edge; bond them together. */
+ lprev(checktri, checkleft);
+ bond(triangleloop, checkleft);
+ }
+ /* Find the next triangle in the stack. */
+ nexttri = checktri.tri[6 + checktri.orient];
+ decode(nexttri, checktri);
+ } while (checktri.tri != m->dummytri);
+ }
+ }
+ triangleloop.tri = triangletraverse(m);
+ elementnumber++;
+ }
+
+#ifdef TRILIBRARY
+ vertexindex = 0;
+#else /* not TRILIBRARY */
+ fclose(elefile);
+ if (b->vararea) {
+ fclose(areafile);
+ }
+#endif /* not TRILIBRARY */
+
+ hullsize = 0; /* Prepare to count the boundary edges. */
+ if (b->poly) {
+ if (b->verbose) {
+ printf(" Marking segments in triangulation.\n");
+ }
+ /* Read the segments from the .poly file, and link them */
+ /* to their neighboring triangles. */
+ boundmarker = 0;
+ traversalinit(&m->subsegs);
+ subsegloop.ss = subsegtraverse(m);
+ segmentnumber = b->firstnumber;
+ while (subsegloop.ss != (subseg *) NULL) {
+#ifdef TRILIBRARY
+ end[0] = segmentlist[vertexindex++];
+ end[1] = segmentlist[vertexindex++];
+ if (segmentmarkers) {
+ boundmarker = segmentmarkerlist[segmentnumber - b->firstnumber];
+ }
+#else /* not TRILIBRARY */
+ /* Read the endpoints of each segment, and possibly a boundary marker. */
+ stringptr = readline(inputline, polyfile, b->inpolyfilename);
+ /* Skip the first (segment number) field. */
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ printf("Error: Segment %ld has no endpoints in %s.\n", segmentnumber,
+ polyfilename);
+ triexit(1);
+ } else {
+ end[0] = (int) strtol(stringptr, &stringptr, 0);
+ }
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ printf("Error: Segment %ld is missing its second endpoint in %s.\n",
+ segmentnumber, polyfilename);
+ triexit(1);
+ } else {
+ end[1] = (int) strtol(stringptr, &stringptr, 0);
+ }
+ if (segmentmarkers) {
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ boundmarker = 0;
+ } else {
+ boundmarker = (int) strtol(stringptr, &stringptr, 0);
+ }
+ }
+#endif /* not TRILIBRARY */
+ for (j = 0; j < 2; j++) {
+ if ((end[j] < b->firstnumber) ||
+ (end[j] >= b->firstnumber + m->invertices)) {
+ printf("Error: Segment %ld has an invalid vertex index.\n",
+ segmentnumber);
+ triexit(1);
+ }
+ }
+
+ /* set the subsegment's vertices. */
+ subsegloop.ssorient = 0;
+ segmentorg = getvertex(m, b, end[0]);
+ segmentdest = getvertex(m, b, end[1]);
+ setsorg(subsegloop, segmentorg);
+ setsdest(subsegloop, segmentdest);
+ setsegorg(subsegloop, segmentorg);
+ setsegdest(subsegloop, segmentdest);
+ setmark(subsegloop, boundmarker);
+ /* Try linking the subsegment to triangles that share these vertices. */
+ for (subsegloop.ssorient = 0; subsegloop.ssorient < 2;
+ subsegloop.ssorient++) {
+ /* Take the number for the destination of subsegloop. */
+ aroundvertex = end[1 - subsegloop.ssorient];
+ /* Look for triangles having this vertex. */
+ prevlink = &vertexarray[aroundvertex - b->firstnumber];
+ nexttri = vertexarray[aroundvertex - b->firstnumber];
+ decode(nexttri, checktri);
+ sorg(subsegloop, shorg);
+ notfound = 1;
+ /* Look for triangles having this edge. Note that I'm only */
+ /* comparing each triangle's destination with the subsegment; */
+ /* each triangle's apex is handled through a different vertex. */
+ /* Because each triangle appears on three vertices' lists, each */
+ /* occurrence of a triangle on a list can (and does) represent */
+ /* an edge. In this way, most edges are represented twice, and */
+ /* every triangle-subsegment bond is represented once. */
+ while (notfound && (checktri.tri != m->dummytri)) {
+ dest(checktri, checkdest);
+ if (shorg == checkdest) {
+ /* We have a match. Remove this triangle from the list. */
+ *prevlink = checktri.tri[6 + checktri.orient];
+ /* Bond the subsegment to the triangle. */
+ tsbond(checktri, subsegloop);
+ /* Check if this is a boundary edge. */
+ sym(checktri, checkneighbor);
+ if (checkneighbor.tri == m->dummytri) {
+ /* The next line doesn't insert a subsegment (because there's */
+ /* already one there), but it sets the boundary markers of */
+ /* the existing subsegment and its vertices. */
+ insertsubseg(m, b, &checktri, 1);
+ hullsize++;
+ }
+ notfound = 0;
+ }
+ /* Find the next triangle in the stack. */
+ prevlink = &checktri.tri[6 + checktri.orient];
+ nexttri = checktri.tri[6 + checktri.orient];
+ decode(nexttri, checktri);
+ }
+ }
+ subsegloop.ss = subsegtraverse(m);
+ segmentnumber++;
+ }
+ }
+
+ /* Mark the remaining edges as not being attached to any subsegment. */
+ /* Also, count the (yet uncounted) boundary edges. */
+ for (i = 0; i < m->vertices.items; i++) {
+ /* Search the stack of triangles adjacent to a vertex. */
+ nexttri = vertexarray[i];
+ decode(nexttri, checktri);
+ while (checktri.tri != m->dummytri) {
+ /* Find the next triangle in the stack before this */
+ /* information gets overwritten. */
+ nexttri = checktri.tri[6 + checktri.orient];
+ /* No adjacent subsegment. (This overwrites the stack info.) */
+ tsdissolve(checktri);
+ sym(checktri, checkneighbor);
+ if (checkneighbor.tri == m->dummytri) {
+ insertsubseg(m, b, &checktri, 1);
+ hullsize++;
+ }
+ decode(nexttri, checktri);
+ }
+ }
+
+ trifree((VOID *) vertexarray);
+ return hullsize;
+}
+
+#endif /* not CDT_ONLY */
+
+/** **/
+/** **/
+/********* General mesh construction routines end here *********/
+
+/********* Segment insertion begins here *********/
+/** **/
+/** **/
+
+/*****************************************************************************/
+/* */
+/* finddirection() Find the first triangle on the path from one point */
+/* to another. */
+/* */
+/* Finds the triangle that intersects a line segment drawn from the */
+/* origin of `searchtri' to the point `searchpoint', and returns the result */
+/* in `searchtri'. The origin of `searchtri' does not change, even though */
+/* the triangle returned may differ from the one passed in. This routine */
+/* is used to find the direction to move in to get from one point to */
+/* another. */
+/* */
+/* The return value notes whether the destination or apex of the found */
+/* triangle is collinear with the two points in question. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+enum finddirectionresult finddirection(struct mesh *m, struct behavior *b,
+ struct otri *searchtri,
+ vertex searchpoint)
+#else /* not ANSI_DECLARATORS */
+enum finddirectionresult finddirection(m, b, searchtri, searchpoint)
+struct mesh *m;
+struct behavior *b;
+struct otri *searchtri;
+vertex searchpoint;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri checktri;
+ vertex startvertex;
+ vertex leftvertex, rightvertex;
+ REAL leftccw, rightccw;
+ int leftflag, rightflag;
+ triangle ptr; /* Temporary variable used by onext() and oprev(). */
+
+ org(*searchtri, startvertex);
+ dest(*searchtri, rightvertex);
+ apex(*searchtri, leftvertex);
+ /* Is `searchpoint' to the left? */
+ leftccw = counterclockwise(m, b, searchpoint, startvertex, leftvertex);
+ leftflag = leftccw > 0.0;
+ /* Is `searchpoint' to the right? */
+ rightccw = counterclockwise(m, b, startvertex, searchpoint, rightvertex);
+ rightflag = rightccw > 0.0;
+ if (leftflag && rightflag) {
+ /* `searchtri' faces directly away from `searchpoint'. We could go left */
+ /* or right. Ask whether it's a triangle or a boundary on the left. */
+ onext(*searchtri, checktri);
+ if (checktri.tri == m->dummytri) {
+ leftflag = 0;
+ } else {
+ rightflag = 0;
+ }
+ }
+ while (leftflag) {
+ /* Turn left until satisfied. */
+ onextself(*searchtri);
+ if (searchtri->tri == m->dummytri) {
+ printf("Internal error in finddirection(): Unable to find a\n");
+ printf(" triangle leading from (%.12g, %.12g) to", startvertex[0],
+ startvertex[1]);
+ printf(" (%.12g, %.12g).\n", searchpoint[0], searchpoint[1]);
+ internalerror();
+ }
+ apex(*searchtri, leftvertex);
+ rightccw = leftccw;
+ leftccw = counterclockwise(m, b, searchpoint, startvertex, leftvertex);
+ leftflag = leftccw > 0.0;
+ }
+ while (rightflag) {
+ /* Turn right until satisfied. */
+ oprevself(*searchtri);
+ if (searchtri->tri == m->dummytri) {
+ printf("Internal error in finddirection(): Unable to find a\n");
+ printf(" triangle leading from (%.12g, %.12g) to", startvertex[0],
+ startvertex[1]);
+ printf(" (%.12g, %.12g).\n", searchpoint[0], searchpoint[1]);
+ internalerror();
+ }
+ dest(*searchtri, rightvertex);
+ leftccw = rightccw;
+ rightccw = counterclockwise(m, b, startvertex, searchpoint, rightvertex);
+ rightflag = rightccw > 0.0;
+ }
+ if (leftccw == 0.0) {
+ return LEFTCOLLINEAR;
+ } else if (rightccw == 0.0) {
+ return RIGHTCOLLINEAR;
+ } else {
+ return WITHIN;
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* segmentintersection() Find the intersection of an existing segment */
+/* and a segment that is being inserted. Insert */
+/* a vertex at the intersection, splitting an */
+/* existing subsegment. */
+/* */
+/* The segment being inserted connects the apex of splittri to endpoint2. */
+/* splitsubseg is the subsegment being split, and MUST adjoin splittri. */
+/* Hence, endpoints of the subsegment being split are the origin and */
+/* destination of splittri. */
+/* */
+/* On completion, splittri is a handle having the newly inserted */
+/* intersection point as its origin, and endpoint1 as its destination. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void segmentintersection(struct mesh *m, struct behavior *b,
+ struct otri *splittri, struct osub *splitsubseg,
+ vertex endpoint2)
+#else /* not ANSI_DECLARATORS */
+void segmentintersection(m, b, splittri, splitsubseg, endpoint2)
+struct mesh *m;
+struct behavior *b;
+struct otri *splittri;
+struct osub *splitsubseg;
+vertex endpoint2;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct osub opposubseg;
+ vertex endpoint1;
+ vertex torg, tdest;
+ vertex leftvertex, rightvertex;
+ vertex newvertex;
+ enum insertvertexresult success;
+ enum finddirectionresult collinear;
+ REAL ex, ey;
+ REAL tx, ty;
+ REAL etx, ety;
+ REAL split, denom;
+ int i;
+ triangle ptr; /* Temporary variable used by onext(). */
+ subseg sptr; /* Temporary variable used by snext(). */
+
+ /* Find the other three segment endpoints. */
+ apex(*splittri, endpoint1);
+ org(*splittri, torg);
+ dest(*splittri, tdest);
+ /* Segment intersection formulae; see the Antonio reference. */
+ tx = tdest[0] - torg[0];
+ ty = tdest[1] - torg[1];
+ ex = endpoint2[0] - endpoint1[0];
+ ey = endpoint2[1] - endpoint1[1];
+ etx = torg[0] - endpoint2[0];
+ ety = torg[1] - endpoint2[1];
+ denom = ty * ex - tx * ey;
+ if (denom == 0.0) {
+ printf("Internal error in segmentintersection():");
+ printf(" Attempt to find intersection of parallel segments.\n");
+ internalerror();
+ return;
+ }
+ split = (ey * etx - ex * ety) / denom;
+ /* Create the new vertex. */
+ newvertex = (vertex) poolalloc(&m->vertices);
+ /* Interpolate its coordinate and attributes. */
+ for (i = 0; i < 2 + m->nextras; i++) {
+ newvertex[i] = torg[i] + split * (tdest[i] - torg[i]);
+ }
+ setvertexmark(newvertex, mark(*splitsubseg));
+ setvertextype(newvertex, INPUTVERTEX);
+ if (b->verbose > 1) {
+ printf(
+ " Splitting subsegment (%.12g, %.12g) (%.12g, %.12g) at (%.12g, %.12g).\n",
+ torg[0], torg[1], tdest[0], tdest[1], newvertex[0], newvertex[1]);
+ }
+ /* Insert the intersection vertex. This should always succeed. */
+ success = insertvertex(m, b, newvertex, splittri, splitsubseg, 0, 0);
+ if (success != SUCCESSFULVERTEX) {
+ printf("Internal error in segmentintersection():\n");
+ printf(" Failure to split a segment.\n");
+ internalerror();
+ return;
+ }
+ /* Record a triangle whose origin is the new vertex. */
+ setvertex2tri(newvertex, encode(*splittri));
+ if (m->steinerleft > 0) {
+ m->steinerleft--;
+ }
+
+ /* Divide the segment into two, and correct the segment endpoints. */
+ ssymself(*splitsubseg);
+ spivot(*splitsubseg, opposubseg);
+ sdissolve(*splitsubseg);
+ sdissolve(opposubseg);
+ do {
+ setsegorg(*splitsubseg, newvertex);
+ snextself(*splitsubseg);
+ } while (splitsubseg->ss != m->dummysub);
+ do {
+ setsegorg(opposubseg, newvertex);
+ snextself(opposubseg);
+ } while (opposubseg.ss != m->dummysub);
+
+ /* Inserting the vertex may have caused edge flips. We wish to rediscover */
+ /* the edge connecting endpoint1 to the new intersection vertex. */
+ collinear = finddirection(m, b, splittri, endpoint1);
+ dest(*splittri, rightvertex);
+ apex(*splittri, leftvertex);
+ if ((leftvertex[0] == endpoint1[0]) && (leftvertex[1] == endpoint1[1])) {
+ onextself(*splittri);
+ } else if ((rightvertex[0] != endpoint1[0]) ||
+ (rightvertex[1] != endpoint1[1])) {
+ printf("Internal error in segmentintersection():\n");
+ printf(" Topological inconsistency after splitting a segment.\n");
+ internalerror();
+ return;
+ }
+ /* `splittri' should have destination endpoint1. */
+}
+
+/*****************************************************************************/
+/* */
+/* scoutsegment() Scout the first triangle on the path from one endpoint */
+/* to another, and check for completion (reaching the */
+/* second endpoint), a collinear vertex, or the */
+/* intersection of two segments. */
+/* */
+/* Returns one if the entire segment is successfully inserted, and zero if */
+/* the job must be finished by conformingedge() or constrainededge(). */
+/* */
+/* If the first triangle on the path has the second endpoint as its */
+/* destination or apex, a subsegment is inserted and the job is done. */
+/* */
+/* If the first triangle on the path has a destination or apex that lies on */
+/* the segment, a subsegment is inserted connecting the first endpoint to */
+/* the collinear vertex, and the search is continued from the collinear */
+/* vertex. */
+/* */
+/* If the first triangle on the path has a subsegment opposite its origin, */
+/* then there is a segment that intersects the segment being inserted. */
+/* Their intersection vertex is inserted, splitting the subsegment. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+int scoutsegment(struct mesh *m, struct behavior *b, struct otri *searchtri,
+ vertex endpoint2, int newmark)
+#else /* not ANSI_DECLARATORS */
+int scoutsegment(m, b, searchtri, endpoint2, newmark)
+struct mesh *m;
+struct behavior *b;
+struct otri *searchtri;
+vertex endpoint2;
+int newmark;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri crosstri;
+ struct osub crosssubseg;
+ vertex leftvertex, rightvertex;
+ enum finddirectionresult collinear;
+ subseg sptr; /* Temporary variable used by tspivot(). */
+
+ collinear = finddirection(m, b, searchtri, endpoint2);
+ dest(*searchtri, rightvertex);
+ apex(*searchtri, leftvertex);
+ if (((leftvertex[0] == endpoint2[0]) && (leftvertex[1] == endpoint2[1])) ||
+ ((rightvertex[0] == endpoint2[0]) && (rightvertex[1] == endpoint2[1]))) {
+ /* The segment is already an edge in the mesh. */
+ if ((leftvertex[0] == endpoint2[0]) && (leftvertex[1] == endpoint2[1])) {
+ lprevself(*searchtri);
+ }
+ /* Insert a subsegment, if there isn't already one there. */
+ insertsubseg(m, b, searchtri, newmark);
+ return 1;
+ } else if (collinear == LEFTCOLLINEAR) {
+ /* We've collided with a vertex between the segment's endpoints. */
+ /* Make the collinear vertex be the triangle's origin. */
+ lprevself(*searchtri);
+ insertsubseg(m, b, searchtri, newmark);
+ /* Insert the remainder of the segment. */
+ return scoutsegment(m, b, searchtri, endpoint2, newmark);
+ } else if (collinear == RIGHTCOLLINEAR) {
+ /* We've collided with a vertex between the segment's endpoints. */
+ insertsubseg(m, b, searchtri, newmark);
+ /* Make the collinear vertex be the triangle's origin. */
+ lnextself(*searchtri);
+ /* Insert the remainder of the segment. */
+ return scoutsegment(m, b, searchtri, endpoint2, newmark);
+ } else {
+ lnext(*searchtri, crosstri);
+ tspivot(crosstri, crosssubseg);
+ /* Check for a crossing segment. */
+ if (crosssubseg.ss == m->dummysub) {
+ return 0;
+ } else {
+ /* Insert a vertex at the intersection. */
+ segmentintersection(m, b, &crosstri, &crosssubseg, endpoint2);
+ if (error_set)
+ return -1;
+ otricopy(crosstri, *searchtri);
+ insertsubseg(m, b, searchtri, newmark);
+ /* Insert the remainder of the segment. */
+ return scoutsegment(m, b, searchtri, endpoint2, newmark);
+ }
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* conformingedge() Force a segment into a conforming Delaunay */
+/* triangulation by inserting a vertex at its midpoint, */
+/* and recursively forcing in the two half-segments if */
+/* necessary. */
+/* */
+/* Generates a sequence of subsegments connecting `endpoint1' to */
+/* `endpoint2'. `newmark' is the boundary marker of the segment, assigned */
+/* to each new splitting vertex and subsegment. */
+/* */
+/* Note that conformingedge() does not always maintain the conforming */
+/* Delaunay property. Once inserted, segments are locked into place; */
+/* vertices inserted later (to force other segments in) may render these */
+/* fixed segments non-Delaunay. The conforming Delaunay property will be */
+/* restored by enforcequality() by splitting encroached subsegments. */
+/* */
+/*****************************************************************************/
+
+#ifndef REDUCED
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void conformingedge(struct mesh *m, struct behavior *b,
+ vertex endpoint1, vertex endpoint2, int newmark)
+#else /* not ANSI_DECLARATORS */
+void conformingedge(m, b, endpoint1, endpoint2, newmark)
+struct mesh *m;
+struct behavior *b;
+vertex endpoint1;
+vertex endpoint2;
+int newmark;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri searchtri1, searchtri2;
+ struct osub brokensubseg;
+ vertex newvertex;
+ vertex midvertex1, midvertex2;
+ enum insertvertexresult success;
+ int i;
+ subseg sptr; /* Temporary variable used by tspivot(). */
+
+ if (b->verbose > 2) {
+ printf("Forcing segment into triangulation by recursive splitting:\n");
+ printf(" (%.12g, %.12g) (%.12g, %.12g)\n", endpoint1[0], endpoint1[1],
+ endpoint2[0], endpoint2[1]);
+ }
+ /* Create a new vertex to insert in the middle of the segment. */
+ newvertex = (vertex) poolalloc(&m->vertices);
+ /* Interpolate coordinates and attributes. */
+ for (i = 0; i < 2 + m->nextras; i++) {
+ newvertex[i] = 0.5 * (endpoint1[i] + endpoint2[i]);
+ }
+ setvertexmark(newvertex, newmark);
+ setvertextype(newvertex, SEGMENTVERTEX);
+ /* No known triangle to search from. */
+ searchtri1.tri = m->dummytri;
+ /* Attempt to insert the new vertex. */
+ success = insertvertex(m, b, newvertex, &searchtri1, (struct osub *) NULL,
+ 0, 0);
+ if (success == DUPLICATEVERTEX) {
+ if (b->verbose > 2) {
+ printf(" Segment intersects existing vertex (%.12g, %.12g).\n",
+ newvertex[0], newvertex[1]);
+ }
+ /* Use the vertex that's already there. */
+ vertexdealloc(m, newvertex);
+ org(searchtri1, newvertex);
+ } else {
+ if (success == VIOLATINGVERTEX) {
+ if (b->verbose > 2) {
+ printf(" Two segments intersect at (%.12g, %.12g).\n",
+ newvertex[0], newvertex[1]);
+ }
+ /* By fluke, we've landed right on another segment. Split it. */
+ tspivot(searchtri1, brokensubseg);
+ success = insertvertex(m, b, newvertex, &searchtri1, &brokensubseg,
+ 0, 0);
+ if (success != SUCCESSFULVERTEX) {
+ printf("Internal error in conformingedge():\n");
+ printf(" Failure to split a segment.\n");
+ internalerror();
+ }
+ }
+ /* The vertex has been inserted successfully. */
+ if (m->steinerleft > 0) {
+ m->steinerleft--;
+ }
+ }
+ otricopy(searchtri1, searchtri2);
+ /* `searchtri1' and `searchtri2' are fastened at their origins to */
+ /* `newvertex', and will be directed toward `endpoint1' and `endpoint2' */
+ /* respectively. First, we must get `searchtri2' out of the way so it */
+ /* won't be invalidated during the insertion of the first half of the */
+ /* segment. */
+ finddirection(m, b, &searchtri2, endpoint2);
+ if (!scoutsegment(m, b, &searchtri1, endpoint1, newmark)) {
+ /* The origin of searchtri1 may have changed if a collision with an */
+ /* intervening vertex on the segment occurred. */
+ org(searchtri1, midvertex1);
+ conformingedge(m, b, midvertex1, endpoint1, newmark);
+ }
+ if (!scoutsegment(m, b, &searchtri2, endpoint2, newmark)) {
+ /* The origin of searchtri2 may have changed if a collision with an */
+ /* intervening vertex on the segment occurred. */
+ org(searchtri2, midvertex2);
+ conformingedge(m, b, midvertex2, endpoint2, newmark);
+ }
+}
+
+#endif /* not CDT_ONLY */
+#endif /* not REDUCED */
+
+/*****************************************************************************/
+/* */
+/* delaunayfixup() Enforce the Delaunay condition at an edge, fanning out */
+/* recursively from an existing vertex. Pay special */
+/* attention to stacking inverted triangles. */
+/* */
+/* This is a support routine for inserting segments into a constrained */
+/* Delaunay triangulation. */
+/* */
+/* The origin of fixuptri is treated as if it has just been inserted, and */
+/* the local Delaunay condition needs to be enforced. It is only enforced */
+/* in one sector, however, that being the angular range defined by */
+/* fixuptri. */
+/* */
+/* This routine also needs to make decisions regarding the "stacking" of */
+/* triangles. (Read the description of constrainededge() below before */
+/* reading on here, so you understand the algorithm.) If the position of */
+/* the new vertex (the origin of fixuptri) indicates that the vertex before */
+/* it on the polygon is a reflex vertex, then "stack" the triangle by */
+/* doing nothing. (fixuptri is an inverted triangle, which is how stacked */
+/* triangles are identified.) */
+/* */
+/* Otherwise, check whether the vertex before that was a reflex vertex. */
+/* If so, perform an edge flip, thereby eliminating an inverted triangle */
+/* (popping it off the stack). The edge flip may result in the creation */
+/* of a new inverted triangle, depending on whether or not the new vertex */
+/* is visible to the vertex three edges behind on the polygon. */
+/* */
+/* If neither of the two vertices behind the new vertex are reflex */
+/* vertices, fixuptri and fartri, the triangle opposite it, are not */
+/* inverted; hence, ensure that the edge between them is locally Delaunay. */
+/* */
+/* `leftside' indicates whether or not fixuptri is to the left of the */
+/* segment being inserted. (Imagine that the segment is pointing up from */
+/* endpoint1 to endpoint2.) */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void delaunayfixup(struct mesh *m, struct behavior *b,
+ struct otri *fixuptri, int leftside)
+#else /* not ANSI_DECLARATORS */
+void delaunayfixup(m, b, fixuptri, leftside)
+struct mesh *m;
+struct behavior *b;
+struct otri *fixuptri;
+int leftside;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri neartri;
+ struct otri fartri;
+ struct osub faredge;
+ vertex nearvertex, leftvertex, rightvertex, farvertex;
+ triangle ptr; /* Temporary variable used by sym(). */
+ subseg sptr; /* Temporary variable used by tspivot(). */
+
+ lnext(*fixuptri, neartri);
+ sym(neartri, fartri);
+ /* Check if the edge opposite the origin of fixuptri can be flipped. */
+ if (fartri.tri == m->dummytri) {
+ return;
+ }
+ tspivot(neartri, faredge);
+ if (faredge.ss != m->dummysub) {
+ return;
+ }
+ /* Find all the relevant vertices. */
+ apex(neartri, nearvertex);
+ org(neartri, leftvertex);
+ dest(neartri, rightvertex);
+ apex(fartri, farvertex);
+ /* Check whether the previous polygon vertex is a reflex vertex. */
+ if (leftside) {
+ if (counterclockwise(m, b, nearvertex, leftvertex, farvertex) <= 0.0) {
+ /* leftvertex is a reflex vertex too. Nothing can */
+ /* be done until a convex section is found. */
+ return;
+ }
+ } else {
+ if (counterclockwise(m, b, farvertex, rightvertex, nearvertex) <= 0.0) {
+ /* rightvertex is a reflex vertex too. Nothing can */
+ /* be done until a convex section is found. */
+ return;
+ }
+ }
+ if (counterclockwise(m, b, rightvertex, leftvertex, farvertex) > 0.0) {
+ /* fartri is not an inverted triangle, and farvertex is not a reflex */
+ /* vertex. As there are no reflex vertices, fixuptri isn't an */
+ /* inverted triangle, either. Hence, test the edge between the */
+ /* triangles to ensure it is locally Delaunay. */
+ if (incircle(m, b, leftvertex, farvertex, rightvertex, nearvertex) <=
+ 0.0) {
+ return;
+ }
+ /* Not locally Delaunay; go on to an edge flip. */
+ } /* else fartri is inverted; remove it from the stack by flipping. */
+ flip(m, b, &neartri);
+ lprevself(*fixuptri); /* Restore the origin of fixuptri after the flip. */
+ /* Recursively process the two triangles that result from the flip. */
+ delaunayfixup(m, b, fixuptri, leftside);
+ delaunayfixup(m, b, &fartri, leftside);
+}
+
+/*****************************************************************************/
+/* */
+/* constrainededge() Force a segment into a constrained Delaunay */
+/* triangulation by deleting the triangles it */
+/* intersects, and triangulating the polygons that */
+/* form on each side of it. */
+/* */
+/* Generates a single subsegment connecting `endpoint1' to `endpoint2'. */
+/* The triangle `starttri' has `endpoint1' as its origin. `newmark' is the */
+/* boundary marker of the segment. */
+/* */
+/* To insert a segment, every triangle whose interior intersects the */
+/* segment is deleted. The union of these deleted triangles is a polygon */
+/* (which is not necessarily monotone, but is close enough), which is */
+/* divided into two polygons by the new segment. This routine's task is */
+/* to generate the Delaunay triangulation of these two polygons. */
+/* */
+/* You might think of this routine's behavior as a two-step process. The */
+/* first step is to walk from endpoint1 to endpoint2, flipping each edge */
+/* encountered. This step creates a fan of edges connected to endpoint1, */
+/* including the desired edge to endpoint2. The second step enforces the */
+/* Delaunay condition on each side of the segment in an incremental manner: */
+/* proceeding along the polygon from endpoint1 to endpoint2 (this is done */
+/* independently on each side of the segment), each vertex is "enforced" */
+/* as if it had just been inserted, but affecting only the previous */
+/* vertices. The result is the same as if the vertices had been inserted */
+/* in the order they appear on the polygon, so the result is Delaunay. */
+/* */
+/* In truth, constrainededge() interleaves these two steps. The procedure */
+/* walks from endpoint1 to endpoint2, and each time an edge is encountered */
+/* and flipped, the newly exposed vertex (at the far end of the flipped */
+/* edge) is "enforced" upon the previously flipped edges, usually affecting */
+/* only one side of the polygon (depending upon which side of the segment */
+/* the vertex falls on). */
+/* */
+/* The algorithm is complicated by the need to handle polygons that are not */
+/* convex. Although the polygon is not necessarily monotone, it can be */
+/* triangulated in a manner similar to the stack-based algorithms for */
+/* monotone polygons. For each reflex vertex (local concavity) of the */
+/* polygon, there will be an inverted triangle formed by one of the edge */
+/* flips. (An inverted triangle is one with negative area - that is, its */
+/* vertices are arranged in clockwise order - and is best thought of as a */
+/* wrinkle in the fabric of the mesh.) Each inverted triangle can be */
+/* thought of as a reflex vertex pushed on the stack, waiting to be fixed */
+/* later. */
+/* */
+/* A reflex vertex is popped from the stack when a vertex is inserted that */
+/* is visible to the reflex vertex. (However, if the vertex behind the */
+/* reflex vertex is not visible to the reflex vertex, a new inverted */
+/* triangle will take its place on the stack.) These details are handled */
+/* by the delaunayfixup() routine above. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void constrainededge(struct mesh *m, struct behavior *b,
+ struct otri *starttri, vertex endpoint2, int newmark)
+#else /* not ANSI_DECLARATORS */
+void constrainededge(m, b, starttri, endpoint2, newmark)
+struct mesh *m;
+struct behavior *b;
+struct otri *starttri;
+vertex endpoint2;
+int newmark;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri fixuptri, fixuptri2;
+ struct osub crosssubseg;
+ vertex endpoint1;
+ vertex farvertex;
+ REAL area;
+ int collision;
+ int done;
+ triangle ptr; /* Temporary variable used by sym() and oprev(). */
+ subseg sptr; /* Temporary variable used by tspivot(). */
+
+ org(*starttri, endpoint1);
+ lnext(*starttri, fixuptri);
+ flip(m, b, &fixuptri);
+ /* `collision' indicates whether we have found a vertex directly */
+ /* between endpoint1 and endpoint2. */
+ collision = 0;
+ done = 0;
+ do {
+ org(fixuptri, farvertex);
+ /* `farvertex' is the extreme point of the polygon we are "digging" */
+ /* to get from endpoint1 to endpoint2. */
+ if ((farvertex[0] == endpoint2[0]) && (farvertex[1] == endpoint2[1])) {
+ oprev(fixuptri, fixuptri2);
+ /* Enforce the Delaunay condition around endpoint2. */
+ delaunayfixup(m, b, &fixuptri, 0);
+ delaunayfixup(m, b, &fixuptri2, 1);
+ done = 1;
+ } else {
+ /* Check whether farvertex is to the left or right of the segment */
+ /* being inserted, to decide which edge of fixuptri to dig */
+ /* through next. */
+ area = counterclockwise(m, b, endpoint1, endpoint2, farvertex);
+ if (area == 0.0) {
+ /* We've collided with a vertex between endpoint1 and endpoint2. */
+ collision = 1;
+ oprev(fixuptri, fixuptri2);
+ /* Enforce the Delaunay condition around farvertex. */
+ delaunayfixup(m, b, &fixuptri, 0);
+ delaunayfixup(m, b, &fixuptri2, 1);
+ done = 1;
+ } else {
+ if (area > 0.0) { /* farvertex is to the left of the segment. */
+ oprev(fixuptri, fixuptri2);
+ /* Enforce the Delaunay condition around farvertex, on the */
+ /* left side of the segment only. */
+ delaunayfixup(m, b, &fixuptri2, 1);
+ /* Flip the edge that crosses the segment. After the edge is */
+ /* flipped, one of its endpoints is the fan vertex, and the */
+ /* destination of fixuptri is the fan vertex. */
+ lprevself(fixuptri);
+ } else { /* farvertex is to the right of the segment. */
+ delaunayfixup(m, b, &fixuptri, 0);
+ /* Flip the edge that crosses the segment. After the edge is */
+ /* flipped, one of its endpoints is the fan vertex, and the */
+ /* destination of fixuptri is the fan vertex. */
+ oprevself(fixuptri);
+ }
+ /* Check for two intersecting segments. */
+ tspivot(fixuptri, crosssubseg);
+ if (crosssubseg.ss == m->dummysub) {
+ flip(m, b, &fixuptri); /* May create inverted triangle at left. */
+ } else {
+ /* We've collided with a segment between endpoint1 and endpoint2. */
+ collision = 1;
+ /* Insert a vertex at the intersection. */
+ segmentintersection(m, b, &fixuptri, &crosssubseg, endpoint2);
+ done = 1;
+ }
+ }
+ }
+ } while (!done);
+ /* Insert a subsegment to make the segment permanent. */
+ insertsubseg(m, b, &fixuptri, newmark);
+ /* If there was a collision with an interceding vertex, install another */
+ /* segment connecting that vertex with endpoint2. */
+ if (collision) {
+ /* Insert the remainder of the segment. */
+ if (!scoutsegment(m, b, &fixuptri, endpoint2, newmark)) {
+ constrainededge(m, b, &fixuptri, endpoint2, newmark);
+ }
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* insertsegment() Insert a PSLG segment into a triangulation. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void insertsegment(struct mesh *m, struct behavior *b,
+ vertex endpoint1, vertex endpoint2, int newmark)
+#else /* not ANSI_DECLARATORS */
+void insertsegment(m, b, endpoint1, endpoint2, newmark)
+struct mesh *m;
+struct behavior *b;
+vertex endpoint1;
+vertex endpoint2;
+int newmark;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri searchtri1, searchtri2;
+ triangle encodedtri;
+ vertex checkvertex;
+ triangle ptr; /* Temporary variable used by sym(). */
+
+ if (b->verbose > 1) {
+ printf(" Connecting (%.12g, %.12g) to (%.12g, %.12g).\n",
+ endpoint1[0], endpoint1[1], endpoint2[0], endpoint2[1]);
+ }
+
+ /* Find a triangle whose origin is the segment's first endpoint. */
+ checkvertex = (vertex) NULL;
+ encodedtri = vertex2tri(endpoint1);
+ if (encodedtri != (triangle) NULL) {
+ decode(encodedtri, searchtri1);
+ org(searchtri1, checkvertex);
+ }
+ if (checkvertex != endpoint1) {
+ /* Find a boundary triangle to search from. */
+ searchtri1.tri = m->dummytri;
+ searchtri1.orient = 0;
+ symself(searchtri1);
+ /* Search for the segment's first endpoint by point location. */
+ if (locate(m, b, endpoint1, &searchtri1) != ONVERTEX) {
+ printf(
+ "Internal error in insertsegment(): Unable to locate PSLG vertex\n");
+ printf(" (%.12g, %.12g) in triangulation.\n",
+ endpoint1[0], endpoint1[1]);
+ internalerror();
+ }
+ }
+ /* Remember this triangle to improve subsequent point location. */
+ otricopy(searchtri1, m->recenttri);
+ /* Scout the beginnings of a path from the first endpoint */
+ /* toward the second. */
+ if (scoutsegment(m, b, &searchtri1, endpoint2, newmark)) {
+ /* The segment was easily inserted. */
+ return;
+ }
+ /* The first endpoint may have changed if a collision with an intervening */
+ /* vertex on the segment occurred. */
+ org(searchtri1, endpoint1);
+
+ /* Find a triangle whose origin is the segment's second endpoint. */
+ checkvertex = (vertex) NULL;
+ encodedtri = vertex2tri(endpoint2);
+ if (encodedtri != (triangle) NULL) {
+ decode(encodedtri, searchtri2);
+ org(searchtri2, checkvertex);
+ }
+ if (checkvertex != endpoint2) {
+ /* Find a boundary triangle to search from. */
+ searchtri2.tri = m->dummytri;
+ searchtri2.orient = 0;
+ symself(searchtri2);
+ /* Search for the segment's second endpoint by point location. */
+ if (locate(m, b, endpoint2, &searchtri2) != ONVERTEX) {
+ printf(
+ "Internal error in insertsegment(): Unable to locate PSLG vertex\n");
+ printf(" (%.12g, %.12g) in triangulation.\n",
+ endpoint2[0], endpoint2[1]);
+ internalerror();
+ }
+ }
+ /* Remember this triangle to improve subsequent point location. */
+ otricopy(searchtri2, m->recenttri);
+ /* Scout the beginnings of a path from the second endpoint */
+ /* toward the first. */
+ if (scoutsegment(m, b, &searchtri2, endpoint1, newmark)) {
+ /* The segment was easily inserted. */
+ return;
+ }
+ /* The second endpoint may have changed if a collision with an intervening */
+ /* vertex on the segment occurred. */
+ org(searchtri2, endpoint2);
+
+#ifndef REDUCED
+#ifndef CDT_ONLY
+ if (b->splitseg) {
+ /* Insert vertices to force the segment into the triangulation. */
+ conformingedge(m, b, endpoint1, endpoint2, newmark);
+ } else {
+#endif /* not CDT_ONLY */
+#endif /* not REDUCED */
+ /* Insert the segment directly into the triangulation. */
+ constrainededge(m, b, &searchtri1, endpoint2, newmark);
+#ifndef REDUCED
+#ifndef CDT_ONLY
+ }
+#endif /* not CDT_ONLY */
+#endif /* not REDUCED */
+}
+
+/*****************************************************************************/
+/* */
+/* markhull() Cover the convex hull of a triangulation with subsegments. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void markhull(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void markhull(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri hulltri;
+ struct otri nexttri;
+ struct otri starttri;
+ triangle ptr; /* Temporary variable used by sym() and oprev(). */
+
+ /* Find a triangle handle on the hull. */
+ hulltri.tri = m->dummytri;
+ hulltri.orient = 0;
+ symself(hulltri);
+ /* Remember where we started so we know when to stop. */
+ otricopy(hulltri, starttri);
+ /* Go once counterclockwise around the convex hull. */
+ do {
+ /* Create a subsegment if there isn't already one here. */
+ insertsubseg(m, b, &hulltri, 1);
+ /* To find the next hull edge, go clockwise around the next vertex. */
+ lnextself(hulltri);
+ oprev(hulltri, nexttri);
+ while (nexttri.tri != m->dummytri) {
+ otricopy(nexttri, hulltri);
+ oprev(hulltri, nexttri);
+ }
+ } while (!otriequal(hulltri, starttri));
+}
+
+/*****************************************************************************/
+/* */
+/* formskeleton() Create the segments of a triangulation, including PSLG */
+/* segments and edges on the convex hull. */
+/* */
+/* The PSLG segments are read from a .poly file. The return value is the */
+/* number of segments in the file. */
+/* */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void formskeleton(struct mesh *m, struct behavior *b, int *segmentlist,
+ int *segmentmarkerlist, int numberofsegments)
+#else /* not ANSI_DECLARATORS */
+void formskeleton(m, b, segmentlist, segmentmarkerlist, numberofsegments)
+struct mesh *m;
+struct behavior *b;
+int *segmentlist;
+int *segmentmarkerlist;
+int numberofsegments;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void formskeleton(struct mesh *m, struct behavior *b,
+ FILE *polyfile, char *polyfilename)
+#else /* not ANSI_DECLARATORS */
+void formskeleton(m, b, polyfile, polyfilename)
+struct mesh *m;
+struct behavior *b;
+FILE *polyfile;
+char *polyfilename;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+ char polyfilename[6];
+ int index;
+#else /* not TRILIBRARY */
+ char inputline[INPUTLINESIZE];
+ char *stringptr;
+#endif /* not TRILIBRARY */
+ vertex endpoint1, endpoint2;
+ int segmentmarkers;
+ int end1, end2;
+ int boundmarker;
+ int i;
+
+ if (b->poly) {
+ if (!b->quiet) {
+ printf("Recovering segments in Delaunay triangulation.\n");
+ }
+#ifdef TRILIBRARY
+ strcpy(polyfilename, "input");
+ m->insegments = numberofsegments;
+ segmentmarkers = segmentmarkerlist != (int *) NULL;
+ index = 0;
+#else /* not TRILIBRARY */
+ /* Read the segments from a .poly file. */
+ /* Read number of segments and number of boundary markers. */
+ stringptr = readline(inputline, polyfile, polyfilename);
+ m->insegments = (int) strtol(stringptr, &stringptr, 0);
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ segmentmarkers = 0;
+ } else {
+ segmentmarkers = (int) strtol(stringptr, &stringptr, 0);
+ }
+#endif /* not TRILIBRARY */
+ /* If the input vertices are collinear, there is no triangulation, */
+ /* so don't try to insert segments. */
+ if (m->triangles.items == 0) {
+ return;
+ }
+
+ /* If segments are to be inserted, compute a mapping */
+ /* from vertices to triangles. */
+ if (m->insegments > 0) {
+ makevertexmap(m, b);
+ if (b->verbose) {
+ printf(" Recovering PSLG segments.\n");
+ }
+ }
+
+ boundmarker = 0;
+ /* Read and insert the segments. */
+ for (i = 0; i < m->insegments; i++) {
+#ifdef TRILIBRARY
+ end1 = segmentlist[index++];
+ end2 = segmentlist[index++];
+ if (segmentmarkers) {
+ boundmarker = segmentmarkerlist[i];
+ }
+#else /* not TRILIBRARY */
+ stringptr = readline(inputline, polyfile, b->inpolyfilename);
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ printf("Error: Segment %d has no endpoints in %s.\n",
+ b->firstnumber + i, polyfilename);
+ triexit(1);
+ } else {
+ end1 = (int) strtol(stringptr, &stringptr, 0);
+ }
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ printf("Error: Segment %d is missing its second endpoint in %s.\n",
+ b->firstnumber + i, polyfilename);
+ triexit(1);
+ } else {
+ end2 = (int) strtol(stringptr, &stringptr, 0);
+ }
+ if (segmentmarkers) {
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ boundmarker = 0;
+ } else {
+ boundmarker = (int) strtol(stringptr, &stringptr, 0);
+ }
+ }
+#endif /* not TRILIBRARY */
+ if ((end1 < b->firstnumber) ||
+ (end1 >= b->firstnumber + m->invertices)) {
+ if (!b->quiet) {
+ printf("Warning: Invalid first endpoint of segment %d in %s.\n",
+ b->firstnumber + i, polyfilename);
+ }
+ } else if ((end2 < b->firstnumber) ||
+ (end2 >= b->firstnumber + m->invertices)) {
+ if (!b->quiet) {
+ printf("Warning: Invalid second endpoint of segment %d in %s.\n",
+ b->firstnumber + i, polyfilename);
+ }
+ } else {
+ /* Find the vertices numbered `end1' and `end2'. */
+ endpoint1 = getvertex(m, b, end1);
+ endpoint2 = getvertex(m, b, end2);
+ if ((endpoint1[0] == endpoint2[0]) && (endpoint1[1] == endpoint2[1])) {
+ if (!b->quiet) {
+ printf("Warning: Endpoints of segment %d are coincident in %s.\n",
+ b->firstnumber + i, polyfilename);
+ }
+ } else {
+ insertsegment(m, b, endpoint1, endpoint2, boundmarker);
+ }
+ }
+ }
+ } else {
+ m->insegments = 0;
+ }
+ if (b->convex || !b->poly) {
+ /* Enclose the convex hull with subsegments. */
+ if (b->verbose) {
+ printf(" Enclosing convex hull with segments.\n");
+ }
+ markhull(m, b);
+ }
+}
+
+/** **/
+/** **/
+/********* Segment insertion ends here *********/
+
+/********* Carving out holes and concavities begins here *********/
+/** **/
+/** **/
+
+/*****************************************************************************/
+/* */
+/* infecthull() Virally infect all of the triangles of the convex hull */
+/* that are not protected by subsegments. Where there are */
+/* subsegments, set boundary markers as appropriate. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void infecthull(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void infecthull(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri hulltri;
+ struct otri nexttri;
+ struct otri starttri;
+ struct osub hullsubseg;
+ triangle **deadtriangle;
+ vertex horg, hdest;
+ triangle ptr; /* Temporary variable used by sym(). */
+ subseg sptr; /* Temporary variable used by tspivot(). */
+
+ if (b->verbose) {
+ printf(" Marking concavities (external triangles) for elimination.\n");
+ }
+ /* Find a triangle handle on the hull. */
+ hulltri.tri = m->dummytri;
+ hulltri.orient = 0;
+ symself(hulltri);
+ /* Remember where we started so we know when to stop. */
+ otricopy(hulltri, starttri);
+ /* Go once counterclockwise around the convex hull. */
+ do {
+ /* Ignore triangles that are already infected. */
+ if (!infected(hulltri)) {
+ /* Is the triangle protected by a subsegment? */
+ tspivot(hulltri, hullsubseg);
+ if (hullsubseg.ss == m->dummysub) {
+ /* The triangle is not protected; infect it. */
+ if (!infected(hulltri)) {
+ infect(hulltri);
+ deadtriangle = (triangle **) poolalloc(&m->viri);
+ *deadtriangle = hulltri.tri;
+ }
+ } else {
+ /* The triangle is protected; set boundary markers if appropriate. */
+ if (mark(hullsubseg) == 0) {
+ setmark(hullsubseg, 1);
+ org(hulltri, horg);
+ dest(hulltri, hdest);
+ if (vertexmark(horg) == 0) {
+ setvertexmark(horg, 1);
+ }
+ if (vertexmark(hdest) == 0) {
+ setvertexmark(hdest, 1);
+ }
+ }
+ }
+ }
+ /* To find the next hull edge, go clockwise around the next vertex. */
+ lnextself(hulltri);
+ oprev(hulltri, nexttri);
+ while (nexttri.tri != m->dummytri) {
+ otricopy(nexttri, hulltri);
+ oprev(hulltri, nexttri);
+ }
+ } while (!otriequal(hulltri, starttri));
+}
+
+/*****************************************************************************/
+/* */
+/* plague() Spread the virus from all infected triangles to any neighbors */
+/* not protected by subsegments. Delete all infected triangles. */
+/* */
+/* This is the procedure that actually creates holes and concavities. */
+/* */
+/* This procedure operates in two phases. The first phase identifies all */
+/* the triangles that will die, and marks them as infected. They are */
+/* marked to ensure that each triangle is added to the virus pool only */
+/* once, so the procedure will terminate. */
+/* */
+/* The second phase actually eliminates the infected triangles. It also */
+/* eliminates orphaned vertices. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void plague(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void plague(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri testtri;
+ struct otri neighbor;
+ triangle **virusloop;
+ triangle **deadtriangle;
+ struct osub neighborsubseg;
+ vertex testvertex;
+ vertex norg, ndest;
+ vertex deadorg, deaddest, deadapex;
+ int killorg;
+ triangle ptr; /* Temporary variable used by sym() and onext(). */
+ subseg sptr; /* Temporary variable used by tspivot(). */
+
+ if (b->verbose) {
+ printf(" Marking neighbors of marked triangles.\n");
+ }
+ /* Loop through all the infected triangles, spreading the virus to */
+ /* their neighbors, then to their neighbors' neighbors. */
+ traversalinit(&m->viri);
+ virusloop = (triangle **) traverse(&m->viri);
+ while (virusloop != (triangle **) NULL) {
+ testtri.tri = *virusloop;
+ /* A triangle is marked as infected by messing with one of its pointers */
+ /* to subsegments, setting it to an illegal value. Hence, we have to */
+ /* temporarily uninfect this triangle so that we can examine its */
+ /* adjacent subsegments. */
+ uninfect(testtri);
+ if (b->verbose > 2) {
+ /* Assign the triangle an orientation for convenience in */
+ /* checking its vertices. */
+ testtri.orient = 0;
+ org(testtri, deadorg);
+ dest(testtri, deaddest);
+ apex(testtri, deadapex);
+ printf(" Checking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+ deadorg[0], deadorg[1], deaddest[0], deaddest[1],
+ deadapex[0], deadapex[1]);
+ }
+ /* Check each of the triangle's three neighbors. */
+ for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) {
+ /* Find the neighbor. */
+ sym(testtri, neighbor);
+ /* Check for a subsegment between the triangle and its neighbor. */
+ tspivot(testtri, neighborsubseg);
+ /* Check if the neighbor is nonexistent or already infected. */
+ if ((neighbor.tri == m->dummytri) || infected(neighbor)) {
+ if (neighborsubseg.ss != m->dummysub) {
+ /* There is a subsegment separating the triangle from its */
+ /* neighbor, but both triangles are dying, so the subsegment */
+ /* dies too. */
+ subsegdealloc(m, neighborsubseg.ss);
+ if (neighbor.tri != m->dummytri) {
+ /* Make sure the subsegment doesn't get deallocated again */
+ /* later when the infected neighbor is visited. */
+ uninfect(neighbor);
+ tsdissolve(neighbor);
+ infect(neighbor);
+ }
+ }
+ } else { /* The neighbor exists and is not infected. */
+ if (neighborsubseg.ss == m->dummysub) {
+ /* There is no subsegment protecting the neighbor, so */
+ /* the neighbor becomes infected. */
+ if (b->verbose > 2) {
+ org(neighbor, deadorg);
+ dest(neighbor, deaddest);
+ apex(neighbor, deadapex);
+ printf(
+ " Marking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+ deadorg[0], deadorg[1], deaddest[0], deaddest[1],
+ deadapex[0], deadapex[1]);
+ }
+ infect(neighbor);
+ /* Ensure that the neighbor's neighbors will be infected. */
+ deadtriangle = (triangle **) poolalloc(&m->viri);
+ *deadtriangle = neighbor.tri;
+ } else { /* The neighbor is protected by a subsegment. */
+ /* Remove this triangle from the subsegment. */
+ stdissolve(neighborsubseg);
+ /* The subsegment becomes a boundary. Set markers accordingly. */
+ if (mark(neighborsubseg) == 0) {
+ setmark(neighborsubseg, 1);
+ }
+ org(neighbor, norg);
+ dest(neighbor, ndest);
+ if (vertexmark(norg) == 0) {
+ setvertexmark(norg, 1);
+ }
+ if (vertexmark(ndest) == 0) {
+ setvertexmark(ndest, 1);
+ }
+ }
+ }
+ }
+ /* Remark the triangle as infected, so it doesn't get added to the */
+ /* virus pool again. */
+ infect(testtri);
+ virusloop = (triangle **) traverse(&m->viri);
+ }
+
+ if (b->verbose) {
+ printf(" Deleting marked triangles.\n");
+ }
+
+ traversalinit(&m->viri);
+ virusloop = (triangle **) traverse(&m->viri);
+ while (virusloop != (triangle **) NULL) {
+ testtri.tri = *virusloop;
+
+ /* Check each of the three corners of the triangle for elimination. */
+ /* This is done by walking around each vertex, checking if it is */
+ /* still connected to at least one live triangle. */
+ for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) {
+ org(testtri, testvertex);
+ /* Check if the vertex has already been tested. */
+ if (testvertex != (vertex) NULL) {
+ killorg = 1;
+ /* Mark the corner of the triangle as having been tested. */
+ setorg(testtri, NULL);
+ /* Walk counterclockwise about the vertex. */
+ onext(testtri, neighbor);
+ /* Stop upon reaching a boundary or the starting triangle. */
+ while ((neighbor.tri != m->dummytri) &&
+ (!otriequal(neighbor, testtri))) {
+ if (infected(neighbor)) {
+ /* Mark the corner of this triangle as having been tested. */
+ setorg(neighbor, NULL);
+ } else {
+ /* A live triangle. The vertex survives. */
+ killorg = 0;
+ }
+ /* Walk counterclockwise about the vertex. */
+ onextself(neighbor);
+ }
+ /* If we reached a boundary, we must walk clockwise as well. */
+ if (neighbor.tri == m->dummytri) {
+ /* Walk clockwise about the vertex. */
+ oprev(testtri, neighbor);
+ /* Stop upon reaching a boundary. */
+ while (neighbor.tri != m->dummytri) {
+ if (infected(neighbor)) {
+ /* Mark the corner of this triangle as having been tested. */
+ setorg(neighbor, NULL);
+ } else {
+ /* A live triangle. The vertex survives. */
+ killorg = 0;
+ }
+ /* Walk clockwise about the vertex. */
+ oprevself(neighbor);
+ }
+ }
+ if (killorg) {
+ if (b->verbose > 1) {
+ printf(" Deleting vertex (%.12g, %.12g)\n",
+ testvertex[0], testvertex[1]);
+ }
+ setvertextype(testvertex, UNDEADVERTEX);
+ m->undeads++;
+ }
+ }
+ }
+
+ /* Record changes in the number of boundary edges, and disconnect */
+ /* dead triangles from their neighbors. */
+ for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) {
+ sym(testtri, neighbor);
+ if (neighbor.tri == m->dummytri) {
+ /* There is no neighboring triangle on this edge, so this edge */
+ /* is a boundary edge. This triangle is being deleted, so this */
+ /* boundary edge is deleted. */
+ m->hullsize--;
+ } else {
+ /* Disconnect the triangle from its neighbor. */
+ dissolve(neighbor);
+ /* There is a neighboring triangle on this edge, so this edge */
+ /* becomes a boundary edge when this triangle is deleted. */
+ m->hullsize++;
+ }
+ }
+ /* Return the dead triangle to the pool of triangles. */
+ triangledealloc(m, testtri.tri);
+ virusloop = (triangle **) traverse(&m->viri);
+ }
+ /* Empty the virus pool. */
+ poolrestart(&m->viri);
+}
+
+/*****************************************************************************/
+/* */
+/* regionplague() Spread regional attributes and/or area constraints */
+/* (from a .poly file) throughout the mesh. */
+/* */
+/* This procedure operates in two phases. The first phase spreads an */
+/* attribute and/or an area constraint through a (segment-bounded) region. */
+/* The triangles are marked to ensure that each triangle is added to the */
+/* virus pool only once, so the procedure will terminate. */
+/* */
+/* The second phase uninfects all infected triangles, returning them to */
+/* normal. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void regionplague(struct mesh *m, struct behavior *b,
+ REAL attribute, REAL area)
+#else /* not ANSI_DECLARATORS */
+void regionplague(m, b, attribute, area)
+struct mesh *m;
+struct behavior *b;
+REAL attribute;
+REAL area;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri testtri;
+ struct otri neighbor;
+ triangle **virusloop;
+ triangle **regiontri;
+ struct osub neighborsubseg;
+ vertex regionorg, regiondest, regionapex;
+ triangle ptr; /* Temporary variable used by sym() and onext(). */
+ subseg sptr; /* Temporary variable used by tspivot(). */
+
+ if (b->verbose > 1) {
+ printf(" Marking neighbors of marked triangles.\n");
+ }
+ /* Loop through all the infected triangles, spreading the attribute */
+ /* and/or area constraint to their neighbors, then to their neighbors' */
+ /* neighbors. */
+ traversalinit(&m->viri);
+ virusloop = (triangle **) traverse(&m->viri);
+ while (virusloop != (triangle **) NULL) {
+ testtri.tri = *virusloop;
+ /* A triangle is marked as infected by messing with one of its pointers */
+ /* to subsegments, setting it to an illegal value. Hence, we have to */
+ /* temporarily uninfect this triangle so that we can examine its */
+ /* adjacent subsegments. */
+ uninfect(testtri);
+ if (b->regionattrib) {
+ /* Set an attribute. */
+ setelemattribute(testtri, m->eextras, attribute);
+ }
+ if (b->vararea) {
+ /* Set an area constraint. */
+ setareabound(testtri, area);
+ }
+ if (b->verbose > 2) {
+ /* Assign the triangle an orientation for convenience in */
+ /* checking its vertices. */
+ testtri.orient = 0;
+ org(testtri, regionorg);
+ dest(testtri, regiondest);
+ apex(testtri, regionapex);
+ printf(" Checking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+ regionorg[0], regionorg[1], regiondest[0], regiondest[1],
+ regionapex[0], regionapex[1]);
+ }
+ /* Check each of the triangle's three neighbors. */
+ for (testtri.orient = 0; testtri.orient < 3; testtri.orient++) {
+ /* Find the neighbor. */
+ sym(testtri, neighbor);
+ /* Check for a subsegment between the triangle and its neighbor. */
+ tspivot(testtri, neighborsubseg);
+ /* Make sure the neighbor exists, is not already infected, and */
+ /* isn't protected by a subsegment. */
+ if ((neighbor.tri != m->dummytri) && !infected(neighbor)
+ && (neighborsubseg.ss == m->dummysub)) {
+ if (b->verbose > 2) {
+ org(neighbor, regionorg);
+ dest(neighbor, regiondest);
+ apex(neighbor, regionapex);
+ printf(" Marking (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+ regionorg[0], regionorg[1], regiondest[0], regiondest[1],
+ regionapex[0], regionapex[1]);
+ }
+ /* Infect the neighbor. */
+ infect(neighbor);
+ /* Ensure that the neighbor's neighbors will be infected. */
+ regiontri = (triangle **) poolalloc(&m->viri);
+ *regiontri = neighbor.tri;
+ }
+ }
+ /* Remark the triangle as infected, so it doesn't get added to the */
+ /* virus pool again. */
+ infect(testtri);
+ virusloop = (triangle **) traverse(&m->viri);
+ }
+
+ /* Uninfect all triangles. */
+ if (b->verbose > 1) {
+ printf(" Unmarking marked triangles.\n");
+ }
+ traversalinit(&m->viri);
+ virusloop = (triangle **) traverse(&m->viri);
+ while (virusloop != (triangle **) NULL) {
+ testtri.tri = *virusloop;
+ uninfect(testtri);
+ virusloop = (triangle **) traverse(&m->viri);
+ }
+ /* Empty the virus pool. */
+ poolrestart(&m->viri);
+}
+
+/*****************************************************************************/
+/* */
+/* carveholes() Find the holes and infect them. Find the area */
+/* constraints and infect them. Infect the convex hull. */
+/* Spread the infection and kill triangles. Spread the */
+/* area constraints. */
+/* */
+/* This routine mainly calls other routines to carry out all these */
+/* functions. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void carveholes(struct mesh *m, struct behavior *b, REAL *holelist, int holes,
+ REAL *regionlist, int regions)
+#else /* not ANSI_DECLARATORS */
+void carveholes(m, b, holelist, holes, regionlist, regions)
+struct mesh *m;
+struct behavior *b;
+REAL *holelist;
+int holes;
+REAL *regionlist;
+int regions;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri searchtri;
+ struct otri triangleloop;
+ struct otri *regiontris;
+ triangle **holetri;
+ triangle **regiontri;
+ vertex searchorg, searchdest;
+ enum locateresult intersect;
+ int i;
+ triangle ptr; /* Temporary variable used by sym(). */
+
+ if (!(b->quiet || (b->noholes && b->convex))) {
+ printf("Removing unwanted triangles.\n");
+ if (b->verbose && (holes > 0)) {
+ printf(" Marking holes for elimination.\n");
+ }
+ }
+
+ if (regions > 0) {
+ /* Allocate storage for the triangles in which region points fall. */
+ regiontris = (struct otri *) trimalloc(regions *
+ (int) sizeof(struct otri));
+ } else {
+ regiontris = (struct otri *) NULL;
+ }
+
+ if (((holes > 0) && !b->noholes) || !b->convex || (regions > 0)) {
+ /* Initialize a pool of viri to be used for holes, concavities, */
+ /* regional attributes, and/or regional area constraints. */
+ poolinit(&m->viri, sizeof(triangle *), VIRUSPERBLOCK, VIRUSPERBLOCK, 0);
+ }
+
+ if (!b->convex) {
+ /* Mark as infected any unprotected triangles on the boundary. */
+ /* This is one way by which concavities are created. */
+ infecthull(m, b);
+ }
+
+ if ((holes > 0) && !b->noholes) {
+ /* Infect each triangle in which a hole lies. */
+ for (i = 0; i < 2 * holes; i += 2) {
+ /* Ignore holes that aren't within the bounds of the mesh. */
+ if ((holelist[i] >= m->xmin) && (holelist[i] <= m->xmax)
+ && (holelist[i + 1] >= m->ymin) && (holelist[i + 1] <= m->ymax)) {
+ /* Start searching from some triangle on the outer boundary. */
+ searchtri.tri = m->dummytri;
+ searchtri.orient = 0;
+ symself(searchtri);
+ /* Ensure that the hole is to the left of this boundary edge; */
+ /* otherwise, locate() will falsely report that the hole */
+ /* falls within the starting triangle. */
+ org(searchtri, searchorg);
+ dest(searchtri, searchdest);
+ if (counterclockwise(m, b, searchorg, searchdest, &holelist[i]) >
+ 0.0) {
+ /* Find a triangle that contains the hole. */
+ intersect = locate(m, b, &holelist[i], &searchtri);
+ if ((intersect != OUTSIDE) && (!infected(searchtri))) {
+ /* Infect the triangle. This is done by marking the triangle */
+ /* as infected and including the triangle in the virus pool. */
+ infect(searchtri);
+ holetri = (triangle **) poolalloc(&m->viri);
+ *holetri = searchtri.tri;
+ }
+ }
+ }
+ }
+ }
+
+ /* Now, we have to find all the regions BEFORE we carve the holes, because */
+ /* locate() won't work when the triangulation is no longer convex. */
+ /* (Incidentally, this is the reason why regional attributes and area */
+ /* constraints can't be used when refining a preexisting mesh, which */
+ /* might not be convex; they can only be used with a freshly */
+ /* triangulated PSLG.) */
+ if (regions > 0) {
+ /* Find the starting triangle for each region. */
+ for (i = 0; i < regions; i++) {
+ regiontris[i].tri = m->dummytri;
+ /* Ignore region points that aren't within the bounds of the mesh. */
+ if ((regionlist[4 * i] >= m->xmin) && (regionlist[4 * i] <= m->xmax) &&
+ (regionlist[4 * i + 1] >= m->ymin) &&
+ (regionlist[4 * i + 1] <= m->ymax)) {
+ /* Start searching from some triangle on the outer boundary. */
+ searchtri.tri = m->dummytri;
+ searchtri.orient = 0;
+ symself(searchtri);
+ /* Ensure that the region point is to the left of this boundary */
+ /* edge; otherwise, locate() will falsely report that the */
+ /* region point falls within the starting triangle. */
+ org(searchtri, searchorg);
+ dest(searchtri, searchdest);
+ if (counterclockwise(m, b, searchorg, searchdest, ®ionlist[4 * i]) >
+ 0.0) {
+ /* Find a triangle that contains the region point. */
+ intersect = locate(m, b, ®ionlist[4 * i], &searchtri);
+ if ((intersect != OUTSIDE) && (!infected(searchtri))) {
+ /* Record the triangle for processing after the */
+ /* holes have been carved. */
+ otricopy(searchtri, regiontris[i]);
+ }
+ }
+ }
+ }
+ }
+
+ if (m->viri.items > 0) {
+ /* Carve the holes and concavities. */
+ plague(m, b);
+ }
+ /* The virus pool should be empty now. */
+
+ if (regions > 0) {
+ if (!b->quiet) {
+ if (b->regionattrib) {
+ if (b->vararea) {
+ printf("Spreading regional attributes and area constraints.\n");
+ } else {
+ printf("Spreading regional attributes.\n");
+ }
+ } else {
+ printf("Spreading regional area constraints.\n");
+ }
+ }
+ if (b->regionattrib && !b->refine) {
+ /* Assign every triangle a regional attribute of zero. */
+ traversalinit(&m->triangles);
+ triangleloop.orient = 0;
+ triangleloop.tri = triangletraverse(m);
+ while (triangleloop.tri != (triangle *) NULL) {
+ setelemattribute(triangleloop, m->eextras, 0.0);
+ triangleloop.tri = triangletraverse(m);
+ }
+ }
+ for (i = 0; i < regions; i++) {
+ if (regiontris[i].tri != m->dummytri) {
+ /* Make sure the triangle under consideration still exists. */
+ /* It may have been eaten by the virus. */
+ if (!deadtri(regiontris[i].tri)) {
+ /* Put one triangle in the virus pool. */
+ infect(regiontris[i]);
+ regiontri = (triangle **) poolalloc(&m->viri);
+ *regiontri = regiontris[i].tri;
+ /* Apply one region's attribute and/or area constraint. */
+ regionplague(m, b, regionlist[4 * i + 2], regionlist[4 * i + 3]);
+ /* The virus pool should be empty now. */
+ }
+ }
+ }
+ if (b->regionattrib && !b->refine) {
+ /* Note the fact that each triangle has an additional attribute. */
+ m->eextras++;
+ }
+ }
+
+ /* Free up memory. */
+ if (((holes > 0) && !b->noholes) || !b->convex || (regions > 0)) {
+ pooldeinit(&m->viri);
+ }
+ if (regions > 0) {
+ trifree((VOID *) regiontris);
+ }
+}
+
+/** **/
+/** **/
+/********* Carving out holes and concavities ends here *********/
+
+/********* Mesh quality maintenance begins here *********/
+/** **/
+/** **/
+
+/*****************************************************************************/
+/* */
+/* tallyencs() Traverse the entire list of subsegments, and check each */
+/* to see if it is encroached. If so, add it to the list. */
+/* */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void tallyencs(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void tallyencs(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct osub subsegloop;
+ int dummy;
+
+ traversalinit(&m->subsegs);
+ subsegloop.ssorient = 0;
+ subsegloop.ss = subsegtraverse(m);
+ while (subsegloop.ss != (subseg *) NULL) {
+ /* If the segment is encroached, add it to the list. */
+ dummy = checkseg4encroach(m, b, &subsegloop);
+ subsegloop.ss = subsegtraverse(m);
+ }
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/* */
+/* precisionerror() Print an error message for precision problems. */
+/* */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+void precisionerror()
+{
+ printf("Try increasing the area criterion and/or reducing the minimum\n");
+ printf(" allowable angle so that tiny triangles are not created.\n");
+#ifdef SINGLE
+ printf("Alternatively, try recompiling me with double precision\n");
+ printf(" arithmetic (by removing \"#define SINGLE\" from the\n");
+ printf(" source file or \"-DSINGLE\" from the makefile).\n");
+#endif /* SINGLE */
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/* */
+/* splitencsegs() Split all the encroached subsegments. */
+/* */
+/* Each encroached subsegment is repaired by splitting it - inserting a */
+/* vertex at or near its midpoint. Newly inserted vertices may encroach */
+/* upon other subsegments; these are also repaired. */
+/* */
+/* `triflaws' is a flag that specifies whether one should take note of new */
+/* bad triangles that result from inserting vertices to repair encroached */
+/* subsegments. */
+/* */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void splitencsegs(struct mesh *m, struct behavior *b, int triflaws)
+#else /* not ANSI_DECLARATORS */
+void splitencsegs(m, b, triflaws)
+struct mesh *m;
+struct behavior *b;
+int triflaws;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri enctri;
+ struct otri testtri;
+ struct osub testsh;
+ struct osub currentenc;
+ struct badsubseg *encloop;
+ vertex eorg, edest, eapex;
+ vertex newvertex;
+ enum insertvertexresult success;
+ REAL segmentlength, nearestpoweroftwo;
+ REAL split;
+ REAL multiplier, divisor;
+ int acuteorg, acuteorg2, acutedest, acutedest2;
+ int dummy;
+ int i;
+ triangle ptr; /* Temporary variable used by stpivot(). */
+ subseg sptr; /* Temporary variable used by snext(). */
+
+ /* Note that steinerleft == -1 if an unlimited number */
+ /* of Steiner points is allowed. */
+ while ((m->badsubsegs.items > 0) && (m->steinerleft != 0)) {
+ traversalinit(&m->badsubsegs);
+ encloop = badsubsegtraverse(m);
+ while ((encloop != (struct badsubseg *) NULL) && (m->steinerleft != 0)) {
+ sdecode(encloop->encsubseg, currentenc);
+ sorg(currentenc, eorg);
+ sdest(currentenc, edest);
+ /* Make sure that this segment is still the same segment it was */
+ /* when it was determined to be encroached. If the segment was */
+ /* enqueued multiple times (because several newly inserted */
+ /* vertices encroached it), it may have already been split. */
+ if (!deadsubseg(currentenc.ss) &&
+ (eorg == encloop->subsegorg) && (edest == encloop->subsegdest)) {
+ /* To decide where to split a segment, we need to know if the */
+ /* segment shares an endpoint with an adjacent segment. */
+ /* The concern is that, if we simply split every encroached */
+ /* segment in its center, two adjacent segments with a small */
+ /* angle between them might lead to an infinite loop; each */
+ /* vertex added to split one segment will encroach upon the */
+ /* other segment, which must then be split with a vertex that */
+ /* will encroach upon the first segment, and so on forever. */
+ /* To avoid this, imagine a set of concentric circles, whose */
+ /* radii are powers of two, about each segment endpoint. */
+ /* These concentric circles determine where the segment is */
+ /* split. (If both endpoints are shared with adjacent */
+ /* segments, split the segment in the middle, and apply the */
+ /* concentric circles for later splittings.) */
+
+ /* Is the origin shared with another segment? */
+ stpivot(currentenc, enctri);
+ lnext(enctri, testtri);
+ tspivot(testtri, testsh);
+ acuteorg = testsh.ss != m->dummysub;
+ /* Is the destination shared with another segment? */
+ lnextself(testtri);
+ tspivot(testtri, testsh);
+ acutedest = testsh.ss != m->dummysub;
+
+ /* If we're using Chew's algorithm (rather than Ruppert's) */
+ /* to define encroachment, delete free vertices from the */
+ /* subsegment's diametral circle. */
+ if (!b->conformdel && !acuteorg && !acutedest) {
+ apex(enctri, eapex);
+ while ((vertextype(eapex) == FREEVERTEX) &&
+ ((eorg[0] - eapex[0]) * (edest[0] - eapex[0]) +
+ (eorg[1] - eapex[1]) * (edest[1] - eapex[1]) < 0.0)) {
+ deletevertex(m, b, &testtri);
+ stpivot(currentenc, enctri);
+ apex(enctri, eapex);
+ lprev(enctri, testtri);
+ }
+ }
+
+ /* Now, check the other side of the segment, if there's a triangle */
+ /* there. */
+ sym(enctri, testtri);
+ if (testtri.tri != m->dummytri) {
+ /* Is the destination shared with another segment? */
+ lnextself(testtri);
+ tspivot(testtri, testsh);
+ acutedest2 = testsh.ss != m->dummysub;
+ acutedest = acutedest || acutedest2;
+ /* Is the origin shared with another segment? */
+ lnextself(testtri);
+ tspivot(testtri, testsh);
+ acuteorg2 = testsh.ss != m->dummysub;
+ acuteorg = acuteorg || acuteorg2;
+
+ /* Delete free vertices from the subsegment's diametral circle. */
+ if (!b->conformdel && !acuteorg2 && !acutedest2) {
+ org(testtri, eapex);
+ while ((vertextype(eapex) == FREEVERTEX) &&
+ ((eorg[0] - eapex[0]) * (edest[0] - eapex[0]) +
+ (eorg[1] - eapex[1]) * (edest[1] - eapex[1]) < 0.0)) {
+ deletevertex(m, b, &testtri);
+ sym(enctri, testtri);
+ apex(testtri, eapex);
+ lprevself(testtri);
+ }
+ }
+ }
+
+ /* Use the concentric circles if exactly one endpoint is shared */
+ /* with another adjacent segment. */
+ if (acuteorg || acutedest) {
+ segmentlength = sqrt((edest[0] - eorg[0]) * (edest[0] - eorg[0]) +
+ (edest[1] - eorg[1]) * (edest[1] - eorg[1]));
+ /* Find the power of two that most evenly splits the segment. */
+ /* The worst case is a 2:1 ratio between subsegment lengths. */
+ nearestpoweroftwo = 1.0;
+ while (segmentlength > 3.0 * nearestpoweroftwo) {
+ nearestpoweroftwo *= 2.0;
+ }
+ while (segmentlength < 1.5 * nearestpoweroftwo) {
+ nearestpoweroftwo *= 0.5;
+ }
+ /* Where do we split the segment? */
+ split = nearestpoweroftwo / segmentlength;
+ if (acutedest) {
+ split = 1.0 - split;
+ }
+ } else {
+ /* If we're not worried about adjacent segments, split */
+ /* this segment in the middle. */
+ split = 0.5;
+ }
+
+ /* Create the new vertex. */
+ newvertex = (vertex) poolalloc(&m->vertices);
+ /* Interpolate its coordinate and attributes. */
+ for (i = 0; i < 2 + m->nextras; i++) {
+ newvertex[i] = eorg[i] + split * (edest[i] - eorg[i]);
+ }
+
+ if (!b->noexact) {
+ /* Roundoff in the above calculation may yield a `newvertex' */
+ /* that is not precisely collinear with `eorg' and `edest'. */
+ /* Improve collinearity by one step of iterative refinement. */
+ multiplier = counterclockwise(m, b, eorg, edest, newvertex);
+ divisor = ((eorg[0] - edest[0]) * (eorg[0] - edest[0]) +
+ (eorg[1] - edest[1]) * (eorg[1] - edest[1]));
+ if ((multiplier != 0.0) && (divisor != 0.0)) {
+ multiplier = multiplier / divisor;
+ /* Watch out for NANs. */
+ if (multiplier == multiplier) {
+ newvertex[0] += multiplier * (edest[1] - eorg[1]);
+ newvertex[1] += multiplier * (eorg[0] - edest[0]);
+ }
+ }
+ }
+
+ setvertexmark(newvertex, mark(currentenc));
+ setvertextype(newvertex, SEGMENTVERTEX);
+ if (b->verbose > 1) {
+ printf(
+ " Splitting subsegment (%.12g, %.12g) (%.12g, %.12g) at (%.12g, %.12g).\n",
+ eorg[0], eorg[1], edest[0], edest[1],
+ newvertex[0], newvertex[1]);
+ }
+ /* Check whether the new vertex lies on an endpoint. */
+ if (((newvertex[0] == eorg[0]) && (newvertex[1] == eorg[1])) ||
+ ((newvertex[0] == edest[0]) && (newvertex[1] == edest[1]))) {
+ printf("Error: Ran out of precision at (%.12g, %.12g).\n",
+ newvertex[0], newvertex[1]);
+ printf("I attempted to split a segment to a smaller size than\n");
+ printf(" can be accommodated by the finite precision of\n");
+ printf(" floating point arithmetic.\n");
+ precisionerror();
+ triexit(1);
+ }
+ /* Insert the splitting vertex. This should always succeed. */
+ success = insertvertex(m, b, newvertex, &enctri, ¤tenc,
+ 1, triflaws);
+ if ((success != SUCCESSFULVERTEX) && (success != ENCROACHINGVERTEX)) {
+ printf("Internal error in splitencsegs():\n");
+ printf(" Failure to split a segment.\n");
+ internalerror();
+ }
+ if (m->steinerleft > 0) {
+ m->steinerleft--;
+ }
+ /* Check the two new subsegments to see if they're encroached. */
+ dummy = checkseg4encroach(m, b, ¤tenc);
+ snextself(currentenc);
+ dummy = checkseg4encroach(m, b, ¤tenc);
+ }
+
+ badsubsegdealloc(m, encloop);
+ encloop = badsubsegtraverse(m);
+ }
+ }
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/* */
+/* tallyfaces() Test every triangle in the mesh for quality measures. */
+/* */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void tallyfaces(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void tallyfaces(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri triangleloop;
+
+ if (b->verbose) {
+ printf(" Making a list of bad triangles.\n");
+ }
+ traversalinit(&m->triangles);
+ triangleloop.orient = 0;
+ triangleloop.tri = triangletraverse(m);
+ while (triangleloop.tri != (triangle *) NULL) {
+ /* If the triangle is bad, enqueue it. */
+ testtriangle(m, b, &triangleloop);
+ triangleloop.tri = triangletraverse(m);
+ }
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/* */
+/* splittriangle() Inserts a vertex at the circumcenter of a triangle. */
+/* Deletes the newly inserted vertex if it encroaches */
+/* upon a segment. */
+/* */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void splittriangle(struct mesh *m, struct behavior *b,
+ struct badtriang *badtri)
+#else /* not ANSI_DECLARATORS */
+void splittriangle(m, b, badtri)
+struct mesh *m;
+struct behavior *b;
+struct badtriang *badtri;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri badotri;
+ vertex borg, bdest, bapex;
+ vertex newvertex;
+ REAL xi, eta;
+ enum insertvertexresult success;
+ int errorflag;
+ int i;
+
+ decode(badtri->poortri, badotri);
+ org(badotri, borg);
+ dest(badotri, bdest);
+ apex(badotri, bapex);
+ /* Make sure that this triangle is still the same triangle it was */
+ /* when it was tested and determined to be of bad quality. */
+ /* Subsequent transformations may have made it a different triangle. */
+ if (!deadtri(badotri.tri) && (borg == badtri->triangorg) &&
+ (bdest == badtri->triangdest) && (bapex == badtri->triangapex)) {
+ if (b->verbose > 1) {
+ printf(" Splitting this triangle at its circumcenter:\n");
+ printf(" (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n", borg[0],
+ borg[1], bdest[0], bdest[1], bapex[0], bapex[1]);
+ }
+
+ errorflag = 0;
+ /* Create a new vertex at the triangle's circumcenter. */
+ newvertex = (vertex) poolalloc(&m->vertices);
+ findcircumcenter(m, b, borg, bdest, bapex, newvertex, &xi, &eta, 1);
+
+ /* Check whether the new vertex lies on a triangle vertex. */
+ if (((newvertex[0] == borg[0]) && (newvertex[1] == borg[1])) ||
+ ((newvertex[0] == bdest[0]) && (newvertex[1] == bdest[1])) ||
+ ((newvertex[0] == bapex[0]) && (newvertex[1] == bapex[1]))) {
+ if (!b->quiet) {
+ printf(
+ "Warning: New vertex (%.12g, %.12g) falls on existing vertex.\n",
+ newvertex[0], newvertex[1]);
+ errorflag = 1;
+ }
+ vertexdealloc(m, newvertex);
+ } else {
+ for (i = 2; i < 2 + m->nextras; i++) {
+ /* Interpolate the vertex attributes at the circumcenter. */
+ newvertex[i] = borg[i] + xi * (bdest[i] - borg[i])
+ + eta * (bapex[i] - borg[i]);
+ }
+ /* The new vertex must be in the interior, and therefore is a */
+ /* free vertex with a marker of zero. */
+ setvertexmark(newvertex, 0);
+ setvertextype(newvertex, FREEVERTEX);
+
+ /* Ensure that the handle `badotri' does not represent the longest */
+ /* edge of the triangle. This ensures that the circumcenter must */
+ /* fall to the left of this edge, so point location will work. */
+ /* (If the angle org-apex-dest exceeds 90 degrees, then the */
+ /* circumcenter lies outside the org-dest edge, and eta is */
+ /* negative. Roundoff error might prevent eta from being */
+ /* negative when it should be, so I test eta against xi.) */
+ if (eta < xi) {
+ lprevself(badotri);
+ }
+
+ /* Insert the circumcenter, searching from the edge of the triangle, */
+ /* and maintain the Delaunay property of the triangulation. */
+ success = insertvertex(m, b, newvertex, &badotri, (struct osub *) NULL,
+ 1, 1);
+ if (success == SUCCESSFULVERTEX) {
+ if (m->steinerleft > 0) {
+ m->steinerleft--;
+ }
+ } else if (success == ENCROACHINGVERTEX) {
+ /* If the newly inserted vertex encroaches upon a subsegment, */
+ /* delete the new vertex. */
+ undovertex(m, b);
+ if (b->verbose > 1) {
+ printf(" Rejecting (%.12g, %.12g).\n", newvertex[0], newvertex[1]);
+ }
+ vertexdealloc(m, newvertex);
+ } else if (success == VIOLATINGVERTEX) {
+ /* Failed to insert the new vertex, but some subsegment was */
+ /* marked as being encroached. */
+ vertexdealloc(m, newvertex);
+ } else { /* success == DUPLICATEVERTEX */
+ /* Couldn't insert the new vertex because a vertex is already there. */
+ if (!b->quiet) {
+ printf(
+ "Warning: New vertex (%.12g, %.12g) falls on existing vertex.\n",
+ newvertex[0], newvertex[1]);
+ errorflag = 1;
+ }
+ vertexdealloc(m, newvertex);
+ }
+ }
+ if (errorflag) {
+ if (b->verbose) {
+ printf(" The new vertex is at the circumcenter of triangle\n");
+ printf(" (%.12g, %.12g) (%.12g, %.12g) (%.12g, %.12g)\n",
+ borg[0], borg[1], bdest[0], bdest[1], bapex[0], bapex[1]);
+ }
+ printf("This probably means that I am trying to refine triangles\n");
+ printf(" to a smaller size than can be accommodated by the finite\n");
+ printf(" precision of floating point arithmetic. (You can be\n");
+ printf(" sure of this if I fail to terminate.)\n");
+ precisionerror();
+ }
+ }
+}
+
+#endif /* not CDT_ONLY */
+
+/*****************************************************************************/
+/* */
+/* enforcequality() Remove all the encroached subsegments and bad */
+/* triangles from the triangulation. */
+/* */
+/*****************************************************************************/
+
+#ifndef CDT_ONLY
+
+#ifdef ANSI_DECLARATORS
+void enforcequality(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void enforcequality(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct badtriang *badtri;
+ int i;
+
+ if (!b->quiet) {
+ printf("Adding Steiner points to enforce quality.\n");
+ }
+ /* Initialize the pool of encroached subsegments. */
+ poolinit(&m->badsubsegs, sizeof(struct badsubseg), BADSUBSEGPERBLOCK,
+ BADSUBSEGPERBLOCK, 0);
+ if (b->verbose) {
+ printf(" Looking for encroached subsegments.\n");
+ }
+ /* Test all segments to see if they're encroached. */
+ tallyencs(m, b);
+ if (b->verbose && (m->badsubsegs.items > 0)) {
+ printf(" Splitting encroached subsegments.\n");
+ }
+ /* Fix encroached subsegments without noting bad triangles. */
+ splitencsegs(m, b, 0);
+ /* At this point, if we haven't run out of Steiner points, the */
+ /* triangulation should be (conforming) Delaunay. */
+
+ /* Next, we worry about enforcing triangle quality. */
+ if ((b->minangle > 0.0) || b->vararea || b->fixedarea || b->usertest) {
+ /* Initialize the pool of bad triangles. */
+ poolinit(&m->badtriangles, sizeof(struct badtriang), BADTRIPERBLOCK,
+ BADTRIPERBLOCK, 0);
+ /* Initialize the queues of bad triangles. */
+ for (i = 0; i < 4096; i++) {
+ m->queuefront[i] = (struct badtriang *) NULL;
+ }
+ m->firstnonemptyq = -1;
+ /* Test all triangles to see if they're bad. */
+ tallyfaces(m, b);
+ /* Initialize the pool of recently flipped triangles. */
+ poolinit(&m->flipstackers, sizeof(struct flipstacker), FLIPSTACKERPERBLOCK,
+ FLIPSTACKERPERBLOCK, 0);
+ m->checkquality = 1;
+ if (b->verbose) {
+ printf(" Splitting bad triangles.\n");
+ }
+ while ((m->badtriangles.items > 0) && (m->steinerleft != 0)) {
+ /* Fix one bad triangle by inserting a vertex at its circumcenter. */
+ badtri = dequeuebadtriang(m);
+ splittriangle(m, b, badtri);
+ if (m->badsubsegs.items > 0) {
+ /* Put bad triangle back in queue for another try later. */
+ enqueuebadtriang(m, b, badtri);
+ /* Fix any encroached subsegments that resulted. */
+ /* Record any new bad triangles that result. */
+ splitencsegs(m, b, 1);
+ } else {
+ /* Return the bad triangle to the pool. */
+ pooldealloc(&m->badtriangles, (VOID *) badtri);
+ }
+ }
+ }
+ /* At this point, if the "-D" switch was selected and we haven't run out */
+ /* of Steiner points, the triangulation should be (conforming) Delaunay */
+ /* and have no low-quality triangles. */
+
+ /* Might we have run out of Steiner points too soon? */
+ if (!b->quiet && b->conformdel && (m->badsubsegs.items > 0) &&
+ (m->steinerleft == 0)) {
+ printf("\nWarning: I ran out of Steiner points, but the mesh has\n");
+ if (m->badsubsegs.items == 1) {
+ printf(" one encroached subsegment, and therefore might not be truly\n"
+ );
+ } else {
+ printf(" %ld encroached subsegments, and therefore might not be truly\n"
+ , m->badsubsegs.items);
+ }
+ printf(" Delaunay. If the Delaunay property is important to you,\n");
+ printf(" try increasing the number of Steiner points (controlled by\n");
+ printf(" the -S switch) slightly and try again.\n\n");
+ }
+}
+
+#endif /* not CDT_ONLY */
+
+/** **/
+/** **/
+/********* Mesh quality maintenance ends here *********/
+
+/*****************************************************************************/
+/* */
+/* highorder() Create extra nodes for quadratic subparametric elements. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void highorder(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void highorder(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri triangleloop, trisym;
+ struct osub checkmark;
+ vertex newvertex;
+ vertex torg, tdest;
+ int i;
+ triangle ptr; /* Temporary variable used by sym(). */
+ subseg sptr; /* Temporary variable used by tspivot(). */
+
+ if (!b->quiet) {
+ printf("Adding vertices for second-order triangles.\n");
+ }
+ /* The following line ensures that dead items in the pool of nodes */
+ /* cannot be allocated for the extra nodes associated with high */
+ /* order elements. This ensures that the primary nodes (at the */
+ /* corners of elements) will occur earlier in the output files, and */
+ /* have lower indices, than the extra nodes. */
+ m->vertices.deaditemstack = (VOID *) NULL;
+
+ traversalinit(&m->triangles);
+ triangleloop.tri = triangletraverse(m);
+ /* To loop over the set of edges, loop over all triangles, and look at */
+ /* the three edges of each triangle. If there isn't another triangle */
+ /* adjacent to the edge, operate on the edge. If there is another */
+ /* adjacent triangle, operate on the edge only if the current triangle */
+ /* has a smaller pointer than its neighbor. This way, each edge is */
+ /* considered only once. */
+ while (triangleloop.tri != (triangle *) NULL) {
+ for (triangleloop.orient = 0; triangleloop.orient < 3;
+ triangleloop.orient++) {
+ sym(triangleloop, trisym);
+ if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) {
+ org(triangleloop, torg);
+ dest(triangleloop, tdest);
+ /* Create a new node in the middle of the edge. Interpolate */
+ /* its attributes. */
+ newvertex = (vertex) poolalloc(&m->vertices);
+ for (i = 0; i < 2 + m->nextras; i++) {
+ newvertex[i] = 0.5 * (torg[i] + tdest[i]);
+ }
+ /* Set the new node's marker to zero or one, depending on */
+ /* whether it lies on a boundary. */
+ setvertexmark(newvertex, trisym.tri == m->dummytri);
+ setvertextype(newvertex,
+ trisym.tri == m->dummytri ? FREEVERTEX : SEGMENTVERTEX);
+ if (b->usesegments) {
+ tspivot(triangleloop, checkmark);
+ /* If this edge is a segment, transfer the marker to the new node. */
+ if (checkmark.ss != m->dummysub) {
+ setvertexmark(newvertex, mark(checkmark));
+ setvertextype(newvertex, SEGMENTVERTEX);
+ }
+ }
+ if (b->verbose > 1) {
+ printf(" Creating (%.12g, %.12g).\n", newvertex[0], newvertex[1]);
+ }
+ /* Record the new node in the (one or two) adjacent elements. */
+ triangleloop.tri[m->highorderindex + triangleloop.orient] =
+ (triangle) newvertex;
+ if (trisym.tri != m->dummytri) {
+ trisym.tri[m->highorderindex + trisym.orient] = (triangle) newvertex;
+ }
+ }
+ }
+ triangleloop.tri = triangletraverse(m);
+ }
+}
+
+/********* File I/O routines begin here *********/
+/** **/
+/** **/
+
+/*****************************************************************************/
+/* */
+/* readline() Read a nonempty line from a file. */
+/* */
+/* A line is considered "nonempty" if it contains something that looks like */
+/* a number. Comments (prefaced by `#') are ignored. */
+/* */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+char *readline(char *string, FILE *infile, char *infilename)
+#else /* not ANSI_DECLARATORS */
+char *readline(string, infile, infilename)
+char *string;
+FILE *infile;
+char *infilename;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ char *result;
+
+ /* Search for something that looks like a number. */
+ do {
+ result = fgets(string, INPUTLINESIZE, infile);
+ if (result == (char *) NULL) {
+ printf(" Error: Unexpected end of file in %s.\n", infilename);
+ triexit(1);
+ }
+ /* Skip anything that doesn't look like a number, a comment, */
+ /* or the end of a line. */
+ while ((*result != '\0') && (*result != '#')
+ && (*result != '.') && (*result != '+') && (*result != '-')
+ && ((*result < '0') || (*result > '9'))) {
+ result++;
+ }
+ /* If it's a comment or end of line, read another line and try again. */
+ } while ((*result == '#') || (*result == '\0'));
+ return result;
+}
+
+#endif /* not TRILIBRARY */
+
+/*****************************************************************************/
+/* */
+/* findfield() Find the next field of a string. */
+/* */
+/* Jumps past the current field by searching for whitespace, then jumps */
+/* past the whitespace to find the next field. */
+/* */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+char *findfield(char *string)
+#else /* not ANSI_DECLARATORS */
+char *findfield(string)
+char *string;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ char *result;
+
+ result = string;
+ /* Skip the current field. Stop upon reaching whitespace. */
+ while ((*result != '\0') && (*result != '#')
+ && (*result != ' ') && (*result != '\t')) {
+ result++;
+ }
+ /* Now skip the whitespace and anything else that doesn't look like a */
+ /* number, a comment, or the end of a line. */
+ while ((*result != '\0') && (*result != '#')
+ && (*result != '.') && (*result != '+') && (*result != '-')
+ && ((*result < '0') || (*result > '9'))) {
+ result++;
+ }
+ /* Check for a comment (prefixed with `#'). */
+ if (*result == '#') {
+ *result = '\0';
+ }
+ return result;
+}
+
+#endif /* not TRILIBRARY */
+
+/*****************************************************************************/
+/* */
+/* readnodes() Read the vertices from a file, which may be a .node or */
+/* .poly file. */
+/* */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void readnodes(struct mesh *m, struct behavior *b, char *nodefilename,
+ char *polyfilename, FILE **polyfile)
+#else /* not ANSI_DECLARATORS */
+void readnodes(m, b, nodefilename, polyfilename, polyfile)
+struct mesh *m;
+struct behavior *b;
+char *nodefilename;
+char *polyfilename;
+FILE **polyfile;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ FILE *infile;
+ vertex vertexloop;
+ char inputline[INPUTLINESIZE];
+ char *stringptr;
+ char *infilename;
+ REAL x, y;
+ int firstnode;
+ int nodemarkers;
+ int currentmarker;
+ int i, j;
+
+ if (b->poly) {
+ /* Read the vertices from a .poly file. */
+ if (!b->quiet) {
+ printf("Opening %s.\n", polyfilename);
+ }
+ *polyfile = fopen(polyfilename, "r");
+ if (*polyfile == (FILE *) NULL) {
+ printf(" Error: Cannot access file %s.\n", polyfilename);
+ triexit(1);
+ }
+ /* Read number of vertices, number of dimensions, number of vertex */
+ /* attributes, and number of boundary markers. */
+ stringptr = readline(inputline, *polyfile, polyfilename);
+ m->invertices = (int) strtol(stringptr, &stringptr, 0);
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ m->mesh_dim = 2;
+ } else {
+ m->mesh_dim = (int) strtol(stringptr, &stringptr, 0);
+ }
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ m->nextras = 0;
+ } else {
+ m->nextras = (int) strtol(stringptr, &stringptr, 0);
+ }
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ nodemarkers = 0;
+ } else {
+ nodemarkers = (int) strtol(stringptr, &stringptr, 0);
+ }
+ if (m->invertices > 0) {
+ infile = *polyfile;
+ infilename = polyfilename;
+ m->readnodefile = 0;
+ } else {
+ /* If the .poly file claims there are zero vertices, that means that */
+ /* the vertices should be read from a separate .node file. */
+ m->readnodefile = 1;
+ infilename = nodefilename;
+ }
+ } else {
+ m->readnodefile = 1;
+ infilename = nodefilename;
+ *polyfile = (FILE *) NULL;
+ }
+
+ if (m->readnodefile) {
+ /* Read the vertices from a .node file. */
+ if (!b->quiet) {
+ printf("Opening %s.\n", nodefilename);
+ }
+ infile = fopen(nodefilename, "r");
+ if (infile == (FILE *) NULL) {
+ printf(" Error: Cannot access file %s.\n", nodefilename);
+ triexit(1);
+ }
+ /* Read number of vertices, number of dimensions, number of vertex */
+ /* attributes, and number of boundary markers. */
+ stringptr = readline(inputline, infile, nodefilename);
+ m->invertices = (int) strtol(stringptr, &stringptr, 0);
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ m->mesh_dim = 2;
+ } else {
+ m->mesh_dim = (int) strtol(stringptr, &stringptr, 0);
+ }
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ m->nextras = 0;
+ } else {
+ m->nextras = (int) strtol(stringptr, &stringptr, 0);
+ }
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ nodemarkers = 0;
+ } else {
+ nodemarkers = (int) strtol(stringptr, &stringptr, 0);
+ }
+ }
+
+ if (m->invertices < 3) {
+ printf("Error: Input must have at least three input vertices.\n");
+ triexit(1);
+ }
+ if (m->mesh_dim != 2) {
+ printf("Error: Triangle only works with two-dimensional meshes.\n");
+ triexit(1);
+ }
+ if (m->nextras == 0) {
+ b->weighted = 0;
+ }
+
+ initializevertexpool(m, b);
+
+ /* Read the vertices. */
+ for (i = 0; i < m->invertices; i++) {
+ vertexloop = (vertex) poolalloc(&m->vertices);
+ stringptr = readline(inputline, infile, infilename);
+ if (i == 0) {
+ firstnode = (int) strtol(stringptr, &stringptr, 0);
+ if ((firstnode == 0) || (firstnode == 1)) {
+ b->firstnumber = firstnode;
+ }
+ }
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ printf("Error: Vertex %d has no x coordinate.\n", b->firstnumber + i);
+ triexit(1);
+ }
+ x = (REAL) strtod(stringptr, &stringptr);
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ printf("Error: Vertex %d has no y coordinate.\n", b->firstnumber + i);
+ triexit(1);
+ }
+ y = (REAL) strtod(stringptr, &stringptr);
+ vertexloop[0] = x;
+ vertexloop[1] = y;
+ /* Read the vertex attributes. */
+ for (j = 2; j < 2 + m->nextras; j++) {
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ vertexloop[j] = 0.0;
+ } else {
+ vertexloop[j] = (REAL) strtod(stringptr, &stringptr);
+ }
+ }
+ if (nodemarkers) {
+ /* Read a vertex marker. */
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ setvertexmark(vertexloop, 0);
+ } else {
+ currentmarker = (int) strtol(stringptr, &stringptr, 0);
+ setvertexmark(vertexloop, currentmarker);
+ }
+ } else {
+ /* If no markers are specified in the file, they default to zero. */
+ setvertexmark(vertexloop, 0);
+ }
+ setvertextype(vertexloop, INPUTVERTEX);
+ /* Determine the smallest and largest x and y coordinates. */
+ if (i == 0) {
+ m->xmin = m->xmax = x;
+ m->ymin = m->ymax = y;
+ } else {
+ m->xmin = (x < m->xmin) ? x : m->xmin;
+ m->xmax = (x > m->xmax) ? x : m->xmax;
+ m->ymin = (y < m->ymin) ? y : m->ymin;
+ m->ymax = (y > m->ymax) ? y : m->ymax;
+ }
+ }
+ if (m->readnodefile) {
+ fclose(infile);
+ }
+
+ /* Nonexistent x value used as a flag to mark circle events in sweepline */
+ /* Delaunay algorithm. */
+ m->xminextreme = 10 * m->xmin - 9 * m->xmax;
+}
+
+#endif /* not TRILIBRARY */
+
+/*****************************************************************************/
+/* */
+/* transfernodes() Read the vertices from memory. */
+/* */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void transfernodes(struct mesh *m, struct behavior *b, REAL *pointlist,
+ REAL *pointattriblist, int *pointmarkerlist,
+ int numberofpoints, int numberofpointattribs)
+#else /* not ANSI_DECLARATORS */
+void transfernodes(m, b, pointlist, pointattriblist, pointmarkerlist,
+ numberofpoints, numberofpointattribs)
+struct mesh *m;
+struct behavior *b;
+REAL *pointlist;
+REAL *pointattriblist;
+int *pointmarkerlist;
+int numberofpoints;
+int numberofpointattribs;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ vertex vertexloop;
+ REAL x, y;
+ int i, j;
+ int coordindex;
+ int attribindex;
+
+ m->invertices = numberofpoints;
+ m->mesh_dim = 2;
+ m->nextras = numberofpointattribs;
+ m->readnodefile = 0;
+ if (m->invertices < 3) {
+ printf("Error: Input must have at least three input vertices.\n");
+ triexit(1);
+ }
+ if (m->nextras == 0) {
+ b->weighted = 0;
+ }
+
+ initializevertexpool(m, b);
+
+ /* Read the vertices. */
+ coordindex = 0;
+ attribindex = 0;
+ for (i = 0; i < m->invertices; i++) {
+ vertexloop = (vertex) poolalloc(&m->vertices);
+ /* Read the vertex coordinates. */
+ x = vertexloop[0] = pointlist[coordindex++];
+ y = vertexloop[1] = pointlist[coordindex++];
+ /* Read the vertex attributes. */
+ for (j = 0; j < numberofpointattribs; j++) {
+ vertexloop[2 + j] = pointattriblist[attribindex++];
+ }
+ if (pointmarkerlist != (int *) NULL) {
+ /* Read a vertex marker. */
+ setvertexmark(vertexloop, pointmarkerlist[i]);
+ } else {
+ /* If no markers are specified, they default to zero. */
+ setvertexmark(vertexloop, 0);
+ }
+ setvertextype(vertexloop, INPUTVERTEX);
+ /* Determine the smallest and largest x and y coordinates. */
+ if (i == 0) {
+ m->xmin = m->xmax = x;
+ m->ymin = m->ymax = y;
+ } else {
+ m->xmin = (x < m->xmin) ? x : m->xmin;
+ m->xmax = (x > m->xmax) ? x : m->xmax;
+ m->ymin = (y < m->ymin) ? y : m->ymin;
+ m->ymax = (y > m->ymax) ? y : m->ymax;
+ }
+ }
+
+ /* Nonexistent x value used as a flag to mark circle events in sweepline */
+ /* Delaunay algorithm. */
+ m->xminextreme = 10 * m->xmin - 9 * m->xmax;
+}
+
+#endif /* TRILIBRARY */
+
+/*****************************************************************************/
+/* */
+/* readholes() Read the holes, and possibly regional attributes and area */
+/* constraints, from a .poly file. */
+/* */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void readholes(struct mesh *m, struct behavior *b,
+ FILE *polyfile, char *polyfilename, REAL **hlist, int *holes,
+ REAL **rlist, int *regions)
+#else /* not ANSI_DECLARATORS */
+void readholes(m, b, polyfile, polyfilename, hlist, holes, rlist, regions)
+struct mesh *m;
+struct behavior *b;
+FILE *polyfile;
+char *polyfilename;
+REAL **hlist;
+int *holes;
+REAL **rlist;
+int *regions;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ REAL *holelist;
+ REAL *regionlist;
+ char inputline[INPUTLINESIZE];
+ char *stringptr;
+ int index;
+ int i;
+
+ /* Read the holes. */
+ stringptr = readline(inputline, polyfile, polyfilename);
+ *holes = (int) strtol(stringptr, &stringptr, 0);
+ if (*holes > 0) {
+ holelist = (REAL *) trimalloc(2 * *holes * (int) sizeof(REAL));
+ *hlist = holelist;
+ for (i = 0; i < 2 * *holes; i += 2) {
+ stringptr = readline(inputline, polyfile, polyfilename);
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ printf("Error: Hole %d has no x coordinate.\n",
+ b->firstnumber + (i >> 1));
+ triexit(1);
+ } else {
+ holelist[i] = (REAL) strtod(stringptr, &stringptr);
+ }
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ printf("Error: Hole %d has no y coordinate.\n",
+ b->firstnumber + (i >> 1));
+ triexit(1);
+ } else {
+ holelist[i + 1] = (REAL) strtod(stringptr, &stringptr);
+ }
+ }
+ } else {
+ *hlist = (REAL *) NULL;
+ }
+
+#ifndef CDT_ONLY
+ if ((b->regionattrib || b->vararea) && !b->refine) {
+ /* Read the area constraints. */
+ stringptr = readline(inputline, polyfile, polyfilename);
+ *regions = (int) strtol(stringptr, &stringptr, 0);
+ if (*regions > 0) {
+ regionlist = (REAL *) trimalloc(4 * *regions * (int) sizeof(REAL));
+ *rlist = regionlist;
+ index = 0;
+ for (i = 0; i < *regions; i++) {
+ stringptr = readline(inputline, polyfile, polyfilename);
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ printf("Error: Region %d has no x coordinate.\n",
+ b->firstnumber + i);
+ triexit(1);
+ } else {
+ regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
+ }
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ printf("Error: Region %d has no y coordinate.\n",
+ b->firstnumber + i);
+ triexit(1);
+ } else {
+ regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
+ }
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ printf(
+ "Error: Region %d has no region attribute or area constraint.\n",
+ b->firstnumber + i);
+ triexit(1);
+ } else {
+ regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
+ }
+ stringptr = findfield(stringptr);
+ if (*stringptr == '\0') {
+ regionlist[index] = regionlist[index - 1];
+ } else {
+ regionlist[index] = (REAL) strtod(stringptr, &stringptr);
+ }
+ index++;
+ }
+ }
+ } else {
+ /* Set `*regions' to zero to avoid an accidental free() later. */
+ *regions = 0;
+ *rlist = (REAL *) NULL;
+ }
+#endif /* not CDT_ONLY */
+
+ fclose(polyfile);
+}
+
+#endif /* not TRILIBRARY */
+
+/*****************************************************************************/
+/* */
+/* finishfile() Write the command line to the output file so the user */
+/* can remember how the file was generated. Close the file. */
+/* */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void finishfile(FILE *outfile, int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void finishfile(outfile, argc, argv)
+FILE *outfile;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ int i;
+
+ fprintf(outfile, "# Generated by");
+ for (i = 0; i < argc; i++) {
+ fprintf(outfile, " ");
+ fputs(argv[i], outfile);
+ }
+ fprintf(outfile, "\n");
+ fclose(outfile);
+}
+
+#endif /* not TRILIBRARY */
+
+/*****************************************************************************/
+/* */
+/* writenodes() Number the vertices and write them to a .node file. */
+/* */
+/* To save memory, the vertex numbers are written over the boundary markers */
+/* after the vertices are written to a file. */
+/* */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writenodes(struct mesh *m, struct behavior *b, REAL **pointlist,
+ REAL **pointattriblist, int **pointmarkerlist)
+#else /* not ANSI_DECLARATORS */
+void writenodes(m, b, pointlist, pointattriblist, pointmarkerlist)
+struct mesh *m;
+struct behavior *b;
+REAL **pointlist;
+REAL **pointattriblist;
+int **pointmarkerlist;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void writenodes(struct mesh *m, struct behavior *b, char *nodefilename,
+ int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writenodes(m, b, nodefilename, argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *nodefilename;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+ REAL *plist;
+ REAL *palist;
+ int *pmlist;
+ int coordindex;
+ int attribindex;
+#else /* not TRILIBRARY */
+ FILE *outfile;
+#endif /* not TRILIBRARY */
+ vertex vertexloop;
+ long outvertices;
+ int vertexnumber;
+ int i;
+
+ if (b->jettison) {
+ outvertices = m->vertices.items - m->undeads;
+ } else {
+ outvertices = m->vertices.items;
+ }
+
+#ifdef TRILIBRARY
+ if (!b->quiet) {
+ printf("Writing vertices.\n");
+ }
+ /* Allocate memory for output vertices if necessary. */
+ if (*pointlist == (REAL *) NULL) {
+ *pointlist = (REAL *) trimalloc((int) (outvertices * 2 * sizeof(REAL)));
+ }
+ /* Allocate memory for output vertex attributes if necessary. */
+ if ((m->nextras > 0) && (*pointattriblist == (REAL *) NULL)) {
+ *pointattriblist = (REAL *) trimalloc((int) (outvertices * m->nextras *
+ sizeof(REAL)));
+ }
+ /* Allocate memory for output vertex markers if necessary. */
+ if (!b->nobound && (*pointmarkerlist == (int *) NULL)) {
+ *pointmarkerlist = (int *) trimalloc((int) (outvertices * sizeof(int)));
+ }
+ plist = *pointlist;
+ palist = *pointattriblist;
+ pmlist = *pointmarkerlist;
+ coordindex = 0;
+ attribindex = 0;
+#else /* not TRILIBRARY */
+ if (!b->quiet) {
+ printf("Writing %s.\n", nodefilename);
+ }
+ outfile = fopen(nodefilename, "w");
+ if (outfile == (FILE *) NULL) {
+ printf(" Error: Cannot create file %s.\n", nodefilename);
+ triexit(1);
+ }
+ /* Number of vertices, number of dimensions, number of vertex attributes, */
+ /* and number of boundary markers (zero or one). */
+ fprintf(outfile, "%ld %d %d %d\n", outvertices, m->mesh_dim,
+ m->nextras, 1 - b->nobound);
+#endif /* not TRILIBRARY */
+
+ traversalinit(&m->vertices);
+ vertexnumber = b->firstnumber;
+ vertexloop = vertextraverse(m);
+ while (vertexloop != (vertex) NULL) {
+ if (!b->jettison || (vertextype(vertexloop) != UNDEADVERTEX)) {
+#ifdef TRILIBRARY
+ /* X and y coordinates. */
+ plist[coordindex++] = vertexloop[0];
+ plist[coordindex++] = vertexloop[1];
+ /* Vertex attributes. */
+ for (i = 0; i < m->nextras; i++) {
+ palist[attribindex++] = vertexloop[2 + i];
+ }
+ if (!b->nobound) {
+ /* Copy the boundary marker. */
+ pmlist[vertexnumber - b->firstnumber] = vertexmark(vertexloop);
+ }
+#else /* not TRILIBRARY */
+ /* Vertex number, x and y coordinates. */
+ fprintf(outfile, "%4d %.17g %.17g", vertexnumber, vertexloop[0],
+ vertexloop[1]);
+ for (i = 0; i < m->nextras; i++) {
+ /* Write an attribute. */
+ fprintf(outfile, " %.17g", vertexloop[i + 2]);
+ }
+ if (b->nobound) {
+ fprintf(outfile, "\n");
+ } else {
+ /* Write the boundary marker. */
+ fprintf(outfile, " %d\n", vertexmark(vertexloop));
+ }
+#endif /* not TRILIBRARY */
+
+ setvertexmark(vertexloop, vertexnumber);
+ vertexnumber++;
+ }
+ vertexloop = vertextraverse(m);
+ }
+
+#ifndef TRILIBRARY
+ finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+}
+
+/*****************************************************************************/
+/* */
+/* numbernodes() Number the vertices. */
+/* */
+/* Each vertex is assigned a marker equal to its number. */
+/* */
+/* Used when writenodes() is not called because no .node file is written. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void numbernodes(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void numbernodes(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ vertex vertexloop;
+ int vertexnumber;
+
+ traversalinit(&m->vertices);
+ vertexnumber = b->firstnumber;
+ vertexloop = vertextraverse(m);
+ while (vertexloop != (vertex) NULL) {
+ setvertexmark(vertexloop, vertexnumber);
+ if (!b->jettison || (vertextype(vertexloop) != UNDEADVERTEX)) {
+ vertexnumber++;
+ }
+ vertexloop = vertextraverse(m);
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* writeelements() Write the triangles to an .ele file. */
+/* */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writeelements(struct mesh *m, struct behavior *b,
+ INDICE **trianglelist, REAL **triangleattriblist)
+#else /* not ANSI_DECLARATORS */
+void writeelements(m, b, trianglelist, triangleattriblist)
+struct mesh *m;
+struct behavior *b;
+INDICE **trianglelist;
+REAL **triangleattriblist;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void writeelements(struct mesh *m, struct behavior *b, char *elefilename,
+ int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writeelements(m, b, elefilename, argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *elefilename;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+ INDICE *tlist;
+ REAL *talist;
+ int vertexindex;
+ int attribindex;
+#else /* not TRILIBRARY */
+ FILE *outfile;
+#endif /* not TRILIBRARY */
+ struct otri triangleloop;
+ vertex p1, p2, p3;
+ vertex mid1, mid2, mid3;
+ long elementnumber;
+ int i;
+
+#ifdef TRILIBRARY
+ if (!b->quiet) {
+ printf("Writing triangles.\n");
+ }
+ /* Allocate memory for output triangles if necessary. */
+ if (*trianglelist == (INDICE *) NULL) {
+ *trianglelist = (INDICE *) trimalloc((INDICE) (m->triangles.items *
+ ((b->order + 1) * (b->order + 2) /
+ 2) * sizeof(int)));
+ }
+ /* Allocate memory for output triangle attributes if necessary. */
+ if ((m->eextras > 0) && (*triangleattriblist == (REAL *) NULL)) {
+ *triangleattriblist = (REAL *) trimalloc((int) (m->triangles.items *
+ m->eextras *
+ sizeof(REAL)));
+ }
+ tlist = *trianglelist;
+ talist = *triangleattriblist;
+ vertexindex = 0;
+ attribindex = 0;
+#else /* not TRILIBRARY */
+ if (!b->quiet) {
+ printf("Writing %s.\n", elefilename);
+ }
+ outfile = fopen(elefilename, "w");
+ if (outfile == (FILE *) NULL) {
+ printf(" Error: Cannot create file %s.\n", elefilename);
+ triexit(1);
+ }
+ /* Number of triangles, vertices per triangle, attributes per triangle. */
+ fprintf(outfile, "%ld %d %d\n", m->triangles.items,
+ (b->order + 1) * (b->order + 2) / 2, m->eextras);
+#endif /* not TRILIBRARY */
+
+ traversalinit(&m->triangles);
+ triangleloop.tri = triangletraverse(m);
+ triangleloop.orient = 0;
+ elementnumber = b->firstnumber;
+ while (triangleloop.tri != (triangle *) NULL) {
+ org(triangleloop, p1);
+ dest(triangleloop, p2);
+ apex(triangleloop, p3);
+ if (b->order == 1) {
+#ifdef TRILIBRARY
+ tlist[vertexindex++] = vertexmark(p1);
+ tlist[vertexindex++] = vertexmark(p2);
+ tlist[vertexindex++] = vertexmark(p3);
+#else /* not TRILIBRARY */
+ /* Triangle number, indices for three vertices. */
+ fprintf(outfile, "%4ld %4d %4d %4d", elementnumber,
+ vertexmark(p1), vertexmark(p2), vertexmark(p3));
+#endif /* not TRILIBRARY */
+ } else {
+ mid1 = (vertex) triangleloop.tri[m->highorderindex + 1];
+ mid2 = (vertex) triangleloop.tri[m->highorderindex + 2];
+ mid3 = (vertex) triangleloop.tri[m->highorderindex];
+#ifdef TRILIBRARY
+ tlist[vertexindex++] = vertexmark(p1);
+ tlist[vertexindex++] = vertexmark(p2);
+ tlist[vertexindex++] = vertexmark(p3);
+ tlist[vertexindex++] = vertexmark(mid1);
+ tlist[vertexindex++] = vertexmark(mid2);
+ tlist[vertexindex++] = vertexmark(mid3);
+#else /* not TRILIBRARY */
+ /* Triangle number, indices for six vertices. */
+ fprintf(outfile, "%4ld %4d %4d %4d %4d %4d %4d", elementnumber,
+ vertexmark(p1), vertexmark(p2), vertexmark(p3), vertexmark(mid1),
+ vertexmark(mid2), vertexmark(mid3));
+#endif /* not TRILIBRARY */
+ }
+
+#ifdef TRILIBRARY
+ for (i = 0; i < m->eextras; i++) {
+ talist[attribindex++] = elemattribute(triangleloop, i);
+ }
+#else /* not TRILIBRARY */
+ for (i = 0; i < m->eextras; i++) {
+ fprintf(outfile, " %.17g", elemattribute(triangleloop, i));
+ }
+ fprintf(outfile, "\n");
+#endif /* not TRILIBRARY */
+
+ triangleloop.tri = triangletraverse(m);
+ elementnumber++;
+ }
+
+#ifndef TRILIBRARY
+ finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+}
+
+/*****************************************************************************/
+/* */
+/* writepoly() Write the segments and holes to a .poly file. */
+/* */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writepoly(struct mesh *m, struct behavior *b,
+ int **segmentlist, int **segmentmarkerlist)
+#else /* not ANSI_DECLARATORS */
+void writepoly(m, b, segmentlist, segmentmarkerlist)
+struct mesh *m;
+struct behavior *b;
+int **segmentlist;
+int **segmentmarkerlist;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void writepoly(struct mesh *m, struct behavior *b, char *polyfilename,
+ REAL *holelist, int holes, REAL *regionlist, int regions,
+ int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writepoly(m, b, polyfilename, holelist, holes, regionlist, regions,
+ argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *polyfilename;
+REAL *holelist;
+int holes;
+REAL *regionlist;
+int regions;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+ int *slist;
+ int *smlist;
+ int index;
+#else /* not TRILIBRARY */
+ FILE *outfile;
+ long holenumber, regionnumber;
+#endif /* not TRILIBRARY */
+ struct osub subsegloop;
+ vertex endpoint1, endpoint2;
+ long subsegnumber;
+
+#ifdef TRILIBRARY
+ if (!b->quiet) {
+ printf("Writing segments.\n");
+ }
+ /* Allocate memory for output segments if necessary. */
+ if (*segmentlist == (int *) NULL) {
+ *segmentlist = (int *) trimalloc((int) (m->subsegs.items * 2 *
+ sizeof(int)));
+ }
+ /* Allocate memory for output segment markers if necessary. */
+ if (!b->nobound && (*segmentmarkerlist == (int *) NULL)) {
+ *segmentmarkerlist = (int *) trimalloc((int) (m->subsegs.items *
+ sizeof(int)));
+ }
+ slist = *segmentlist;
+ smlist = *segmentmarkerlist;
+ index = 0;
+#else /* not TRILIBRARY */
+ if (!b->quiet) {
+ printf("Writing %s.\n", polyfilename);
+ }
+ outfile = fopen(polyfilename, "w");
+ if (outfile == (FILE *) NULL) {
+ printf(" Error: Cannot create file %s.\n", polyfilename);
+ triexit(1);
+ }
+ /* The zero indicates that the vertices are in a separate .node file. */
+ /* Followed by number of dimensions, number of vertex attributes, */
+ /* and number of boundary markers (zero or one). */
+ fprintf(outfile, "%d %d %d %d\n", 0, m->mesh_dim, m->nextras,
+ 1 - b->nobound);
+ /* Number of segments, number of boundary markers (zero or one). */
+ fprintf(outfile, "%ld %d\n", m->subsegs.items, 1 - b->nobound);
+#endif /* not TRILIBRARY */
+
+ traversalinit(&m->subsegs);
+ subsegloop.ss = subsegtraverse(m);
+ subsegloop.ssorient = 0;
+ subsegnumber = b->firstnumber;
+ while (subsegloop.ss != (subseg *) NULL) {
+ sorg(subsegloop, endpoint1);
+ sdest(subsegloop, endpoint2);
+#ifdef TRILIBRARY
+ /* Copy indices of the segment's two endpoints. */
+ slist[index++] = vertexmark(endpoint1);
+ slist[index++] = vertexmark(endpoint2);
+ if (!b->nobound) {
+ /* Copy the boundary marker. */
+ smlist[subsegnumber - b->firstnumber] = mark(subsegloop);
+ }
+#else /* not TRILIBRARY */
+ /* Segment number, indices of its two endpoints, and possibly a marker. */
+ if (b->nobound) {
+ fprintf(outfile, "%4ld %4d %4d\n", subsegnumber,
+ vertexmark(endpoint1), vertexmark(endpoint2));
+ } else {
+ fprintf(outfile, "%4ld %4d %4d %4d\n", subsegnumber,
+ vertexmark(endpoint1), vertexmark(endpoint2), mark(subsegloop));
+ }
+#endif /* not TRILIBRARY */
+
+ subsegloop.ss = subsegtraverse(m);
+ subsegnumber++;
+ }
+
+#ifndef TRILIBRARY
+#ifndef CDT_ONLY
+ fprintf(outfile, "%d\n", holes);
+ if (holes > 0) {
+ for (holenumber = 0; holenumber < holes; holenumber++) {
+ /* Hole number, x and y coordinates. */
+ fprintf(outfile, "%4ld %.17g %.17g\n", b->firstnumber + holenumber,
+ holelist[2 * holenumber], holelist[2 * holenumber + 1]);
+ }
+ }
+ if (regions > 0) {
+ fprintf(outfile, "%d\n", regions);
+ for (regionnumber = 0; regionnumber < regions; regionnumber++) {
+ /* Region number, x and y coordinates, attribute, maximum area. */
+ fprintf(outfile, "%4ld %.17g %.17g %.17g %.17g\n",
+ b->firstnumber + regionnumber,
+ regionlist[4 * regionnumber], regionlist[4 * regionnumber + 1],
+ regionlist[4 * regionnumber + 2],
+ regionlist[4 * regionnumber + 3]);
+ }
+ }
+#endif /* not CDT_ONLY */
+
+ finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+}
+
+/*****************************************************************************/
+/* */
+/* writeedges() Write the edges to an .edge file. */
+/* */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writeedges(struct mesh *m, struct behavior *b,
+ int **edgelist, int **edgemarkerlist)
+#else /* not ANSI_DECLARATORS */
+void writeedges(m, b, edgelist, edgemarkerlist)
+struct mesh *m;
+struct behavior *b;
+int **edgelist;
+int **edgemarkerlist;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void writeedges(struct mesh *m, struct behavior *b, char *edgefilename,
+ int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writeedges(m, b, edgefilename, argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *edgefilename;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+ int *elist;
+ int *emlist;
+ int index;
+#else /* not TRILIBRARY */
+ FILE *outfile;
+#endif /* not TRILIBRARY */
+ struct otri triangleloop, trisym;
+ struct osub checkmark;
+ vertex p1, p2;
+ long edgenumber;
+ triangle ptr; /* Temporary variable used by sym(). */
+ subseg sptr; /* Temporary variable used by tspivot(). */
+
+#ifdef TRILIBRARY
+ if (!b->quiet) {
+ printf("Writing edges.\n");
+ }
+ /* Allocate memory for edges if necessary. */
+ if (*edgelist == (int *) NULL) {
+ *edgelist = (int *) trimalloc((int) (m->edges * 2 * sizeof(int)));
+ }
+ /* Allocate memory for edge markers if necessary. */
+ if (!b->nobound && (*edgemarkerlist == (int *) NULL)) {
+ *edgemarkerlist = (int *) trimalloc((int) (m->edges * sizeof(int)));
+ }
+ elist = *edgelist;
+ emlist = *edgemarkerlist;
+ index = 0;
+#else /* not TRILIBRARY */
+ if (!b->quiet) {
+ printf("Writing %s.\n", edgefilename);
+ }
+ outfile = fopen(edgefilename, "w");
+ if (outfile == (FILE *) NULL) {
+ printf(" Error: Cannot create file %s.\n", edgefilename);
+ triexit(1);
+ }
+ /* Number of edges, number of boundary markers (zero or one). */
+ fprintf(outfile, "%ld %d\n", m->edges, 1 - b->nobound);
+#endif /* not TRILIBRARY */
+
+ traversalinit(&m->triangles);
+ triangleloop.tri = triangletraverse(m);
+ edgenumber = b->firstnumber;
+ /* To loop over the set of edges, loop over all triangles, and look at */
+ /* the three edges of each triangle. If there isn't another triangle */
+ /* adjacent to the edge, operate on the edge. If there is another */
+ /* adjacent triangle, operate on the edge only if the current triangle */
+ /* has a smaller pointer than its neighbor. This way, each edge is */
+ /* considered only once. */
+ while (triangleloop.tri != (triangle *) NULL) {
+ for (triangleloop.orient = 0; triangleloop.orient < 3;
+ triangleloop.orient++) {
+ sym(triangleloop, trisym);
+ if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) {
+ org(triangleloop, p1);
+ dest(triangleloop, p2);
+#ifdef TRILIBRARY
+ elist[index++] = vertexmark(p1);
+ elist[index++] = vertexmark(p2);
+#endif /* TRILIBRARY */
+ if (b->nobound) {
+#ifndef TRILIBRARY
+ /* Edge number, indices of two endpoints. */
+ fprintf(outfile, "%4ld %d %d\n", edgenumber,
+ vertexmark(p1), vertexmark(p2));
+#endif /* not TRILIBRARY */
+ } else {
+ /* Edge number, indices of two endpoints, and a boundary marker. */
+ /* If there's no subsegment, the boundary marker is zero. */
+ if (b->usesegments) {
+ tspivot(triangleloop, checkmark);
+ if (checkmark.ss == m->dummysub) {
+#ifdef TRILIBRARY
+ emlist[edgenumber - b->firstnumber] = 0;
+#else /* not TRILIBRARY */
+ fprintf(outfile, "%4ld %d %d %d\n", edgenumber,
+ vertexmark(p1), vertexmark(p2), 0);
+#endif /* not TRILIBRARY */
+ } else {
+#ifdef TRILIBRARY
+ emlist[edgenumber - b->firstnumber] = mark(checkmark);
+#else /* not TRILIBRARY */
+ fprintf(outfile, "%4ld %d %d %d\n", edgenumber,
+ vertexmark(p1), vertexmark(p2), mark(checkmark));
+#endif /* not TRILIBRARY */
+ }
+ } else {
+#ifdef TRILIBRARY
+ emlist[edgenumber - b->firstnumber] = trisym.tri == m->dummytri;
+#else /* not TRILIBRARY */
+ fprintf(outfile, "%4ld %d %d %d\n", edgenumber,
+ vertexmark(p1), vertexmark(p2), trisym.tri == m->dummytri);
+#endif /* not TRILIBRARY */
+ }
+ }
+ edgenumber++;
+ }
+ }
+ triangleloop.tri = triangletraverse(m);
+ }
+
+#ifndef TRILIBRARY
+ finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+}
+
+/*****************************************************************************/
+/* */
+/* writevoronoi() Write the Voronoi diagram to a .v.node and .v.edge */
+/* file. */
+/* */
+/* The Voronoi diagram is the geometric dual of the Delaunay triangulation. */
+/* Hence, the Voronoi vertices are listed by traversing the Delaunay */
+/* triangles, and the Voronoi edges are listed by traversing the Delaunay */
+/* edges. */
+/* */
+/* WARNING: In order to assign numbers to the Voronoi vertices, this */
+/* procedure messes up the subsegments or the extra nodes of every */
+/* element. Hence, you should call this procedure last. */
+/* */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writevoronoi(struct mesh *m, struct behavior *b, REAL **vpointlist,
+ REAL **vpointattriblist, int **vpointmarkerlist,
+ int **vedgelist, int **vedgemarkerlist, REAL **vnormlist)
+#else /* not ANSI_DECLARATORS */
+void writevoronoi(m, b, vpointlist, vpointattriblist, vpointmarkerlist,
+ vedgelist, vedgemarkerlist, vnormlist)
+struct mesh *m;
+struct behavior *b;
+REAL **vpointlist;
+REAL **vpointattriblist;
+int **vpointmarkerlist;
+int **vedgelist;
+int **vedgemarkerlist;
+REAL **vnormlist;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void writevoronoi(struct mesh *m, struct behavior *b, char *vnodefilename,
+ char *vedgefilename, int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writevoronoi(m, b, vnodefilename, vedgefilename, argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *vnodefilename;
+char *vedgefilename;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+ REAL *plist;
+ REAL *palist;
+ int *elist;
+ REAL *normlist;
+ int coordindex;
+ int attribindex;
+#else /* not TRILIBRARY */
+ FILE *outfile;
+#endif /* not TRILIBRARY */
+ struct otri triangleloop, trisym;
+ vertex torg, tdest, tapex;
+ REAL circumcenter[2];
+ REAL xi, eta;
+ long vnodenumber, vedgenumber;
+ int p1, p2;
+ int i;
+ triangle ptr; /* Temporary variable used by sym(). */
+
+#ifdef TRILIBRARY
+ if (!b->quiet) {
+ printf("Writing Voronoi vertices.\n");
+ }
+ /* Allocate memory for Voronoi vertices if necessary. */
+ if (*vpointlist == (REAL *) NULL) {
+ *vpointlist = (REAL *) trimalloc((int) (m->triangles.items * 2 *
+ sizeof(REAL)));
+ }
+ /* Allocate memory for Voronoi vertex attributes if necessary. */
+ if (*vpointattriblist == (REAL *) NULL) {
+ *vpointattriblist = (REAL *) trimalloc((int) (m->triangles.items *
+ m->nextras * sizeof(REAL)));
+ }
+ *vpointmarkerlist = (int *) NULL;
+ plist = *vpointlist;
+ palist = *vpointattriblist;
+ coordindex = 0;
+ attribindex = 0;
+#else /* not TRILIBRARY */
+ if (!b->quiet) {
+ printf("Writing %s.\n", vnodefilename);
+ }
+ outfile = fopen(vnodefilename, "w");
+ if (outfile == (FILE *) NULL) {
+ printf(" Error: Cannot create file %s.\n", vnodefilename);
+ triexit(1);
+ }
+ /* Number of triangles, two dimensions, number of vertex attributes, */
+ /* no markers. */
+ fprintf(outfile, "%ld %d %d %d\n", m->triangles.items, 2, m->nextras, 0);
+#endif /* not TRILIBRARY */
+
+ traversalinit(&m->triangles);
+ triangleloop.tri = triangletraverse(m);
+ triangleloop.orient = 0;
+ vnodenumber = b->firstnumber;
+ while (triangleloop.tri != (triangle *) NULL) {
+ org(triangleloop, torg);
+ dest(triangleloop, tdest);
+ apex(triangleloop, tapex);
+ findcircumcenter(m, b, torg, tdest, tapex, circumcenter, &xi, &eta, 0);
+#ifdef TRILIBRARY
+ /* X and y coordinates. */
+ plist[coordindex++] = circumcenter[0];
+ plist[coordindex++] = circumcenter[1];
+ for (i = 2; i < 2 + m->nextras; i++) {
+ /* Interpolate the vertex attributes at the circumcenter. */
+ palist[attribindex++] = torg[i] + xi * (tdest[i] - torg[i])
+ + eta * (tapex[i] - torg[i]);
+ }
+#else /* not TRILIBRARY */
+ /* Voronoi vertex number, x and y coordinates. */
+ fprintf(outfile, "%4ld %.17g %.17g", vnodenumber, circumcenter[0],
+ circumcenter[1]);
+ for (i = 2; i < 2 + m->nextras; i++) {
+ /* Interpolate the vertex attributes at the circumcenter. */
+ fprintf(outfile, " %.17g", torg[i] + xi * (tdest[i] - torg[i])
+ + eta * (tapex[i] - torg[i]));
+ }
+ fprintf(outfile, "\n");
+#endif /* not TRILIBRARY */
+
+ * (int *) (triangleloop.tri + 6) = (int) vnodenumber;
+ triangleloop.tri = triangletraverse(m);
+ vnodenumber++;
+ }
+
+#ifndef TRILIBRARY
+ finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+
+#ifdef TRILIBRARY
+ if (!b->quiet) {
+ printf("Writing Voronoi edges.\n");
+ }
+ /* Allocate memory for output Voronoi edges if necessary. */
+ if (*vedgelist == (int *) NULL) {
+ *vedgelist = (int *) trimalloc((int) (m->edges * 2 * sizeof(int)));
+ }
+ *vedgemarkerlist = (int *) NULL;
+ /* Allocate memory for output Voronoi norms if necessary. */
+ if (*vnormlist == (REAL *) NULL) {
+ *vnormlist = (REAL *) trimalloc((int) (m->edges * 2 * sizeof(REAL)));
+ }
+ elist = *vedgelist;
+ normlist = *vnormlist;
+ coordindex = 0;
+#else /* not TRILIBRARY */
+ if (!b->quiet) {
+ printf("Writing %s.\n", vedgefilename);
+ }
+ outfile = fopen(vedgefilename, "w");
+ if (outfile == (FILE *) NULL) {
+ printf(" Error: Cannot create file %s.\n", vedgefilename);
+ triexit(1);
+ }
+ /* Number of edges, zero boundary markers. */
+ fprintf(outfile, "%ld %d\n", m->edges, 0);
+#endif /* not TRILIBRARY */
+
+ traversalinit(&m->triangles);
+ triangleloop.tri = triangletraverse(m);
+ vedgenumber = b->firstnumber;
+ /* To loop over the set of edges, loop over all triangles, and look at */
+ /* the three edges of each triangle. If there isn't another triangle */
+ /* adjacent to the edge, operate on the edge. If there is another */
+ /* adjacent triangle, operate on the edge only if the current triangle */
+ /* has a smaller pointer than its neighbor. This way, each edge is */
+ /* considered only once. */
+ while (triangleloop.tri != (triangle *) NULL) {
+ for (triangleloop.orient = 0; triangleloop.orient < 3;
+ triangleloop.orient++) {
+ sym(triangleloop, trisym);
+ if ((triangleloop.tri < trisym.tri) || (trisym.tri == m->dummytri)) {
+ /* Find the number of this triangle (and Voronoi vertex). */
+ p1 = * (int *) (triangleloop.tri + 6);
+ if (trisym.tri == m->dummytri) {
+ org(triangleloop, torg);
+ dest(triangleloop, tdest);
+#ifdef TRILIBRARY
+ /* Copy an infinite ray. Index of one endpoint, and -1. */
+ elist[coordindex] = p1;
+ normlist[coordindex++] = tdest[1] - torg[1];
+ elist[coordindex] = -1;
+ normlist[coordindex++] = torg[0] - tdest[0];
+#else /* not TRILIBRARY */
+ /* Write an infinite ray. Edge number, index of one endpoint, -1, */
+ /* and x and y coordinates of a vector representing the */
+ /* direction of the ray. */
+ fprintf(outfile, "%4ld %d %d %.17g %.17g\n", vedgenumber,
+ p1, -1, tdest[1] - torg[1], torg[0] - tdest[0]);
+#endif /* not TRILIBRARY */
+ } else {
+ /* Find the number of the adjacent triangle (and Voronoi vertex). */
+ p2 = * (int *) (trisym.tri + 6);
+ /* Finite edge. Write indices of two endpoints. */
+#ifdef TRILIBRARY
+ elist[coordindex] = p1;
+ normlist[coordindex++] = 0.0;
+ elist[coordindex] = p2;
+ normlist[coordindex++] = 0.0;
+#else /* not TRILIBRARY */
+ fprintf(outfile, "%4ld %d %d\n", vedgenumber, p1, p2);
+#endif /* not TRILIBRARY */
+ }
+ vedgenumber++;
+ }
+ }
+ triangleloop.tri = triangletraverse(m);
+ }
+
+#ifndef TRILIBRARY
+ finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+}
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writeneighbors(struct mesh *m, struct behavior *b, int **neighborlist)
+#else /* not ANSI_DECLARATORS */
+void writeneighbors(m, b, neighborlist)
+struct mesh *m;
+struct behavior *b;
+int **neighborlist;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+void writeneighbors(struct mesh *m, struct behavior *b, char *neighborfilename,
+ int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writeneighbors(m, b, neighborfilename, argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *neighborfilename;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+#ifdef TRILIBRARY
+ int *nlist;
+ int index;
+#else /* not TRILIBRARY */
+ FILE *outfile;
+#endif /* not TRILIBRARY */
+ struct otri triangleloop, trisym;
+ long elementnumber;
+ int neighbor1, neighbor2, neighbor3;
+ triangle ptr; /* Temporary variable used by sym(). */
+
+#ifdef TRILIBRARY
+ if (!b->quiet) {
+ printf("Writing neighbors.\n");
+ }
+ /* Allocate memory for neighbors if necessary. */
+ if (*neighborlist == (int *) NULL) {
+ *neighborlist = (int *) trimalloc((int) (m->triangles.items * 3 *
+ sizeof(int)));
+ }
+ nlist = *neighborlist;
+ index = 0;
+#else /* not TRILIBRARY */
+ if (!b->quiet) {
+ printf("Writing %s.\n", neighborfilename);
+ }
+ outfile = fopen(neighborfilename, "w");
+ if (outfile == (FILE *) NULL) {
+ printf(" Error: Cannot create file %s.\n", neighborfilename);
+ triexit(1);
+ }
+ /* Number of triangles, three neighbors per triangle. */
+ fprintf(outfile, "%ld %d\n", m->triangles.items, 3);
+#endif /* not TRILIBRARY */
+
+ traversalinit(&m->triangles);
+ triangleloop.tri = triangletraverse(m);
+ triangleloop.orient = 0;
+ elementnumber = b->firstnumber;
+ while (triangleloop.tri != (triangle *) NULL) {
+ * (int *) (triangleloop.tri + 6) = (int) elementnumber;
+ triangleloop.tri = triangletraverse(m);
+ elementnumber++;
+ }
+ * (int *) (m->dummytri + 6) = -1;
+
+ traversalinit(&m->triangles);
+ triangleloop.tri = triangletraverse(m);
+ elementnumber = b->firstnumber;
+ while (triangleloop.tri != (triangle *) NULL) {
+ triangleloop.orient = 1;
+ sym(triangleloop, trisym);
+ neighbor1 = * (int *) (trisym.tri + 6);
+ triangleloop.orient = 2;
+ sym(triangleloop, trisym);
+ neighbor2 = * (int *) (trisym.tri + 6);
+ triangleloop.orient = 0;
+ sym(triangleloop, trisym);
+ neighbor3 = * (int *) (trisym.tri + 6);
+#ifdef TRILIBRARY
+ nlist[index++] = neighbor1;
+ nlist[index++] = neighbor2;
+ nlist[index++] = neighbor3;
+#else /* not TRILIBRARY */
+ /* Triangle number, neighboring triangle numbers. */
+ fprintf(outfile, "%4ld %d %d %d\n", elementnumber,
+ neighbor1, neighbor2, neighbor3);
+#endif /* not TRILIBRARY */
+
+ triangleloop.tri = triangletraverse(m);
+ elementnumber++;
+ }
+
+#ifndef TRILIBRARY
+ finishfile(outfile, argc, argv);
+#endif /* not TRILIBRARY */
+}
+
+/*****************************************************************************/
+/* */
+/* writeoff() Write the triangulation to an .off file. */
+/* */
+/* OFF stands for the Object File Format, a format used by the Geometry */
+/* Center's Geomview package. */
+/* */
+/*****************************************************************************/
+
+#ifndef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void writeoff(struct mesh *m, struct behavior *b, char *offfilename,
+ int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+void writeoff(m, b, offfilename, argc, argv)
+struct mesh *m;
+struct behavior *b;
+char *offfilename;
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ FILE *outfile;
+ struct otri triangleloop;
+ vertex vertexloop;
+ vertex p1, p2, p3;
+ long outvertices;
+
+ if (!b->quiet) {
+ printf("Writing %s.\n", offfilename);
+ }
+
+ if (b->jettison) {
+ outvertices = m->vertices.items - m->undeads;
+ } else {
+ outvertices = m->vertices.items;
+ }
+
+ outfile = fopen(offfilename, "w");
+ if (outfile == (FILE *) NULL) {
+ printf(" Error: Cannot create file %s.\n", offfilename);
+ triexit(1);
+ }
+ /* Number of vertices, triangles, and edges. */
+ fprintf(outfile, "OFF\n%ld %ld %ld\n", outvertices, m->triangles.items,
+ m->edges);
+
+ /* Write the vertices. */
+ traversalinit(&m->vertices);
+ vertexloop = vertextraverse(m);
+ while (vertexloop != (vertex) NULL) {
+ if (!b->jettison || (vertextype(vertexloop) != UNDEADVERTEX)) {
+ /* The "0.0" is here because the OFF format uses 3D coordinates. */
+ fprintf(outfile, " %.17g %.17g %.17g\n", vertexloop[0], vertexloop[1],
+ 0.0);
+ }
+ vertexloop = vertextraverse(m);
+ }
+
+ /* Write the triangles. */
+ traversalinit(&m->triangles);
+ triangleloop.tri = triangletraverse(m);
+ triangleloop.orient = 0;
+ while (triangleloop.tri != (triangle *) NULL) {
+ org(triangleloop, p1);
+ dest(triangleloop, p2);
+ apex(triangleloop, p3);
+ /* The "3" means a three-vertex polygon. */
+ fprintf(outfile, " 3 %4d %4d %4d\n", vertexmark(p1) - b->firstnumber,
+ vertexmark(p2) - b->firstnumber, vertexmark(p3) - b->firstnumber);
+ triangleloop.tri = triangletraverse(m);
+ }
+ finishfile(outfile, argc, argv);
+}
+
+#endif /* not TRILIBRARY */
+
+/** **/
+/** **/
+/********* File I/O routines end here *********/
+
+/*****************************************************************************/
+/* */
+/* quality_statistics() Print statistics about the quality of the mesh. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void quality_statistics(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void quality_statistics(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ struct otri triangleloop;
+ vertex p[3];
+ REAL cossquaretable[8];
+ REAL ratiotable[16];
+ REAL dx[3], dy[3];
+ REAL edgelength[3];
+ REAL dotproduct;
+ REAL cossquare;
+ REAL triarea;
+ REAL shortest, longest;
+ REAL trilongest2;
+ REAL smallestarea, biggestarea;
+ REAL triminaltitude2;
+ REAL minaltitude;
+ REAL triaspect2;
+ REAL worstaspect;
+ REAL smallestangle, biggestangle;
+ REAL radconst, degconst;
+ int angletable[18];
+ int aspecttable[16];
+ int aspectindex;
+ int tendegree;
+ int acutebiggest;
+ int i, ii, j, k;
+
+ printf("Mesh quality statistics:\n\n");
+ radconst = PI / 18.0;
+ degconst = 180.0 / PI;
+ for (i = 0; i < 8; i++) {
+ cossquaretable[i] = cos(radconst * (REAL) (i + 1));
+ cossquaretable[i] = cossquaretable[i] * cossquaretable[i];
+ }
+ for (i = 0; i < 18; i++) {
+ angletable[i] = 0;
+ }
+
+ ratiotable[0] = 1.5; ratiotable[1] = 2.0;
+ ratiotable[2] = 2.5; ratiotable[3] = 3.0;
+ ratiotable[4] = 4.0; ratiotable[5] = 6.0;
+ ratiotable[6] = 10.0; ratiotable[7] = 15.0;
+ ratiotable[8] = 25.0; ratiotable[9] = 50.0;
+ ratiotable[10] = 100.0; ratiotable[11] = 300.0;
+ ratiotable[12] = 1000.0; ratiotable[13] = 10000.0;
+ ratiotable[14] = 100000.0; ratiotable[15] = 0.0;
+ for (i = 0; i < 16; i++) {
+ aspecttable[i] = 0;
+ }
+
+ worstaspect = 0.0;
+ minaltitude = m->xmax - m->xmin + m->ymax - m->ymin;
+ minaltitude = minaltitude * minaltitude;
+ shortest = minaltitude;
+ longest = 0.0;
+ smallestarea = minaltitude;
+ biggestarea = 0.0;
+ worstaspect = 0.0;
+ smallestangle = 0.0;
+ biggestangle = 2.0;
+ acutebiggest = 1;
+
+ traversalinit(&m->triangles);
+ triangleloop.tri = triangletraverse(m);
+ triangleloop.orient = 0;
+ while (triangleloop.tri != (triangle *) NULL) {
+ org(triangleloop, p[0]);
+ dest(triangleloop, p[1]);
+ apex(triangleloop, p[2]);
+ trilongest2 = 0.0;
+
+ for (i = 0; i < 3; i++) {
+ j = plus1mod3[i];
+ k = minus1mod3[i];
+ dx[i] = p[j][0] - p[k][0];
+ dy[i] = p[j][1] - p[k][1];
+ edgelength[i] = dx[i] * dx[i] + dy[i] * dy[i];
+ if (edgelength[i] > trilongest2) {
+ trilongest2 = edgelength[i];
+ }
+ if (edgelength[i] > longest) {
+ longest = edgelength[i];
+ }
+ if (edgelength[i] < shortest) {
+ shortest = edgelength[i];
+ }
+ }
+
+ triarea = counterclockwise(m, b, p[0], p[1], p[2]);
+ if (triarea < smallestarea) {
+ smallestarea = triarea;
+ }
+ if (triarea > biggestarea) {
+ biggestarea = triarea;
+ }
+ triminaltitude2 = triarea * triarea / trilongest2;
+ if (triminaltitude2 < minaltitude) {
+ minaltitude = triminaltitude2;
+ }
+ triaspect2 = trilongest2 / triminaltitude2;
+ if (triaspect2 > worstaspect) {
+ worstaspect = triaspect2;
+ }
+ aspectindex = 0;
+ while ((triaspect2 > ratiotable[aspectindex] * ratiotable[aspectindex])
+ && (aspectindex < 15)) {
+ aspectindex++;
+ }
+ aspecttable[aspectindex]++;
+
+ for (i = 0; i < 3; i++) {
+ j = plus1mod3[i];
+ k = minus1mod3[i];
+ dotproduct = dx[j] * dx[k] + dy[j] * dy[k];
+ cossquare = dotproduct * dotproduct / (edgelength[j] * edgelength[k]);
+ tendegree = 8;
+ for (ii = 7; ii >= 0; ii--) {
+ if (cossquare > cossquaretable[ii]) {
+ tendegree = ii;
+ }
+ }
+ if (dotproduct <= 0.0) {
+ angletable[tendegree]++;
+ if (cossquare > smallestangle) {
+ smallestangle = cossquare;
+ }
+ if (acutebiggest && (cossquare < biggestangle)) {
+ biggestangle = cossquare;
+ }
+ } else {
+ angletable[17 - tendegree]++;
+ if (acutebiggest || (cossquare > biggestangle)) {
+ biggestangle = cossquare;
+ acutebiggest = 0;
+ }
+ }
+ }
+ triangleloop.tri = triangletraverse(m);
+ }
+
+ shortest = sqrt(shortest);
+ longest = sqrt(longest);
+ minaltitude = sqrt(minaltitude);
+ worstaspect = sqrt(worstaspect);
+ smallestarea *= 0.5;
+ biggestarea *= 0.5;
+ if (smallestangle >= 1.0) {
+ smallestangle = 0.0;
+ } else {
+ smallestangle = degconst * acos(sqrt(smallestangle));
+ }
+ if (biggestangle >= 1.0) {
+ biggestangle = 180.0;
+ } else {
+ if (acutebiggest) {
+ biggestangle = degconst * acos(sqrt(biggestangle));
+ } else {
+ biggestangle = 180.0 - degconst * acos(sqrt(biggestangle));
+ }
+ }
+
+ printf(" Smallest area: %16.5g | Largest area: %16.5g\n",
+ smallestarea, biggestarea);
+ printf(" Shortest edge: %16.5g | Longest edge: %16.5g\n",
+ shortest, longest);
+ printf(" Shortest altitude: %12.5g | Largest aspect ratio: %8.5g\n\n",
+ minaltitude, worstaspect);
+
+ printf(" Triangle aspect ratio histogram:\n");
+ printf(" 1.1547 - %-6.6g : %8d | %6.6g - %-6.6g : %8d\n",
+ ratiotable[0], aspecttable[0], ratiotable[7], ratiotable[8],
+ aspecttable[8]);
+ for (i = 1; i < 7; i++) {
+ printf(" %6.6g - %-6.6g : %8d | %6.6g - %-6.6g : %8d\n",
+ ratiotable[i - 1], ratiotable[i], aspecttable[i],
+ ratiotable[i + 7], ratiotable[i + 8], aspecttable[i + 8]);
+ }
+ printf(" %6.6g - %-6.6g : %8d | %6.6g - : %8d\n",
+ ratiotable[6], ratiotable[7], aspecttable[7], ratiotable[14],
+ aspecttable[15]);
+ printf(" (Aspect ratio is longest edge divided by shortest altitude)\n\n");
+
+ printf(" Smallest angle: %15.5g | Largest angle: %15.5g\n\n",
+ smallestangle, biggestangle);
+
+ printf(" Angle histogram:\n");
+ for (i = 0; i < 9; i++) {
+ printf(" %3d - %3d degrees: %8d | %3d - %3d degrees: %8d\n",
+ i * 10, i * 10 + 10, angletable[i],
+ i * 10 + 90, i * 10 + 100, angletable[i + 9]);
+ }
+ printf("\n");
+}
+
+/*****************************************************************************/
+/* */
+/* statistics() Print all sorts of cool facts. */
+/* */
+/*****************************************************************************/
+
+#ifdef ANSI_DECLARATORS
+void statistics(struct mesh *m, struct behavior *b)
+#else /* not ANSI_DECLARATORS */
+void statistics(m, b)
+struct mesh *m;
+struct behavior *b;
+#endif /* not ANSI_DECLARATORS */
+
+{
+ printf("\nStatistics:\n\n");
+ printf(" Input vertices: %d\n", m->invertices);
+ if (b->refine) {
+ printf(" Input triangles: %d\n", m->inelements);
+ }
+ if (b->poly) {
+ printf(" Input segments: %d\n", m->insegments);
+ if (!b->refine) {
+ printf(" Input holes: %d\n", m->holes);
+ }
+ }
+
+ printf("\n Mesh vertices: %ld\n", m->vertices.items - m->undeads);
+ printf(" Mesh triangles: %ld\n", m->triangles.items);
+ printf(" Mesh edges: %ld\n", m->edges);
+ printf(" Mesh exterior boundary edges: %ld\n", m->hullsize);
+ if (b->poly || b->refine) {
+ printf(" Mesh interior boundary edges: %ld\n",
+ m->subsegs.items - m->hullsize);
+ printf(" Mesh subsegments (constrained edges): %ld\n",
+ m->subsegs.items);
+ }
+ printf("\n");
+
+ if (b->verbose) {
+ quality_statistics(m, b);
+ printf("Memory allocation statistics:\n\n");
+ printf(" Maximum number of vertices: %ld\n", m->vertices.maxitems);
+ printf(" Maximum number of triangles: %ld\n", m->triangles.maxitems);
+ if (m->subsegs.maxitems > 0) {
+ printf(" Maximum number of subsegments: %ld\n", m->subsegs.maxitems);
+ }
+ if (m->viri.maxitems > 0) {
+ printf(" Maximum number of viri: %ld\n", m->viri.maxitems);
+ }
+ if (m->badsubsegs.maxitems > 0) {
+ printf(" Maximum number of encroached subsegments: %ld\n",
+ m->badsubsegs.maxitems);
+ }
+ if (m->badtriangles.maxitems > 0) {
+ printf(" Maximum number of bad triangles: %ld\n",
+ m->badtriangles.maxitems);
+ }
+ if (m->flipstackers.maxitems > 0) {
+ printf(" Maximum number of stacked triangle flips: %ld\n",
+ m->flipstackers.maxitems);
+ }
+ if (m->splaynodes.maxitems > 0) {
+ printf(" Maximum number of splay tree nodes: %ld\n",
+ m->splaynodes.maxitems);
+ }
+ printf(" Approximate heap memory use (bytes): %ld\n\n",
+ m->vertices.maxitems * m->vertices.itembytes +
+ m->triangles.maxitems * m->triangles.itembytes +
+ m->subsegs.maxitems * m->subsegs.itembytes +
+ m->viri.maxitems * m->viri.itembytes +
+ m->badsubsegs.maxitems * m->badsubsegs.itembytes +
+ m->badtriangles.maxitems * m->badtriangles.itembytes +
+ m->flipstackers.maxitems * m->flipstackers.itembytes +
+ m->splaynodes.maxitems * m->splaynodes.itembytes);
+
+ printf("Algorithmic statistics:\n\n");
+ if (!b->weighted) {
+ printf(" Number of incircle tests: %ld\n", m->incirclecount);
+ } else {
+ printf(" Number of 3D orientation tests: %ld\n", m->orient3dcount);
+ }
+ printf(" Number of 2D orientation tests: %ld\n", m->counterclockcount);
+ if (m->hyperbolacount > 0) {
+ printf(" Number of right-of-hyperbola tests: %ld\n",
+ m->hyperbolacount);
+ }
+ if (m->circletopcount > 0) {
+ printf(" Number of circle top computations: %ld\n",
+ m->circletopcount);
+ }
+ if (m->circumcentercount > 0) {
+ printf(" Number of triangle circumcenter computations: %ld\n",
+ m->circumcentercount);
+ }
+ printf("\n");
+ }
+}
+
+/*****************************************************************************/
+/* */
+/* main() or triangulate() Gosh, do everything. */
+/* */
+/* The sequence is roughly as follows. Many of these steps can be skipped, */
+/* depending on the command line switches. */
+/* */
+/* - Initialize constants and parse the command line. */
+/* - Read the vertices from a file and either */
+/* - triangulate them (no -r), or */
+/* - read an old mesh from files and reconstruct it (-r). */
+/* - Insert the PSLG segments (-p), and possibly segments on the convex */
+/* hull (-c). */
+/* - Read the holes (-p), regional attributes (-pA), and regional area */
+/* constraints (-pa). Carve the holes and concavities, and spread the */
+/* regional attributes and area constraints. */
+/* - Enforce the constraints on minimum angle (-q) and maximum area (-a). */
+/* Also enforce the conforming Delaunay property (-q and -a). */
+/* - Compute the number of edges in the resulting mesh. */
+/* - Promote the mesh's linear triangles to higher order elements (-o). */
+/* - Write the output files and print the statistics. */
+/* - Check the consistency and Delaunay property of the mesh (-C). */
+/* */
+/*****************************************************************************/
+
+#ifdef TRILIBRARY
+
+#ifdef ANSI_DECLARATORS
+void triangulate(char *triswitches, struct triangulateio *in,
+ struct triangulateio *out, struct triangulateio *vorout)
+#else /* not ANSI_DECLARATORS */
+void triangulate(triswitches, in, out, vorout)
+char *triswitches;
+struct triangulateio *in;
+struct triangulateio *out;
+struct triangulateio *vorout;
+#endif /* not ANSI_DECLARATORS */
+
+#else /* not TRILIBRARY */
+
+#ifdef ANSI_DECLARATORS
+int main(int argc, char **argv)
+#else /* not ANSI_DECLARATORS */
+int main(argc, argv)
+int argc;
+char **argv;
+#endif /* not ANSI_DECLARATORS */
+
+#endif /* not TRILIBRARY */
+
+{
+ struct mesh m;
+ struct behavior b;
+ REAL *holearray; /* Array of holes. */
+ REAL *regionarray; /* Array of regional attributes and area constraints. */
+#ifndef TRILIBRARY
+ FILE *polyfile;
+#endif /* not TRILIBRARY */
+#ifndef NO_TIMER
+ /* Variables for timing the performance of Triangle. The types are */
+ /* defined in sys/time.h. */
+ struct timeval tv0, tv1, tv2, tv3, tv4, tv5, tv6;
+ struct timezone tz;
+#endif /* not NO_TIMER */
+
+#ifndef NO_TIMER
+ gettimeofday(&tv0, &tz);
+#endif /* not NO_TIMER */
+
+ triangleinit(&m);
+#ifdef TRILIBRARY
+ parsecommandline(1, &triswitches, &b);
+#else /* not TRILIBRARY */
+ parsecommandline(argc, argv, &b);
+#endif /* not TRILIBRARY */
+ m.steinerleft = b.steiner;
+
+#ifdef TRILIBRARY
+ transfernodes(&m, &b, in->pointlist, in->pointattributelist,
+ in->pointmarkerlist, in->numberofpoints,
+ in->numberofpointattributes);
+#else /* not TRILIBRARY */
+ readnodes(&m, &b, b.innodefilename, b.inpolyfilename, &polyfile);
+#endif /* not TRILIBRARY */
+
+#ifndef NO_TIMER
+ if (!b.quiet) {
+ gettimeofday(&tv1, &tz);
+ }
+#endif /* not NO_TIMER */
+
+#ifdef CDT_ONLY
+ m.hullsize = delaunay(&m, &b); /* Triangulate the vertices. */
+#else /* not CDT_ONLY */
+ if (b.refine) {
+ /* Read and reconstruct a mesh. */
+#ifdef TRILIBRARY
+ m.hullsize = reconstruct(&m, &b, in->trianglelist,
+ in->triangleattributelist, in->trianglearealist,
+ in->numberoftriangles, in->numberofcorners,
+ in->numberoftriangleattributes,
+ in->segmentlist, in->segmentmarkerlist,
+ in->numberofsegments);
+#else /* not TRILIBRARY */
+ m.hullsize = reconstruct(&m, &b, b.inelefilename, b.areafilename,
+ b.inpolyfilename, polyfile);
+#endif /* not TRILIBRARY */
+ } else {
+ m.hullsize = delaunay(&m, &b); /* Triangulate the vertices. */
+ }
+#endif /* not CDT_ONLY */
+
+#ifndef NO_TIMER
+ if (!b.quiet) {
+ gettimeofday(&tv2, &tz);
+ if (b.refine) {
+ printf("Mesh reconstruction");
+ } else {
+ printf("Delaunay");
+ }
+ printf(" milliseconds: %ld\n", 1000l * (tv2.tv_sec - tv1.tv_sec) +
+ (tv2.tv_usec - tv1.tv_usec) / 1000l);
+ }
+#endif /* not NO_TIMER */
+
+ /* Ensure that no vertex can be mistaken for a triangular bounding */
+ /* box vertex in insertvertex(). */
+ m.infvertex1 = (vertex) NULL;
+ m.infvertex2 = (vertex) NULL;
+ m.infvertex3 = (vertex) NULL;
+
+ if (b.usesegments) {
+ m.checksegments = 1; /* Segments will be introduced next. */
+ if (!b.refine) {
+ /* Insert PSLG segments and/or convex hull segments. */
+#ifdef TRILIBRARY
+ formskeleton(&m, &b, in->segmentlist,
+ in->segmentmarkerlist, in->numberofsegments);
+#else /* not TRILIBRARY */
+ formskeleton(&m, &b, polyfile, b.inpolyfilename);
+#endif /* not TRILIBRARY */
+ }
+ }
+
+#ifndef NO_TIMER
+ if (!b.quiet) {
+ gettimeofday(&tv3, &tz);
+ if (b.usesegments && !b.refine) {
+ printf("Segment milliseconds: %ld\n",
+ 1000l * (tv3.tv_sec - tv2.tv_sec) +
+ (tv3.tv_usec - tv2.tv_usec) / 1000l);
+ }
+ }
+#endif /* not NO_TIMER */
+
+ if (b.poly && (m.triangles.items > 0)) {
+#ifdef TRILIBRARY
+ holearray = in->holelist;
+ m.holes = in->numberofholes;
+ regionarray = in->regionlist;
+ m.regions = in->numberofregions;
+#else /* not TRILIBRARY */
+ readholes(&m, &b, polyfile, b.inpolyfilename, &holearray, &m.holes,
+ ®ionarray, &m.regions);
+#endif /* not TRILIBRARY */
+ if (!b.refine) {
+ /* Carve out holes and concavities. */
+ carveholes(&m, &b, holearray, m.holes, regionarray, m.regions);
+ }
+ } else {
+ /* Without a PSLG, there can be no holes or regional attributes */
+ /* or area constraints. The following are set to zero to avoid */
+ /* an accidental free() later. */
+ m.holes = 0;
+ m.regions = 0;
+ }
+
+#ifndef NO_TIMER
+ if (!b.quiet) {
+ gettimeofday(&tv4, &tz);
+ if (b.poly && !b.refine) {
+ printf("Hole milliseconds: %ld\n", 1000l * (tv4.tv_sec - tv3.tv_sec) +
+ (tv4.tv_usec - tv3.tv_usec) / 1000l);
+ }
+ }
+#endif /* not NO_TIMER */
+
+#ifndef CDT_ONLY
+ if (b.quality && (m.triangles.items > 0)) {
+ enforcequality(&m, &b); /* Enforce angle and area constraints. */
+ }
+#endif /* not CDT_ONLY */
+
+#ifndef NO_TIMER
+ if (!b.quiet) {
+ gettimeofday(&tv5, &tz);
+#ifndef CDT_ONLY
+ if (b.quality) {
+ printf("Quality milliseconds: %ld\n",
+ 1000l * (tv5.tv_sec - tv4.tv_sec) +
+ (tv5.tv_usec - tv4.tv_usec) / 1000l);
+ }
+#endif /* not CDT_ONLY */
+ }
+#endif /* not NO_TIMER */
+
+ /* Calculate the number of edges. */
+ m.edges = (3l * m.triangles.items + m.hullsize) / 2l;
+
+ if (b.order > 1) {
+ highorder(&m, &b); /* Promote elements to higher polynomial order. */
+ }
+ if (!b.quiet) {
+ printf("\n");
+ }
+
+#ifdef TRILIBRARY
+ if (b.jettison) {
+ out->numberofpoints = m.vertices.items - m.undeads;
+ } else {
+ out->numberofpoints = m.vertices.items;
+ }
+ out->numberofpointattributes = m.nextras;
+ out->numberoftriangles = m.triangles.items;
+ out->numberofcorners = (b.order + 1) * (b.order + 2) / 2;
+ out->numberoftriangleattributes = m.eextras;
+ out->numberofedges = m.edges;
+ if (b.usesegments) {
+ out->numberofsegments = m.subsegs.items;
+ } else {
+ out->numberofsegments = m.hullsize;
+ }
+ if (vorout != (struct triangulateio *) NULL) {
+ vorout->numberofpoints = m.triangles.items;
+ vorout->numberofpointattributes = m.nextras;
+ vorout->numberofedges = m.edges;
+ }
+#endif /* TRILIBRARY */
+ /* If not using iteration numbers, don't write a .node file if one was */
+ /* read, because the original one would be overwritten! */
+ if (b.nonodewritten || (b.noiterationnum && m.readnodefile)) {
+ if (!b.quiet) {
+#ifdef TRILIBRARY
+ printf("NOT writing vertices.\n");
+#else /* not TRILIBRARY */
+ printf("NOT writing a .node file.\n");
+#endif /* not TRILIBRARY */
+ }
+ numbernodes(&m, &b); /* We must remember to number the vertices. */
+ } else {
+ /* writenodes() numbers the vertices too. */
+#ifdef TRILIBRARY
+ writenodes(&m, &b, &out->pointlist, &out->pointattributelist,
+ &out->pointmarkerlist);
+#else /* not TRILIBRARY */
+ writenodes(&m, &b, b.outnodefilename, argc, argv);
+#endif /* TRILIBRARY */
+ }
+ if (b.noelewritten) {
+ if (!b.quiet) {
+#ifdef TRILIBRARY
+ printf("NOT writing triangles.\n");
+#else /* not TRILIBRARY */
+ printf("NOT writing an .ele file.\n");
+#endif /* not TRILIBRARY */
+ }
+ } else {
+#ifdef TRILIBRARY
+ writeelements(&m, &b, &out->trianglelist, &out->triangleattributelist);
+#else /* not TRILIBRARY */
+ writeelements(&m, &b, b.outelefilename, argc, argv);
+#endif /* not TRILIBRARY */
+ }
+ /* The -c switch (convex switch) causes a PSLG to be written */
+ /* even if none was read. */
+ if (b.poly || b.convex) {
+ /* If not using iteration numbers, don't overwrite the .poly file. */
+ if (b.nopolywritten || b.noiterationnum) {
+ if (!b.quiet) {
+#ifdef TRILIBRARY
+ printf("NOT writing segments.\n");
+#else /* not TRILIBRARY */
+ printf("NOT writing a .poly file.\n");
+#endif /* not TRILIBRARY */
+ }
+ } else {
+#ifdef TRILIBRARY
+ writepoly(&m, &b, &out->segmentlist, &out->segmentmarkerlist);
+ out->numberofholes = m.holes;
+ out->numberofregions = m.regions;
+ if (b.poly) {
+ out->holelist = in->holelist;
+ out->regionlist = in->regionlist;
+ } else {
+ out->holelist = (REAL *) NULL;
+ out->regionlist = (REAL *) NULL;
+ }
+#else /* not TRILIBRARY */
+ writepoly(&m, &b, b.outpolyfilename, holearray, m.holes, regionarray,
+ m.regions, argc, argv);
+#endif /* not TRILIBRARY */
+ }
+ }
+#ifndef TRILIBRARY
+#ifndef CDT_ONLY
+ if (m.regions > 0) {
+ trifree((VOID *) regionarray);
+ }
+#endif /* not CDT_ONLY */
+ if (m.holes > 0) {
+ trifree((VOID *) holearray);
+ }
+ if (b.geomview) {
+ writeoff(&m, &b, b.offfilename, argc, argv);
+ }
+#endif /* not TRILIBRARY */
+ if (b.edgesout) {
+#ifdef TRILIBRARY
+ writeedges(&m, &b, &out->edgelist, &out->edgemarkerlist);
+#else /* not TRILIBRARY */
+ writeedges(&m, &b, b.edgefilename, argc, argv);
+#endif /* not TRILIBRARY */
+ }
+ if (b.voronoi) {
+#ifdef TRILIBRARY
+ writevoronoi(&m, &b, &vorout->pointlist, &vorout->pointattributelist,
+ &vorout->pointmarkerlist, &vorout->edgelist,
+ &vorout->edgemarkerlist, &vorout->normlist);
+#else /* not TRILIBRARY */
+ writevoronoi(&m, &b, b.vnodefilename, b.vedgefilename, argc, argv);
+#endif /* not TRILIBRARY */
+ }
+ if (b.neighbors) {
+#ifdef TRILIBRARY
+ writeneighbors(&m, &b, &out->neighborlist);
+#else /* not TRILIBRARY */
+ writeneighbors(&m, &b, b.neighborfilename, argc, argv);
+#endif /* not TRILIBRARY */
+ }
+
+ if (!b.quiet) {
+#ifndef NO_TIMER
+ gettimeofday(&tv6, &tz);
+ printf("\nOutput milliseconds: %ld\n",
+ 1000l * (tv6.tv_sec - tv5.tv_sec) +
+ (tv6.tv_usec - tv5.tv_usec) / 1000l);
+ printf("Total running milliseconds: %ld\n",
+ 1000l * (tv6.tv_sec - tv0.tv_sec) +
+ (tv6.tv_usec - tv0.tv_usec) / 1000l);
+#endif /* not NO_TIMER */
+
+ statistics(&m, &b);
+ }
+
+#ifndef REDUCED
+ if (b.docheck) {
+ checkmesh(&m, &b);
+ checkdelaunay(&m, &b);
+ }
+#endif /* not REDUCED */
+
+ triangledeinit(&m, &b);
+#ifndef TRILIBRARY
+ return 0;
+#endif /* not TRILIBRARY */
+}
diff --git a/VectorTileMap/jni/triangle.h b/VectorTileMap/jni/triangle.h
new file mode 100644
index 0000000..4195ceb
--- /dev/null
+++ b/VectorTileMap/jni/triangle.h
@@ -0,0 +1,300 @@
+/*****************************************************************************/
+/* */
+/* (triangle.h) */
+/* */
+/* Include file for programs that call Triangle. */
+/* */
+/* Accompanies Triangle Version 1.6 */
+/* July 28, 2005 */
+/* */
+/* Copyright 1996, 2005 */
+/* Jonathan Richard Shewchuk */
+/* 2360 Woolsey #H */
+/* Berkeley, California 94705-1927 */
+/* jrs@cs.berkeley.edu */
+/* */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* */
+/* How to call Triangle from another program */
+/* */
+/* */
+/* If you haven't read Triangle's instructions (run "triangle -h" to read */
+/* them), you won't understand what follows. */
+/* */
+/* Triangle must be compiled into an object file (triangle.o) with the */
+/* TRILIBRARY symbol defined (generally by using the -DTRILIBRARY compiler */
+/* switch). The makefile included with Triangle will do this for you if */
+/* you run "make trilibrary". The resulting object file can be called via */
+/* the procedure triangulate(). */
+/* */
+/* If the size of the object file is important to you, you may wish to */
+/* generate a reduced version of triangle.o. The REDUCED symbol gets rid */
+/* of all features that are primarily of research interest. Specifically, */
+/* the -DREDUCED switch eliminates Triangle's -i, -F, -s, and -C switches. */
+/* The CDT_ONLY symbol gets rid of all meshing algorithms above and beyond */
+/* constrained Delaunay triangulation. Specifically, the -DCDT_ONLY switch */
+/* eliminates Triangle's -r, -q, -a, -u, -D, -Y, -S, and -s switches. */
+/* */
+/* IMPORTANT: These definitions (TRILIBRARY, REDUCED, CDT_ONLY) must be */
+/* made in the makefile or in triangle.c itself. Putting these definitions */
+/* in this file (triangle.h) will not create the desired effect. */
+/* */
+/* */
+/* The calling convention for triangulate() follows. */
+/* */
+/* void triangulate(triswitches, in, out, vorout) */
+/* char *triswitches; */
+/* struct triangulateio *in; */
+/* struct triangulateio *out; */
+/* struct triangulateio *vorout; */
+/* */
+/* `triswitches' is a string containing the command line switches you wish */
+/* to invoke. No initial dash is required. Some suggestions: */
+/* */
+/* - You'll probably find it convenient to use the `z' switch so that */
+/* points (and other items) are numbered from zero. This simplifies */
+/* indexing, because the first item of any type always starts at index */
+/* [0] of the corresponding array, whether that item's number is zero or */
+/* one. */
+/* - You'll probably want to use the `Q' (quiet) switch in your final code, */
+/* but you can take advantage of Triangle's printed output (including the */
+/* `V' switch) while debugging. */
+/* - If you are not using the `q', `a', `u', `D', `j', or `s' switches, */
+/* then the output points will be identical to the input points, except */
+/* possibly for the boundary markers. If you don't need the boundary */
+/* markers, you should use the `N' (no nodes output) switch to save */
+/* memory. (If you do need boundary markers, but need to save memory, a */
+/* good nasty trick is to set out->pointlist equal to in->pointlist */
+/* before calling triangulate(), so that Triangle overwrites the input */
+/* points with identical copies.) */
+/* - The `I' (no iteration numbers) and `g' (.off file output) switches */
+/* have no effect when Triangle is compiled with TRILIBRARY defined. */
+/* */
+/* `in', `out', and `vorout' are descriptions of the input, the output, */
+/* and the Voronoi output. If the `v' (Voronoi output) switch is not used, */
+/* `vorout' may be NULL. `in' and `out' may never be NULL. */
+/* */
+/* Certain fields of the input and output structures must be initialized, */
+/* as described below. */
+/* */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* */
+/* The `triangulateio' structure. */
+/* */
+/* Used to pass data into and out of the triangulate() procedure. */
+/* */
+/* */
+/* Arrays are used to store points, triangles, markers, and so forth. In */
+/* all cases, the first item in any array is stored starting at index [0]. */
+/* However, that item is item number `1' unless the `z' switch is used, in */
+/* which case it is item number `0'. Hence, you may find it easier to */
+/* index points (and triangles in the neighbor list) if you use the `z' */
+/* switch. Unless, of course, you're calling Triangle from a Fortran */
+/* program. */
+/* */
+/* Description of fields (except the `numberof' fields, which are obvious): */
+/* */
+/* `pointlist': An array of point coordinates. The first point's x */
+/* coordinate is at index [0] and its y coordinate at index [1], followed */
+/* by the coordinates of the remaining points. Each point occupies two */
+/* REALs. */
+/* `pointattributelist': An array of point attributes. Each point's */
+/* attributes occupy `numberofpointattributes' REALs. */
+/* `pointmarkerlist': An array of point markers; one int per point. */
+/* */
+/* `trianglelist': An array of triangle corners. The first triangle's */
+/* first corner is at index [0], followed by its other two corners in */
+/* counterclockwise order, followed by any other nodes if the triangle */
+/* represents a nonlinear element. Each triangle occupies */
+/* `numberofcorners' ints. */
+/* `triangleattributelist': An array of triangle attributes. Each */
+/* triangle's attributes occupy `numberoftriangleattributes' REALs. */
+/* `trianglearealist': An array of triangle area constraints; one REAL per */
+/* triangle. Input only. */
+/* `neighborlist': An array of triangle neighbors; three ints per */
+/* triangle. Output only. */
+/* */
+/* `segmentlist': An array of segment endpoints. The first segment's */
+/* endpoints are at indices [0] and [1], followed by the remaining */
+/* segments. Two ints per segment. */
+/* `segmentmarkerlist': An array of segment markers; one int per segment. */
+/* */
+/* `holelist': An array of holes. The first hole's x and y coordinates */
+/* are at indices [0] and [1], followed by the remaining holes. Two */
+/* REALs per hole. Input only, although the pointer is copied to the */
+/* output structure for your convenience. */
+/* */
+/* `regionlist': An array of regional attributes and area constraints. */
+/* The first constraint's x and y coordinates are at indices [0] and [1], */
+/* followed by the regional attribute at index [2], followed by the */
+/* maximum area at index [3], followed by the remaining area constraints. */
+/* Four REALs per area constraint. Note that each regional attribute is */
+/* used only if you select the `A' switch, and each area constraint is */
+/* used only if you select the `a' switch (with no number following), but */
+/* omitting one of these switches does not change the memory layout. */
+/* Input only, although the pointer is copied to the output structure for */
+/* your convenience. */
+/* */
+/* `edgelist': An array of edge endpoints. The first edge's endpoints are */
+/* at indices [0] and [1], followed by the remaining edges. Two ints per */
+/* edge. Output only. */
+/* `edgemarkerlist': An array of edge markers; one int per edge. Output */
+/* only. */
+/* `normlist': An array of normal vectors, used for infinite rays in */
+/* Voronoi diagrams. The first normal vector's x and y magnitudes are */
+/* at indices [0] and [1], followed by the remaining vectors. For each */
+/* finite edge in a Voronoi diagram, the normal vector written is the */
+/* zero vector. Two REALs per edge. Output only. */
+/* */
+/* */
+/* Any input fields that Triangle will examine must be initialized. */
+/* Furthermore, for each output array that Triangle will write to, you */
+/* must either provide space by setting the appropriate pointer to point */
+/* to the space you want the data written to, or you must initialize the */
+/* pointer to NULL, which tells Triangle to allocate space for the results. */
+/* The latter option is preferable, because Triangle always knows exactly */
+/* how much space to allocate. The former option is provided mainly for */
+/* people who need to call Triangle from Fortran code, though it also makes */
+/* possible some nasty space-saving tricks, like writing the output to the */
+/* same arrays as the input. */
+/* */
+/* Triangle will not free() any input or output arrays, including those it */
+/* allocates itself; that's up to you. You should free arrays allocated by */
+/* Triangle by calling the trifree() procedure defined below. (By default, */
+/* trifree() just calls the standard free() library procedure, but */
+/* applications that call triangulate() may replace trimalloc() and */
+/* trifree() in triangle.c to use specialized memory allocators.) */
+/* */
+/* Here's a guide to help you decide which fields you must initialize */
+/* before you call triangulate(). */
+/* */
+/* `in': */
+/* */
+/* - `pointlist' must always point to a list of points; `numberofpoints' */
+/* and `numberofpointattributes' must be properly set. */
+/* `pointmarkerlist' must either be set to NULL (in which case all */
+/* markers default to zero), or must point to a list of markers. If */
+/* `numberofpointattributes' is not zero, `pointattributelist' must */
+/* point to a list of point attributes. */
+/* - If the `r' switch is used, `trianglelist' must point to a list of */
+/* triangles, and `numberoftriangles', `numberofcorners', and */
+/* `numberoftriangleattributes' must be properly set. If */
+/* `numberoftriangleattributes' is not zero, `triangleattributelist' */
+/* must point to a list of triangle attributes. If the `a' switch is */
+/* used (with no number following), `trianglearealist' must point to a */
+/* list of triangle area constraints. `neighborlist' may be ignored. */
+/* - If the `p' switch is used, `segmentlist' must point to a list of */
+/* segments, `numberofsegments' must be properly set, and */
+/* `segmentmarkerlist' must either be set to NULL (in which case all */
+/* markers default to zero), or must point to a list of markers. */
+/* - If the `p' switch is used without the `r' switch, then */
+/* `numberofholes' and `numberofregions' must be properly set. If */
+/* `numberofholes' is not zero, `holelist' must point to a list of */
+/* holes. If `numberofregions' is not zero, `regionlist' must point to */
+/* a list of region constraints. */
+/* - If the `p' switch is used, `holelist', `numberofholes', */
+/* `regionlist', and `numberofregions' is copied to `out'. (You can */
+/* nonetheless get away with not initializing them if the `r' switch is */
+/* used.) */
+/* - `edgelist', `edgemarkerlist', `normlist', and `numberofedges' may be */
+/* ignored. */
+/* */
+/* `out': */
+/* */
+/* - `pointlist' must be initialized (NULL or pointing to memory) unless */
+/* the `N' switch is used. `pointmarkerlist' must be initialized */
+/* unless the `N' or `B' switch is used. If `N' is not used and */
+/* `in->numberofpointattributes' is not zero, `pointattributelist' must */
+/* be initialized. */
+/* - `trianglelist' must be initialized unless the `E' switch is used. */
+/* `neighborlist' must be initialized if the `n' switch is used. If */
+/* the `E' switch is not used and (`in->numberofelementattributes' is */
+/* not zero or the `A' switch is used), `elementattributelist' must be */
+/* initialized. `trianglearealist' may be ignored. */
+/* - `segmentlist' must be initialized if the `p' or `c' switch is used, */
+/* and the `P' switch is not used. `segmentmarkerlist' must also be */
+/* initialized under these circumstances unless the `B' switch is used. */
+/* - `edgelist' must be initialized if the `e' switch is used. */
+/* `edgemarkerlist' must be initialized if the `e' switch is used and */
+/* the `B' switch is not. */
+/* - `holelist', `regionlist', `normlist', and all scalars may be ignored.*/
+/* */
+/* `vorout' (only needed if `v' switch is used): */
+/* */
+/* - `pointlist' must be initialized. If `in->numberofpointattributes' */
+/* is not zero, `pointattributelist' must be initialized. */
+/* `pointmarkerlist' may be ignored. */
+/* - `edgelist' and `normlist' must both be initialized. */
+/* `edgemarkerlist' may be ignored. */
+/* - Everything else may be ignored. */
+/* */
+/* After a call to triangulate(), the valid fields of `out' and `vorout' */
+/* will depend, in an obvious way, on the choice of switches used. Note */
+/* that when the `p' switch is used, the pointers `holelist' and */
+/* `regionlist' are copied from `in' to `out', but no new space is */
+/* allocated; be careful that you don't free() the same array twice. On */
+/* the other hand, Triangle will never copy the `pointlist' pointer (or any */
+/* others); new space is allocated for `out->pointlist', or if the `N' */
+/* switch is used, `out->pointlist' remains uninitialized. */
+/* */
+/* All of the meaningful `numberof' fields will be properly set; for */
+/* instance, `numberofedges' will represent the number of edges in the */
+/* triangulation whether or not the edges were written. If segments are */
+/* not used, `numberofsegments' will indicate the number of boundary edges. */
+/* */
+/*****************************************************************************/
+
+
+#define SINGLE
+
+#ifdef SINGLE
+#define REAL float
+#else /* not SINGLE */
+#define REAL double
+#endif /* not SINGLE */
+
+#define INDICE unsigned short
+
+struct triangulateio {
+ REAL *pointlist; /* In / out */
+ REAL *pointattributelist; /* In / out */
+ int *pointmarkerlist; /* In / out */
+ int numberofpoints; /* In / out */
+ int numberofpointattributes; /* In / out */
+
+ INDICE *trianglelist; /* In / out */
+ REAL *triangleattributelist; /* In / out */
+ REAL *trianglearealist; /* In only */
+ int *neighborlist; /* Out only */
+ int numberoftriangles; /* In / out */
+ int numberofcorners; /* In / out */
+ int numberoftriangleattributes; /* In / out */
+
+ int *segmentlist; /* In / out */
+ int *segmentmarkerlist; /* In / out */
+ int numberofsegments; /* In / out */
+
+ REAL *holelist; /* In / pointer to array copied out */
+ int numberofholes; /* In / copied out */
+
+ REAL *regionlist; /* In / pointer to array copied out */
+ int numberofregions; /* In / copied out */
+
+ int *edgelist; /* Out only */
+ int *edgemarkerlist; /* Not used with Voronoi diagram; out only */
+ REAL *normlist; /* Used only with Voronoi diagram; out only */
+ int numberofedges; /* Out only */
+};
+
+#ifdef ANSI_DECLARATORS
+void triangulate(char *, struct triangulateio *, struct triangulateio *,
+ struct triangulateio *);
+void trifree(VOID *memptr);
+#else /* not ANSI_DECLARATORS */
+void triangulate();
+void trifree();
+#endif /* not ANSI_DECLARATORS */
diff --git a/VectorTileMap/lib/poly2tri-core-0.1.1-SNAPSHOT-javadoc.jar b/VectorTileMap/lib/poly2tri-core-0.1.1-SNAPSHOT-javadoc.jar
deleted file mode 100644
index 10efdf6..0000000
Binary files a/VectorTileMap/lib/poly2tri-core-0.1.1-SNAPSHOT-javadoc.jar and /dev/null differ
diff --git a/VectorTileMap/lib/poly2tri-core-0.1.1-SNAPSHOT.jar b/VectorTileMap/lib/poly2tri-core-0.1.1-SNAPSHOT.jar
deleted file mode 100644
index d653331..0000000
Binary files a/VectorTileMap/lib/poly2tri-core-0.1.1-SNAPSHOT.jar and /dev/null differ
diff --git a/VectorTileMap/libs/armeabi/libtriangle-jni.so b/VectorTileMap/libs/armeabi/libtriangle-jni.so
new file mode 100755
index 0000000..1ac43a6
Binary files /dev/null and b/VectorTileMap/libs/armeabi/libtriangle-jni.so differ
diff --git a/VectorTileMap/lib/postgresql-9.0-801.jdbc4.jar b/VectorTileMap/libs/postgresql-9.0-801.jdbc4.jar
similarity index 100%
rename from VectorTileMap/lib/postgresql-9.0-801.jdbc4.jar
rename to VectorTileMap/libs/postgresql-9.0-801.jdbc4.jar
diff --git a/VectorTileMap/myproguard.cfg b/VectorTileMap/myproguard.cfg
new file mode 100644
index 0000000..91a2a7d
--- /dev/null
+++ b/VectorTileMap/myproguard.cfg
@@ -0,0 +1,55 @@
+-injars bin/classes
+
+-libraryjars /home/jeff/src/android/platforms/android-16/android.jar
+-libraryjars lib/postgresql-9.0-801.jdbc4.jar
+
+-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
+-optimizationpasses 5
+-dontusemixedcaseclassnames
+-dontpreverify
+-verbose
+
+
+-keep public class * extends android.app.Activity
+
+-keep public class * extends android.app.Application
+
+-keep public class * extends android.app.Service
+
+-keep public class * extends android.content.BroadcastReceiver
+
+-keep public class * extends android.content.ContentProvider
+
+-keep public class * extends android.app.backup.BackupAgentHelper
+
+-keep public class * extends android.preference.Preference
+
+-keep public class com.android.vending.licensing.ILicensingService
+
+-keepclasseswithmembers class * {
+ public (android.content.Context,android.util.AttributeSet);
+}
+
+-keepclasseswithmembers class * {
+ public (android.content.Context,android.util.AttributeSet,int);
+}
+
+-keepclassmembers class * extends android.app.Activity {
+ public void *(android.view.View);
+}
+
+-keep class * extends android.os.Parcelable {
+ public static final android.os.Parcelable$Creator *;
+}
+
+# Also keep - Enumerations. Keep the special static methods that are required in
+# enumeration classes.
+-keepclassmembers enum * {
+ public static **[] values();
+ public static ** valueOf(java.lang.String);
+}
+
+# Keep names - Native method names. Keep all native class/method names.
+-keepclasseswithmembers,allowshrinking class * {
+ native ;
+}
diff --git a/VectorTileMap/proguard.cfg b/VectorTileMap/proguard.cfg
index b1cdf17..a10bf7a 100644
--- a/VectorTileMap/proguard.cfg
+++ b/VectorTileMap/proguard.cfg
@@ -1,8 +1,12 @@
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
+-dontskipnonpubliclibraryclassmembers
-dontpreverify
-verbose
+-libraryjars lib/postgresql-9.0-801.jdbc4.jar
+-ignorewarnings
+
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-keep public class * extends android.app.Activity
diff --git a/VectorTileMap/project.properties b/VectorTileMap/project.properties
index 8f81673..bef2caf 100644
--- a/VectorTileMap/project.properties
+++ b/VectorTileMap/project.properties
@@ -10,4 +10,7 @@
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
-target=android-16
+target=android-17
+
+#proguard.config=proguard.cfg
+android.library=true
diff --git a/VectorTileMap/res/drawable-hdpi/bar_globe.png b/VectorTileMap/res/drawable-hdpi/bar_globe.png
deleted file mode 100644
index 4a5756d..0000000
Binary files a/VectorTileMap/res/drawable-hdpi/bar_globe.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-hdpi/bar_globe2.png b/VectorTileMap/res/drawable-hdpi/bar_globe2.png
deleted file mode 100644
index 750487f..0000000
Binary files a/VectorTileMap/res/drawable-hdpi/bar_globe2.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-hdpi/btn_snap_normal.png b/VectorTileMap/res/drawable-hdpi/btn_snap_normal.png
deleted file mode 100644
index b5689a5..0000000
Binary files a/VectorTileMap/res/drawable-hdpi/btn_snap_normal.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-hdpi/btn_snap_pressed.png b/VectorTileMap/res/drawable-hdpi/btn_snap_pressed.png
deleted file mode 100644
index fc88a17..0000000
Binary files a/VectorTileMap/res/drawable-hdpi/btn_snap_pressed.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-hdpi/btn_snap_selected.png b/VectorTileMap/res/drawable-hdpi/btn_snap_selected.png
deleted file mode 100644
index e887dfd..0000000
Binary files a/VectorTileMap/res/drawable-hdpi/btn_snap_selected.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-hdpi/file_picker_back.png b/VectorTileMap/res/drawable-hdpi/file_picker_back.png
deleted file mode 100644
index 1efe071..0000000
Binary files a/VectorTileMap/res/drawable-hdpi/file_picker_back.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-hdpi/file_picker_file.png b/VectorTileMap/res/drawable-hdpi/file_picker_file.png
deleted file mode 100644
index 68fc48f..0000000
Binary files a/VectorTileMap/res/drawable-hdpi/file_picker_file.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-hdpi/file_picker_folder.png b/VectorTileMap/res/drawable-hdpi/file_picker_folder.png
deleted file mode 100644
index 0fc3e09..0000000
Binary files a/VectorTileMap/res/drawable-hdpi/file_picker_folder.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-hdpi/ic_menu_archive.png b/VectorTileMap/res/drawable-hdpi/ic_menu_archive.png
deleted file mode 100644
index 6f441b4..0000000
Binary files a/VectorTileMap/res/drawable-hdpi/ic_menu_archive.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-hdpi/ic_menu_camera.png b/VectorTileMap/res/drawable-hdpi/ic_menu_camera.png
deleted file mode 100644
index 4e10e3e..0000000
Binary files a/VectorTileMap/res/drawable-hdpi/ic_menu_camera.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-hdpi/ic_menu_info_details.png b/VectorTileMap/res/drawable-hdpi/ic_menu_info_details.png
deleted file mode 100644
index 7696ceb..0000000
Binary files a/VectorTileMap/res/drawable-hdpi/ic_menu_info_details.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-hdpi/ic_menu_mapmode.png b/VectorTileMap/res/drawable-hdpi/ic_menu_mapmode.png
deleted file mode 100644
index 80c2aa9..0000000
Binary files a/VectorTileMap/res/drawable-hdpi/ic_menu_mapmode.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-hdpi/ic_menu_mylocation.png b/VectorTileMap/res/drawable-hdpi/ic_menu_mylocation.png
deleted file mode 100644
index d7fae7e..0000000
Binary files a/VectorTileMap/res/drawable-hdpi/ic_menu_mylocation.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-hdpi/ic_menu_options.png b/VectorTileMap/res/drawable-hdpi/ic_menu_options.png
deleted file mode 100644
index 3e4580e..0000000
Binary files a/VectorTileMap/res/drawable-hdpi/ic_menu_options.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-hdpi/ic_menu_preferences.png b/VectorTileMap/res/drawable-hdpi/ic_menu_preferences.png
deleted file mode 100644
index 81bca4a..0000000
Binary files a/VectorTileMap/res/drawable-hdpi/ic_menu_preferences.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-hdpi/my_location.png b/VectorTileMap/res/drawable-hdpi/my_location.png
deleted file mode 100644
index 3d12cc2..0000000
Binary files a/VectorTileMap/res/drawable-hdpi/my_location.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-mdpi/bar_globe2.png b/VectorTileMap/res/drawable-mdpi/bar_globe2.png
deleted file mode 100644
index ac676e3..0000000
Binary files a/VectorTileMap/res/drawable-mdpi/bar_globe2.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-mdpi/globe.png b/VectorTileMap/res/drawable-mdpi/globe.png
deleted file mode 100644
index f7a97a6..0000000
Binary files a/VectorTileMap/res/drawable-mdpi/globe.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-mdpi/ic_menu_archive.png b/VectorTileMap/res/drawable-mdpi/ic_menu_archive.png
deleted file mode 100644
index cc81e57..0000000
Binary files a/VectorTileMap/res/drawable-mdpi/ic_menu_archive.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-mdpi/ic_menu_camera.png b/VectorTileMap/res/drawable-mdpi/ic_menu_camera.png
deleted file mode 100644
index 7fddf7c..0000000
Binary files a/VectorTileMap/res/drawable-mdpi/ic_menu_camera.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-mdpi/ic_menu_info_details.png b/VectorTileMap/res/drawable-mdpi/ic_menu_info_details.png
deleted file mode 100755
index 1786d1e..0000000
Binary files a/VectorTileMap/res/drawable-mdpi/ic_menu_info_details.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-mdpi/ic_menu_mapmode.png b/VectorTileMap/res/drawable-mdpi/ic_menu_mapmode.png
deleted file mode 100644
index ef453e1..0000000
Binary files a/VectorTileMap/res/drawable-mdpi/ic_menu_mapmode.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-mdpi/ic_menu_preferences.png b/VectorTileMap/res/drawable-mdpi/ic_menu_preferences.png
deleted file mode 100644
index 60dbff6..0000000
Binary files a/VectorTileMap/res/drawable-mdpi/ic_menu_preferences.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-mdpi/my_location.png b/VectorTileMap/res/drawable-mdpi/my_location.png
deleted file mode 100644
index f9fa859..0000000
Binary files a/VectorTileMap/res/drawable-mdpi/my_location.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable-mdpi/snap_to_position.xml b/VectorTileMap/res/drawable-mdpi/snap_to_position.xml
deleted file mode 100644
index 8180613..0000000
--- a/VectorTileMap/res/drawable-mdpi/snap_to_position.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/VectorTileMap/res/drawable/action_bar.xml b/VectorTileMap/res/drawable/action_bar.xml
deleted file mode 100644
index 11c7f98..0000000
--- a/VectorTileMap/res/drawable/action_bar.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/VectorTileMap/res/drawable/globe2.png b/VectorTileMap/res/drawable/globe2.png
deleted file mode 100644
index 64c4e31..0000000
Binary files a/VectorTileMap/res/drawable/globe2.png and /dev/null differ
diff --git a/VectorTileMap/res/drawable/marker_default.png b/VectorTileMap/res/drawable/marker_default.png
new file mode 100644
index 0000000..e161c69
Binary files /dev/null and b/VectorTileMap/res/drawable/marker_default.png differ
diff --git a/VectorTileMap/res/drawable/marker_default_focused_base.png b/VectorTileMap/res/drawable/marker_default_focused_base.png
new file mode 100644
index 0000000..e9d775d
Binary files /dev/null and b/VectorTileMap/res/drawable/marker_default_focused_base.png differ
diff --git a/VectorTileMap/res/drawable/pin.png b/VectorTileMap/res/drawable/pin.png
new file mode 100644
index 0000000..46c7018
Binary files /dev/null and b/VectorTileMap/res/drawable/pin.png differ
diff --git a/VectorTileMap/res/menu/options_menu.xml b/VectorTileMap/res/menu/options_menu.xml
deleted file mode 100644
index bc29d58..0000000
--- a/VectorTileMap/res/menu/options_menu.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-
-
-
- -
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/VectorTileMap/res/menu/options_menu_pre_honeycomb.xml b/VectorTileMap/res/menu/options_menu_pre_honeycomb.xml
deleted file mode 100644
index bd8e33e..0000000
--- a/VectorTileMap/res/menu/options_menu_pre_honeycomb.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-
-
-
- -
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/VectorTileMap/res/values-de/strings.xml b/VectorTileMap/res/values-de/strings.xml
deleted file mode 100755
index d667815..0000000
--- a/VectorTileMap/res/values-de/strings.xml
+++ /dev/null
@@ -1,105 +0,0 @@
-
-
-
- - Mapfile
- - PostGIS
- - OpenScienceMap
-
-
-
- - angloamerikanisch
- - metrisch
-
-
-
- - winzig
- - klein
- - normal
- - groß
- - riesig
-
-
- OpenScienceMap
- Abbrechen
- Fehler
- Die letzte Position ist unbekannt
- Die gewählte Datei ist ungültig.
- Bitte wählen Sie eine Datei.
- byte
- bytes
- kB
- MB
- GB
- Gehe zu Position
- Fläche
- Kommentar
- Erzeugt von
- Datum
- Debug-Informationen
- nicht enthalten
- enthalten
- Bevorzugte Sprache
- Datei
- Dateigröße
- Startposition
- Startposition
- Start-Zoomstufe
- Version
- Breitengrad
- Längengrad
- Info
- Kartendatei-Info
- Über diese Software
- Kartendatei
- Position
- Koordinaten eingeben
- Letzte bekannte Position
- Zentrum der Kartendatei
- Meine Position anzeigen
- Meine Position entfernen
- Einstellungen
- Render-Theme
- Default Theme
- XML-Datei wählen …
- Screenshot
- JPEG (verlustbehaftet)
- PNG (verlustlos)
- Keine Standortquelle verfügbar
- OK
- Entwicklungs-Einstellungen
- Allgemeine Einstellungen
- Cache-Persistenz
- Zwischenspeicher beim Beenden beibehalten
- Externer Speicher
- Größe des Caches anpassen
- %.1f MB
- Vollbildmodus
- Verberge die Statusleiste
- Karteneinstellungen
- Karten-Modus
- Arbeitsmodus der Karte auswählen
- Scrollgeschwindigkeit
- Scrollgeschwindigkeit der Karte anpassen
- %d %% Scrollgeschwindigkeit
- Kartenmaßstabs-Einheit
- Einheit des Kartenmaßstabs auswählen
- Bildfrequenz
- FPS-Zähler anzeigen
- Kartenmaßstab
- Maßstabsbalken der Karte anzeigen
- Tile-Koordinaten
- Koordinaten auf Kacheln malen
- Tile-Umrandungen
- Rahmen um Tiles malen
- Wasser-Kacheln
- Kacheln mit gesetztem Wasser-Bit hervorheben
- Schriftgröße
- Basisgröße für Karten-Beschriftungen auswählen
- Aktiv bleiben
- Abschaltung des Bildschirms verhindern
- Snap to position ist aktiviert
- Snap-to-Position ist deaktiviert
- km
- m
- Zoomstufe
-
\ No newline at end of file
diff --git a/VectorTileMap/res/values-fi/strings.xml b/VectorTileMap/res/values-fi/strings.xml
deleted file mode 100644
index 3d3ce7f..0000000
--- a/VectorTileMap/res/values-fi/strings.xml
+++ /dev/null
@@ -1,97 +0,0 @@
-
-
-
- - Mapfile
- - PostGIS
- - OpenScienceMap
-
-
-
- - hyvin pieni
- - pieni
- - normaali
- - suuri
- - hyvin suuri
-
-
- OpenScienceMap
- Peruuta
- Virhe
- Viimeinen sijainti ei ole tiedossa
- Valittu tiedosto ei ole kelvollinen karttatiedosto.
- Valitse karttatiedosto.
- tavu
- tavua
- kB
- MB
- GB
- Mene sijaintiin
- Alue
- Kommentti
- Päivämäärä
- Debuggaustietoja
- ei sisälletty
- sisälletty
- Suosittu kieli
- Tiedosto
- Tiedoston koko
- Aloitussijainti
- Aloitussijainti
- Aloituszoomaustaso
- Versio
- Leveyspiiri
- Pituuspiiri
- Info
- Karttatiedoston ominaisuudet
- Tästä ohjelmasta
- Karttatiedosto
- Sijainti
- Anna koordinaatit
- Viimeinen tunnettu sijainti
- Karttatiedoston keskipiste
- Näytä sijaintini
- Poista sijaintini
- Suositukset
- Render theme
- Default Theme
- Valitse XML tiedosto …
- Screenshot
- JPEG (häviöllinen)
- PNG (häviötön)
- Sijaintilähde ei ole käytettävissä
- OK
- Debuggausasetukset
- Yleiset asetukset
- Välimuistin pysyvyys
- Pitää välimuistin lopettaessa
- Ulkopuolinen muisti
- Säädä välimuistin kokoa
- %.1f MB
- Kokoruututila
- Piilota tilapalkki
- Kartta-asetukset
- Kartta-asetus
- Valitse toiminta-asetus
- Liikkumisnopeus
- Säädä kartan liikkumisnopeutta
- %d %% liikkumisnopeus
- Kehysnopeus
- Salli kehyksen sekunttilaskin
- Kartan mittakaava
- Näytä kartan mittakaava
- Laattakoordinaatit
- Näytä koordinaatit laatalla
- Laattarajat
- Piirrä laattarajat
- Vesilaatat
- Korostaa vettä sisältävät laatat
- Fonttikoko
- Valitse karttamerkintöjen tekstikoko
- Pysyy aktiivisena
- Estää kuvaruudun himmenemisen
- Kohdista sijainti on aktivoitu
- Kohdista sijainti on deaktivoitu
- km
- m
- Zoomaustaso
-
\ No newline at end of file
diff --git a/VectorTileMap/res/values-it/strings.xml b/VectorTileMap/res/values-it/strings.xml
deleted file mode 100755
index b5e0565..0000000
--- a/VectorTileMap/res/values-it/strings.xml
+++ /dev/null
@@ -1,96 +0,0 @@
-
-
-
- - Mapfile
- - PostGIS
- - OpenScienceMap
-
-
-
- - minuscola
- - piccola
- - normale
- - grande
- - enorme
-
-
- OpenScienceMap
- Annulla
- Errore
- L\'ultima posizione è sconosciuta
- Il file selezionato non è valido.
- Si prega di selezionare un file.
- byte
- bytes
- kB
- MB
- GB
- Vai alla posizione
- Area
- Commento
- Data
- Informazioni di debug
- non incluso
- incluso
- Preferenza di lingua
- File
- Grandezza del file
- Punto di partenza
- Inizio zoom
- Versione
- Grado di latitudine
- Meridiano
- Info
- Informazioni sul file mappa
- A proposito di questo software
- Mappa
- Posizione
- Inserisci le coordinate
- Ultima posizione nota
- Centro della mappa
- Mostra la mia posizione
- Rimuovere la mia posizione
- Impostazioni
- Render modi
- Default Theme
- Selezionare un file XML …
- Screenshot
- JPEG (con perdita di dati)
- PNG (senza perdita di dati)
- Nessuna operatore per posizione disponibile
- OK
- Impostazioni sviluppo
- Impostazioni generali
- Persistenza cache
- Mantenere il cache in uscita
- Memoria esterna
- Impostare la dimensione della cache
- %.1f MB
- A schermo intero
- Nascondere la barra di stato
- Impostazioni mappa
- Modalitá della mappa
- Selezionare la modalità della mappa
- Velocità di scorrimento
- Impostare velocità di scorrimento della mappa
- %d %% velocità di scorrimento
- Frame rate
- Visualizzare contatore di frame rate
- Scala della mappa
- Visualizzare barra della scala della mappa
- Coordinate del Tile
- Dipingere le coordinate sul Tile
- Bordare del Tile
- Dipingere bordare attorno Tile
- Tiles di acqua
- Accentare i Tiles con acqua-bit
- Grandezza del carattere
- Scegli la grandezza standard per le diciture della mappa
- Rimanere attivo
- Impedisce la chiusura dello schermo
- Scatto alla posizione è attivato
- Scatto alla posizione è disattivato
- km
- m
- Livello di zoom
-
\ No newline at end of file
diff --git a/VectorTileMap/res/values/arrays.xml b/VectorTileMap/res/values/arrays.xml
deleted file mode 100644
index f34e3ba..0000000
--- a/VectorTileMap/res/values/arrays.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
- - MAP_READER
- - POSTGIS_READER
- - PBMAP_READER
-
- POSTGIS_READER
-
-
- - imperial
- - metric
-
- metric
-
-
- - 0.7
- - 0.85
- - 1.0
- - 1.3
- - 1.6
-
- 1.0
-
-
- - Map
- - Routes
- - Overlays
- - etc
-
-
\ No newline at end of file
diff --git a/VectorTileMap/res/values/strings.xml b/VectorTileMap/res/values/strings.xml
deleted file mode 100755
index 264fa79..0000000
--- a/VectorTileMap/res/values/strings.xml
+++ /dev/null
@@ -1,106 +0,0 @@
-
-
-
- - Mapfile
- - PostGIS
- - OpenScienceMap
-
-
-
- - Imperial
- - Metric
-
-
-
- - tiny
- - small
- - normal
- - large
- - huge
-
-
- OpenScienceMap
- Cancel
- Error
- The last location is unknown
- The selected file is invalid.
- Please select a file.
- byte
- bytes
- kB
- MB
- GB
- Go to position
- Area
- Comment
- Created by
- Date
- Debug information
- not included
- included
- Language preference
- File
- File size
- Start position
- Start zoom level
- Version
- Latitude
- Longitude
- Info
- Map file properties
- About this software
- Map file
- Position
- Enter coordinates
- Last known location
- Map file center
- Show my location
- Remove my location
- Preferences
- Render theme
- Default Theme
- Select XML file …
- Screenshot
- JPEG (lossy)
- PNG (lossless)
- No location source available
- OK
- Debug settings
- General settings
- Cache persistence
- Keep cached images on exit
- External storage
- Adjust the size of the cache
- %.1f MB
- Full screen mode
- Hide the status bar
- Map settings
- Map mode
- Select the operating mode
- Move speed
- Adjust the move speed of the map
- %d %% move speed
- Scale bar unit
- Select the unit for the map scale bar
- Frame rate
- Enable frames per second counter
- Map scale bar
- Show the scale of the map
- Tile coordinates
- Show coordinates on tiles
- Draw unmatched ways
- Tile boundaries
- Draw tile boundaries
- Disable Polygon rendering
- Highlight tiles which have the water flag set
- Font size
- Select the text size of map labels
- Stay awake
- Stop the screen from dimming
- Snap to position is activated
- Snap to position is disabled
- km
- m
- Zoom level
- Options
-
\ No newline at end of file
diff --git a/VectorTileMap/res/values/styles.xml b/VectorTileMap/res/values/styles.xml
deleted file mode 100644
index 705ab3a..0000000
--- a/VectorTileMap/res/values/styles.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/VectorTileMap/res/xml/preferences.xml b/VectorTileMap/res/xml/preferences.xml
deleted file mode 100644
index 2dad545..0000000
--- a/VectorTileMap/res/xml/preferences.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/VectorTileMap/src/org/mapsforge/android/IMapRenderer.java b/VectorTileMap/src/org/mapsforge/android/IMapRenderer.java
deleted file mode 100644
index 9ed921d..0000000
--- a/VectorTileMap/src/org/mapsforge/android/IMapRenderer.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2012 Hannes Janetzek
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android;
-
-import org.mapsforge.android.mapgenerator.IMapGenerator;
-import org.mapsforge.android.mapgenerator.MapGeneratorJob;
-import org.mapsforge.android.rendertheme.RenderTheme;
-
-import android.opengl.GLSurfaceView;
-
-/**
- *
- */
-public interface IMapRenderer extends GLSurfaceView.Renderer {
-
- /**
- * @param mapGeneratorJob
- * the mapGeneratorJob holding Tile data
- * @return true if the tile was processed
- */
- public boolean passTile(MapGeneratorJob mapGeneratorJob);
-
- /**
- * @return true when tile passed to renderer is processed false otherwise. used to lock overwriting resources passed
- * with the tile (e.g. lock until bitmap is loaded to texture)
- */
- public boolean processedTile();
-
- /**
- * called by MapView on position and map changes
- *
- * @param clear
- * ...
- */
- public void redrawTiles(boolean clear);
-
- public IMapGenerator createMapGenerator();
-
- public void setRenderTheme(RenderTheme t);
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/MapController.java b/VectorTileMap/src/org/mapsforge/android/MapController.java
deleted file mode 100644
index 716c7a0..0000000
--- a/VectorTileMap/src/org/mapsforge/android/MapController.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android;
-
-import org.mapsforge.core.GeoPoint;
-
-import android.view.KeyEvent;
-import android.view.View;
-
-/**
- * A MapController is used to programmatically modify the position and zoom level of a MapView. Each MapController is
- * assigned to a single MapView instance. To retrieve a MapController for a given MapView, use the
- * {@link MapView#getController()} method.
- */
-public final class MapController implements View.OnKeyListener {
- private final MapView mMapView;
-
- /**
- * @param mapView
- * the MapView which should be controlled by this MapController.
- */
- MapController(MapView mapView) {
- mMapView = mapView;
- }
-
- @Override
- public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
- if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
- // forward the event to the MapView
- return mMapView.onKeyDown(keyCode, keyEvent);
- } else if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
- // forward the event to the MapView
- return mMapView.onKeyUp(keyCode, keyEvent);
- }
- return false;
- }
-
- /**
- * Sets the center of the MapView without an animation to the given point.
- *
- * @param geoPoint
- * the new center point of the map.
- */
- public void setCenter(GeoPoint geoPoint) {
- mMapView.setCenter(geoPoint);
- }
-
- /**
- * Sets the zoom level of the MapView.
- *
- * @param zoomLevel
- * the new zoom level, will be limited by the maximum and minimum possible zoom level.
- * @return the new zoom level.
- */
- public int setZoom(int zoomLevel) {
- mMapView.zoom((byte) (zoomLevel - mMapView.getMapPosition().getZoomLevel()));
- return mMapView.getMapPosition().getZoomLevel();
- }
-
- /**
- * Increases the zoom level of the MapView, unless the maximum zoom level has been reached.
- *
- * @return true if the zoom level has been changed, false otherwise.
- */
- public boolean zoomIn() {
- return mMapView.zoom((byte) 1);
- }
-
- /**
- * Decreases the zoom level of the MapView, unless the minimum zoom level has been reached.
- *
- * @return true if the zoom level has been changed, false otherwise.
- */
- public boolean zoomOut() {
- return mMapView.zoom((byte) -1);
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/MapView.java b/VectorTileMap/src/org/mapsforge/android/MapView.java
deleted file mode 100644
index af376cf..0000000
--- a/VectorTileMap/src/org/mapsforge/android/MapView.java
+++ /dev/null
@@ -1,798 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-
-import javax.xml.parsers.ParserConfigurationException;
-
-import org.mapsforge.android.input.TouchHandler;
-import org.mapsforge.android.mapgenerator.IMapGenerator;
-import org.mapsforge.android.mapgenerator.JobParameters;
-import org.mapsforge.android.mapgenerator.JobQueue;
-import org.mapsforge.android.mapgenerator.MapDatabaseFactory;
-import org.mapsforge.android.mapgenerator.MapDatabases;
-import org.mapsforge.android.mapgenerator.MapGeneratorJob;
-import org.mapsforge.android.mapgenerator.MapRendererFactory;
-import org.mapsforge.android.mapgenerator.MapRenderers;
-import org.mapsforge.android.mapgenerator.MapWorker;
-import org.mapsforge.android.mapgenerator.Theme;
-import org.mapsforge.android.rendertheme.ExternalRenderTheme;
-import org.mapsforge.android.rendertheme.InternalRenderTheme;
-import org.mapsforge.android.rendertheme.RenderTheme;
-import org.mapsforge.android.rendertheme.RenderThemeHandler;
-import org.mapsforge.android.utils.GlConfigChooser;
-import org.mapsforge.core.GeoPoint;
-import org.mapsforge.core.MapPosition;
-import org.mapsforge.core.Tile;
-import org.mapsforge.database.FileOpenResult;
-import org.mapsforge.database.IMapDatabase;
-import org.mapsforge.database.MapFileInfo;
-import org.mapsforge.database.mapfile.MapDatabase;
-import org.xml.sax.SAXException;
-
-import android.content.Context;
-import android.opengl.GLSurfaceView;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.MotionEvent;
-
-/**
- * A MapView shows a map on the display of the device. It handles all user input and touch gestures to move and zoom the
- * map. This MapView also includes a scale bar and zoom controls. The {@link #getController()} method returns a
- * {@link MapController} to programmatically modify the position and zoom level of the map.
- *
- * This implementation supports offline map rendering as well as downloading map images (tiles) over an Internet
- * connection. The operation mode of a MapView can be set in the constructor and changed at runtime with the
- * {@link #setMapDatabase(MapDatabases)} method. Some MapView parameters depend on the selected operation mode.
- *
- * In offline rendering mode a special database file is required which contains the map data. Map files can be stored in
- * any folder. The current map file is set by calling {@link #setMapFile(String)}. To retrieve the current
- * {@link MapDatabase}, use the {@link #getMapDatabase()} method.
- *
- */
-public class MapView extends GLSurfaceView {
-
- final static String TAG = "MapView";
-
- /**
- * Default render theme of the MapView.
- */
- public static final InternalRenderTheme DEFAULT_RENDER_THEME = InternalRenderTheme.OSMARENDER;
-
- private static final float DEFAULT_TEXT_SCALE = 1;
- private static final Byte DEFAULT_START_ZOOM_LEVEL = Byte.valueOf((byte) 16);
-
- private final MapController mMapController;
- // private final MapMover mMapMover;
- // private final ZoomAnimator mZoomAnimator;
- // private final MapScaleBar mMapScaleBar;
- private final MapViewPosition mMapViewPosition;
-
- private final MapZoomControls mMapZoomControls;
- private final Projection mProjection;
- private final TouchHandler mTouchEventHandler;
-
- private IMapDatabase mMapDatabase;
- private MapDatabases mMapDatabaseType;
- private IMapRenderer mMapRenderer;
- private JobQueue mJobQueue;
- private MapWorker mMapWorkers[];
- private int mNumMapWorkers = 6;
- private JobParameters mJobParameters;
- public DebugSettings debugSettings;
- private String mMapFile;
-
- /**
- * @param context
- * the enclosing MapActivity instance.
- * @throws IllegalArgumentException
- * if the context object is not an instance of {@link MapActivity} .
- */
- public MapView(Context context) {
- this(context, null, MapRenderers.GL_RENDERER, MapDatabases.MAP_READER);
- }
-
- /**
- * @param context
- * the enclosing MapActivity instance.
- * @param attributeSet
- * a set of attributes.
- * @throws IllegalArgumentException
- * if the context object is not an instance of {@link MapActivity} .
- */
- public MapView(Context context, AttributeSet attributeSet) {
- this(context, attributeSet,
- MapRendererFactory.getMapGenerator(attributeSet),
- MapDatabaseFactory.getMapDatabase(attributeSet));
- }
-
- private boolean mDebugDatabase = false;
-
- private MapView(Context context, AttributeSet attributeSet,
- MapRenderers mapGeneratorType, MapDatabases mapDatabaseType) {
-
- super(context, attributeSet);
-
- if (!(context instanceof MapActivity)) {
- throw new IllegalArgumentException(
- "context is not an instance of MapActivity");
- }
- Log.d(TAG, "create MapView: " + mapDatabaseType.name());
-
- // TODO make this dpi dependent
- Tile.TILE_SIZE = 400;
-
- // setWillNotDraw(true);
- // setWillNotCacheDrawing(true);
-
- MapActivity mapActivity = (MapActivity) context;
-
- debugSettings = new DebugSettings(false, false, false, false);
-
- mJobParameters = new JobParameters(DEFAULT_RENDER_THEME, DEFAULT_TEXT_SCALE);
- mMapController = new MapController(this);
-
- mMapDatabaseType = mapDatabaseType;
-
- mMapViewPosition = new MapViewPosition(this);
- // mMapScaleBar = new MapScaleBar(this);
- mMapZoomControls = new MapZoomControls(mapActivity, this);
- mProjection = new MapViewProjection(this);
- mTouchEventHandler = new TouchHandler(mapActivity, this);
-
- mJobQueue = new JobQueue(this);
-
- // mMapMover = new MapMover(this);
- // mMapMover.start();
- // mZoomAnimator = new ZoomAnimator(this);
- // mZoomAnimator.start();
-
- mMapRenderer = MapRendererFactory.createMapRenderer(this, mapGeneratorType);
- mMapWorkers = new MapWorker[mNumMapWorkers];
-
- for (int i = 0; i < mNumMapWorkers; i++) {
- IMapDatabase mapDatabase;
- if (mDebugDatabase) {
- mapDatabase = MapDatabaseFactory
- .createMapDatabase(MapDatabases.JSON_READER);
-
- } else {
- mapDatabase = MapDatabaseFactory.createMapDatabase(mapDatabaseType);
- }
-
- IMapGenerator mapGenerator = mMapRenderer.createMapGenerator();
- mapGenerator.setMapDatabase(mapDatabase);
-
- if (i == 0) {
- mMapDatabase = mapDatabase;
- initMapStartPosition();
- }
- mMapWorkers[i] = new MapWorker(i, this, mapGenerator, mMapRenderer);
- mMapWorkers[i].start();
- }
-
- if (!setRenderTheme(InternalRenderTheme.OSMARENDER)) {
- Log.d(TAG, "EEEK could parse theme");
- // FIXME show init error dialog
- }
-
- setEGLConfigChooser(new GlConfigChooser());
- setEGLContextClientVersion(2);
-
- setRenderer(mMapRenderer);
- setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
-
- mapActivity.registerMapView(this);
- }
-
- private void initMapStartPosition() {
- GeoPoint startPoint = getStartPoint();
- if (startPoint != null) {
- mMapViewPosition.setMapCenter(startPoint);
- }
-
- Byte startZoomLevel = getStartZoomLevel();
- if (startZoomLevel != null) {
- mMapViewPosition.setZoomLevel(startZoomLevel.byteValue());
- }
-
- }
-
- /**
- * @return the MapController for this MapView.
- */
- public MapController getController() {
- return mMapController;
- }
-
- /**
- * @return the debug settings which are used in this MapView.
- */
- public DebugSettings getDebugSettings() {
- return debugSettings;
- }
-
- /**
- * @return the job queue which is used in this MapView.
- */
- public JobQueue getJobQueue() {
- return mJobQueue;
- }
-
- /**
- * @return the map database which is used for reading map files.
- */
- public IMapDatabase getMapDatabase() {
- return mMapDatabase;
- }
-
- /**
- * @return the currently used map file.
- */
- public String getMapFile() {
- return mMapFile;
- }
-
- // /**
- // * @return the MapMover which is used by this MapView.
- // */
- // public MapMover getMapMover() {
- // return mMapMover;
- // }
-
- /**
- * @return the current position and zoom level of this MapView.
- */
- public MapViewPosition getMapPosition() {
- return mMapViewPosition;
- }
-
- // /**
- // * @return the scale bar which is used in this MapView.
- // */
- // public MapScaleBar getMapScaleBar() {
- // return mMapScaleBar;
- // }
-
- // /**
- // * @return the zoom controls instance which is used in this MapView.
- // */
- // public MapZoomControls getMapZoomControls() {
- // return mMapZoomControls;
- // }
-
- /**
- * @return the currently used projection of the map. Do not keep this object for a longer time.
- */
- public Projection getProjection() {
- return mProjection;
- }
-
- // /**
- // * @return true if the ZoomAnimator is currently running, false otherwise.
- // */
- // public boolean isZoomAnimatorRunning() {
- // return mZoomAnimator.isExecuting();
- // }
-
- // @Override
- // public boolean onKeyDown(int keyCode, KeyEvent keyEvent) {
- // return mMapMover.onKeyDown(keyCode, keyEvent);
- // }
- //
- // @Override
- // public boolean onKeyUp(int keyCode, KeyEvent keyEvent) {
- // return mMapMover.onKeyUp(keyCode, keyEvent);
- // }
-
- @Override
- public boolean onTouchEvent(MotionEvent motionEvent) {
- return mTouchEventHandler.handleMotionEvent(motionEvent);
- }
-
- // @Override
- // public boolean onTrackballEvent(MotionEvent motionEvent) {
- // return mMapMover.onTrackballEvent(motionEvent);
- // }
-
- /**
- * Calculates all necessary tiles and adds jobs accordingly.
- */
- public void redrawTiles() {
- if (getWidth() <= 0 || getHeight() <= 0)
- return;
-
- mMapRenderer.redrawTiles(false);
- }
-
- private void clearAndRedrawMapView() {
- if (getWidth() <= 0 || getHeight() <= 0)
- return;
-
- mMapRenderer.redrawTiles(true);
- }
-
- /**
- * Sets the visibility of the zoom controls.
- *
- * @param showZoomControls
- * true if the zoom controls should be visible, false otherwise.
- */
- public void setBuiltInZoomControls(boolean showZoomControls) {
- mMapZoomControls.setShowMapZoomControls(showZoomControls);
-
- }
-
- /**
- * Sets the center of the MapView and triggers a redraw.
- *
- * @param geoPoint
- * the new center point of the map.
- */
- public void setCenter(GeoPoint geoPoint) {
- MapPosition mapPosition = new MapPosition(geoPoint,
- mMapViewPosition.getZoomLevel(), 1);
- setCenterAndZoom(mapPosition);
- }
-
- /**
- * @param debugSettings
- * the new DebugSettings for this MapView.
- */
- public void setDebugSettings(DebugSettings debugSettings) {
- this.debugSettings = debugSettings;
-
- clearAndRedrawMapView();
- }
-
- /**
- * Sets the map file for this MapView.
- *
- * @param mapFile
- * the path to the map file.
- * @return true if the map file was set correctly, false otherwise.
- */
- public boolean setMapFile(String mapFile) {
- FileOpenResult fileOpenResult = null;
-
- Log.d(TAG, "set mapfile " + mapFile);
-
- if (mapFile != null && mapFile.equals(mMapFile)) {
- // same map file as before
- return false;
- }
-
- // mZoomAnimator.pause();
- // mMapMover.pause();
- // mZoomAnimator.awaitPausing();
- // mMapMover.awaitPausing();
- // mZoomAnimator.proceed();
- // mMapMover.stopMove();
- // mMapMover.proceed();
-
- boolean initialized = false;
-
- mJobQueue.clear();
-
- mapWorkersPause(true);
-
- for (MapWorker mapWorker : mMapWorkers) {
-
- IMapGenerator mapGenerator = mapWorker.getMapGenerator();
- IMapDatabase mapDatabase = mapGenerator.getMapDatabase();
-
- mapDatabase.closeFile();
-
- if (mapFile != null)
- fileOpenResult = mapDatabase.openFile(new File(mapFile));
- else
- fileOpenResult = mapDatabase.openFile(null);
-
- if (fileOpenResult != null && fileOpenResult.isSuccess()) {
- mMapFile = mapFile;
-
- if (!initialized)
- initialized = true;
- }
- }
-
- mapWorkersProceed();
-
- if (initialized) {
- clearAndRedrawMapView();
- Log.d(TAG, "mapfile set");
- return true;
- }
-
- mMapFile = null;
- Log.d(TAG, "loading mapfile failed");
-
- return false;
- }
-
- private GeoPoint getStartPoint() {
- if (mMapDatabase != null && mMapDatabase.hasOpenFile()) {
- MapFileInfo mapFileInfo = mMapDatabase.getMapFileInfo();
- if (mapFileInfo.startPosition != null) {
- return mapFileInfo.startPosition;
- } else if (mapFileInfo.mapCenter != null) {
- return mapFileInfo.mapCenter;
- }
- }
-
- return null;
- }
-
- private Byte getStartZoomLevel() {
- if (mMapDatabase != null && mMapDatabase.hasOpenFile()) {
- MapFileInfo mapFileInfo = mMapDatabase.getMapFileInfo();
- if (mapFileInfo.startZoomLevel != null) {
- return mapFileInfo.startZoomLevel;
- }
- }
-
- return DEFAULT_START_ZOOM_LEVEL;
- }
-
- /**
- * Sets the MapDatabase for this MapView.
- *
- * @param mapDatabaseType
- * the new MapDatabase.
- */
-
- public void setMapDatabase(MapDatabases mapDatabaseType) {
- if (mDebugDatabase)
- return;
-
- IMapGenerator mapGenerator;
-
- Log.d(TAG, "setMapDatabase " + mapDatabaseType.name());
-
- if (mMapDatabaseType == mapDatabaseType)
- return;
-
- mMapDatabaseType = mapDatabaseType;
-
- mapWorkersPause(true);
-
- for (MapWorker mapWorker : mMapWorkers) {
- mapGenerator = mapWorker.getMapGenerator();
-
- mapGenerator.setMapDatabase(MapDatabaseFactory
- .createMapDatabase(mapDatabaseType));
- }
-
- mJobQueue.clear();
-
- String mapFile = mMapFile;
- mMapFile = null;
- setMapFile(mapFile);
-
- mapWorkersProceed();
-
- Log.d(TAG, ">>>");
- }
-
- /**
- * Sets the internal theme which is used for rendering the map.
- *
- * @param internalRenderTheme
- * the internal rendering theme.
- * @return ...
- * @throws IllegalArgumentException
- * if the supplied internalRenderTheme is null.
- */
- public boolean setRenderTheme(InternalRenderTheme internalRenderTheme) {
- if (internalRenderTheme == null) {
- throw new IllegalArgumentException("render theme must not be null");
- }
-
- boolean ret = setRenderTheme((Theme) internalRenderTheme);
-
- clearAndRedrawMapView();
- return ret;
- }
-
- /**
- * Sets the theme file which is used for rendering the map.
- *
- * @param renderThemePath
- * the path to the XML file which defines the rendering theme.
- * @throws IllegalArgumentException
- * if the supplied internalRenderTheme is null.
- * @throws FileNotFoundException
- * if the supplied file does not exist, is a directory or cannot be read.
- */
- public void setRenderTheme(String renderThemePath) throws FileNotFoundException {
- if (renderThemePath == null) {
- throw new IllegalArgumentException("render theme path must not be null");
- }
-
- setRenderTheme(new ExternalRenderTheme(renderThemePath));
-
- clearAndRedrawMapView();
- }
-
- private boolean setRenderTheme(Theme theme) {
-
- mapWorkersPause(true);
-
- InputStream inputStream = null;
- try {
- inputStream = theme.getRenderThemeAsStream();
- RenderTheme t = RenderThemeHandler.getRenderTheme(inputStream);
- mMapRenderer.setRenderTheme(t);
- mMapWorkers[0].getMapGenerator().setRenderTheme(t);
- return true;
- } catch (ParserConfigurationException e) {
- Log.e(TAG, e.getMessage());
- } catch (SAXException e) {
- Log.e(TAG, e.getMessage());
- } catch (IOException e) {
- Log.e(TAG, e.getMessage());
- } finally {
- try {
- if (inputStream != null) {
- inputStream.close();
- }
- } catch (IOException e) {
- Log.e(TAG, e.getMessage());
- }
- mapWorkersProceed();
- }
-
- return false;
- }
-
- /**
- * Sets the text scale for the map rendering. Has no effect in downloading mode.
- *
- * @param textScale
- * the new text scale for the map rendering.
- */
- public void setTextScale(float textScale) {
- mJobParameters = new JobParameters(mJobParameters.theme, textScale);
- clearAndRedrawMapView();
- }
-
- /**
- * Zooms in or out by the given amount of zoom levels.
- *
- * @param zoomLevelDiff
- * the difference to the current zoom level.
- * @return true if the zoom level was changed, false otherwise.
- */
- public boolean zoom(byte zoomLevelDiff) {
-
- int z = mMapViewPosition.getZoomLevel() + zoomLevelDiff;
- if (zoomLevelDiff > 0) {
- // check if zoom in is possible
- if (z > getMaximumPossibleZoomLevel()) {
- return false;
- }
-
- } else if (zoomLevelDiff < 0) {
- // check if zoom out is possible
- if (z < mMapZoomControls.getZoomLevelMin()) {
- return false;
- }
- }
-
- mMapViewPosition.setZoomLevel((byte) z);
-
- redrawTiles();
-
- return true;
- }
-
- // @Override
- // protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- // super.onLayout(changed, left, top, right, bottom);
- // // mMapZoomControls.onLayout(changed, left, top, right, bottom);
- // }
-
- // @Override
- // protected final void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // // find out how big the zoom controls should be
- // mMapZoomControls.measure(
- // MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec),
- // MeasureSpec.AT_MOST),
- // MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec),
- // MeasureSpec.AT_MOST));
- //
- // // make sure that MapView is big enough to display the zoom controls
- // setMeasuredDimension(
- // Math.max(MeasureSpec.getSize(widthMeasureSpec),
- // mMapZoomControls.getMeasuredWidth()),
- // Math.max(MeasureSpec.getSize(heightMeasureSpec),
- // mMapZoomControls.getMeasuredHeight()));
- // }
-
- @Override
- protected synchronized void onSizeChanged(int width, int height, int oldWidth,
- int oldHeight) {
- mJobQueue.clear();
-
- mapWorkersPause(true);
-
- super.onSizeChanged(width, height, oldWidth, oldHeight);
-
- mapWorkersProceed();
- }
-
- void destroy() {
- // mMapMover.interrupt();
- // mZoomAnimator.interrupt();
-
- for (MapWorker mapWorker : mMapWorkers) {
- mapWorker.pause();
- mapWorker.interrupt();
-
- try {
- mapWorker.join();
- } catch (InterruptedException e) {
- // restore the interrupted status
- Thread.currentThread().interrupt();
- }
- IMapDatabase mapDatabase = mapWorker.getMapGenerator().getMapDatabase();
- mapDatabase.closeFile();
- }
-
- // mMapScaleBar.destroy();
-
- }
-
- /**
- * @return the maximum possible zoom level.
- */
- byte getMaximumPossibleZoomLevel() {
- return (byte) 20;
- // FIXME Math.min(mMapZoomControls.getZoomLevelMax(),
- // mMapGenerator.getZoomLevelMax());
- }
-
- /**
- * @return true if the current center position of this MapView is valid, false otherwise.
- */
- boolean hasValidCenter() {
- if (!mMapViewPosition.isValid()) {
- return false;
- } else if (!mMapDatabase.hasOpenFile()
- || !mMapDatabase.getMapFileInfo().boundingBox.contains(getMapPosition()
- .getMapCenter())) {
- return false;
- }
-
- return true;
- }
-
- byte limitZoomLevel(byte zoom) {
- return (byte) Math.max(Math.min(zoom, getMaximumPossibleZoomLevel()),
- mMapZoomControls.getZoomLevelMin());
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mapWorkersPause(false);
-
- // mMapMover.pause();
- // mZoomAnimator.pause();
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mapWorkersProceed();
-
- // mMapMover.proceed();
- // mZoomAnimator.proceed();
- }
-
- /**
- * Sets the center and zoom level of this MapView and triggers a redraw.
- *
- * @param mapPosition
- * the new map position of this MapView.
- */
- void setCenterAndZoom(MapPosition mapPosition) {
-
- // if (hasValidCenter()) {
- // // calculate the distance between previous and current position
- // MapPosition mapPositionOld = mapViewPosition.getMapPosition();
-
- // GeoPoint geoPointOld = mapPositionOld.geoPoint;
- // GeoPoint geoPointNew = mapPosition.geoPoint;
- // double oldPixelX =
- // MercatorProjection.longitudeToPixelX(geoPointOld.getLongitude(),
- // mapPositionOld.zoomLevel);
- // double newPixelX =
- // MercatorProjection.longitudeToPixelX(geoPointNew.getLongitude(),
- // mapPosition.zoomLevel);
- //
- // double oldPixelY =
- // MercatorProjection.latitudeToPixelY(geoPointOld.getLatitude(),
- // mapPositionOld.zoomLevel);
- // double newPixelY =
- // MercatorProjection.latitudeToPixelY(geoPointNew.getLatitude(),
- // mapPosition.zoomLevel);
-
- // float matrixTranslateX = (float) (oldPixelX - newPixelX);
- // float matrixTranslateY = (float) (oldPixelY - newPixelY);
- // frameBuffer.matrixPostTranslate(matrixTranslateX,
- // matrixTranslateY);
- // }
- //
- mMapViewPosition.setMapCenterAndZoomLevel(mapPosition);
- // mapZoomControls.onZoomLevelChange(mapViewPosition.getZoomLevel());
- redrawTiles();
- }
-
- /**
- * @return MapPosition
- */
- public MapViewPosition getMapViewPosition() {
- return mMapViewPosition;
- }
-
- /**
- * @return current JobParameters
- */
- public JobParameters getJobParameters() {
- return mJobParameters;
- }
-
- /**
- * add jobs and remember MapWorkers that stuff needs to be done
- *
- * @param jobs
- * tile jobs
- */
- public void addJobs(ArrayList jobs) {
- mJobQueue.setJobs(jobs);
-
- for (int i = 0; i < mNumMapWorkers; i++) {
- MapWorker m = mMapWorkers[i];
- synchronized (m) {
- m.notify();
- }
- }
- }
-
- private void mapWorkersPause(boolean wait) {
- for (MapWorker mapWorker : mMapWorkers) {
- if (!mapWorker.isPausing())
- mapWorker.pause();
- }
- if (wait) {
- for (MapWorker mapWorker : mMapWorkers) {
- if (!mapWorker.isPausing())
- mapWorker.awaitPausing();
- }
- }
- }
-
- private void mapWorkersProceed() {
- for (MapWorker mapWorker : mMapWorkers)
- mapWorker.proceed();
- }
-
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/MapViewPosition.java b/VectorTileMap/src/org/mapsforge/android/MapViewPosition.java
deleted file mode 100644
index e3e2056..0000000
--- a/VectorTileMap/src/org/mapsforge/android/MapViewPosition.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android;
-
-import org.mapsforge.core.GeoPoint;
-import org.mapsforge.core.MapPosition;
-import org.mapsforge.core.MercatorProjection;
-
-import android.util.FloatMath;
-
-/**
- * A MapPosition stores the latitude and longitude coordinate of a MapView together with its zoom level.
- */
-public class MapViewPosition {
- private static float MAX_SCALE = 2.0f;
- private static float MIN_SCALE = 1.0f;
- private static int MAX_ZOOMLEVEL = 16;
-
- private double mLatitude;
- private double mLongitude;
- private final MapView mMapView;
- private byte mZoomLevel;
- private float mScale;
-
- // private float mRotation;
-
- MapViewPosition(MapView mapView) {
- mMapView = mapView;
-
- mLatitude = Double.NaN;
- mLongitude = Double.NaN;
- mZoomLevel = -1;
- mScale = 1;
- // mRotation = 0.0f;
- }
-
- /**
- * @return the current center point of the MapView.
- */
- public synchronized GeoPoint getMapCenter() {
- return new GeoPoint(mLatitude, mLongitude);
- }
-
- /**
- * @return an immutable MapPosition or null, if this map position is not valid.
- * @see #isValid()
- */
- public synchronized MapPosition getMapPosition() {
- if (!isValid()) {
- return null;
- }
- GeoPoint geoPoint = new GeoPoint(mLatitude, mLongitude);
- return new MapPosition(geoPoint, mZoomLevel, mScale);
- }
-
- /**
- * @return the current zoom level of the MapView.
- */
- public synchronized byte getZoomLevel() {
- return mZoomLevel;
- }
-
- /**
- * @return the current scale of the MapView.
- */
- public synchronized float getScale() {
- return mScale;
- }
-
- /**
- * @return true if this MapViewPosition is valid, false otherwise.
- */
- public synchronized boolean isValid() {
- if (Double.isNaN(mLatitude)) {
- return false;
- } else if (mLatitude < MercatorProjection.LATITUDE_MIN) {
- return false;
- } else if (mLatitude > MercatorProjection.LATITUDE_MAX) {
- return false;
- }
-
- if (Double.isNaN(mLongitude)) {
- return false;
- } else if (mLongitude < MercatorProjection.LONGITUDE_MIN) {
- return false;
- } else if (mLongitude > MercatorProjection.LONGITUDE_MAX) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Moves this MapViewPosition by the given amount of pixels.
- *
- * @param moveHorizontal
- * the amount of pixels to move the map horizontally.
- * @param moveVertical
- * the amount of pixels to move the map vertically.
- */
- public synchronized void moveMap(float moveHorizontal, float moveVertical) {
- double pixelX = MercatorProjection.longitudeToPixelX(mLongitude, mZoomLevel);
- double pixelY = MercatorProjection.latitudeToPixelY(mLatitude, mZoomLevel);
-
- mLatitude = MercatorProjection.pixelYToLatitude(pixelY - moveVertical / mScale,
- mZoomLevel);
- mLatitude = MercatorProjection.limitLatitude(mLatitude);
-
- mLongitude = MercatorProjection.pixelXToLongitude(pixelX - moveHorizontal
- / mScale,
- mZoomLevel);
- mLongitude = MercatorProjection.limitLongitude(mLongitude);
- }
-
- // public synchronized void rotateMap(float angle) {
- // mRotation = angle;
- // }
-
- synchronized void setMapCenter(GeoPoint geoPoint) {
- mLatitude = MercatorProjection.limitLatitude(geoPoint.getLatitude());
- mLongitude = MercatorProjection.limitLongitude(geoPoint.getLongitude());
- }
-
- synchronized void setMapCenterAndZoomLevel(MapPosition mapPosition) {
- GeoPoint geoPoint = mapPosition.geoPoint;
- mLatitude = MercatorProjection.limitLatitude(geoPoint.getLatitude());
- mLongitude = MercatorProjection.limitLongitude(geoPoint.getLongitude());
- mZoomLevel = mMapView.limitZoomLevel(mapPosition.zoomLevel);
- }
-
- synchronized void setZoomLevel(byte zoomLevel) {
- mZoomLevel = mMapView.limitZoomLevel(zoomLevel);
- }
-
- synchronized void setScale(float scale) {
- mScale = scale;
- }
-
- /**
- * @param scale
- * ...
- * @param pivotX
- * ...
- * @param pivotY
- * ...
- */
- public synchronized void scaleMap(float scale, float pivotX, float pivotY) {
- moveMap(pivotX * (1.0f - scale),
- pivotY * (1.0f - scale));
-
- float s = mScale * scale;
-
- if (s >= MAX_SCALE) {
- if (s > 8)
- return;
-
- if (mZoomLevel <= MAX_ZOOMLEVEL) {
- byte z = (byte) FloatMath.sqrt(s);
- mZoomLevel += z;
- s *= 1.0f / (1 << z);
- }
- } else if (s < MIN_SCALE) {
- byte z = (byte) FloatMath.sqrt(1 / s);
- if (z != 0 && mZoomLevel == 1)
- return;
- mZoomLevel -= z;
- s *= 1 << z;
- }
-
- mScale = s;
- }
-
- /**
- * Zooms in or out by the given amount of zoom levels.
- *
- * @param zoomLevelDiff
- * the difference to the current zoom level.
- * @param s
- * scale between min/max zoom
- * @return true if the zoom level was changed, false otherwise.
- */
- // public boolean zoom(byte zoomLevelDiff, float s) {
- // float scale = s;
- //
- // if (zoomLevelDiff > 0) {
- // // check if zoom in is possible
- // if (mMapViewPosition.getZoomLevel() + zoomLevelDiff > getMaximumPossibleZoomLevel()) {
- // return false;
- // }
- //
- // scale *= 1.0f / (1 << zoomLevelDiff);
- // } else if (zoomLevelDiff < 0) {
- // // check if zoom out is possible
- // if (mMapViewPosition.getZoomLevel() + zoomLevelDiff < mMapZoomControls.getZoomLevelMin()) {
- // return false;
- // }
- //
- // scale *= 1 << -zoomLevelDiff;
- // }
- //
- // if (scale == 0)
- // scale = 1;
- // // else
- // // scale = Math.round(256.0f * scale) / 256.0f;
- //
- // mMapViewPosition.setZoomLevel((byte) (mMapViewPosition.getZoomLevel() + zoomLevelDiff));
- //
- // // mapZoomControls.onZoomLevelChange(mapViewPosition.getZoomLevel());
- //
- // // zoomAnimator.setParameters(zoomStart, matrixScaleFactor,
- // // getWidth() >> 1, getHeight() >> 1);
- // // zoomAnimator.startAnimation();
- //
- // // if (scale > MAX_ZOOM) {
- // // scale = MAX_ZOOM;
- // // }
- //
- // if (zoomLevelDiff != 0 || mZoomFactor != scale) {
- // mZoomFactor = scale;
- // redrawTiles();
- // }
- //
- // return true;
- // }
-
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/MapViewProjection.java b/VectorTileMap/src/org/mapsforge/android/MapViewProjection.java
deleted file mode 100644
index 1685df2..0000000
--- a/VectorTileMap/src/org/mapsforge/android/MapViewProjection.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android;
-
-import org.mapsforge.core.GeoPoint;
-import org.mapsforge.core.MapPosition;
-import org.mapsforge.core.MercatorProjection;
-
-import android.graphics.Point;
-
-class MapViewProjection implements Projection {
- private static final String INVALID_MAP_VIEW_DIMENSIONS = "invalid MapView dimensions";
-
- private final MapView mMapView;
-
- MapViewProjection(MapView mapView) {
- mMapView = mapView;
- }
-
- @Override
- public GeoPoint fromPixels(int x, int y) {
- if (mMapView.getWidth() <= 0 || mMapView.getHeight() <= 0) {
- return null;
- }
-
- MapPosition mapPosition = mMapView.getMapPosition().getMapPosition();
-
- // calculate the pixel coordinates of the top left corner
- GeoPoint geoPoint = mapPosition.geoPoint;
- double pixelX = MercatorProjection.longitudeToPixelX(geoPoint.getLongitude(), mapPosition.zoomLevel);
- double pixelY = MercatorProjection.latitudeToPixelY(geoPoint.getLatitude(), mapPosition.zoomLevel);
- pixelX -= mMapView.getWidth() >> 1;
- pixelY -= mMapView.getHeight() >> 1;
-
- // convert the pixel coordinates to a GeoPoint and return it
- return new GeoPoint(MercatorProjection.pixelYToLatitude(pixelY + y, mapPosition.zoomLevel),
- MercatorProjection.pixelXToLongitude(pixelX + x, mapPosition.zoomLevel));
- }
-
- @Override
- public int getLatitudeSpan() {
- if (mMapView.getWidth() > 0 && mMapView.getWidth() > 0) {
- GeoPoint top = fromPixels(0, 0);
- GeoPoint bottom = fromPixels(0, mMapView.getHeight());
- return Math.abs(top.latitudeE6 - bottom.latitudeE6);
- }
- throw new IllegalStateException(INVALID_MAP_VIEW_DIMENSIONS);
- }
-
- @Override
- public int getLongitudeSpan() {
- if (mMapView.getWidth() > 0 && mMapView.getWidth() > 0) {
- GeoPoint left = fromPixels(0, 0);
- GeoPoint right = fromPixels(mMapView.getWidth(), 0);
- return Math.abs(left.longitudeE6 - right.longitudeE6);
- }
- throw new IllegalStateException(INVALID_MAP_VIEW_DIMENSIONS);
- }
-
- @Override
- public float metersToPixels(float meters, byte zoom) {
- double latitude = mMapView.getMapPosition().getMapCenter().getLatitude();
- double groundResolution = MercatorProjection.calculateGroundResolution(latitude, zoom);
- return (float) (meters * (1 / groundResolution));
- }
-
- @Override
- public Point toPixels(GeoPoint in, Point out) {
- if (mMapView.getWidth() <= 0 || mMapView.getHeight() <= 0) {
- return null;
- }
-
- MapPosition mapPosition = mMapView.getMapPosition().getMapPosition();
-
- // calculate the pixel coordinates of the top left corner
- GeoPoint geoPoint = mapPosition.geoPoint;
- double pixelX = MercatorProjection.longitudeToPixelX(geoPoint.getLongitude(), mapPosition.zoomLevel);
- double pixelY = MercatorProjection.latitudeToPixelY(geoPoint.getLatitude(), mapPosition.zoomLevel);
- pixelX -= mMapView.getWidth() >> 1;
- pixelY -= mMapView.getHeight() >> 1;
-
- if (out == null) {
- // create a new point and return it
- return new Point(
- (int) (MercatorProjection.longitudeToPixelX(in.getLongitude(), mapPosition.zoomLevel) - pixelX),
- (int) (MercatorProjection.latitudeToPixelY(in.getLatitude(), mapPosition.zoomLevel) - pixelY));
- }
-
- // reuse the existing point
- out.x = (int) (MercatorProjection.longitudeToPixelX(in.getLongitude(), mapPosition.zoomLevel) - pixelX);
- out.y = (int) (MercatorProjection.latitudeToPixelY(in.getLatitude(), mapPosition.zoomLevel) - pixelY);
- return out;
- }
-
- @Override
- public Point toPoint(GeoPoint in, Point out, byte zoom) {
- if (out == null) {
- // create a new point and return it
- return new Point((int) MercatorProjection.longitudeToPixelX(in.getLongitude(), zoom),
- (int) MercatorProjection.latitudeToPixelY(in.getLatitude(), zoom));
- }
-
- // reuse the existing point
- out.x = (int) MercatorProjection.longitudeToPixelX(in.getLongitude(), zoom);
- out.y = (int) MercatorProjection.latitudeToPixelY(in.getLatitude(), zoom);
- return out;
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/Projection.java b/VectorTileMap/src/org/mapsforge/android/Projection.java
deleted file mode 100644
index 578cd40..0000000
--- a/VectorTileMap/src/org/mapsforge/android/Projection.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android;
-
-import org.mapsforge.core.GeoPoint;
-
-import android.graphics.Point;
-
-/**
- * A Projection translates between the pixel coordinate system on the screen and geographical points on the earth. To
- * retrieve the currently used Projection for a given MapView, call the {@link MapView#getProjection()} method.
- */
-public interface Projection {
- /**
- * Translates the given screen coordinates to a {@link GeoPoint}. If the corresponding MapView has no valid
- * dimensions (width and height > 0), null is returned.
- *
- * @param x
- * the pixel x coordinate on the screen.
- * @param y
- * the pixel y coordinate on the screen.
- * @return a new {@link GeoPoint} or null, if the corresponding MapView has no valid dimensions.
- */
- GeoPoint fromPixels(int x, int y);
-
- /**
- * @return the latitude span from the top to the bottom of the map in microdegrees (degrees * 10^6).
- * @throws IllegalStateException
- * if the MapView dimensions are not valid (width and height > 0).
- */
- int getLatitudeSpan();
-
- /**
- * @return the longitude span from the left to the right of the map in microdegrees (degrees * 10^6).
- * @throws IllegalStateException
- * if the MapView dimensions are not valid (width and height > 0).
- */
- int getLongitudeSpan();
-
- /**
- * Converts the given distance in meters at the given zoom level to the corresponding number of horizontal pixels.
- * The calculation is carried out at the current latitude coordinate.
- *
- * @param meters
- * the distance in meters.
- * @param zoomLevel
- * the zoom level at which the distance should be calculated.
- * @return the number of pixels at the current map position and the given zoom level.
- */
- float metersToPixels(float meters, byte zoomLevel);
-
- /**
- * Translates the given {@link GeoPoint} to relative pixel coordinates on the screen. If the corresponding MapView
- * has no valid dimensions (width and height > 0), null is returned.
- *
- * @param in
- * the geographical point to convert.
- * @param out
- * an already existing object to use for the output. If this parameter is null, a new Point object will
- * be created and returned.
- * @return a Point which is relative to the top-left of the MapView or null, if the corresponding MapView has no
- * valid dimensions.
- */
- Point toPixels(GeoPoint in, Point out);
-
- /**
- * Translates the given {@link GeoPoint} to absolute pixel coordinates on the world map.
- *
- * @param in
- * the geographical point to convert.
- * @param out
- * an already existing object to use for the output. If this parameter is null, a new Point object will
- * be created and returned.
- * @param zoomLevel
- * the zoom level at which the point should be converted.
- * @return a Point which is relative to the top-left of the world map.
- */
- Point toPoint(GeoPoint in, Point out, byte zoomLevel);
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/GLMapTile.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/GLMapTile.java
deleted file mode 100644
index 5c35274..0000000
--- a/VectorTileMap/src/org/mapsforge/android/glrenderer/GLMapTile.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2012 Hannes Janetzek
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General License for more details.
- *
- * You should have received a copy of the GNU Lesser General License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.glrenderer;
-
-import org.mapsforge.android.mapgenerator.MapTile;
-import org.mapsforge.core.Tile;
-
-class GLMapTile extends MapTile {
- long lastDraw = 0;
-
- VertexBufferObject vbo;
-
- // polygonOffset is always 8
- int lineOffset;
-
- TextTexture texture;
-
- LineLayer lineLayers;
- PolygonLayer polygonLayers;
-
- TextItem labels;
-
- boolean newData;
- boolean loading;
-
- // pixel coordinates (y-flipped)
- final long x;
- final long y;
-
- final GLMapTile[] child = { null, null, null, null };
- GLMapTile parent;
-
- GLMapTile(long tileX, long tileY, byte zoomLevel) {
- super(tileX, tileY, zoomLevel);
-
- x = pixelX;
- y = pixelY + Tile.TILE_SIZE;
- }
-
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/Layer.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/Layer.java
deleted file mode 100644
index 7c40f63..0000000
--- a/VectorTileMap/src/org/mapsforge/android/glrenderer/Layer.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2012 Hannes Janetzek
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General License for more details.
- *
- * You should have received a copy of the GNU Lesser General License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.glrenderer;
-
-class Layer {
- PoolItem pool;
-
- protected PoolItem curItem;
-
- int verticesCnt;
- int offset;
-
- final int layer;
-
- Layer(int l) {
- layer = l;
- verticesCnt = 0;
- }
-
- float[] getNextPoolItem() {
- curItem.used = PoolItem.SIZE;
-
- curItem.next = VertexPool.get();
- curItem = curItem.next;
-
- return curItem.vertices;
- }
-
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/LineLayer.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/LineLayer.java
deleted file mode 100644
index b0bf8d7..0000000
--- a/VectorTileMap/src/org/mapsforge/android/glrenderer/LineLayer.java
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * Copyright 2012 Hannes Janetzek
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General License for more details.
- *
- * You should have received a copy of the GNU Lesser General License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.glrenderer;
-
-import org.mapsforge.android.rendertheme.renderinstruction.Line;
-import org.mapsforge.core.Tile;
-
-import android.graphics.Paint.Cap;
-import android.util.FloatMath;
-
-class LineLayer {
-
- private static final float S = MapRenderer.COORD_MULTIPLIER;
- private static final float S1000 = 1000;
-
- LineLayer next;
- LineLayer outlines;
-
- Line line;
- float width;
- boolean isOutline;
- int layer;
-
- ShortItem pool;
- protected ShortItem curItem;
- int verticesCnt;
- int offset;
- short[] mVertex;
-
- LineLayer(int layer, Line line, float width, boolean outline) {
- this.layer = layer;
- this.width = width;
- this.line = line;
- this.isOutline = outline;
- }
-
- void addOutline(LineLayer link) {
- for (LineLayer l = outlines; l != null; l = l.outlines)
- if (link == l)
- return;
-
- link.outlines = outlines;
- outlines = link;
- }
-
- private static ShortItem addTwoVertex(short[] vertex, ShortItem item) {
- ShortItem it = item;
-
- if (it.used + 6 >= ShortItem.SIZE) {
-
- if (it.used == ShortItem.SIZE) {
- it.next = ShortPool.get();
- it = it.next;
-
- } else {
- System.arraycopy(vertex, 0, it.vertices, it.used, 6);
- it.used += 6;
-
- it.next = ShortPool.get();
- it = it.next;
-
- System.arraycopy(vertex, 6, it.vertices, it.used, 6);
- it.used += 6;
-
- return it;
- }
- }
-
- System.arraycopy(vertex, 0, it.vertices, it.used, 12);
- it.used += 12;
-
- return it;
- }
-
- private static ShortItem addVertex(short[] vertex, ShortItem item) {
- ShortItem it = item;
-
- if (it.used == ShortItem.SIZE) {
- it.next = ShortPool.get();
- it = it.next;
- }
-
- System.arraycopy(vertex, 0, it.vertices, it.used, 6);
- it.used += 6;
-
- return it;
- }
-
- /*
- * line extrusion is based on code from GLMap (https://github.com/olofsj/GLMap/) by olofsj -- need some way to know
- * how the road connects to set the ending angles
- */
- void addLine(float[] pointArray, int pos, int length) {
- float x, y, nextX, nextY, prevX, prevY, ux, uy, vx, vy, wx, wy;
- float a;
- int pointPos = pos;
- boolean rounded = false;
- boolean squared = false;
-
- if (line.cap == Cap.ROUND)
- rounded = true;
- else if (line.cap == Cap.SQUARE)
- squared = true;
-
- if (pool == null) {
- curItem = ShortPool.get();
- pool = curItem;
-
- mVertex = new short[12];
- }
-
- // amount of vertices used
- verticesCnt += length + (rounded ? 6 : 2);
-
- ShortItem si = curItem;
-
- x = pointArray[pointPos++];
- y = pointArray[pointPos++];
-
- nextX = pointArray[pointPos++];
- nextY = pointArray[pointPos++];
-
- // Calculate triangle corners for the given width
- vx = nextX - x;
- vy = nextY - y;
-
- a = FloatMath.sqrt(vx * vx + vy * vy);
-
- vx = (vx / a);
- vy = (vy / a);
-
- ux = -vy;
- uy = vx;
-
- float uxw = ux;
- float uyw = uy;
-
- float vxw = vx;
- float vyw = vy;
- int tsize = Tile.TILE_SIZE;
-
- short v[] = mVertex;
-
- v[0] = (short) (x * S);
- v[1] = (short) (y * S);
-
- boolean outside = (x <= 0 || x >= tsize || y <= 0 || y >= tsize)
- && (x - vxw <= 0 || x - vxw >= tsize || y - vyw <= 0 || y - vyw >= tsize);
-
- if (rounded && !outside) {
- v[2] = (short) ((uxw - vxw) * S1000);
- v[3] = (short) ((uyw - vyw) * S1000);
- v[4] = -1;
- v[5] = 1;
- si = addVertex(v, si);
- si = addVertex(v, si);
-
- v[2] = (short) (-(uxw + vxw) * S1000);
- v[3] = (short) (-(uyw + vyw) * S1000);
- v[4] = 1;
- v[5] = 1;
- si = addVertex(v, si);
-
- // Start of line
- v[2] = (short) ((uxw) * S1000);
- v[3] = (short) ((uyw) * S1000);
- v[4] = -1;
- v[5] = 0;
- si = addVertex(v, si);
-
- v[2] = (short) ((-uxw) * S1000);
- v[3] = (short) ((-uyw) * S1000);
- v[4] = 1;
- v[5] = 0;
- si = addVertex(v, si);
-
- } else {
- // outside means line is probably clipped
- // TODO should align ending with tile boundary
- // for now, just extend the line a little
-
- if (squared) {
- vxw = 0;
- vyw = 0;
- } else if (!outside) {
- vxw *= 0.5;
- vyw *= 0.5;
- }
-
- if (rounded)
- verticesCnt -= 2;
-
- // Add the first point twice to be able to draw with GL_TRIANGLE_STRIP
- v[2] = (short) ((uxw - vxw) * S1000);
- v[3] = (short) ((uyw - vyw) * S1000);
- v[4] = -1;
- v[5] = 0;
- si = addVertex(v, si);
- si = addVertex(v, si);
-
- v[2] = (short) (-(uxw + vxw) * S1000);
- v[3] = (short) (-(uyw + vyw) * S1000);
- v[4] = 1;
- v[5] = 0;
- si = addVertex(v, si);
- }
-
- prevX = x;
- prevY = y;
- x = nextX;
- y = nextY;
-
- for (; pointPos < pos + length;) {
- nextX = pointArray[pointPos++];
- nextY = pointArray[pointPos++];
-
- // Unit vector pointing back to previous node
- vx = prevX - x;
- vy = prevY - y;
- a = FloatMath.sqrt(vx * vx + vy * vy);
- vx = (vx / a);
- vy = (vy / a);
-
- // Unit vector pointing forward to next node
- wx = nextX - x;
- wy = nextY - y;
- a = FloatMath.sqrt(wx * wx + wy * wy);
- wx = (wx / a);
- wy = (wy / a);
-
- // Sum of these two vectors points
- ux = vx + wx;
- uy = vy + wy;
-
- a = -wy * ux + wx * uy;
-
- // boolean split = false;
- if (a < 0.1f && a > -0.1f) {
- // Almost straight or miter goes to infinity, use normal vector
- ux = -wy;
- uy = wx;
- } else {
- ux = (ux / a);
- uy = (uy / a);
-
- if (ux > 2.0f || ux < -2.0f || uy > 2.0f || uy < -2.0f) {
- ux = -wy;
- uy = wx;
- }
- }
-
- uxw = ux * S1000;
- uyw = uy * S1000;
-
- v[6] = v[0] = (short) (x * S);
- v[7] = v[1] = (short) (y * S);
-
- v[2] = (short) uxw;
- v[3] = (short) uyw;
- v[4] = -1;
- v[5] = 0;
-
- v[8] = (short) -uxw;
- v[9] = (short) -uyw;
- v[10] = 1;
- v[11] = 0;
- si = addTwoVertex(v, si);
-
- prevX = x;
- prevY = y;
- x = nextX;
- y = nextY;
- }
-
- vx = prevX - x;
- vy = prevY - y;
-
- a = FloatMath.sqrt(vx * vx + vy * vy);
-
- vx = (vx / a);
- vy = (vy / a);
-
- ux = vy;
- uy = -vx;
-
- uxw = ux;
- uyw = uy;
-
- vxw = vx;
- vyw = vy;
-
- outside = (x <= 0 || x >= tsize || y <= 0 || y >= tsize)
- && (x - vxw <= 0 || x - vxw >= tsize || y - vyw <= 0 || y - vyw >= tsize);
-
- v[0] = (short) (x * S);
- v[1] = (short) (y * S);
-
- if (rounded && !outside) {
- v[2] = (short) ((uxw) * S1000);
- v[3] = (short) ((uyw) * S1000);
- v[4] = -1;
- v[5] = 0;
- si = addVertex(v, si);
-
- v[2] = (short) ((-uxw) * S1000);
- v[3] = (short) ((-uyw) * S1000);
- v[4] = 1;
- v[5] = 0;
- si = addVertex(v, si);
-
- // For rounded line edges
- v[2] = (short) ((uxw - vxw) * S1000);
- v[3] = (short) ((uyw - vyw) * S1000);
- v[4] = -1;
- v[5] = -1;
- si = addVertex(v, si);
-
- v[2] = (short) (-(uxw + vxw) * S1000);
- v[3] = (short) (-(uyw + vyw) * S1000);
- v[4] = 1;
- v[5] = -1;
- si = addVertex(v, si);
- si = addVertex(v, si);
-
- } else {
- if (squared) {
- vxw = 0;
- vyw = 0;
- } else if (!outside) {
- vxw *= 0.5;
- vyw *= 0.5;
- }
-
- if (rounded)
- verticesCnt -= 2;
-
- v[2] = (short) ((uxw) * S1000);
- v[3] = (short) ((uyw) * S1000);
- v[4] = -1;
- v[5] = 0;
- si = addVertex(v, si);
-
- v[2] = (short) (-(uxw + vxw) * S1000);
- v[3] = (short) (-(uyw + vyw) * S1000);
- v[4] = 1;
- v[5] = 0;
- si = addVertex(v, si);
- si = addVertex(v, si);
- }
-
- curItem = si;
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/LineLayers.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/LineLayers.java
deleted file mode 100644
index 825bf5b..0000000
--- a/VectorTileMap/src/org/mapsforge/android/glrenderer/LineLayers.java
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Copyright 2012 Hannes Janetzek
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General License for more details.
- *
- * You should have received a copy of the GNU Lesser General License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.glrenderer;
-
-import static android.opengl.GLES20.GL_TRIANGLE_STRIP;
-import static android.opengl.GLES20.glDisableVertexAttribArray;
-import static android.opengl.GLES20.glDrawArrays;
-import static android.opengl.GLES20.glEnableVertexAttribArray;
-import static android.opengl.GLES20.glGetAttribLocation;
-import static android.opengl.GLES20.glGetUniformLocation;
-import static android.opengl.GLES20.glUniform4f;
-import static android.opengl.GLES20.glUniform4fv;
-import static android.opengl.GLES20.glUniformMatrix4fv;
-import static android.opengl.GLES20.glUseProgram;
-import static android.opengl.GLES20.glVertexAttribPointer;
-
-import java.nio.ShortBuffer;
-
-import org.mapsforge.android.rendertheme.renderinstruction.Line;
-import org.mapsforge.android.utils.GlUtils;
-
-import android.opengl.GLES20;
-import android.util.FloatMath;
-
-class LineLayers {
- private static int NUM_VERTEX_SHORTS = 6;
-
- private static final int LINE_VERTICES_DATA_POS_OFFSET = 0;
- private static final int LINE_VERTICES_DATA_TEX_OFFSET = 8;
-
- // shader handles
- private static int lineProgram;
- private static int hLineVertexPosition;
- private static int hLineTexturePosition;
- private static int hLineColor;
- private static int hLineMatrix;
- private static int hLineScale;
- private static int hLineWidth;
-
- static boolean init() {
- lineProgram = GlUtils.createProgram(Shaders.lineVertexShader,
- Shaders.lineFragmentShader);
- if (lineProgram == 0) {
- // Log.e(TAG, "Could not create line program.");
- return false;
- }
-
- hLineMatrix = glGetUniformLocation(lineProgram, "mvp");
- hLineScale = glGetUniformLocation(lineProgram, "u_wscale");
- hLineWidth = glGetUniformLocation(lineProgram, "u_width");
- hLineColor = glGetUniformLocation(lineProgram, "u_color");
- hLineVertexPosition = GLES20.glGetAttribLocation(lineProgram, "a_position");
- hLineTexturePosition = glGetAttribLocation(lineProgram, "a_st");
-
- return true;
- }
-
- static LineLayer drawLines(GLMapTile tile, LineLayer layer, int next, float[] matrix,
- float div, double zoom, float scale) {
-
- float z = 1 / div;
-
- if (layer == null)
- return null;
-
- glUseProgram(lineProgram);
-
- glEnableVertexAttribArray(hLineVertexPosition);
- glEnableVertexAttribArray(hLineTexturePosition);
-
- glVertexAttribPointer(hLineVertexPosition, 4, GLES20.GL_SHORT,
- false, 12, tile.lineOffset + LINE_VERTICES_DATA_POS_OFFSET);
-
- glVertexAttribPointer(hLineTexturePosition, 2, GLES20.GL_SHORT,
- false, 12, tile.lineOffset + LINE_VERTICES_DATA_TEX_OFFSET);
-
- glUniformMatrix4fv(hLineMatrix, 1, false, matrix, 0);
-
- // if (diff != 0)
- // // diff < 0 means tile is parent
- // z = (diff > 0) ? 1.0f / (1 << diff) : (1 << -diff);
-
- // scale factor to map one pixel on tile to one pixel on screen:
- // float pixel = 2.0f / (scale * z);
- // GLES20.glUniform1f(hLineScale, pixel);
-
- // line scale factor (for non fixed lines)
- float s = FloatMath.sqrt(scale * z);
- boolean blur = false;
- GLES20.glUniform1f(hLineScale, 0);
-
- LineLayer l = layer;
- for (; l != null && l.layer < next; l = l.next) {
- Line line = l.line;
- if (line.fade != -1 && line.fade > zoom)
- continue;
-
- if (line.fade >= zoom) {
- float alpha = 1.0f;
-
- alpha = (scale > 1.2f ? scale : 1.2f) - alpha;
- if (alpha > 1.0f)
- alpha = 1.0f;
- glUniform4f(hLineColor,
- line.color[0], line.color[1], line.color[2], alpha);
- } else {
- glUniform4fv(hLineColor, 1, line.color, 0);
- }
-
- if (blur) {
- GLES20.glUniform1f(hLineScale, 0);
- blur = false;
- }
- if (l.isOutline) {
- for (LineLayer o = l.outlines; o != null; o = o.outlines) {
- if (line.blur != 0) {
- GLES20.glUniform1f(hLineScale, (l.width + o.width) / (scale * z)
- - (line.blur / (scale * z)));
- blur = true;
- }
-
- if (zoom > MapGenerator.STROKE_MAX_ZOOM_LEVEL)
- GLES20.glUniform1f(hLineWidth,
- (l.width + o.width) / (scale * z));
- else
- GLES20.glUniform1f(hLineWidth, l.width / (scale * z)
- + o.width / s);
-
- glDrawArrays(GL_TRIANGLE_STRIP, o.offset, o.verticesCnt);
- }
- }
- else {
- if (line.blur != 0) {
- GLES20.glUniform1f(hLineScale, (l.width / s) * line.blur);
- blur = true;
- }
-
- if (line.fixed || zoom > MapGenerator.STROKE_MAX_ZOOM_LEVEL) {
- // invert scaling of extrusion vectors so that line width stays the same
- GLES20.glUniform1f(hLineWidth, (l.width / (scale * z)));
- } else {
- GLES20.glUniform1f(hLineWidth, (l.width / s));
- }
-
- glDrawArrays(GL_TRIANGLE_STRIP, l.offset, l.verticesCnt);
- }
- }
-
- glDisableVertexAttribArray(hLineVertexPosition);
- glDisableVertexAttribArray(hLineTexturePosition);
-
- return l;
- }
-
- static int sizeOf(LineLayer layers) {
- int size = 0;
- for (LineLayer l = layers; l != null; l = l.next)
- size += l.verticesCnt;
-
- size *= NUM_VERTEX_SHORTS;
- return size;
- }
-
- static void compileLayerData(LineLayer layers, ShortBuffer sbuf) {
- int pos = 0;
- ShortItem last = null, items = null;
-
- for (LineLayer l = layers; l != null; l = l.next) {
- if (l.isOutline)
- continue;
-
- for (ShortItem item = l.pool; item != null; item = item.next) {
- sbuf.put(item.vertices, 0, item.used);
- last = item;
- }
-
- l.offset = pos;
- pos += l.verticesCnt;
-
- if (last != null) {
- last.next = items;
- items = l.pool;
- }
-
- l.pool = null;
- l.curItem = null;
- }
-
- ShortPool.add(items);
- }
-
- // @SuppressLint("UseValueOf")
- // private static final Boolean lock = new Boolean(true);
- // private static final int POOL_LIMIT = 1500;
- //
- // static private LineLayer pool = null;
- // static private int count = 0;
- // static private int countAll = 0;
- //
- // static void finish() {
- // synchronized (lock) {
- // count = 0;
- // countAll = 0;
- // pool = null;
- // }
- // }
- //
- // static LineLayer get(int layer, Line line, float width, boolean outline) {
- // synchronized (lock) {
- //
- // if (count == 0 && pool == null) {
- // countAll++;
- // return new LineLayer(layer, line, width, outline);
- // }
- // if (count > 0) {
- // count--;
- // } else {
- // int c = 0;
- // LineLayer tmp = pool;
- //
- // while (tmp != null) {
- // c++;
- // tmp = tmp.next;
- // }
- //
- // Log.d("LineLayersl", "eek wrong count: " + c + " left");
- // }
- //
- // LineLayer it = pool;
- // pool = pool.next;
- // it.next = null;
- // it.layer = layer;
- // it.line = line;
- // it.isOutline = outline;
- // it.width = width;
- // return it;
- // }
- // }
- //
- // static void add(LineLayer layers) {
- // if (layers == null)
- // return;
- //
- // synchronized (lock) {
- //
- // // limit pool items
- // if (countAll < POOL_LIMIT) {
- // LineLayer last = layers;
- //
- // while (true) {
- // count++;
- //
- // if (last.next == null)
- // break;
- //
- // last = last.next;
- // }
- //
- // last.next = pool;
- // pool = layers;
- //
- // } else {
- // int cleared = 0;
- // LineLayer prev, tmp = layers;
- // while (tmp != null) {
- // prev = tmp;
- // tmp = tmp.next;
- //
- // countAll--;
- // cleared++;
- //
- // prev.next = null;
- //
- // }
- // Log.d("LineLayers", "sum: " + countAll + " free: " + count + " freed "
- // + cleared);
- // }
- //
- // }
- // }
- //
- static void clear(LineLayer layer) {
- for (LineLayer l = layer; l != null; l = l.next) {
- if (l.pool != null) {
- ShortPool.add(l.pool);
- l.pool = null;
- l.curItem = null;
- }
- }
- // LineLayers.add(layer);
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/MapGenerator.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/MapGenerator.java
deleted file mode 100644
index 602c518..0000000
--- a/VectorTileMap/src/org/mapsforge/android/glrenderer/MapGenerator.java
+++ /dev/null
@@ -1,677 +0,0 @@
-/*
- * Copyright 2012 Hannes Janetzek
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.glrenderer;
-
-import org.mapsforge.android.mapgenerator.IMapGenerator;
-import org.mapsforge.android.mapgenerator.MapGeneratorJob;
-import org.mapsforge.android.rendertheme.IRenderCallback;
-import org.mapsforge.android.rendertheme.RenderTheme;
-import org.mapsforge.android.rendertheme.renderinstruction.Area;
-import org.mapsforge.android.rendertheme.renderinstruction.Caption;
-import org.mapsforge.android.rendertheme.renderinstruction.Line;
-import org.mapsforge.android.rendertheme.renderinstruction.PathText;
-import org.mapsforge.android.rendertheme.renderinstruction.RenderInstruction;
-import org.mapsforge.core.MercatorProjection;
-import org.mapsforge.core.Tag;
-import org.mapsforge.core.Tile;
-import org.mapsforge.core.WebMercator;
-import org.mapsforge.database.IMapDatabase;
-import org.mapsforge.database.IMapDatabaseCallback;
-import org.mapsforge.database.QueryResult;
-
-import android.graphics.Bitmap;
-import android.graphics.Paint;
-import android.util.Log;
-
-/**
- *
- */
-public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabaseCallback {
-
- private static String TAG = MapGenerator.class.getName();
-
- private static final double PI180 = (Math.PI / 180) / 1000000.0;
- private static final double PIx4 = Math.PI * 4;
- private static final double STROKE_INCREASE = Math.sqrt(2);
- private static final byte LAYERS = 11;
- private static final double f900913 = 20037508.342789244;
-
- static final byte STROKE_MIN_ZOOM_LEVEL = 12;
- static final byte STROKE_MAX_ZOOM_LEVEL = 17;
-
- private static RenderTheme renderTheme;
-
- private IMapDatabase mMapDatabase;
-
- private GLMapTile mCurrentTile;
-
- private float[] mWayNodes;
- private short[] mWays;
-
- private LineLayer mLineLayers;
- private PolygonLayer mPolyLayers;
- private LineLayer mCurLineLayer;
- private PolygonLayer mCurPolyLayer;
-
- private TextItem mLabels;
-
- private int mDrawingLayer;
- private int mLevels;
-
- private float mStrokeScale = 1.0f;
-
- private boolean mProjected;
- // private boolean mProjectedResult;
- private float mSimplify;
- // private boolean firstMatch;
- // private boolean prevClosed;
-
- private RenderInstruction[] mRenderInstructions = null;
-
- private final String TAG_WATER = "water".intern();
-
- /**
- *
- */
- public MapGenerator() {
- Log.d(TAG, "init DatabaseRenderer");
- }
-
- private float mPoiX = 256;
- private float mPoiY = 256;
-
- private Tag mTagEmptyName = new Tag(Tag.TAG_KEY_NAME, null, false);
- private Tag mTagName;
-
- private void filterTags(Tag[] tags) {
- for (int i = 0; i < tags.length; i++) {
- // Log.d(TAG, "check tag: " + tags[i]);
- if (tags[i].key == Tag.TAG_KEY_NAME && tags[i].value != null) {
- mTagName = tags[i];
- tags[i] = mTagEmptyName;
- }
- }
- }
-
- // private RenderInstruction[] mNodeRenderInstructions;
-
- @Override
- public void renderPointOfInterest(byte layer, float latitude, float longitude,
- Tag[] tags) {
-
- mTagName = null;
-
- if (mMapProjection != null)
- {
- long x = mCurrentTile.x;
- long y = mCurrentTile.y;
- long z = Tile.TILE_SIZE << mCurrentTile.zoomLevel;
-
- double divx, divy;
- long dx = (x - (z >> 1));
- long dy = (y - (z >> 1));
-
- if (mMapProjection == WebMercator.NAME) {
- double div = f900913 / (z >> 1);
- // divy = f900913 / (z >> 1);
- mPoiX = (float) (longitude / div - dx);
- mPoiY = (float) (latitude / div + dy);
- } else {
- divx = 180000000.0 / (z >> 1);
- divy = z / PIx4;
- mPoiX = (float) (longitude / divx - dx);
- double sinLat = Math.sin(latitude * PI180);
- mPoiY = (float) (Math.log((1.0 + sinLat) / (1.0 - sinLat)) * divy + dy);
- if (mPoiX < -10 || mPoiX > Tile.TILE_SIZE + 10 || mPoiY < -10
- || mPoiY > Tile.TILE_SIZE + 10)
- return;
- }
- } else {
- mPoiX = longitude;
- mPoiY = latitude;
- }
-
- // remove tags that should not be cached in Rendertheme
- filterTags(tags);
- // Log.d(TAG, "renderPointOfInterest: " + mTagName);
-
- // mNodeRenderInstructions =
- MapGenerator.renderTheme.matchNode(this, tags, mCurrentTile.zoomLevel);
- }
-
- @Override
- public void renderWaterBackground() {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void renderWay(byte layer, Tag[] tags, float[] wayNodes, short[] wayLength,
- boolean closed) {
-
- mTagName = null;
- mProjected = false;
-
- mDrawingLayer = getValidLayer(layer) * mLevels;
- mSimplify = 0.5f;
-
- if (closed) {
- if (mCurrentTile.zoomLevel < 14)
- mSimplify = 0.5f;
- else
- mSimplify = 0.2f;
-
- if (tags.length == 1 && TAG_WATER == (tags[0].value))
- mSimplify = 0;
- }
-
- mWayNodes = wayNodes;
- mWays = wayLength;
-
- // remove tags that should not be cached in Rendertheme
- filterTags(tags);
-
- // if (mRenderInstructions != null) {
- // for (int i = 0, n = mRenderInstructions.length; i < n; i++)
- // mRenderInstructions[i].renderWay(this, tags);
- // }
-
- mRenderInstructions = MapGenerator.renderTheme.matchWay(this, tags,
- (byte) (mCurrentTile.zoomLevel + 0),
- closed, true);
-
- if (mRenderInstructions == null && mDebugDrawUnmatched)
- debugUnmatched(closed, tags);
- }
-
- private void debugUnmatched(boolean closed, Tag[] tags) {
-
- Log.d(TAG, "way not matched: " + tags[0] + " "
- + (tags.length > 1 ? tags[1] : "") + " " + closed);
-
- mTagName = new Tag("name", tags[0].key + ":" + tags[0].value, false);
-
- if (closed) {
- mRenderInstructions = MapGenerator.renderTheme.matchWay(this, debugTagArea,
- (byte) 0, true, true);
- } else {
- mRenderInstructions = MapGenerator.renderTheme.matchWay(this, debugTagWay,
- (byte) 0, true, true);
- }
- }
-
- @Override
- public void renderAreaCaption(Caption caption) {
- // Log.d(TAG, "renderAreaCaption: " + mTagName);
-
- if (mTagName == null)
- return;
-
- if (caption.textKey == mTagEmptyName.key) {
-
- TextItem t = new TextItem(mWayNodes[0], mWayNodes[1], mTagName.value, caption);
- t.next = mLabels;
- mLabels = t;
- }
- }
-
- @Override
- public void renderPointOfInterestCaption(Caption caption) {
- // Log.d(TAG, "renderPointOfInterestCaption: " + mPoiX + " " + mPoiY + " "
- // + mTagName);
-
- if (mTagName == null)
- return;
-
- if (caption.textKey == mTagEmptyName.key) {
- TextItem t = new TextItem(mPoiX, mPoiY, mTagName.value, caption);
- t.next = mLabels;
- mLabels = t;
- }
- }
-
- @Override
- public void renderWayText(PathText pathText) {
- // Log.d(TAG, "renderWayText: " + mTagName);
-
- if (mTagName == null)
- return;
-
- if (pathText.textKey == mTagEmptyName.key) {
-
- mLabels = WayDecorator.renderText(mWayNodes, mTagName.value, pathText, 0,
- mWays[0], mLabels);
- }
- }
-
- @Override
- public void renderAreaSymbol(Bitmap symbol) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void renderPointOfInterestCircle(float radius, Paint fill, int level) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void renderPointOfInterestSymbol(Bitmap symbol) {
- // TODO Auto-generated method stub
-
- }
-
- private int countLines;
- private int countNodes;
-
- @Override
- public void renderWay(Line line, int level) {
-
- projectToTile();
-
- if (line.outline && mCurLineLayer == null)
- return;
-
- float w = line.width;
-
- if (!line.fixed) {
- w *= mStrokeScale;
- w *= mProjectionScaleFactor;
- }
-
- LineLayer lineLayer = null;
-
- int numLayer = mDrawingLayer + level;
-
- LineLayer l = mLineLayers;
-
- if (mCurLineLayer != null && mCurLineLayer.layer == numLayer) {
- lineLayer = mCurLineLayer;
- } else if (l == null || l.layer > numLayer) {
- // insert new layer at start
- lineLayer = new LineLayer(numLayer, line, w, line.outline);
- // lineLayer = LineLayers.get(numLayer, line, w, false);
-
- lineLayer.next = l;
- mLineLayers = lineLayer;
- } else {
- while (l != null) {
- // found layer
- if (l.layer == numLayer) {
- lineLayer = l;
- break;
- }
- // insert new layer between current and next layer
- if (l.next == null || l.next.layer > numLayer) {
- lineLayer = new LineLayer(numLayer, line, w, line.outline);
- // lineLayer = LineLayers.get(numLayer, line, w, false);
- lineLayer.next = l.next;
- l.next = lineLayer;
- }
- l = l.next;
- }
- }
-
- if (lineLayer == null)
- return;
-
- if (line.outline) {
- lineLayer.addOutline(mCurLineLayer);
- return;
- }
-
- mCurLineLayer = lineLayer;
-
- boolean round = line.round;
-
- for (int i = 0, pos = 0, n = mWays.length; i < n; i++) {
- int length = mWays[i];
- if (length < 0)
- break;
-
- // save some vertices
- if (round && i > 200) {
- // Log.d(TAG, "WAY TOO MANY LINES!!!");
- round = false;
- }
- // need at least two points
- if (length >= 4) {
- lineLayer.addLine(mWayNodes, pos, length);
- countLines++;
- countNodes += length;
- }
- pos += length;
- }
-
- // if (line.outline < 0)
- // return;
- //
- // Line outline = MapGenerator.renderTheme.getOutline(line.outline);
- //
- // if (outline == null)
- // return;
- //
- // numLayer = mDrawingLayer + outline.getLevel();
- //
- // l = mLineLayers;
- //
- // if (l == null || l.layer > numLayer) {
- // // insert new layer at start
- // outlineLayer = new LineLayer(numLayer, outline, w, true);
- // // outlineLayer = LineLayers.get(numLayer, outline, w, true);
- // outlineLayer.next = l;
- // mLineLayers = outlineLayer;
- // } else {
- // while (l != null) {
- // if (l.layer == numLayer) {
- // outlineLayer = l;
- // break;
- // }
- // // insert new layer between current and next layer
- // if (l.next == null || l.next.layer > numLayer) {
- // outlineLayer = new LineLayer(numLayer, outline, w, true);
- // // outlineLayer = LineLayers.get(numLayer, outline, w, true);
- // outlineLayer.next = l.next;
- // l.next = outlineLayer;
- // }
- // l = l.next;
- // }
- // }
- //
- // if (outlineLayer != null)
- // outlineLayer.addOutline(lineLayer);
-
- }
-
- @Override
- public void renderArea(Area area, int level) {
- if (!mDebugDrawPolygons)
- return;
-
- if (!mProjected && !projectToTile())
- return;
-
- int numLayer = mDrawingLayer + level;
-
- PolygonLayer layer = null;
- PolygonLayer l = mPolyLayers;
-
- if (mCurPolyLayer != null && mCurPolyLayer.layer == numLayer) {
- layer = mCurPolyLayer;
- } else if (l == null || l.layer > numLayer) {
- // insert new layer at start
- layer = new PolygonLayer(numLayer, area);
- layer.next = l;
- mPolyLayers = layer;
- } else {
- while (l != null) {
-
- if (l.layer == numLayer) {
- layer = l;
- break;
- }
-
- // insert new layer between current and next layer
- if (l.next == null || l.next.layer > numLayer) {
- layer = new PolygonLayer(numLayer, area);
- layer.next = l.next;
- l.next = layer;
- }
- l = l.next;
- }
- }
- if (layer == null)
- return;
-
- mCurPolyLayer = layer;
-
- for (int i = 0, pos = 0, n = mWays.length; i < n; i++) {
- int length = mWays[i];
- if (length < 0)
- break;
-
- // need at least three points
- if (length >= 6)
- layer.addPolygon(mWayNodes, pos, length);
-
- pos += length;
- }
-
- // if (area.line != null)
- }
-
- @Override
- public void renderWaySymbol(Bitmap symbol, boolean alignCenter, boolean repeat) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void cleanup() {
- // TODO Auto-generated method stub
-
- }
-
- private boolean mDebugDrawPolygons;
- boolean mDebugDrawUnmatched;
-
- @Override
- public boolean executeJob(MapGeneratorJob mapGeneratorJob) {
- GLMapTile tile;
-
- if (mMapDatabase == null)
- return false;
-
- tile = mCurrentTile = (GLMapTile) mapGeneratorJob.tile;
- mDebugDrawPolygons = !mapGeneratorJob.debugSettings.mDisablePolygons;
- mDebugDrawUnmatched = mapGeneratorJob.debugSettings.mDrawUnmatchted;
-
- if (tile.isLoading || tile.isReady || tile.isCanceled)
- return false;
-
- tile.isLoading = true;
-
- mLevels = MapGenerator.renderTheme.getLevels();
-
- // limit stroke scale at z=17
- if (tile.zoomLevel < STROKE_MAX_ZOOM_LEVEL)
- setScaleStrokeWidth(tile.zoomLevel);
- else
- setScaleStrokeWidth(STROKE_MAX_ZOOM_LEVEL);
-
- mLineLayers = null;
- mPolyLayers = null;
- mLabels = null;
-
- // firstMatch = true;
- countLines = 0;
- countNodes = 0;
-
- // acount for area changes with latitude
- mProjectionScaleFactor = 0.5f + (float) (0.5 / Math.cos(MercatorProjection
- .pixelYToLatitude(tile.pixelY, tile.zoomLevel)
- * (Math.PI / 180)));
-
- if (mMapDatabase.executeQuery(tile, this) != QueryResult.SUCCESS) {
- Log.d(TAG, "Failed loading: " + tile);
- LineLayers.clear(mLineLayers);
- PolygonLayers.clear(mPolyLayers);
- mLineLayers = null;
- mPolyLayers = null;
- tile.isLoading = false;
- return false;
- }
-
- if (mapGeneratorJob.debugSettings.mDrawTileFrames) {
-
- final float[] debugBoxCoords = { 0, 0, 0, Tile.TILE_SIZE,
- Tile.TILE_SIZE, Tile.TILE_SIZE, Tile.TILE_SIZE, 0, 0, 0 };
- final short[] debugBoxIndex = { 10 };
-
- mTagName = new Tag("name", countLines + " " + countNodes + " "
- + tile.toString(), false);
- mPoiX = Tile.TILE_SIZE >> 1;
- mPoiY = 10;
- MapGenerator.renderTheme.matchNode(this, debugTagWay, (byte) 0);
-
- mWays = debugBoxIndex;
- mWayNodes = debugBoxCoords;
- mDrawingLayer = 10 * mLevels;
- MapGenerator.renderTheme.matchWay(this, debugTagBox, (byte) 0, false, true);
- }
- tile.lineLayers = mLineLayers;
- tile.polygonLayers = mPolyLayers;
- tile.labels = mLabels;
- mCurPolyLayer = null;
- mCurLineLayer = null;
-
- tile.newData = true;
- tile.isLoading = false;
-
- return true;
- }
-
- private final Tag[] debugTagBox = { new Tag("debug", "box") };
- private final Tag[] debugTagWay = { new Tag("debug", "way") };
- private final Tag[] debugTagArea = { new Tag("debug", "area") };
-
- private float mProjectionScaleFactor;
-
- private static byte getValidLayer(byte layer) {
- if (layer < 0) {
- return 0;
- /**
- * return instances of MapRenderer
- */
- } else if (layer >= LAYERS) {
- return LAYERS - 1;
- } else {
- return layer;
- }
- }
-
- /**
- * Sets the scale stroke factor for the given zoom level.
- *
- * @param zoomLevel
- * the zoom level for which the scale stroke factor should be set.
- */
- private void setScaleStrokeWidth(byte zoomLevel) {
- int zoomLevelDiff = Math.max(zoomLevel - STROKE_MIN_ZOOM_LEVEL, 0);
- mStrokeScale = (float) Math.pow(STROKE_INCREASE, zoomLevelDiff);
- if (mStrokeScale < 1)
- mStrokeScale = 1;
- }
-
- private String mMapProjection;
-
- @Override
- public void setMapDatabase(IMapDatabase mapDatabase) {
- mMapDatabase = mapDatabase;
- mMapProjection = mMapDatabase.getMapProjection();
- }
-
- @Override
- public IMapDatabase getMapDatabase() {
- return mMapDatabase;
- }
-
- @Override
- public void setRenderTheme(RenderTheme theme) {
- MapGenerator.renderTheme = theme;
- }
-
- @Override
- public boolean checkWay(Tag[] tags, boolean closed) {
-
- mRenderInstructions = MapGenerator.renderTheme.matchWay(this, tags,
- (byte) (mCurrentTile.zoomLevel + 0), closed, false);
-
- return mRenderInstructions != null;
- }
-
- private boolean projectToTile() {
- if (mProjected || mMapProjection == null)
- return true;
-
- boolean useWebMercator = false;
-
- if (mMapProjection == WebMercator.NAME)
- useWebMercator = true;
-
- float[] coords = mWayNodes;
-
- long x = mCurrentTile.x;
- long y = mCurrentTile.y;
- long z = Tile.TILE_SIZE << mCurrentTile.zoomLevel;
- float min = mSimplify;
-
- double divx, divy;
- long dx = (x - (z >> 1));
- long dy = (y - (z >> 1));
-
- if (useWebMercator) {
- divx = f900913 / (z >> 1);
- divy = f900913 / (z >> 1);
- } else {
- divx = 180000000.0 / (z >> 1);
- divy = z / PIx4;
- }
-
- for (int pos = 0, outPos = 0, i = 0, m = mWays.length; i < m; i++) {
- int len = mWays[i];
- if (len == 0)
- continue;
- if (len < 0)
- break;
-
- int cnt = 0;
- float lat, lon, prevLon = 0, prevLat = 0;
-
- for (int end = pos + len; pos < end; pos += 2) {
-
- if (useWebMercator) {
- lon = (float) (coords[pos] / divx - dx);
- lat = (float) (coords[pos + 1] / divy + dy);
- } else {
- lon = (float) ((coords[pos]) / divx - dx);
- double sinLat = Math.sin(coords[pos + 1] * PI180);
- lat = (float) (Math.log((1.0 + sinLat) / (1.0 - sinLat)) * divy + dy);
- }
-
- if (cnt != 0) {
- // drop small distance intermediate nodes
- if (lat == prevLat && lon == prevLon)
- continue;
-
- if ((pos != end - 2) &&
- !((lat > prevLat + min || lat < prevLat - min) ||
- (lon > prevLon + min || lon < prevLon - min)))
- continue;
- }
- coords[outPos++] = prevLon = lon;
- coords[outPos++] = prevLat = lat;
-
- cnt += 2;
- }
-
- mWays[i] = (short) cnt;
- }
- mProjected = true;
- // mProjectedResult = true;
- return true;
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java
deleted file mode 100644
index 7bbf219..0000000
--- a/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java
+++ /dev/null
@@ -1,1209 +0,0 @@
-/*
- * Copyright 2012 Hannes Janetzek
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General License for more details.
- *
- * You should have received a copy of the GNU Lesser General License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.glrenderer;
-
-import static android.opengl.GLES20.GL_ARRAY_BUFFER;
-import static android.opengl.GLES20.GL_BLEND;
-import static android.opengl.GLES20.GL_DITHER;
-import static android.opengl.GLES20.GL_DYNAMIC_DRAW;
-import static android.opengl.GLES20.GL_ONE;
-import static android.opengl.GLES20.GL_ONE_MINUS_SRC_ALPHA;
-import static android.opengl.GLES20.GL_SCISSOR_TEST;
-import static android.opengl.GLES20.GL_SRC_ALPHA;
-import static android.opengl.GLES20.GL_STENCIL_BUFFER_BIT;
-import static android.opengl.GLES20.glBindBuffer;
-import static android.opengl.GLES20.glBlendFunc;
-import static android.opengl.GLES20.glBufferData;
-import static android.opengl.GLES20.glClear;
-import static android.opengl.GLES20.glClearColor;
-import static android.opengl.GLES20.glClearStencil;
-import static android.opengl.GLES20.glDepthMask;
-import static android.opengl.GLES20.glDisable;
-import static android.opengl.GLES20.glEnable;
-import static android.opengl.GLES20.glFinish;
-import static android.opengl.GLES20.glGenBuffers;
-import static android.opengl.GLES20.glScissor;
-import static android.opengl.GLES20.glViewport;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.ShortBuffer;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.opengles.GL10;
-
-import org.mapsforge.android.DebugSettings;
-import org.mapsforge.android.MapView;
-import org.mapsforge.android.mapgenerator.IMapGenerator;
-import org.mapsforge.android.mapgenerator.JobParameters;
-import org.mapsforge.android.mapgenerator.MapGeneratorJob;
-import org.mapsforge.android.mapgenerator.TileCacheKey;
-import org.mapsforge.android.mapgenerator.TileDistanceSort;
-import org.mapsforge.android.rendertheme.RenderTheme;
-import org.mapsforge.android.utils.GlUtils;
-import org.mapsforge.core.MapPosition;
-import org.mapsforge.core.MercatorProjection;
-import org.mapsforge.core.Tile;
-
-import android.opengl.GLES20;
-import android.opengl.Matrix;
-import android.os.SystemClock;
-import android.util.FloatMath;
-import android.util.Log;
-
-/**
- * TODO - use proxy child/parent tile nearer to current tile (currently it is always parent first) - use stencil instead
- * of scissor mask for rotation - draw up to two parents above current tile, maybe prefetch parent
- */
-public class MapRenderer implements org.mapsforge.android.IMapRenderer {
- private static final String TAG = "MapRenderer";
- private static final int MB = 1024 * 1024;
-
- private static int CACHE_TILES_MAX = 250;
- private static int CACHE_TILES = CACHE_TILES_MAX;
- private static int LIMIT_BUFFERS = 20 * MB;
-
- private static final int SHORT_BYTES = 2;
-
- static final float COORD_MULTIPLIER = 8.0f;
-
- private final MapView mMapView;
- private static ArrayList mJobList;
- private static ArrayList mVBOs;
- private static TileCacheKey mTileCacheKey;
- private static HashMap mTiles;
-
- // all tiles currently referenced
- private static ArrayList mTileList;
-
- // tiles that have new data to upload, see passTile()
- private static ArrayList mTilesLoaded;
-
- private static TileDistanceSort mTileDistanceSort;
-
- private static DebugSettings mDebugSettings;
- private static JobParameters mJobParameter;
-
- // private static MapPosition mMapPosition;
-
- private static int mWidth, mHeight;
- private static float mAspect;
-
- // draw position is updated from current position in onDrawFrame
- // keeping the position consistent while drawing
- private static double mDrawX, mDrawY, mDrawZ, mCurX, mCurY, mCurZ;
- private static float mDrawScale, mCurScale;
-
- // current center tile
- private static long mTileX, mTileY;
- private static float mLastScale;
- private static byte mLastZoom;
-
- private static int rotateBuffers = 2;
- private static ShortBuffer shortBuffer[];
- private static short[] mFillCoords;
-
- // bytes currently loaded in VBOs
- private static int mBufferMemoryUsage;
-
- // flag set by updateVisibleList when current visible tiles changed.
- // used in onDrawFrame to nextTiles to curTiles
- private static boolean mUpdateTiles;
-
- class TilesData {
- int cnt = 0;
- final GLMapTile[] tiles;
-
- TilesData(int numTiles) {
- tiles = new GLMapTile[numTiles];
- }
- }
-
- private static float[] mMVPMatrix = new float[16];
-
- // newTiles is set in updateVisibleList and swapped
- // with curTiles on main thread. curTiles is swapped
- // with drawTiles in onDrawFrame in GL thread.
- private static TilesData newTiles, curTiles, drawTiles;
-
- private static boolean mInitial;
- private static short mDrawCount = 0;
-
- /**
- *
- */
- public boolean timing = false;
-
- @Override
- public void setRenderTheme(RenderTheme t) {
- int bg = t.getMapBackground();
- float[] c = new float[4];
- c[0] = (bg >> 16 & 0xff) / 255.0f;
- c[1] = (bg >> 8 & 0xff) / 255.0f;
- c[2] = (bg >> 0 & 0xff) / 255.0f;
- c[3] = (bg >> 24 & 0xff) / 255.0f;
- mClearColor = c;
- }
-
- /**
- * @param mapView
- * the MapView
- */
- public MapRenderer(MapView mapView) {
- Log.d(TAG, "init MapRenderer");
-
- mMapView = mapView;
-
- if (mInitial)
- return;
-
- mDebugSettings = mapView.getDebugSettings();
-
- mVBOs = new ArrayList();
- mJobList = new ArrayList();
-
- mTiles = new HashMap(CACHE_TILES * 2);
- mTileList = new ArrayList();
- mTileCacheKey = new TileCacheKey();
- mTilesLoaded = new ArrayList(30);
-
- mTileDistanceSort = new TileDistanceSort();
- Matrix.setIdentityM(mMVPMatrix, 0);
- mInitial = true;
- mUpdateTiles = false;
- }
-
- private static int updateTileDistances() {
- int h = (Tile.TILE_SIZE >> 1);
- byte zoom = mLastZoom;
- long x = mTileX + h;
- long y = mTileY + h;
- int diff;
- long dx, dy;
- int cnt = 0;
-
- // TODO this could need some fixing..
- // and be optimized to consider move/zoom direction
- for (int i = 0, n = mTileList.size(); i < n; i++) {
- GLMapTile t = mTileList.get(i);
- diff = (t.zoomLevel - zoom);
- if (t.isActive)
- cnt++;
-
- if (diff == 0) {
- dx = (t.pixelX + h) - x;
- dy = (t.pixelY + h) - y;
- t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) * 0.5f;
- // t.distance = FloatMath.sqrt((dx * dx + dy * dy)) * 0.25f;
- } else if (diff > 0) {
- // tile zoom level is child of current
- dx = ((t.pixelX + h) >> diff) - x;
- dy = ((t.pixelY + h) >> diff) - y;
-
- // dy *= mAspect;
- t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) * diff;
- // t.distance = FloatMath.sqrt((dx * dx + dy * dy)) * diff;
-
- } else {
- // tile zoom level is parent of current
- dx = ((t.pixelX + h) << -diff) - x;
- dy = ((t.pixelY + h) << -diff) - y;
-
- t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy))
- * (-diff * 0.25f);
- // t.distance = FloatMath.sqrt((dx * dx + dy * dy)) * (-diff * 0.5f);
- }
-
- // Log.d(TAG, diff + " " + (float) t.distance / Tile.TILE_SIZE + " " + t);
- }
- return cnt;
- }
-
- private static boolean childIsActive(GLMapTile t) {
- GLMapTile c = null;
-
- for (int i = 0; i < 4; i++) {
- c = t.child[i];
- if (c != null && c.isActive && !(c.isReady || c.newData))
- return true;
- }
-
- return false;
- }
-
- private static void limitCache(int remove) {
- byte z = mLastZoom;
- // Log.d(TAG, "--------------------------------");
- for (int j = mTileList.size() - 1, cnt = 0; cnt < remove && j > 0; j--) {
-
- GLMapTile t = mTileList.remove(j);
- // dont remove tile used by renderthread or mapgenerator
- // FIXME set tile loading state in main thread
- if (t.isLoading) {
-
- Log.d(TAG, "cancel loading " + t + " " + (t.zoomLevel - mCurZ)
- + " " + (t.zoomLevel - mDrawZ) + " " + t.distance);
- t.isCanceled = true;
- }
- else if (t.isActive) {
- Log.d(TAG, "EEEK removing active " + t + " " + (t.zoomLevel - mCurZ)
- + " " + (t.zoomLevel - mDrawZ) + " " + t.distance);
- mTileList.add(t);
- continue;
- }
- // check if this tile is used as proxy for not yet drawn active tile
- // TODO to be simplified...
- else if (t.isReady || t.newData) {
- if (t.zoomLevel == z + 1) {
- if (t.parent != null && t.parent.isActive
- && !(t.parent.isReady || t.parent.newData)) {
- mTileList.add(t);
- Log.d(TAG, "EEEK removing active proxy child");
- continue;
- }
- } else if (t.zoomLevel == z - 1) {
- GLMapTile c = null;
- for (int i = 0; i < 4; i++) {
- c = t.child[i];
- if (c != null && c.isActive && !(c.isReady || c.newData))
- break;
- c = null;
- }
-
- if (c != null) {
- Log.d(TAG, "EEEK removing active proxy parent");
- mTileList.add(t);
- continue;
- }
- } else if (t.zoomLevel == z - 2) {
- GLMapTile c = null, c2 = null;
- for (int i = 0; i < 4; i++) {
- c = t.child[i];
- if (c != null) {
- for (int k = 0; k < 4; k++) {
- c2 = c.child[k];
- if (c2 != null && c2.isActive
- && !(c2.isReady || c2.newData))
- break;
-
- c2 = null;
- }
- if (c2 != null)
- break;
- }
- c = null;
- }
-
- if (c != null) {
- // Log.d(TAG, "EEEK removing active second level proxy parent");
- mTileList.add(t);
- continue;
- }
- }
- }
- // Log.d(TAG, ">>> remove " + t + " " + (t.zoomLevel - mCurZ)
- // + " " + t.distance);
-
- cnt++;
- mTileCacheKey.set(t.tileX, t.tileY, t.zoomLevel);
- mTiles.remove(mTileCacheKey);
-
- // clear references to this tile
- for (int i = 0; i < 4; i++) {
- if (t.child[i] != null)
- t.child[i].parent = null;
- }
-
- if (t.parent != null) {
- for (int i = 0; i < 4; i++) {
- if (t.parent.child[i] == t) {
- t.parent.child[i] = null;
- break;
- }
- }
- }
-
- synchronized (mVBOs) {
- clearTile(t);
- }
- }
- // Log.d(TAG, "--------------------------------<<");
- }
-
- private Object lock = new Object();
-
- private boolean updateVisibleList(double x, double y, int zdir) {
- byte zoomLevel = mLastZoom;
- float scale = mLastScale;
- double add = 1.0f / scale;
- int offsetX = (int) ((mWidth >> 1) * add);
- int offsetY = (int) ((mHeight >> 1) * add);
-
- long pixelRight = (long) x + offsetX;
- long pixelBottom = (long) y + offsetY;
- long pixelLeft = (long) x - offsetX;
- long pixelTop = (long) y - offsetY;
-
- long tileLeft = MercatorProjection.pixelXToTileX(pixelLeft, zoomLevel) - 1;
- long tileTop = MercatorProjection.pixelYToTileY(pixelTop, zoomLevel) - 1;
- long tileRight = MercatorProjection.pixelXToTileX(pixelRight, zoomLevel) + 1;
- long tileBottom = MercatorProjection.pixelYToTileY(pixelBottom, zoomLevel) + 1;
-
- mJobList.clear();
- mJobParameter = mMapView.getJobParameters();
-
- int tiles = 0;
- if (newTiles == null)
- return false;
-
- int max = newTiles.tiles.length - 1;
- long limit = (long) Math.pow(2, zoomLevel) - 1;
-
- if (tileTop < 0)
- tileTop = 0;
- if (tileLeft < 0)
- tileLeft = 0;
- if (tileBottom >= limit)
- tileBottom = limit;
- if (tileRight >= limit)
- tileRight = limit;
-
- for (long tileY = tileTop; tileY <= tileBottom; tileY++) {
- for (long tileX = tileLeft; tileX <= tileRight; tileX++) {
- // FIXME
- if (tiles == max)
- break;
-
- GLMapTile tile = mTiles.get(mTileCacheKey.set(tileX, tileY, zoomLevel));
-
- if (tile == null) {
- tile = new GLMapTile(tileX, tileY, zoomLevel);
- TileCacheKey key = new TileCacheKey(mTileCacheKey);
-
- // FIXME use sparse matrix or sth.
-
- if (mTiles.put(key, tile) != null)
- Log.d(TAG, "eeek collision");
- mTileList.add(tile);
-
- mTileCacheKey.set((tileX >> 1), (tileY >> 1), (byte) (zoomLevel - 1));
- tile.parent = mTiles.get(mTileCacheKey);
- int idx = (int) ((tileX & 0x01) + 2 * (tileY & 0x01));
-
- // set this tile to be child of its parent
- if (tile.parent != null) {
- tile.parent.child[idx] = tile;
- } else if (zdir > 0 && zoomLevel > 0) {
- tile.parent = new GLMapTile(tileX >> 1, tileY >> 1,
- (byte) (zoomLevel - 1));
- key = new TileCacheKey(mTileCacheKey);
- if (mTiles.put(key, tile.parent) != null)
- Log.d(TAG, "eeek collision");
- mTileList.add(tile.parent);
- setTileChildren(tile.parent);
- }
-
- setTileChildren(tile);
-
- }
-
- newTiles.tiles[tiles++] = tile;
-
- if (!tile.isReady && !tile.newData && !tile.isLoading) {
- MapGeneratorJob job = new MapGeneratorJob(tile, mJobParameter,
- mDebugSettings);
- mJobList.add(job);
- }
-
- if (zdir > 0) {
- // prefetch parent
- if (tile.parent != null && !tile.parent.isReady
- && !tile.parent.newData
- && !tile.parent.isLoading) {
- MapGeneratorJob job = new MapGeneratorJob(tile.parent,
- mJobParameter,
- mDebugSettings);
- if (!mJobList.contains(job))
- mJobList.add(job);
- }
- }
- }
- }
-
- // scramble tile draw order: might help to make gl
- // pipelines more independent... just a guess :)
- // for (int i = 1; i < tiles / 2; i += 2) {
- // GLMapTile tmp = newTiles.tiles[i];
- // newTiles.tiles[i] = newTiles.tiles[tiles - i];
- // newTiles.tiles[tiles - i] = tmp;
- // }
-
- newTiles.cnt = tiles;
-
- // pass new tile list to glThread
- synchronized (this) {
-
- for (int i = 0; i < curTiles.cnt; i++) {
- boolean found = false;
-
- for (int j = 0; j < drawTiles.cnt && !found; j++)
- if (curTiles.tiles[i] == drawTiles.tiles[j])
- found = true;
-
- for (int j = 0; j < newTiles.cnt && !found; j++)
- if (curTiles.tiles[i] == newTiles.tiles[j])
- found = true;
-
- if (!found) {
- curTiles.tiles[i].isActive = false;
- // activeList.remove(newTiles.tiles[i]);
- }
- }
-
- for (int i = 0; i < tiles; i++) {
- if (!newTiles.tiles[i].isActive) {
- newTiles.tiles[i].isActive = true;
- // activeList.add(newTiles.tiles[i]);
- }
- }
-
- TilesData tmp = curTiles;
- curTiles = newTiles;
- curTiles.cnt = tiles;
- newTiles = tmp;
-
- mCurX = x;
- mCurY = y;
- mCurZ = mLastZoom;
- mCurScale = mLastScale;
-
- mUpdateTiles = true;
- }
-
- int active = 0;
- // see FIXME in passTile
- synchronized (mTiles) {
- active = updateTileDistances();
- }
-
- if (mJobList.size() > 0)
- mMapView.addJobs(mJobList);
-
- int removes = mTiles.size() - CACHE_TILES;
-
- if (removes > 20) {
-
- Log.d(TAG, "---- remove " + removes + " on " + zoomLevel + " active:"
- + active + "------");
- Collections.sort(mTileList, mTileDistanceSort);
- limitCache(removes);
- }
-
- return true;
- }
-
- // private static ArrayList activeList = new ArrayList(100);
-
- private static void setTileChildren(GLMapTile tile) {
-
- long xx = tile.tileX << 1;
- long yy = tile.tileY << 1;
- byte z = (byte) (tile.zoomLevel + 1);
-
- tile.child[0] = mTiles.get(mTileCacheKey.set(xx, yy, z));
- tile.child[1] = mTiles.get(mTileCacheKey.set(xx + 1, yy, z));
- tile.child[2] = mTiles.get(mTileCacheKey.set(xx, yy + 1, z));
- tile.child[3] = mTiles.get(mTileCacheKey.set(xx + 1, yy + 1, z));
-
- // set this tile to be parent of its children
- for (int i = 0; i < 4; i++) {
- if (tile.child[i] != null)
- tile.child[i].parent = tile;
- }
- }
-
- private static void clearTile(GLMapTile t) {
- LineLayers.clear(t.lineLayers);
- PolygonLayers.clear(t.polygonLayers);
-
- if (t.vbo != null)
- mVBOs.add(t.vbo);
-
- t.labels = null;
- t.lineLayers = null;
- t.polygonLayers = null;
- t.newData = false;
- t.isLoading = false;
- t.isReady = false;
- }
-
- /**
- * called by MapView when position or map settings changes
- */
- @Override
- public void redrawTiles(boolean clear) {
- boolean changedPos = false;
- boolean changedZoom = false;
- MapPosition mapPosition = mMapView.getMapPosition().getMapPosition();
-
- if (mapPosition == null) {
- Log.d(TAG, ">>> no map position");
- return;
- }
-
- if (clear) {
- mInitial = true;
- synchronized (this) {
- for (GLMapTile t : mTileList)
- clearTile(t);
-
- mTileList.clear();
- mTiles.clear();
- curTiles.cnt = 0;
- mBufferMemoryUsage = 0;
- }
- }
-
- byte zoomLevel = mapPosition.zoomLevel;
- float scale = mapPosition.scale;
-
- double x = MercatorProjection.longitudeToPixelX(
- mapPosition.geoPoint.getLongitude(), zoomLevel);
- double y = MercatorProjection.latitudeToPixelY(
- mapPosition.geoPoint.getLatitude(), zoomLevel);
-
- long tileX = MercatorProjection.pixelXToTileX(x, zoomLevel) * Tile.TILE_SIZE;
- long tileY = MercatorProjection.pixelYToTileY(y, zoomLevel) * Tile.TILE_SIZE;
-
- int zdir = 0;
- if (mInitial || mLastZoom != zoomLevel) {
- changedZoom = true;
- mLastScale = scale;
- }
- else if (tileX != mTileX || tileY != mTileY) {
- if (mLastScale - scale > 0 && scale > 1.2)
- zdir = 1;
- mLastScale = scale;
- changedPos = true;
- }
- else if (mLastScale - scale > 0.2 || mLastScale - scale < -0.2) {
- if (mLastScale - scale > 0 && scale > 1.2)
- zdir = 1;
- mLastScale = scale;
- changedPos = true;
- }
- mInitial = false;
-
- mTileX = tileX;
- mTileY = tileY;
- mLastZoom = zoomLevel;
-
- if (zdir > 0)
- Log.d(TAG, "prefetch parent");
-
- if (changedZoom) {
- // need to update visible list first when zoom level changes
- // as scaling is relative to the tiles of current zoom-level
- updateVisibleList(x, y, 0);
- } else {
- // pass new position to glThread
- synchronized (this) {
- // do not change position while drawing
- mCurX = x;
- mCurY = y;
- mCurZ = zoomLevel;
- mCurScale = scale;
- }
- }
-
- if (!timing)
- mMapView.requestRender();
-
- if (changedPos)
- updateVisibleList(x, y, zdir);
- }
-
- private static final int MAX_TILES_IN_QUEUE = 40;
-
- /**
- * called by MapWorkers when tile is loaded
- */
- @Override
- public synchronized boolean passTile(MapGeneratorJob mapGeneratorJob) {
- GLMapTile tile = (GLMapTile) mapGeneratorJob.tile;
-
- if (tile.isCanceled) {
- Log.d(TAG, "passTile: canceld " + tile);
- clearTile(tile);
- return true;
- }
-
- if (!timing && tile.isVisible)
- mMapView.requestRender();
-
- int size = mTilesLoaded.size();
- if (size > MAX_TILES_IN_QUEUE) {
- // remove uploaded tiles
- for (int i = 0; i < size;) {
- GLMapTile t = mTilesLoaded.get(i);
-
- if (!t.newData) {
- mTilesLoaded.remove(i);
- size--;
- continue;
- }
- i++;
- }
-
- if (size > MAX_TILES_IN_QUEUE) {
- // FIXME pass tile via queue back to mainloop instead...
- synchronized (mTiles) {
- Collections.sort(mTilesLoaded, mTileDistanceSort);
- }
- // clear loaded but not used tiles
- while (size-- > MAX_TILES_IN_QUEUE) {
- GLMapTile t = mTilesLoaded.get(size);
- // FIXME race condition: tile could be uploaded as proxy
- // therefore sync with upload tile data
- synchronized (t) {
-
- // dont remove tile if currently used or is direct parent
- // or child of currently active tile
- if (t.isActive || childIsActive(t))
- // (t.parent != null && t.parent.isActive))
- {
- // Log.d(TAG, "keep unused tile data: " + t + " " + t.isActive);
- continue;
- }
- mTilesLoaded.remove(size);
- // Log.d(TAG, "remove unused tile data: " + t);
- clearTile(t);
- // TODO could also remove from mTileHash/List ?
- }
- }
- }
- }
- mTilesLoaded.add(0, tile);
-
- return true;
- }
-
- private static void setMatrix(GLMapTile tile, float div) {
- float x, y, scale;
-
- scale = (float) (2.0 * mDrawScale / (mHeight * div));
- x = (float) (tile.x - mDrawX * div);
- y = (float) (tile.y - mDrawY * div);
-
- mMVPMatrix[12] = x * scale * mAspect;
- mMVPMatrix[13] = -y * scale;
- mMVPMatrix[0] = scale * mAspect / COORD_MULTIPLIER;
- mMVPMatrix[5] = scale / COORD_MULTIPLIER;
- }
-
- private static boolean setTileScissor(GLMapTile tile, float div) {
- double dx, dy, scale;
-
- if (div == 0) {
- dx = tile.pixelX - mDrawX;
- dy = tile.pixelY - mDrawY;
- scale = mDrawScale;
- } else {
- dx = tile.pixelX - mDrawX * div;
- dy = tile.pixelY - mDrawY * div;
- scale = mDrawScale / div;
- }
- int size = Tile.TILE_SIZE;
- int sx = (int) (dx * scale);
- int sy = (int) (dy * scale);
-
- if (sy > mHeight / 2 || sx > mWidth / 2
- || sx + size * scale < -mWidth / 2
- || sy + size * scale < -mHeight / 2) {
- tile.isVisible = false;
- return false;
- }
-
- tile.isVisible = true;
-
- return true;
- }
-
- private int uploadCnt = 0;
-
- private boolean uploadTileData(GLMapTile tile) {
- ShortBuffer sbuf = null;
-
- // use multiple buffers to avoid overwriting buffer while current
- // data is uploaded (or rather the blocking which is probably done to avoid this)
- if (uploadCnt >= rotateBuffers) {
- uploadCnt = 0;
- GLES20.glFlush();
- }
-
- // Upload line data to vertex buffer object
-
- // FIXME do this in main thread:
- if (tile.vbo == null) {
- synchronized (mVBOs) {
- if (mVBOs.size() < 1) {
- Log.d(TAG, "uploadTileData, no VBOs left");
- return false;
- }
- tile.vbo = mVBOs.remove(mVBOs.size() - 1);
- }
- }
-
- synchronized (tile) {
- if (!tile.newData)
- return false;
-
- tile.isReady = true;
- tile.newData = false;
- }
-
- int lineSize = LineLayers.sizeOf(tile.lineLayers);
- int polySize = PolygonLayers.sizeOf(tile.polygonLayers);
- int newSize = lineSize + polySize;
-
- if (newSize == 0) {
- LineLayers.clear(tile.lineLayers);
- PolygonLayers.clear(tile.polygonLayers);
- tile.lineLayers = null;
- tile.polygonLayers = null;
- return false;
- }
-
- // Log.d(TAG, "uploadTileData, " + tile);
-
- glBindBuffer(GL_ARRAY_BUFFER, tile.vbo.id);
-
- sbuf = shortBuffer[uploadCnt];
-
- // add fill coordinates
- newSize += 8;
-
- // FIXME probably not a good idea to do this in gl thread...
- if (sbuf == null || sbuf.capacity() < newSize) {
- ByteBuffer bbuf = ByteBuffer.allocateDirect(newSize * SHORT_BYTES).order(
- ByteOrder.nativeOrder());
- sbuf = bbuf.asShortBuffer();
- shortBuffer[uploadCnt] = sbuf;
- sbuf.put(mFillCoords, 0, 8);
- }
-
- sbuf.clear();
- sbuf.position(8);
-
- PolygonLayers.compileLayerData(tile.polygonLayers, sbuf);
- LineLayers.compileLayerData(tile.lineLayers, sbuf);
- sbuf.flip();
-
- newSize *= SHORT_BYTES;
-
- if (tile.vbo.size > newSize && tile.vbo.size < newSize * 4) {
- GLES20.glBufferSubData(GL_ARRAY_BUFFER, 0, newSize, sbuf);
- } else {
- mBufferMemoryUsage -= tile.vbo.size;
- tile.vbo.size = newSize;
- glBufferData(GL_ARRAY_BUFFER, tile.vbo.size, sbuf, GL_DYNAMIC_DRAW);
- mBufferMemoryUsage += tile.vbo.size;
- }
- tile.lineOffset = (8 + polySize) * SHORT_BYTES;
-
- // tile.isLoading = false;
-
- uploadCnt++;
-
- return true;
- }
-
- private float[] mClearColor;
- private static long mRedrawCnt = 0;
-
- @Override
- public void onDrawFrame(GL10 glUnused) {
- long start = 0, poly_time = 0, clear_time = 0;
-
- if (mClearColor != null) {
- glClearColor(mClearColor[0], mClearColor[1], mClearColor[2],
- mClearColor[3]);
- mClearColor = null;
- }
-
- glClear(GLES20.GL_COLOR_BUFFER_BIT);// | GLES20.GL_DEPTH_BUFFER_BIT);
-
- if (mInitial)
- return;
-
- if (timing)
- start = SystemClock.uptimeMillis();
-
- // get position and current tiles to draw
- synchronized (this) {
- mDrawX = mCurX;
- mDrawY = mCurY;
- mDrawZ = mCurZ;
- mDrawScale = mCurScale;
-
- if (mUpdateTiles) {
- TilesData tmp = drawTiles;
- drawTiles = curTiles;
- curTiles = tmp;
- mUpdateTiles = false;
- }
- }
-
- int tileCnt = drawTiles.cnt;
- GLMapTile[] tiles = drawTiles.tiles;
-
- // not really tested, try to clear some vbo when exceding limit
- if (mBufferMemoryUsage > LIMIT_BUFFERS) {
- Log.d(TAG, "buffer object usage: " + mBufferMemoryUsage / MB + "MB");
-
- synchronized (mVBOs) {
- for (VertexBufferObject vbo : mVBOs) {
- if (vbo.size == 0)
- continue;
-
- mBufferMemoryUsage -= vbo.size;
- glBindBuffer(GL_ARRAY_BUFFER, vbo.id);
- glBufferData(GL_ARRAY_BUFFER, 0, null, GL_DYNAMIC_DRAW);
- vbo.size = 0;
- }
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- }
- Log.d(TAG, " > " + mBufferMemoryUsage / MB + "MB");
-
- if (CACHE_TILES > 100)
- CACHE_TILES -= 50;
- } else if (CACHE_TILES < CACHE_TILES_MAX) {
- CACHE_TILES += 50;
- }
-
- uploadCnt = 0;
- int updateTextures = 0;
-
- // check visible tiles, upload new vertex data
- for (int i = 0; i < tileCnt; i++) {
- GLMapTile tile = tiles[i];
-
- if (!setTileScissor(tile, 1))
- continue;
-
- if (updateTextures < 4 && tile.texture == null
- && TextRenderer.drawToTexture(tile))
- updateTextures++;
-
- if (tile.newData) {
- uploadTileData(tile);
- continue;
- }
-
- if (!tile.isReady) {
- if (tile.parent != null && tile.parent.newData) {
- uploadTileData(tile.parent);
- } else {
- if (tile.child[0] != null && tile.child[0].newData)
- uploadTileData(tile.child[0]);
- if (tile.child[1] != null && tile.child[1].newData)
- uploadTileData(tile.child[1]);
- if (tile.child[2] != null && tile.child[2].newData)
- uploadTileData(tile.child[2]);
- if (tile.child[3] != null && tile.child[3].newData)
- uploadTileData(tile.child[3]);
- }
- }
- }
-
- if (updateTextures > 0) {
- TextRenderer.compileTextures();
- if (updateTextures == 4)
- mMapView.requestRender();
- }
- // if (GlUtils.checkGlOutOfMemory("upload: " + mBufferMemoryUsage)
- // && LIMIT_BUFFERS > MB)
- // LIMIT_BUFFERS -= MB;
-
- if (timing)
- clear_time = (SystemClock.uptimeMillis() - start);
-
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- for (int i = 0; i < tileCnt; i++) {
- if (tiles[i].isVisible && !tiles[i].isReady) {
- drawProxyTile(tiles[i]);
- }
- }
-
- for (int i = 0; i < tileCnt; i++) {
- if (tiles[i].isVisible && tiles[i].isReady) {
- drawTile(tiles[i], 1);
- }
- }
- mDrawCount = 0;
-
- glEnable(GL_BLEND);
-
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-
- int zoomLevelDiff = Math
- .max((int) mDrawZ - MapGenerator.STROKE_MAX_ZOOM_LEVEL, 0);
- float scale = (float) Math.pow(1.4, zoomLevelDiff);
- if (scale < 1)
- scale = 1;
-
- if (mDrawZ >= MapGenerator.STROKE_MAX_ZOOM_LEVEL)
- TextRenderer.beginDraw(FloatMath.sqrt(mDrawScale) / scale);
- else
- TextRenderer.beginDraw(mDrawScale);
-
- for (int i = 0; i < tileCnt; i++) {
- if (!tiles[i].isVisible || tiles[i].texture == null)
- continue;
-
- setMatrix(tiles[i], 1);
- TextRenderer.drawTile(tiles[i], mMVPMatrix);
- }
- TextRenderer.endDraw();
-
- if (timing) {
- glFinish();
- Log.d(TAG, "draw took " + (SystemClock.uptimeMillis() - start)
- + " " + clear_time + " " + poly_time);
- }
-
- mRedrawCnt++;
- }
-
- private static void drawTile(GLMapTile tile, float div) {
-
- if (tile.lastDraw == mRedrawCnt)
- return;
-
- tile.lastDraw = mRedrawCnt;
-
- // if (div != 1)
- // Log.d(TAG, "draw proxy " + div + " " + tile);
-
- setMatrix(tile, div);
-
- glBindBuffer(GL_ARRAY_BUFFER, tile.vbo.id);
- LineLayer ll = tile.lineLayers;
- PolygonLayer pl = tile.polygonLayers;
-
- boolean clipped = false;
-
- for (; pl != null || ll != null;) {
- int lnext = Integer.MAX_VALUE;
- int pnext = Integer.MAX_VALUE;
-
- if (ll != null)
- lnext = ll.layer;
-
- if (pl != null)
- pnext = pl.layer;
-
- if (pl != null && pnext < lnext) {
- glDisable(GL_BLEND);
-
- pl = PolygonLayers.drawPolygons(pl, lnext, mMVPMatrix, mDrawZ,
- mDrawScale, mDrawCount, !clipped);
-
- if (!clipped) {
- clipped = true;
- glDisable(GLES20.GL_DEPTH_TEST);
- }
- } else {
- // nastay
- if (!clipped)
- PolygonLayers.drawPolygons(null, 0, mMVPMatrix, mDrawZ,
- mDrawScale, mDrawCount, true);
- else {
- GLES20.glEnable(GLES20.GL_DEPTH_TEST);
- glEnable(GLES20.GL_POLYGON_OFFSET_FILL);
- GLES20.glPolygonOffset(0, mDrawCount);
- }
- glEnable(GL_BLEND);
-
- ll = LineLayers.drawLines(tile, ll, pnext, mMVPMatrix, div,
- mDrawZ, mDrawScale);
-
- glDisable(GLES20.GL_DEPTH_TEST);
- glDisable(GLES20.GL_POLYGON_OFFSET_FILL);
- }
- }
-
- mDrawCount++;
- }
-
- private static boolean drawProxyChild(GLMapTile tile) {
- int drawn = 0;
- for (int i = 0; i < 4; i++) {
- GLMapTile c = tile.child[i];
- if (c != null) {
- if (!setTileScissor(c, 2)) {
- drawn++;
- continue;
- }
-
- if (c.isReady && c.isVisible) {
- drawTile(c, 2);
- drawn++;
- }
- }
- }
- return drawn == 4;
- }
-
- private static void drawProxyTile(GLMapTile tile) {
- if (mDrawScale < 1.5f) {
- if (!drawProxyChild(tile)) {
- if (tile.parent != null) {
- if (tile.parent.isReady) {
- drawTile(tile.parent, 0.5f);
- } else {
- GLMapTile p = tile.parent.parent;
- if (p != null && p.isReady)
- drawTile(p, 0.25f);
- }
- }
- }
- } else {
- if (tile.parent != null && tile.parent.isReady) {
- drawTile(tile.parent, 0.5f);
- } else if (!drawProxyChild(tile)) {
- if (tile.parent != null) {
- GLMapTile p = tile.parent.parent;
- if (p != null && p.isReady)
- drawTile(p, 0.25f);
- }
- }
- }
- }
-
- @Override
- public void onSurfaceChanged(GL10 glUnused, int width, int height) {
- GlUtils.checkGlError("onSurfaceChanged");
-
- mVBOs.clear();
- mTiles.clear();
- mTileList.clear();
- ShortPool.finish();
- // LineLayers.finish();
-
- drawTiles = newTiles = curTiles = null;
- mBufferMemoryUsage = 0;
- mInitial = true;
-
- if (width <= 0 || height <= 0)
- return;
-
- // STENCIL_BITS = GlConfigChooser.stencilSize;
-
- mWidth = width;
- mHeight = height;
- mAspect = (float) height / width;
-
- glViewport(0, 0, width, height);
- GlUtils.checkGlError("glViewport");
-
- glScissor(0, 0, mWidth, mHeight);
-
- int numTiles = (mWidth / (Tile.TILE_SIZE / 2) + 2)
- * (mHeight / (Tile.TILE_SIZE / 2) + 2);
-
- drawTiles = new TilesData(numTiles);
- newTiles = new TilesData(numTiles);
- curTiles = new TilesData(numTiles);
-
- Log.d(TAG, "using: " + numTiles + " + cache: " + CACHE_TILES);
-
- // Set up vertex buffer objects
- int numVBO = (CACHE_TILES + numTiles);
- int[] mVboIds = new int[numVBO];
- glGenBuffers(numVBO, mVboIds, 0);
- GlUtils.checkGlError("glGenBuffers");
-
- for (int i = 1; i < numVBO; i++)
- mVBOs.add(new VertexBufferObject(mVboIds[i]));
-
- // Set up textures
- TextRenderer.init(numTiles * 2);
-
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glDisable(GLES20.GL_DEPTH_TEST);
- glDepthMask(false);
- glDisable(GL_DITHER);
- glClearColor(0.98f, 0.98f, 0.97f, 1.0f);
- glClearStencil(0);
- glClear(GL_STENCIL_BUFFER_BIT);
-
- // glClear(GL_STENCIL_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
- // glEnable(GLES20.GL_DEPTH_TEST);
-
- glEnable(GL_SCISSOR_TEST);
-
- mDebugSettings = mMapView.getDebugSettings();
- mJobParameter = mMapView.getJobParameters();
-
- mMapView.redrawTiles();
- }
-
- @Override
- public void onSurfaceCreated(GL10 gl, EGLConfig config) {
-
- String ext = GLES20.glGetString(GLES20.GL_EXTENSIONS);
- Log.d(TAG, "Extensions: " + ext);
-
- // if (ext.indexOf("GL_OES_vertex_half_float") >= 0) {
- // useHalfFloat = true;
- shortBuffer = new ShortBuffer[rotateBuffers];
-
- // add half pixel to tile clip/fill coordinates
- // to avoid rounding issues
- short min = -4;
- short max = (short) ((Tile.TILE_SIZE << 3) + 4);
- mFillCoords = new short[8];
- mFillCoords[0] = min;
- mFillCoords[1] = max;
- mFillCoords[2] = max;
- mFillCoords[3] = max;
- mFillCoords[4] = min;
- mFillCoords[5] = min;
- mFillCoords[6] = max;
- mFillCoords[7] = min;
-
- LineLayers.init();
- PolygonLayers.init();
-
- GlUtils.checkGlError("onSurfaceCreated");
-
- }
-
- @Override
- public boolean processedTile() {
- return true;
- }
-
- @Override
- public IMapGenerator createMapGenerator() {
- return new MapGenerator();
-
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/PolygonLayer.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/PolygonLayer.java
deleted file mode 100644
index abe5636..0000000
--- a/VectorTileMap/src/org/mapsforge/android/glrenderer/PolygonLayer.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 2012 Hannes Janetzek
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General License for more details.
- *
- * You should have received a copy of the GNU Lesser General License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.glrenderer;
-
-import org.mapsforge.android.rendertheme.renderinstruction.Area;
-import org.mapsforge.core.Tile;
-
-class PolygonLayer {
- private static final float S = MapRenderer.COORD_MULTIPLIER;
-
- PolygonLayer next;
- Area area;
- // private static final float MapRenderer.COORD_MULTIPLIER = 8.0f;
- private boolean first = true;
- private float originX;
- private float originY;
-
- ShortItem pool;
- protected ShortItem curItem;
- int verticesCnt;
- int offset;
-
- final int layer;
-
- PolygonLayer(int layer, Area area) {
- this.layer = layer;
- this.area = area;
- curItem = ShortPool.get();
- pool = curItem;
- }
-
- short[] getNextItem() {
- curItem.used = ShortItem.SIZE;
-
- curItem.next = ShortPool.get();
- curItem = curItem.next;
-
- return curItem.vertices;
- }
-
- void addPolygon(float[] points, int pos, int length) {
-
- verticesCnt += length / 2 + 2;
-
- if (first) {
- first = false;
- originX = Tile.TILE_SIZE >> 1; // points[pos];
- originY = Tile.TILE_SIZE >> 1; // points[pos + 1];
- }
-
- short[] curVertices = curItem.vertices;
- int outPos = curItem.used;
-
- if (outPos == ShortItem.SIZE) {
- curVertices = getNextItem();
- outPos = 0;
- }
-
- curVertices[outPos++] = (short) (originX * S);
- curVertices[outPos++] = (short) (originY * S);
-
- int MAX = ShortItem.SIZE;
- int remaining = length;
- int inPos = pos;
- while (remaining > 0) {
-
- if (outPos == MAX) {
- curVertices = getNextItem();
- outPos = 0;
- }
-
- int len = remaining;
- if (len > MAX - outPos)
- len = MAX - outPos;
-
- for (int i = 0; i < len; i++)
- curVertices[outPos++] = (short) (points[inPos++] * S);
-
- // System.arraycopy(points, inPos, curVertices, outPos, len);
-
- // outPos += len;
- // inPos += len;
-
- remaining -= len;
- }
-
- if (outPos == MAX) {
- curVertices = getNextItem();
- outPos = 0;
- }
-
- curVertices[outPos++] = (short) (points[pos + 0] * S);
- curVertices[outPos++] = (short) (points[pos + 1] * S);
-
- curItem.used = outPos;
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/PolygonLayers.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/PolygonLayers.java
deleted file mode 100644
index f20dcd0..0000000
--- a/VectorTileMap/src/org/mapsforge/android/glrenderer/PolygonLayers.java
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * Copyright 2012 Hannes Janetzek
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General License for more details.
- *
- * You should have received a copy of the GNU Lesser General License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.glrenderer;
-
-import static android.opengl.GLES20.GL_BLEND;
-import static android.opengl.GLES20.GL_EQUAL;
-import static android.opengl.GLES20.GL_INVERT;
-import static android.opengl.GLES20.GL_KEEP;
-import static android.opengl.GLES20.GL_NEVER;
-import static android.opengl.GLES20.GL_STENCIL_TEST;
-import static android.opengl.GLES20.GL_TRIANGLE_FAN;
-import static android.opengl.GLES20.GL_TRIANGLE_STRIP;
-import static android.opengl.GLES20.GL_ZERO;
-import static android.opengl.GLES20.glColorMask;
-import static android.opengl.GLES20.glDisable;
-import static android.opengl.GLES20.glDrawArrays;
-import static android.opengl.GLES20.glEnable;
-import static android.opengl.GLES20.glEnableVertexAttribArray;
-import static android.opengl.GLES20.glGetAttribLocation;
-import static android.opengl.GLES20.glGetUniformLocation;
-import static android.opengl.GLES20.glStencilFunc;
-import static android.opengl.GLES20.glStencilMask;
-import static android.opengl.GLES20.glStencilOp;
-import static android.opengl.GLES20.glUniform4f;
-import static android.opengl.GLES20.glUniform4fv;
-import static android.opengl.GLES20.glUniformMatrix4fv;
-import static android.opengl.GLES20.glUseProgram;
-import static android.opengl.GLES20.glVertexAttribPointer;
-
-import java.nio.ShortBuffer;
-
-import org.mapsforge.android.utils.GlUtils;
-
-import android.opengl.GLES20;
-
-class PolygonLayers {
- private static final int NUM_VERTEX_SHORTS = 2;
- private static final int POLYGON_VERTICES_DATA_POS_OFFSET = 0;
- private static int STENCIL_BITS = 8;
-
- private static PolygonLayer[] mFillPolys;
-
- private static int polygonProgram;
- private static int hPolygonVertexPosition;
- private static int hPolygonMatrix;
- private static int hPolygonColor;
-
- static boolean init() {
-
- // Set up the program for rendering polygons
- polygonProgram = GlUtils.createProgram(Shaders.polygonVertexShader,
- Shaders.polygonFragmentShader);
- if (polygonProgram == 0) {
- // Log.e(TAG, "Could not create polygon program.");
- return false;
- }
- hPolygonMatrix = glGetUniformLocation(polygonProgram, "mvp");
- hPolygonVertexPosition = glGetAttribLocation(polygonProgram, "a_position");
- hPolygonColor = glGetUniformLocation(polygonProgram, "u_color");
-
- mFillPolys = new PolygonLayer[STENCIL_BITS];
-
- return true;
- }
-
- private static void fillPolygons(int count, double zoom, float scale) {
- boolean blend = false;
-
- // draw to framebuffer
- glColorMask(true, true, true, true);
-
- // do not modify stencil buffer
- glStencilMask(0);
-
- for (int c = 0; c < count; c++) {
- PolygonLayer l = mFillPolys[c];
-
- float alpha = 1.0f;
-
- if (l.area.fade >= zoom || l.area.color[3] != 1.0) {
-
- if (l.area.fade >= zoom) {
- alpha = (scale > 1.3f ? scale : 1.3f) - alpha;
- if (alpha > 1.0f)
- alpha = 1.0f;
- }
-
- alpha *= l.area.color[3];
-
- if (!blend) {
- glEnable(GL_BLEND);
- blend = true;
- }
- glUniform4f(hPolygonColor,
- l.area.color[0], l.area.color[1], l.area.color[2], alpha);
-
- } else if (l.area.blend == zoom) {
- alpha = scale - 1.0f;
- if (alpha > 1.0f)
- alpha = 1.0f;
- else if (alpha < 0)
- alpha = 0;
-
- glUniform4f(hPolygonColor,
- l.area.color[0] * (1 - alpha) + l.area.blendColor[0] * alpha,
- l.area.color[1] * (1 - alpha) + l.area.blendColor[1] * alpha,
- l.area.color[2] * (1 - alpha) + l.area.blendColor[2] * alpha, 1);
- } else {
- if (blend) {
- glDisable(GL_BLEND);
- blend = false;
- }
- if (l.area.blend <= zoom && l.area.blend > 0)
- glUniform4fv(hPolygonColor, 1, l.area.blendColor, 0);
- else
- glUniform4fv(hPolygonColor, 1, l.area.color, 0);
- }
-
- // set stencil buffer mask used to draw this layer
- glStencilFunc(GL_EQUAL, 0xff, 1 << c);
-
- // draw tile fill coordinates
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
- }
-
- if (blend)
- glDisable(GL_BLEND);
- }
-
- static PolygonLayer drawPolygons(PolygonLayer layer, int next,
- float[] matrix, double zoom, float scale, short drawCount, boolean clip) {
- int cnt = 0;
-
- glUseProgram(polygonProgram);
- glEnableVertexAttribArray(hPolygonVertexPosition);
-
- glVertexAttribPointer(hPolygonVertexPosition, 2,
- GLES20.GL_SHORT, false, 0,
- POLYGON_VERTICES_DATA_POS_OFFSET);
-
- glUniformMatrix4fv(hPolygonMatrix, 1, false, matrix, 0);
-
- glEnable(GL_STENCIL_TEST);
-
- PolygonLayer l = layer;
-
- for (; l != null && l.layer < next; l = l.next) {
- // fade out polygon layers (set in RederTheme)
- if (l.area.fade > 0 && l.area.fade > zoom)
- continue;
-
- if (cnt == 0) {
- // disable drawing to framebuffer
- glColorMask(false, false, false, false);
-
- // never pass the test, i.e. always apply first stencil op (sfail)
- glStencilFunc(GL_NEVER, 0, 0xff);
-
- // clear stencilbuffer
- glStencilMask(0xFF);
- // glClear(GL_STENCIL_BUFFER_BIT);
-
- // clear stencilbuffer (tile region)
- glStencilOp(GL_ZERO, GL_KEEP, GL_KEEP);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
- // stencil op for stencil method polygon drawing
- glStencilOp(GL_INVERT, GL_KEEP, GL_KEEP);
- }
-
- mFillPolys[cnt] = l;
-
- // set stencil mask to draw to
- glStencilMask(1 << cnt++);
-
- glDrawArrays(GL_TRIANGLE_FAN, l.offset, l.verticesCnt);
-
- // draw up to 8 layers into stencil buffer
- if (cnt == STENCIL_BITS) {
- fillPolygons(cnt, zoom, scale);
- cnt = 0;
- }
- }
-
- if (cnt > 0)
- fillPolygons(cnt, zoom, scale);
-
- glDisable(GL_STENCIL_TEST);
-
- if (clip) {
- drawDepthClip(drawCount);
- }
-
- // required on GalaxyII, Android 2.3.3 (cant just VAA enable once...)
- GLES20.glDisableVertexAttribArray(hPolygonVertexPosition);
-
- return l;
- }
-
- // static void drawStencilClip(byte drawCount) {
- // // set stencil mask for line drawing... HACK!!!
- // glColorMask(false, false, false, false);
- //
- // int c = drawCount % 16;
- // // never pass the test, i.e. always apply first stencil op (sfail)
- // // glStencilFunc(GLES20.GL_NEVER, drawCount, 0);
- // glStencilFunc(GLES20.GL_NEVER, flipdabit[c], 0);
- //
- // // replace stencilbuffer
- // glStencilMask(0xff);
- //
- // // set stencilbuffer for (tile region)
- // glStencilOp(GLES20.GL_REPLACE, GLES20.GL_KEEP, GLES20.GL_KEEP);
- // glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
- //
- // glStencilFunc(GL_EQUAL, flipdabit[c], 0xff);
- // // do not modify stencil buffer
- // glStencilMask(0);
- //
- // glColorMask(true, true, true, true);
- // }
-
- // this only increases the chance for the stencil buffer clip to work
- // should check for clipping with depth buffer or sth
- // private static short[] flipdabit = {
- // 255, 254, 253, 251,
- // 247, 239, 223, 191,
- // 127, 63, 252, 250,
- // 249, 243, 231, 207 };
-
- static void drawDepthClip(short drawCount) {
-
- glColorMask(false, false, false, false);
- glEnable(GLES20.GL_DEPTH_TEST);
- glEnable(GLES20.GL_POLYGON_OFFSET_FILL);
- // GLES20.GL_PO
- GLES20.glPolygonOffset(0, drawCount);
-
- // int i[] = new int[1];
- // GLES20.glGetIntegerv(GLES20.GL_POLYGON_OFFSET_UNITS, i, 0);
- // Log.d("...", "UNITS " + i[0]);
- //
- // System.out.println("set offset: " + drawCount);
- GLES20.glDepthMask(true);
-
- GLES20.glDepthFunc(GLES20.GL_ALWAYS);
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
- GLES20.glDepthMask(false);
- glColorMask(true, true, true, true);
-
- GLES20.glDepthFunc(GLES20.GL_EQUAL);
- }
-
- static int sizeOf(PolygonLayer layers) {
- int size = 0;
-
- for (PolygonLayer l = layers; l != null; l = l.next)
- size += l.verticesCnt;
-
- size *= NUM_VERTEX_SHORTS;
-
- return size;
- }
-
- static void compileLayerData(PolygonLayer layers, ShortBuffer sbuf) {
- int pos = 4;
-
- ShortItem last = null, items = null;
-
- for (PolygonLayer l = layers; l != null; l = l.next) {
-
- for (ShortItem item = l.pool; item != null; item = item.next) {
- sbuf.put(item.vertices, 0, item.used);
- last = item;
- }
-
- l.offset = pos;
- pos += l.verticesCnt;
-
- if (last != null) {
- last.next = items;
- items = l.pool;
- }
-
- l.pool = null;
- }
-
- ShortPool.add(items);
- }
-
- static void clear(PolygonLayer layers) {
- for (PolygonLayer l = layers; l != null; l = l.next) {
- if (l.pool != null)
- ShortPool.add(l.pool);
- }
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/PoolItem.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/PoolItem.java
deleted file mode 100644
index 1be769c..0000000
--- a/VectorTileMap/src/org/mapsforge/android/glrenderer/PoolItem.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2012 Hannes Janetzek
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General License for more details.
- *
- * You should have received a copy of the GNU Lesser General License along with
- * this program. If not, see .
- */
-
-package org.mapsforge.android.glrenderer;
-
-import java.nio.ByteBuffer;
-import java.nio.FloatBuffer;
-import java.nio.IntBuffer;
-
-// TODO use byte[] for half-float, not converting on compilation (in glThread)
-
-class PoolItem {
- final float[] vertices;
- int used;
- PoolItem next;
-
- PoolItem() {
- vertices = new float[SIZE];
- used = 0;
- }
-
- static int SIZE = 128;
-
- private static final float FLOAT_HALF_PREC = 5.96046E-8f;
- private static final float FLOAT_HALF_MAX = 65504f;
-
- private static ByteBuffer byteBuffer = ByteBuffer.allocate(SIZE * 4);
- private static IntBuffer intBuffer = byteBuffer.asIntBuffer();
- private static FloatBuffer floatBuffer = byteBuffer.asFloatBuffer();
- private static int[] intArray = new int[SIZE];
-
- static void toHalfFloat(PoolItem item, short[] data) {
- floatBuffer.position(0);
- floatBuffer.put(item.vertices, 0, item.used);
- intBuffer.position(0);
- intBuffer.get(intArray, 0, item.used);
-
- int out = 0;
- for (int j = 0; j < item.used; j++) {
- float flt = item.vertices[j];
- int f = intArray[j];
-
- if (f == 0x0000000) {
- // == 0
- data[out++] = (short) 0x0000;
- } else if (f == 0x80000000) {
- // == -0
- data[out++] = (short) 0x8000;
- } else if (f == 0x3f800000) {
- // == 1
- data[out++] = (short) 0x3c00;
- } else if (f == 0xbf800000) {
- // == -1
- data[out++] = (short) 0xbc00;
- } else if (flt > FLOAT_HALF_MAX) {
- if (flt == Float.POSITIVE_INFINITY) {
- data[out++] = (short) 0x7c00;
- } else {
- data[out++] = (short) 0x7bff;
- }
- } else if (flt < -FLOAT_HALF_MAX) {
- if (flt == Float.NEGATIVE_INFINITY) {
- data[out++] = (short) 0xfc00;
- } else {
- data[out++] = (short) 0xfbff;
- }
- } else if (flt > 0f && flt < FLOAT_HALF_PREC) {
- data[out++] = (short) 0x0001;
- } else if (flt < 0f && flt > -FLOAT_HALF_PREC) {
- data[out++] = (short) 0x8001;
- } else {
- // maybe just ignore and set 0? -- we'll see. when this happens
- if (f == 0x7fc00000)
- throw new UnsupportedOperationException(
- "NaN to half conversion not supported!");
-
- data[out++] = (short) (((f >> 16) & 0x8000)
- | ((((f & 0x7f800000) - 0x38000000) >> 13) & 0x7c00)
- | ((f >> 13) & 0x03ff));
- }
- }
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/Shaders.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/Shaders.java
deleted file mode 100644
index 89339b0..0000000
--- a/VectorTileMap/src/org/mapsforge/android/glrenderer/Shaders.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright 2012 Hannes Janetzek
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General License for more details.
- *
- * You should have received a copy of the GNU Lesser General License along with
- * this program. If not, see .
- */
-
-package org.mapsforge.android.glrenderer;
-
-class Shaders {
-
- final static String lineVertexShader = ""
- + "precision mediump float; \n"
- + "uniform mat4 mvp;"
- + "attribute vec4 a_position;"
- + "attribute vec2 a_st;"
- + "varying vec2 v_st;"
- + "uniform float u_width;"
- + "uniform float u_offset;"
- + "const float dscale = 8.0/1000.0;"
- + "void main() {"
- + " vec2 dir = dscale * u_width * a_position.zw;"
- + " gl_Position = mvp * vec4(a_position.xy + dir, 0.0,1.0);"
- + " v_st = u_width * a_st;"
- + "}";
-
- // final static String lineFragmentShader = ""
- // + "precision mediump float;\n"
- // + "uniform float u_wscale;"
- // + "uniform float u_width;"
- // + "uniform vec4 u_color;"
- // + "varying vec2 v_st;"
- // + "const float zero = 0.0;"
- // + "void main() {"
- // + " vec4 color = u_color;"
- // + " float len;"
- // + " if (v_st.t == zero)"
- // + " len = abs(v_st.s);"
- // + " else "
- // + " len = length(v_st);"
- // + " color.a *= smoothstep(zero, u_wscale, u_width - len);"
- // + " gl_FragColor = color;"
- // + "}";
-
- final static String lineFragmentShader = ""
- + "#extension GL_OES_standard_derivatives : enable\n"
- + "precision mediump float;\n"
- + "uniform float u_wscale;"
- + "uniform float u_width;"
- + "uniform vec4 u_color;"
- + "varying vec2 v_st;"
- + "const float zero = 0.0;"
- + "void main() {"
- + " vec4 color = u_color;"
- + " float width = u_width;"
- + " float len;"
- + " if (v_st.t == zero)"
- + " len = abs(v_st.s);"
- + " else "
- + " len = length(v_st);"
- // + " if (u_width - len < 2.0){"
- + " vec2 st_width = fwidth(v_st);"
- + " float fuzz = max(st_width.s, st_width.t) * 1.5;"
- + " color.a *= smoothstep(zero, fuzz + u_wscale, u_width - len);"
- // + " }"
- + " gl_FragColor = color;"
- + "}";
-
- final static String polygonVertexShader = ""
- + "precision mediump float;"
- + "uniform mat4 mvp;"
- + "attribute vec4 a_position;"
- + "uniform float u_offset;"
- + "void main() {"
- + " gl_Position = mvp * a_position;"
- + "}";
-
- final static String polygonFragmentShader = ""
- + "precision mediump float;"
- + "uniform vec4 u_color;"
- + "void main() {"
- + " gl_FragColor = u_color;"
- + "}";
-
- final static String textVertexShader = ""
- + "precision highp float; "
- + "attribute vec4 vertex;"
- + "attribute vec2 tex_coord;"
- + "uniform mat4 mvp;"
- + "uniform float scale;"
- + "varying vec2 tex_c;"
- + "const vec2 div = vec2(1.0/4096.0,1.0/2048.0);"
- + "void main() {"
- + " gl_Position = mvp * vec4(vertex.xy + (vertex.zw / scale), 0.0, 1.0);"
- + " tex_c = tex_coord * div;"
- + "}";
-
- final static String textFragmentShader = ""
- + "precision highp float;"
- + "uniform sampler2D tex;"
- + "uniform vec4 col;"
- + "varying vec2 tex_c;"
- + "void main() {"
- + " gl_FragColor = texture2D(tex, tex_c.xy);"
- + "}";
-
- // final static String lineVertexZigZagShader = ""
- // + "precision mediump float; \n"
- // + "uniform mat4 mvp;"
- // + "attribute vec4 a_pos1;"
- // + "attribute vec2 a_st1;"
- // + "attribute vec4 a_pos2;"
- // + "attribute vec2 a_st2;"
- // + "varying vec2 v_st;"
- // + "uniform vec2 u_mode;"
- // + "const float dscale = 1.0/1000.0;"
- // + "void main() {"
- // + "if (gl_VertexID & 1 == 0) {"
- // + " vec2 dir = dscale * u_mode[1] * a_pos1.zw;"
- // + " gl_Position = mvp * vec4(a_pos1.xy + dir, 0.0,1.0);"
- // + " v_st = u_mode[1] * a_st1;"
- // + "} else {"
- // + " vec2 dir = dscale * u_mode[1] * a_pos2.zw;"
- // + " gl_Position = mvp * vec4( a_pos1.xy, dir, 0.0,1.0);"
- // + " v_st = u_mode[1] * vec2(-a_st2.s , a_st2.t);"
- // + "}";
-
- // final static String lineFragmentShader = ""
- // + "#extension GL_OES_standard_derivatives : enable\n"
- // + "precision mediump float;\n"
- // + "uniform vec2 u_mode;"
- // + "uniform vec4 u_color;"
- // + "varying vec2 v_st;"
- // + "const float zero = 0.0;"
- // + "void main() {"
- // + " vec4 color = u_color;"
- // + " float width = u_mode[1];"
- // + " float len;"
- // + " if (v_st.t == zero)"
- // + " len = abs(v_st.s);"
- // + " else "
- // + " len = length(v_st);"
- // + " vec2 st_width = fwidth(v_st);"
- // + " float fuzz = max(st_width.s, st_width.t);"
- // + " color.a *= smoothstep(-pixel, fuzz*pixel, width - (len * width));"
- // + " gl_FragColor = color;"
- // + "}";
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/ShortItem.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/ShortItem.java
deleted file mode 100644
index 37a6608..0000000
--- a/VectorTileMap/src/org/mapsforge/android/glrenderer/ShortItem.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.glrenderer;
-
-public class ShortItem {
- final short[] vertices;
- int used;
- ShortItem next;
-
- ShortItem() {
- vertices = new short[SIZE];
- used = 0;
- }
-
- // must be multiple of 6 and 4 (expected in LineLayer/PolygonLayer)
- static final int SIZE = 240;
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/TextItem.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/TextItem.java
deleted file mode 100644
index 5a8848e..0000000
--- a/VectorTileMap/src/org/mapsforge/android/glrenderer/TextItem.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.glrenderer;
-
-import org.mapsforge.android.rendertheme.renderinstruction.Caption;
-import org.mapsforge.android.rendertheme.renderinstruction.PathText;
-
-public class TextItem {
- TextItem next;
-
- final float x, y;
- final String text;
- final Caption caption;
- final PathText path;
- final float width;
-
- short x1, y1, x2, y2;
-
- public TextItem(float x, float y, String text, Caption caption) {
- this.x = x;
- this.y = y;
- this.text = text;
- this.caption = caption;
- this.width = caption.paint.measureText(text);
- this.path = null;
- }
-
- public TextItem(float x, float y, String text, PathText pathText, float width) {
- this.x = x;
- this.y = y;
- this.text = text;
- this.path = pathText;
- this.caption = null;
- this.width = width;
- }
-
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/TextRenderer.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/TextRenderer.java
deleted file mode 100644
index b22100d..0000000
--- a/VectorTileMap/src/org/mapsforge/android/glrenderer/TextRenderer.java
+++ /dev/null
@@ -1,439 +0,0 @@
-/*
- * Copyright 2012 Hannes Janetzek
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.glrenderer;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.ShortBuffer;
-
-import org.mapsforge.android.utils.GlUtils;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.opengl.GLES20;
-import android.opengl.GLUtils;
-import android.util.FloatMath;
-import android.util.Log;
-
-public class TextRenderer {
- private final static int TEXTURE_WIDTH = 512;
- private final static int TEXTURE_HEIGHT = 256;
- private final static float SCALE_FACTOR = 8.0f;
-
- final static int INDICES_PER_SPRITE = 6;
- final static int VERTICES_PER_SPRITE = 4;
- final static int SHORTS_PER_VERTICE = 6;
- final static int MAX_LABELS = 35;
-
- private static Bitmap mBitmap;
- private static Canvas mCanvas;
- private static int mFontPadX = 1;
- private static int mFontPadY = 1;
- private static int mBitmapFormat;
- private static int mBitmapType;
- private static ByteBuffer mByteBuffer;
- private static ShortBuffer mShortBuffer;
- private static TextTexture[] mTextures;
-
- private static int mIndicesVBO;
- private static int mVerticesVBO;
-
- private static int mTextProgram;
- private static int hTextUVPMatrix;
- private static int hTextVertex;
- private static int hTextScale;
- private static int hTextTextureCoord;
- // private static int hTextUColor;
-
- static Paint mPaint = new Paint(Color.BLACK);
-
- private static boolean debug = false;
- private static short[] debugVertices = {
-
- 0, 0,
- 0, TEXTURE_HEIGHT * 4,
-
- 0, TEXTURE_HEIGHT - 1,
- 0, 0,
-
- TEXTURE_WIDTH - 1, 0,
- TEXTURE_WIDTH * 4, TEXTURE_HEIGHT * 4,
-
- TEXTURE_WIDTH - 1, TEXTURE_HEIGHT - 1,
- TEXTURE_WIDTH * 4, 0,
-
- };
-
- static boolean init(int numTextures) {
- mBitmap = Bitmap
- .createBitmap(TEXTURE_WIDTH, TEXTURE_HEIGHT, Bitmap.Config.ARGB_8888);
- mCanvas = new Canvas(mBitmap);
-
- mBitmapFormat = GLUtils.getInternalFormat(mBitmap);
- mBitmapType = GLUtils.getType(mBitmap);
-
- mTextProgram = GlUtils.createProgram(Shaders.textVertexShader,
- Shaders.textFragmentShader);
-
- hTextUVPMatrix = GLES20.glGetUniformLocation(mTextProgram, "mvp");
- hTextVertex = GLES20.glGetAttribLocation(mTextProgram, "vertex");
- hTextScale = GLES20.glGetUniformLocation(mTextProgram, "scale");
- hTextTextureCoord = GLES20.glGetAttribLocation(mTextProgram, "tex_coord");
-
- int bufferSize = numTextures
- * MAX_LABELS * VERTICES_PER_SPRITE
- * SHORTS_PER_VERTICE * (Short.SIZE / 8);
-
- mByteBuffer = ByteBuffer.allocateDirect(bufferSize)
- .order(ByteOrder.nativeOrder());
-
- mShortBuffer = mByteBuffer.asShortBuffer();
-
- int[] textureIds = new int[numTextures];
- TextTexture[] textures = new TextTexture[numTextures];
- GLES20.glGenTextures(numTextures, textureIds, 0);
-
- for (int i = 0; i < numTextures; i++) {
- // setup filters for texture
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[i]);
-
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
- GLES20.GL_LINEAR);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
- GLES20.GL_LINEAR);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
- GLES20.GL_CLAMP_TO_EDGE); // Set U Wrapping
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
- GLES20.GL_CLAMP_TO_EDGE); // Set V Wrapping
-
- // load the generated bitmap onto the texture
- GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmapFormat, mBitmap,
- mBitmapType, 0);
-
- textures[i] = new TextTexture(textureIds[i]);
- }
-
- GlUtils.checkGlError("init textures");
-
- mTextures = textures;
-
- // Setup triangle indices
- short[] indices = new short[MAX_LABELS * INDICES_PER_SPRITE];
- int len = indices.length;
- short j = 0;
- for (int i = 0; i < len; i += INDICES_PER_SPRITE, j += VERTICES_PER_SPRITE) {
- // indices[i + 0] = (short) (j + 0);
- // indices[i + 1] = (short) (j + 1);
- // indices[i + 2] = (short) (j + 2);
- // indices[i + 3] = (short) (j + 2);
- // indices[i + 4] = (short) (j + 3);
- // indices[i + 5] = (short) (j + 0);
- indices[i + 0] = (short) (j + 0);
- indices[i + 1] = (short) (j + 0);
- indices[i + 2] = (short) (j + 1);
- indices[i + 3] = (short) (j + 3);
- indices[i + 4] = (short) (j + 2);
- indices[i + 5] = (short) (j + 2);
- }
-
- ShortBuffer tmpIndices = mByteBuffer.asShortBuffer();
-
- tmpIndices.put(indices, 0, len);
- tmpIndices.flip();
-
- int[] mVboIds = new int[2];
- GLES20.glGenBuffers(2, mVboIds, 0);
- mIndicesVBO = mVboIds[0];
- mVerticesVBO = mVboIds[1];
-
- GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO);
- GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, len * (Short.SIZE / 8),
- tmpIndices, GLES20.GL_STATIC_DRAW);
- GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
-
- GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO);
- GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, bufferSize,
- mShortBuffer, GLES20.GL_DYNAMIC_DRAW);
- GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
- return true;
- }
-
- static boolean drawToTexture(GLMapTile tile) {
- TextTexture tex = null;
-
- if (tile.labels == null)
- return false;
-
- for (int i = 0; i < mTextures.length; i++) {
- tex = mTextures[i];
- if (tex.tile == null)
- break;
- if (!tex.tile.isActive)
- break;
-
- tex = null;
- }
-
- if (tex == null) {
- Log.d(TAG, "no textures left");
- return false;
- }
- if (tex.tile != null)
- tex.tile.texture = null;
-
- mBitmap.eraseColor(Color.TRANSPARENT);
-
- int pos = 0;
- short[] buf = tex.vertices;
-
- float y = 0;
- float x = mFontPadX;
- float width, height;
-
- int max = MAX_LABELS;
-
- if (debug) {
- mCanvas.drawLine(debugVertices[0], debugVertices[1], debugVertices[4],
- debugVertices[5], mPaint);
- mCanvas.drawLine(debugVertices[0], debugVertices[1], debugVertices[8],
- debugVertices[9], mPaint);
-
- mCanvas.drawLine(debugVertices[12], debugVertices[13], debugVertices[4],
- debugVertices[5], mPaint);
- mCanvas.drawLine(debugVertices[12], debugVertices[13], debugVertices[8],
- debugVertices[9], mPaint);
- }
-
- int advanceY = 0;
-
- TextItem t = tile.labels;
- float yy;
- short x1, x2, x3, x4, y1, y2, y3, y4;
-
- for (int i = 0; t != null && i < max; t = t.next, i++) {
-
- if (t.caption != null) {
- height = (int) (t.caption.fontHeight) + 2 * mFontPadY;
- } else {
- height = (int) (t.path.fontHeight) + 2 * mFontPadY;
- }
-
- width = t.width + 2 * mFontPadX;
-
- if (height > advanceY)
- advanceY = (int) height;
-
- if (x + width > TEXTURE_WIDTH) {
- x = mFontPadX;
- y += advanceY;
- advanceY = (int) height;
- }
-
- if (t.caption != null) {
- yy = y + (height - 1) - t.caption.fontDescent - mFontPadY;
- } else {
- yy = y + (height - 1) - t.path.fontDescent - mFontPadY;
- }
-
- if (yy > TEXTURE_HEIGHT) {
- Log.d(TAG, "reached max labels");
- continue;
- }
-
- if (t.caption != null) {
- if (t.caption.stroke != null)
- mCanvas.drawText(t.text, x + t.width / 2, yy, t.caption.stroke);
-
- mCanvas.drawText(t.text, x + t.width / 2, yy, t.caption.paint);
- } else {
- if (t.path.stroke != null)
- mCanvas.drawText(t.text, x + t.width / 2, yy, t.path.stroke);
-
- mCanvas.drawText(t.text, x + t.width / 2, yy, t.path.paint);
- }
- if (width > TEXTURE_WIDTH)
- width = TEXTURE_WIDTH;
-
- float hw = width / 2.0f;
- float hh = height / 2.0f;
-
- if (t.caption != null) {
- x1 = x3 = (short) (SCALE_FACTOR * (-hw));
- y1 = y3 = (short) (SCALE_FACTOR * (-hh));
- x2 = x4 = (short) (SCALE_FACTOR * (hw));
- y2 = y4 = (short) (SCALE_FACTOR * (hh));
- }
- else {
- float vx = t.x1 - t.x2;
- float vy = t.y1 - t.y2;
- float a = FloatMath.sqrt(vx * vx + vy * vy);
- vx = vx / a;
- vy = vy / a;
-
- float ux = -vy;
- float uy = vx;
-
- x1 = (short) (SCALE_FACTOR * (vx * hw + ux * hh));
- y1 = (short) (SCALE_FACTOR * (vy * hw + uy * hh));
-
- x2 = (short) (SCALE_FACTOR * (-vx * hw + ux * hh));
- y3 = (short) (SCALE_FACTOR * (-vy * hw + uy * hh));
-
- x4 = (short) (SCALE_FACTOR * (-vx * hw - ux * hh));
- y4 = (short) (SCALE_FACTOR * (-vy * hw - uy * hh));
-
- x3 = (short) (SCALE_FACTOR * (vx * hw - ux * hh));
- y2 = (short) (SCALE_FACTOR * (vy * hw - uy * hh));
-
- }
- short u1 = (short) (SCALE_FACTOR * x);
- short v1 = (short) (SCALE_FACTOR * y);
- short u2 = (short) (SCALE_FACTOR * (x + width));
- short v2 = (short) (SCALE_FACTOR * (y + height));
-
- short tx = (short) (SCALE_FACTOR * t.x);
- short ty = (short) (SCALE_FACTOR * t.y);
- // top-left
- buf[pos++] = tx;
- buf[pos++] = ty;
- buf[pos++] = x1;
- buf[pos++] = y1;
- buf[pos++] = u1;
- buf[pos++] = v2;
-
- // top-right
- buf[pos++] = tx;
- buf[pos++] = ty;
- buf[pos++] = x2;
- buf[pos++] = y3;
- buf[pos++] = u2;
- buf[pos++] = v2;
-
- // bot-right
- buf[pos++] = tx;
- buf[pos++] = ty;
- buf[pos++] = x4;
- buf[pos++] = y4;
- buf[pos++] = u2;
- buf[pos++] = v1;
-
- // bot-left
- buf[pos++] = tx;
- buf[pos++] = ty;
- buf[pos++] = x3;
- buf[pos++] = y2;
- buf[pos++] = u1;
- buf[pos++] = v1;
-
- x += width;
-
- if (y > TEXTURE_HEIGHT) {
- Log.d(TAG, "reached max labels: texture is full");
- break;
- }
- }
-
- tex.length = pos;
- tile.texture = tex;
- tex.tile = tile;
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex.id);
- GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, mBitmap,
- mBitmapFormat, mBitmapType);
-
- return true;
- }
-
- private static String TAG = "TextRenderer";
-
- static void compileTextures() {
- int offset = 0;
- TextTexture tex;
-
- GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO);
-
- mShortBuffer.clear();
-
- for (int i = 0; i < mTextures.length; i++) {
- tex = mTextures[i];
- if (tex.tile == null || !tex.tile.isActive)
- continue;
-
- mShortBuffer.put(tex.vertices, 0, tex.length);
- tex.offset = offset;
- offset += tex.length;
- }
-
- mShortBuffer.flip();
- // Log.d(TAG, "compileTextures" + mFloatBuffer.remaining() + " " + offset);
-
- // TODO use sub-bufferdata function
- GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, offset * (Short.SIZE / 8),
- mShortBuffer);
- }
-
- static void beginDraw(float scale) {
- GLES20.glUseProgram(mTextProgram);
-
- GLES20.glEnableVertexAttribArray(hTextTextureCoord);
- GLES20.glEnableVertexAttribArray(hTextVertex);
-
- GLES20.glUniform1f(hTextScale, scale);
-
- if (debug) {
- GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
- mShortBuffer.clear();
- mShortBuffer.put(debugVertices, 0, 16);
- mShortBuffer.flip();
- GLES20.glVertexAttribPointer(hTextVertex, 2,
- GLES20.GL_SHORT, false, 8, mShortBuffer);
- mShortBuffer.position(2);
- GLES20.glVertexAttribPointer(hTextTextureCoord, 2,
- GLES20.GL_SHORT, false, 8, mShortBuffer);
- } else {
- GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO);
- GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO);
- }
- }
-
- static void endDraw() {
-
- GLES20.glDisableVertexAttribArray(hTextTextureCoord);
- GLES20.glDisableVertexAttribArray(hTextVertex);
- }
-
- static void drawTile(GLMapTile tile, float[] matrix) {
-
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tile.texture.id);
-
- GLES20.glUniformMatrix4fv(hTextUVPMatrix, 1, false, matrix, 0);
-
- if (debug) {
- GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
- } else {
-
- GLES20.glVertexAttribPointer(hTextVertex, 4,
- GLES20.GL_SHORT, false, 12, tile.texture.offset * (Short.SIZE / 8));
-
- GLES20.glVertexAttribPointer(hTextTextureCoord, 2,
- GLES20.GL_SHORT, false, 12, tile.texture.offset * (Short.SIZE / 8)
- + 8);
-
- GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, (tile.texture.length / 24) *
- INDICES_PER_SPRITE, GLES20.GL_UNSIGNED_SHORT, 0);
- }
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/TextTexture.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/TextTexture.java
deleted file mode 100644
index eefe4fa..0000000
--- a/VectorTileMap/src/org/mapsforge/android/glrenderer/TextTexture.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.glrenderer;
-
-public class TextTexture {
-
- final short[] vertices;
- final int id;
- int length;
- int offset;
- GLMapTile tile;
-
- String[] text;
-
- TextTexture(int textureID) {
- vertices = new short[TextRenderer.MAX_LABELS *
- TextRenderer.VERTICES_PER_SPRITE *
- TextRenderer.SHORTS_PER_VERTICE];
- id = textureID;
- }
-
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/VertexBufferObject.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/VertexBufferObject.java
deleted file mode 100644
index 40c5d6e..0000000
--- a/VectorTileMap/src/org/mapsforge/android/glrenderer/VertexBufferObject.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2012 Hannes Janetzek
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General License for more details.
- *
- * You should have received a copy of the GNU Lesser General License along with
- * this program. If not, see .
- */
-
-package org.mapsforge.android.glrenderer;
-
-class VertexBufferObject {
- final int id;
- int size;
-
- VertexBufferObject(int id) {
- this.id = id;
- size = 0;
- }
-}
\ No newline at end of file
diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/VertexPool.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/VertexPool.java
deleted file mode 100644
index e453ef0..0000000
--- a/VectorTileMap/src/org/mapsforge/android/glrenderer/VertexPool.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2012 Hannes Janetzek
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General License for more details.
- *
- * You should have received a copy of the GNU Lesser General License along with
- * this program. If not, see .
- */
-
-package org.mapsforge.android.glrenderer;
-
-import android.annotation.SuppressLint;
-
-class VertexPool {
- private static final int POOL_LIMIT = 8192;
-
- @SuppressLint("UseValueOf")
- private static final Boolean lock = new Boolean(true);
-
- static private PoolItem pool = null;
- static private int count = 0;
-
- static PoolItem get() {
- synchronized (lock) {
-
- if (count == 0)
- return new PoolItem();
-
- count--;
-
- PoolItem it = pool;
- pool = pool.next;
- it.used = 0;
- it.next = null;
- return it;
- }
- }
-
- static void add(PoolItem items) {
- if (items == null)
- return;
-
- synchronized (lock) {
- PoolItem last = items;
-
- // limit pool items
- while (count < POOL_LIMIT) {
- if (last.next == null) {
- break;
- }
- last = last.next;
- count++;
- }
-
- // clear references
- PoolItem tmp2, tmp = last.next;
- while (tmp != null) {
- tmp2 = tmp;
- tmp = tmp.next;
- tmp2.next = null;
- }
-
- last.next = pool;
- pool = items;
- }
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/WayDecorator.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/WayDecorator.java
deleted file mode 100644
index 411bff2..0000000
--- a/VectorTileMap/src/org/mapsforge/android/glrenderer/WayDecorator.java
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.glrenderer;
-
-import org.mapsforge.android.rendertheme.renderinstruction.PathText;
-import org.mapsforge.android.utils.GeometryUtils;
-
-import android.util.FloatMath;
-
-final class WayDecorator {
- // /**
- // * Minimum distance in pixels before the symbol is repeated.
- // */
- // private static final int DISTANCE_BETWEEN_SYMBOLS = 200;
-
- /**
- * Minimum distance in pixels before the way name is repeated.
- */
- private static final int DISTANCE_BETWEEN_WAY_NAMES = 500;
-
- // /**
- // * Distance in pixels to skip from both ends of a segment.
- // */
- // private static final int SEGMENT_SAFETY_DISTANCE = 30;
-
- // static void renderSymbol(Bitmap symbolBitmap, boolean alignCenter,
- // boolean repeatSymbol, float[][] coordinates,
- // List waySymbols) {
- // int skipPixels = SEGMENT_SAFETY_DISTANCE;
- //
- // // get the first way point coordinates
- // float previousX = coordinates[0][0];
- // float previousY = coordinates[0][1];
- //
- // // draw the symbol on each way segment
- // float segmentLengthRemaining;
- // float segmentSkipPercentage;
- // float symbolAngle;
- // for (int i = 2; i < coordinates[0].length; i += 2) {
- // // get the current way point coordinates
- // float currentX = coordinates[0][i];
- // float currentY = coordinates[0][i + 1];
- //
- // // calculate the length of the current segment (Euclidian distance)
- // float diffX = currentX - previousX;
- // float diffY = currentY - previousY;
- // double segmentLengthInPixel = Math.sqrt(diffX * diffX + diffY * diffY);
- // segmentLengthRemaining = (float) segmentLengthInPixel;
- //
- // while (segmentLengthRemaining - skipPixels > SEGMENT_SAFETY_DISTANCE) {
- // // calculate the percentage of the current segment to skip
- // segmentSkipPercentage = skipPixels / segmentLengthRemaining;
- //
- // // move the previous point forward towards the current point
- // previousX += diffX * segmentSkipPercentage;
- // previousY += diffY * segmentSkipPercentage;
- // symbolAngle = (float) Math.toDegrees(Math.atan2(currentY - previousY,
- // currentX - previousX));
- //
- // waySymbols.add(new SymbolContainer(symbolBitmap, previousX, previousY,
- // alignCenter, symbolAngle));
- //
- // // check if the symbol should only be rendered once
- // if (!repeatSymbol) {
- // return;
- // }
- //
- // // recalculate the distances
- // diffX = currentX - previousX;
- // diffY = currentY - previousY;
- //
- // // recalculate the remaining length of the current segment
- // segmentLengthRemaining -= skipPixels;
- //
- // // set the amount of pixels to skip before repeating the symbol
- // skipPixels = DISTANCE_BETWEEN_SYMBOLS;
- // }
- //
- // skipPixels -= segmentLengthRemaining;
- // if (skipPixels < SEGMENT_SAFETY_DISTANCE) {
- // skipPixels = SEGMENT_SAFETY_DISTANCE;
- // }
- //
- // // set the previous way point coordinates for the next loop
- // previousX = currentX;
- // previousY = currentY;
- // }
- // }
-
- static TextItem renderText(float[] coordinates, String text, PathText pathText,
- int pos, int len, TextItem textItems) {
- TextItem items = textItems;
- TextItem t = null;
- // calculate the way name length plus some margin of safety
- float wayNameWidth = -1;
- float minWidth = 100;
- int skipPixels = 0;
-
- // get the first way point coordinates
- int previousX = (int) coordinates[pos + 0];
- int previousY = (int) coordinates[pos + 1];
-
- // find way segments long enough to draw the way name on them
- for (int i = pos + 2; i < pos + len; i += 2) {
- // get the current way point coordinates
- int currentX = (int) coordinates[i];
- int currentY = (int) coordinates[i + 1];
-
- // calculate the length of the current segment (Euclidian distance)
- float diffX = currentX - previousX;
- float diffY = currentY - previousY;
-
- for (int j = i + 2; j < pos + len; j += 2) {
- int nextX = (int) coordinates[j];
- int nextY = (int) coordinates[j + 1];
-
- if (diffY == 0) {
- if ((currentY - nextY) != 0)
- break;
-
- currentX = nextX;
- currentY = nextY;
- continue;
- } else if ((currentY - nextY) == 0)
- break;
-
- float diff = ((diffX) / (diffY) - (float) (currentX - nextX)
- / (currentY - nextY));
-
- // skip segments with corners
- if (diff >= 0.1f || diff <= -0.1f)
- break;
-
- currentX = nextX;
- currentY = nextY;
- }
-
- diffX = currentX - previousX;
- diffY = currentY - previousY;
-
- if (diffX < 0)
- diffX = -diffX;
- if (diffY < 0)
- diffY = -diffY;
-
- if (diffX + diffY < minWidth) {
- previousX = currentX;
- previousY = currentY;
- continue;
- }
-
- if (wayNameWidth > 0 && diffX + diffY < wayNameWidth) {
- previousX = currentX;
- previousY = currentY;
- continue;
- }
-
- float segmentLengthInPixel = FloatMath.sqrt(diffX * diffX + diffY * diffY);
-
- if (skipPixels > 0) {
- skipPixels -= segmentLengthInPixel;
-
- } else if (segmentLengthInPixel > minWidth) {
-
- if (wayNameWidth < 0) {
- wayNameWidth = pathText.paint.measureText(text);
- }
-
- if (segmentLengthInPixel > wayNameWidth + 25) {
-
- float s = (wayNameWidth + 25) / segmentLengthInPixel;
- int width, height;
- int x1, y1, x2, y2;
-
- if (previousX < currentX) {
- x1 = previousX;
- y1 = previousY;
- x2 = currentX;
- y2 = currentY;
- } else {
- x1 = currentX;
- y1 = currentY;
- x2 = previousX;
- y2 = previousY;
- }
-
- // estimate position of text on path
- width = (x2 - x1) / 2;
- x2 = x2 - (int) (width - s * width);
- x1 = x1 + (int) (width - s * width);
-
- height = (y2 - y1) / 2;
- y2 = y2 - (int) (height - s * height);
- y1 = y1 + (int) (height - s * height);
-
- short top = (short) (y1 < y2 ? y1 : y2);
- short bot = (short) (y1 < y2 ? y2 : y1);
-
- boolean intersects = false;
-
- for (TextItem t2 = items; t2 != null; t2 = t2.next) {
-
- // check crossings
- if (GeometryUtils.lineIntersect(x1, y1, x2, y2, t2.x1, t2.y1,
- t2.x2, t2.y2)) {
- intersects = true;
- break;
- }
-
- // check overlapping labels of road with more than one
- // way
- short top2 = (t2.y1 < t2.y2 ? t2.y1 : t2.y2);
- short bot2 = (t2.y1 < t2.y2 ? t2.y2 : t2.y1);
-
- if (x1 - 10 < t2.x2 && t2.x1 - 10 < x2 && top - 10 < bot2
- && top2 - 10 < bot) {
-
- if (t2.text.equals(text)) {
- intersects = true;
- break;
- }
- }
- }
-
- if (intersects) {
- previousX = (int) coordinates[pos + i];
- previousY = (int) coordinates[pos + i + 1];
- continue;
- }
-
- // Log.d("mapsforge", "add " + text + " " + first + " " + last);
-
- if (previousX < currentX) {
- x1 = previousX;
- y1 = previousY;
- x2 = currentX;
- y2 = currentY;
- } else {
- x1 = currentX;
- y1 = currentY;
- x2 = previousX;
- y2 = previousY;
- }
-
- // if (t == null)
- t = new TextItem(x1 + (x2 - x1) / 2, y1 + (y2 - y1) / 2, text,
- pathText, wayNameWidth);
-
- t.x1 = (short) x1;
- t.y1 = (short) y1;
- t.x2 = (short) x2;
- t.y2 = (short) y2;
-
- t.next = items;
- items = t;
-
- // skipPixels = DISTANCE_BETWEEN_WAY_NAMES;
-
- return items;
- }
- }
-
- // store the previous way point coordinates
- previousX = currentX;
- previousY = currentY;
- }
- return items;
- }
-
- private WayDecorator() {
- throw new IllegalStateException();
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/input/MapMover.java b/VectorTileMap/src/org/mapsforge/android/input/MapMover.java
deleted file mode 100644
index fc2e2b5..0000000
--- a/VectorTileMap/src/org/mapsforge/android/input/MapMover.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.input;
-
-import org.mapsforge.android.MapView;
-import org.mapsforge.android.utils.PausableThread;
-
-import android.os.SystemClock;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-
-/**
- * A MapMover moves the map horizontally and vertically at a configurable speed. It runs in a separate thread to avoid
- * blocking the UI thread.
- */
-public class MapMover extends PausableThread implements KeyEvent.Callback {
-
- private static final int DEFAULT_MOVE_SPEED_FACTOR = 10;
- private static final int FRAME_LENGTH_IN_MS = 15;
- private static final float MOVE_SPEED = 0.2f;
- private static final String THREAD_NAME = "MapMover";
- private static final float TRACKBALL_MOVE_SPEED_FACTOR = 40;
-
- private final MapView mMapView;
- private float mMoveSpeedFactor;
- private float mMoveX;
- private float mMoveY;
- private long mTimePrevious;
-
- /**
- * @param mapView
- * the MapView which should be moved by this MapMover.
- */
- public MapMover(MapView mapView) {
- super();
- mMapView = mapView;
- mMoveSpeedFactor = DEFAULT_MOVE_SPEED_FACTOR;
- }
-
- /**
- * @return the move speed factor, used for trackball and keyboard events.
- */
- public float getMoveSpeedFactor() {
- return mMoveSpeedFactor;
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent keyEvent) {
- if (!mMapView.isClickable()) {
- return false;
- }
-
- if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
- moveLeft();
- return true;
- } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
- moveRight();
- return true;
- } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
- moveUp();
- return true;
- } else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
- moveDown();
- return true;
- }
- return false;
- }
-
- @Override
- public boolean onKeyLongPress(int keyCode, KeyEvent keyEvent) {
- return false;
- }
-
- @Override
- public boolean onKeyMultiple(int keyCode, int count, KeyEvent keyEvent) {
- return false;
- }
-
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent keyEvent) {
- if (!mMapView.isClickable()) {
- return false;
- }
-
- if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
- mMoveX = 0;
- return true;
- } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
- mMoveY = 0;
- return true;
- }
- return false;
- }
-
- /**
- * @param motionEvent
- * a trackball event which should be handled.
- * @return true if the event was handled, false otherwise.
- */
- public boolean onTrackballEvent(MotionEvent motionEvent) {
- if (!mMapView.isClickable()) {
- return false;
- }
-
- if (motionEvent.getAction() == MotionEvent.ACTION_MOVE) {
- float mapMoveX = motionEvent.getX() * TRACKBALL_MOVE_SPEED_FACTOR * getMoveSpeedFactor();
- float mapMoveY = motionEvent.getY() * TRACKBALL_MOVE_SPEED_FACTOR * getMoveSpeedFactor();
-
- // mapView.getFrameBuffer().matrixPostTranslate(mapMoveX,
- // mapMoveY);
- mMapView.getMapPosition().moveMap(mapMoveX, mapMoveY);
- mMapView.redrawTiles();
- return true;
- }
- return false;
- }
-
- /**
- * Sets the move speed factor of the map, used for trackball and keyboard events.
- *
- * @param moveSpeedFactor
- * the factor by which the move speed of the map will be multiplied.
- * @throws IllegalArgumentException
- * if the new move speed factor is negative.
- */
- public void setMoveSpeedFactor(float moveSpeedFactor) {
- if (moveSpeedFactor < 0) {
- throw new IllegalArgumentException();
- }
- mMoveSpeedFactor = moveSpeedFactor;
- }
-
- /**
- * Stops moving the map completely.
- */
- public void stopMove() {
- mMoveX = 0;
- mMoveY = 0;
- }
-
- private void moveDown() {
- if (mMoveY > 0) {
- // stop moving the map vertically
- mMoveY = 0;
- } else if (mMoveY == 0) {
- // start moving the map
- mMoveY = -MOVE_SPEED * mMoveSpeedFactor;
- mTimePrevious = SystemClock.uptimeMillis();
- synchronized (this) {
- notify();
- }
- }
- }
-
- private void moveLeft() {
- if (mMoveX < 0) {
- // stop moving the map horizontally
- mMoveX = 0;
- } else if (mMoveX == 0) {
- // start moving the map
- mMoveX = MOVE_SPEED * mMoveSpeedFactor;
- mTimePrevious = SystemClock.uptimeMillis();
- synchronized (this) {
- notify();
- }
- }
- }
-
- private void moveRight() {
- if (mMoveX > 0) {
- // stop moving the map horizontally
- mMoveX = 0;
- } else if (mMoveX == 0) {
- // start moving the map
- mMoveX = -MOVE_SPEED * mMoveSpeedFactor;
- mTimePrevious = SystemClock.uptimeMillis();
- synchronized (this) {
- notify();
- }
- }
- }
-
- private void moveUp() {
- if (mMoveY < 0) {
- // stop moving the map vertically
- mMoveY = 0;
- } else if (mMoveY == 0) {
- // start moving the map
- mMoveY = MOVE_SPEED * mMoveSpeedFactor;
- mTimePrevious = SystemClock.uptimeMillis();
- synchronized (this) {
- notify();
- }
- }
- }
-
- @Override
- protected void afterPause() {
- mTimePrevious = SystemClock.uptimeMillis();
- }
-
- @Override
- protected void doWork() throws InterruptedException {
- // calculate the time difference to previous call
- long timeCurrent = SystemClock.uptimeMillis();
- long timeElapsed = timeCurrent - mTimePrevious;
- mTimePrevious = timeCurrent;
-
- // add the movement to the transformation matrices
- // mapView.getFrameBuffer().matrixPostTranslate(timeElapsed *
- // moveX, timeElapsed * moveY);
-
- // move the map and the overlays
- mMapView.getMapPosition().moveMap(timeElapsed * mMoveX, timeElapsed * mMoveY);
- mMapView.redrawTiles();
- sleep(FRAME_LENGTH_IN_MS);
- }
-
- @Override
- protected String getThreadName() {
- return THREAD_NAME;
- }
-
- @Override
- protected boolean hasWork() {
- return mMoveX != 0 || mMoveY != 0;
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/input/ScaleListener.java b/VectorTileMap/src/org/mapsforge/android/input/ScaleListener.java
deleted file mode 100644
index f01e126..0000000
--- a/VectorTileMap/src/org/mapsforge/android/input/ScaleListener.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.input;
-
-import org.mapsforge.android.MapView;
-import org.mapsforge.android.MapViewPosition;
-
-import android.view.ScaleGestureDetector;
-
-class ScaleListener implements ScaleGestureDetector.OnScaleGestureListener {
- private final MapView mMapView;
- private MapViewPosition mMapPosition;
- private float mCenterX;
- private float mCenterY;
- // private float mFocusX;
- // private float mFocusY;
- private float mScale;
-
- // private boolean mScaling;
- /**
- * Creates a new ScaleListener for the given MapView.
- *
- * @param mapView
- * the MapView which should be scaled.
- */
- ScaleListener(MapView mapView) {
- mMapView = mapView;
- }
-
- @Override
- public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
-
- float focusX = scaleGestureDetector.getFocusX();
- float focusY = scaleGestureDetector.getFocusY();
- mScale = scaleGestureDetector.getScaleFactor();
-
- // mMapPosition.moveMap((focusX - mFocusX), (focusY - mFocusY));
- // if (mScale > 1.001 || mScale < 0.999) {
-
- mMapPosition.scaleMap(mScale,
- focusX - mCenterX,
- focusY - mCenterY);
- mMapView.redrawTiles();
- // mScale = 1;
-
- // mFocusX = focusX;
- // mFocusY = focusY;
-
- // }
- // else if (Math.abs(focusX - mFocusX) > 0.5 || Math.abs(focusY - mFocusY) > 0.5) {
- // mMapPosition.moveMap((focusX - mFocusX), (focusY - mFocusY));
- //
- // mFocusX = focusX;
- // mFocusY = focusY;
- // mScale = 1;
- // mMapView.redrawTiles();
- // }
-
- return true;
- }
-
- @Override
- public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
-
- mCenterX = mMapView.getWidth() >> 1;
- mCenterY = mMapView.getHeight() >> 1;
- // mFocusX = scaleGestureDetector.getFocusX();
- // mFocusY = scaleGestureDetector.getFocusY();
- mScale = 1;
- mMapPosition = mMapView.getMapPosition();
- // mScaling = false;
- return true;
- }
-
- @Override
- public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
- // do nothing
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/input/TouchHandler.java b/VectorTileMap/src/org/mapsforge/android/input/TouchHandler.java
deleted file mode 100644
index b7bcb53..0000000
--- a/VectorTileMap/src/org/mapsforge/android/input/TouchHandler.java
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.input;
-
-import org.mapsforge.android.MapView;
-import org.mapsforge.core.Tile;
-
-import android.content.Context;
-import android.os.CountDownTimer;
-import android.util.Log;
-import android.view.GestureDetector;
-import android.view.GestureDetector.SimpleOnGestureListener;
-import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
-import android.view.ViewConfiguration;
-import android.widget.Scroller;
-
-/**
- * Implementation for multi-touch capable devices.
- */
-public class TouchHandler {
- private static final int INVALID_POINTER_ID = -1;
-
- /**
- * is pritected correct? share MapView with inner class
- */
- protected final MapView mMapView;
-
- private final float mMapMoveDelta;
- private boolean mMoveThresholdReached;
- private float mPreviousPositionX;
- private float mPreviousPositionY;
- private int mActivePointerId;
-
- private final ScaleGestureDetector mScaleGestureDetector;
- private final GestureDetector mGestureDetector;
-
- /**
- * @param context
- * the Context
- * @param mapView
- * the MapView
- */
- public TouchHandler(Context context, MapView mapView) {
- ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
- mMapView = mapView;
- mMapMoveDelta = viewConfiguration.getScaledTouchSlop();
- mActivePointerId = INVALID_POINTER_ID;
- mScaleGestureDetector = new ScaleGestureDetector(context, new ScaleListener(
- mMapView));
- mGestureDetector = new GestureDetector(new MapGestureDetector(mMapView));
- }
-
- private static int getAction(MotionEvent motionEvent) {
- return motionEvent.getAction() & MotionEvent.ACTION_MASK;
- }
-
- private boolean onActionCancel() {
- mActivePointerId = INVALID_POINTER_ID;
- return true;
- }
-
- private boolean onActionDown(MotionEvent motionEvent) {
- mPreviousPositionX = motionEvent.getX();
- mPreviousPositionY = motionEvent.getY();
- mMoveThresholdReached = false;
- // save the ID of the pointer
- mActivePointerId = motionEvent.getPointerId(0);
- return true;
- }
-
- private boolean onActionMove(MotionEvent motionEvent) {
- int pointerIndex = motionEvent.findPointerIndex(mActivePointerId);
-
- // calculate the distance between previous and current position
- float moveX = motionEvent.getX(pointerIndex) - mPreviousPositionX;
- float moveY = motionEvent.getY(pointerIndex) - mPreviousPositionY;
- boolean scaling = mScaleGestureDetector.isInProgress();
- if (!scaling && !mMoveThresholdReached) {
-
- if (Math.abs(moveX) > 3 * mMapMoveDelta
- || Math.abs(moveY) > 3 * mMapMoveDelta) {
- // the map movement threshold has been reached
- // longPressDetector.pressStop();
- mMoveThresholdReached = true;
-
- // save the position of the event
- mPreviousPositionX = motionEvent.getX(pointerIndex);
- mPreviousPositionY = motionEvent.getY(pointerIndex);
- }
- return true;
- }
-
- // save the position of the event
- mPreviousPositionX = motionEvent.getX(pointerIndex);
- mPreviousPositionY = motionEvent.getY(pointerIndex);
-
- if (scaling) {
- return true;
- }
-
- mMapView.getMapPosition().moveMap(moveX, moveY);
- mMapView.redrawTiles();
-
- return true;
- }
-
- // private boolean onActionPointerDown(MotionEvent motionEvent) {
- // longPressDetector.pressStop();
- // multiTouchDownTime = motionEvent.getEventTime();
- // return true;
- // }
-
- private boolean onActionPointerUp(MotionEvent motionEvent) {
-
- // extract the index of the pointer that left the touch sensor
- int pointerIndex = (motionEvent.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
- if (motionEvent.getPointerId(pointerIndex) == mActivePointerId) {
- // the active pointer has gone up, choose a new one
- if (pointerIndex == 0) {
- pointerIndex = 1;
- } else {
- pointerIndex = 0;
- }
- // save the position of the event
- mPreviousPositionX = motionEvent.getX(pointerIndex);
- mPreviousPositionY = motionEvent.getY(pointerIndex);
- mActivePointerId = motionEvent.getPointerId(pointerIndex);
- }
-
- // calculate the time difference since the pointer has gone down
- // long multiTouchTime = motionEvent.getEventTime() -
- // multiTouchDownTime;
- // if (multiTouchTime < doubleTapTimeout) {
- // // multi-touch tap event, zoom out
- // previousEventTap = false;
- // mapView.zoom((byte) -1);
- // }
- return true;
- }
-
- /**
- * @param motionEvent
- * ...
- * @return ...
- */
- private boolean onActionUp(MotionEvent motionEvent) {
- // longPressDetector.pressStop();
- // int pointerIndex = motionEvent.findPointerIndex(mActivePointerId);
-
- mActivePointerId = INVALID_POINTER_ID;
- // if (mMoveThresholdReached // || longPressDetector.isEventHandled()
- // ) {
- // mPreviousEventTap = false;
- // } else {
- // if (mPreviousEventTap) {
- //
- // } else {
- // mPreviousEventTap = true;
- // }
- //
- // // store the position and the time of this tap event
- // mPreviousTapX = motionEvent.getX(pointerIndex);
- // mPreviousTapY = motionEvent.getY(pointerIndex);
- // mPreviousTapTime = motionEvent.getEventTime();
- //
- // }
- return true;
- }
-
- private long lastRun = 0;
-
- /**
- * @param motionEvent
- * ...
- * @return ...
- */
- public boolean handleMotionEvent(MotionEvent motionEvent) {
-
- // workaround for a bug in the ScaleGestureDetector, see Android issue
- // #12976
- if (motionEvent.getAction() != MotionEvent.ACTION_MOVE
- || motionEvent.getPointerCount() > 1) {
- mScaleGestureDetector.onTouchEvent(motionEvent);
- }
-
- mGestureDetector.onTouchEvent(motionEvent);
- // if () {
- // // mActivePointerId = INVALID_POINTER_ID;
- // // return true;
- // }
- int action = getAction(motionEvent);
- boolean ret = false;
- if (action == MotionEvent.ACTION_DOWN) {
- ret = onActionDown(motionEvent);
- } else if (action == MotionEvent.ACTION_MOVE) {
- ret = onActionMove(motionEvent);
- } else if (action == MotionEvent.ACTION_UP) {
- ret = onActionUp(motionEvent);
- } else if (action == MotionEvent.ACTION_CANCEL) {
- ret = onActionCancel();
- // } else if (action == MotionEvent.ACTION_POINTER_DOWN) {
- // return onActionPointerDown(motionEvent);
- } else if (action == MotionEvent.ACTION_POINTER_UP) {
- ret = onActionPointerUp(motionEvent);
- }
-
- // if (ret) {
- // // throttle input
- // long diff = SystemClock.uptimeMillis() - lastRun;
- // if (diff < 16 && diff > 5) {
- // // Log.d("", "" + diff);
- // SystemClock.sleep(16 - diff);
- // }
- // lastRun = SystemClock.uptimeMillis();
- // }
-
- return ret;
- }
-
- class MapGestureDetector extends SimpleOnGestureListener {
- private Scroller mScroller;
- private float mPrevX, mPrevY;
- private CountDownTimer mTimer = null;
-
- public MapGestureDetector(MapView mapView) {
- mScroller = new Scroller(mapView.getContext(),
- new android.view.animation.LinearInterpolator());
- }
-
- @Override
- public boolean onDown(MotionEvent e) {
- mScroller.forceFinished(true);
-
- if (mTimer != null) {
- mTimer.cancel();
- mTimer = null;
- }
- return true;
- }
-
- boolean scroll() {
- if (mScroller.isFinished()) {
- return false;
- }
- mScroller.computeScrollOffset();
- float moveX = mScroller.getCurrX() - mPrevX;
- float moveY = mScroller.getCurrY() - mPrevY;
-
- if (moveX >= 1 || moveY >= 1 || moveX <= -1 || moveY <= -1) {
- mMapView.getMapPosition().moveMap(moveX, moveY);
- mMapView.redrawTiles();
- mPrevX = mScroller.getCurrX();
- mPrevY = mScroller.getCurrY();
- }
- return true;
- }
-
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
- float velocityY) {
- int w = Tile.TILE_SIZE * 10;
- int h = Tile.TILE_SIZE * 10;
- mPrevX = 0;
- mPrevY = 0;
-
- if (mTimer != null) {
- mTimer.cancel();
- mTimer = null;
- }
-
- mScroller.fling(0, 0, Math.round(velocityX) / 2, Math.round(velocityY) / 2,
- -w, w, -h, h);
- // animate for two seconds
- mTimer = new CountDownTimer(2000, 40) {
- @Override
- public void onTick(long tick) {
- if (!scroll())
- cancel();
- }
-
- @Override
- public void onFinish() {
- // do nothing
- }
- }.start();
-
- return true;
- }
-
- @Override
- public void onLongPress(MotionEvent e) {
- // mMapView.zoom((byte) 1);
- Log.d("mapsforge", "long press");
- // return true;
- }
-
- @Override
- public boolean onDoubleTap(MotionEvent e) {
- mMapView.zoom((byte) 1);
- Log.d("mapsforge", "double tap");
- return true;
- }
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/input/ZoomAnimator.java b/VectorTileMap/src/org/mapsforge/android/input/ZoomAnimator.java
deleted file mode 100644
index b45860f..0000000
--- a/VectorTileMap/src/org/mapsforge/android/input/ZoomAnimator.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.input;
-
-import org.mapsforge.android.MapView;
-import org.mapsforge.android.utils.PausableThread;
-
-import android.os.SystemClock;
-
-/**
- * A ZoomAnimator handles the zoom-in and zoom-out animations of the corresponding MapView. It runs in a separate thread
- * to avoid blocking the UI thread.
- */
-public class ZoomAnimator extends PausableThread {
- private static final int DEFAULT_DURATION = 250;
- private static final int FRAME_LENGTH_IN_MS = 15;
- private static final String THREAD_NAME = "ZoomAnimator";
-
- private boolean mExecuteAnimation;
- private final MapView mMapView;
- // private float mPivotX;
- // private float mPivotY;
- private float mScaleFactorApplied;
- private long mTimeStart;
- private float mZoomDifference;
- private float mZoomEnd;
- private float mZoomStart;
-
- /**
- * @param mapView
- * the MapView whose zoom level changes should be animated.
- */
- public ZoomAnimator(MapView mapView) {
- super();
- mMapView = mapView;
- }
-
- /**
- * @return true if the ZoomAnimator is working, false otherwise.
- */
- public boolean isExecuting() {
- return mExecuteAnimation;
- }
-
- /**
- * Sets the parameters for the zoom animation.
- *
- * @param zoomStart
- * the zoom factor at the begin of the animation.
- * @param zoomEnd
- * the zoom factor at the end of the animation.
- * @param pivotX
- * the x coordinate of the animation center.
- * @param pivotY
- * the y coordinate of the animation center.
- */
- public void setParameters(float zoomStart, float zoomEnd, float pivotX, float pivotY) {
- mZoomStart = zoomStart;
- mZoomEnd = zoomEnd;
- // mPivotX = pivotX;
- // mPivotY = pivotY;
- }
-
- /**
- * Starts a zoom animation with the current parameters.
- */
- public void startAnimation() {
- mZoomDifference = mZoomEnd - mZoomStart;
- mScaleFactorApplied = mZoomStart;
- mExecuteAnimation = true;
- mTimeStart = SystemClock.uptimeMillis();
- synchronized (this) {
- notify();
- }
- }
-
- @Override
- protected void doWork() throws InterruptedException {
- // calculate the elapsed time
- long timeElapsed = SystemClock.uptimeMillis() - mTimeStart;
- float timeElapsedPercent = Math.min(1, timeElapsed / (float) DEFAULT_DURATION);
-
- // calculate the zoom and scale values at the current moment
- float currentZoom = mZoomStart + timeElapsedPercent * mZoomDifference;
- float scaleFactor = currentZoom / mScaleFactorApplied;
- mScaleFactorApplied *= scaleFactor;
- // mapView.getFrameBuffer().matrixPostScale(scaleFactor, scaleFactor,
- // pivotX, pivotY);
-
- // check if the animation time is over
- if (timeElapsed >= DEFAULT_DURATION) {
- mExecuteAnimation = false;
- mMapView.redrawTiles();
- } else {
- mMapView.postInvalidate();
- sleep(FRAME_LENGTH_IN_MS);
- }
- }
-
- @Override
- protected String getThreadName() {
- return THREAD_NAME;
- }
-
- @Override
- protected boolean hasWork() {
- return mExecuteAnimation;
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/mapgenerator/IMapGenerator.java b/VectorTileMap/src/org/mapsforge/android/mapgenerator/IMapGenerator.java
deleted file mode 100644
index 344c1d1..0000000
--- a/VectorTileMap/src/org/mapsforge/android/mapgenerator/IMapGenerator.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.mapgenerator;
-
-import org.mapsforge.android.rendertheme.RenderTheme;
-import org.mapsforge.database.IMapDatabase;
-
-/**
- * A MapGenerator provides map tiles either by downloading or rendering them.
- */
-public interface IMapGenerator {
- /**
- * Called once at the end of the MapGenerator lifecycle.
- */
- void cleanup();
-
- /**
- * Called when a job needs to be executed.
- *
- * @param mapGeneratorJob
- * the job that should be executed.
- * @return true if the job was executed successfully, false otherwise.
- */
- boolean executeJob(MapGeneratorJob mapGeneratorJob);
-
- /**
- * @param mapDatabase
- * the MapDatabase from which the map data will be read.
- */
- void setMapDatabase(IMapDatabase mapDatabase);
-
- IMapDatabase getMapDatabase();
-
- void setRenderTheme(RenderTheme theme);
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/mapgenerator/JobParameters.java b/VectorTileMap/src/org/mapsforge/android/mapgenerator/JobParameters.java
deleted file mode 100644
index b9be225..0000000
--- a/VectorTileMap/src/org/mapsforge/android/mapgenerator/JobParameters.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.mapgenerator;
-
-/**
- * A JobParameters instance is a simple DTO to store the rendering parameters for a job.
- */
-public class JobParameters {
-
- /**
- * The render theme which should be used.
- */
- public final Theme theme;
-
- /**
- * The text scale factor which should applied to the render theme.
- */
- public final float textScale;
-
- private final int mHashCodeValue;
-
- /**
- * @param theme
- * render theme which should be used.
- * @param textScale
- * the text scale factor which should applied to the render theme.
- */
- public JobParameters(Theme theme, float textScale) {
- this.theme = theme;
- this.textScale = textScale;
- mHashCodeValue = calculateHashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (!(obj instanceof JobParameters)) {
- return false;
- }
- JobParameters other = (JobParameters) obj;
- if (theme == null) {
- if (other.theme != null) {
- return false;
- }
- } else if (!theme.equals(other.theme)) {
- return false;
- }
- if (Float.floatToIntBits(textScale) != Float.floatToIntBits(other.textScale)) {
- return false;
- }
- return true;
- }
-
- @Override
- public int hashCode() {
- return mHashCodeValue;
- }
-
- /**
- * @return the hash code of this object.
- */
- private int calculateHashCode() {
- int result = 7;
- result = 31 * result + ((theme == null) ? 0 : theme.hashCode());
- result = 31 * result + Float.floatToIntBits(textScale);
- return result;
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/mapgenerator/JobQueue.java b/VectorTileMap/src/org/mapsforge/android/mapgenerator/JobQueue.java
deleted file mode 100644
index 3afa9e0..0000000
--- a/VectorTileMap/src/org/mapsforge/android/mapgenerator/JobQueue.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.mapgenerator;
-
-import java.util.ArrayList;
-import java.util.PriorityQueue;
-
-import org.mapsforge.android.MapView;
-
-import android.os.SystemClock;
-
-/**
- * A JobQueue keeps the list of pending jobs for a MapView and prioritizes them.
- */
-public class JobQueue {
- private static final int INITIAL_CAPACITY = 128;
-
- private final MapView mMapView;
- private PriorityQueue mPriorityQueue;
- private boolean mScheduleNeeded;
-
- /**
- * @param mapView
- * the MapView whose jobs should be organized.
- */
- public JobQueue(MapView mapView) {
- mMapView = mapView;
- mPriorityQueue = new PriorityQueue(INITIAL_CAPACITY);
- }
-
- /**
- * Adds the given job to this queue. Does nothing if the given job is already in this queue.
- *
- * @param mapGeneratorJob
- * the job to be added to this queue.
- */
- // public synchronized void addJob(MapGeneratorJob mapGeneratorJob) {
- // if (!mPriorityQueue.contains(mapGeneratorJob))
- // // priorityQueue.remove(mapGeneratorJob);
- // {
- // //mapGeneratorJob.tile.isLoading = true;
- // mPriorityQueue.offer(mapGeneratorJob);
- // }
- // }
-
- /**
- * @param jobs
- * the job to be added to this queue.
- */
- public synchronized void setJobs(ArrayList jobs) {
- mPriorityQueue.clear();
- for (MapGeneratorJob job : jobs)
- mPriorityQueue.offer(job);
- // priorityQueue.addAll(jobs);
- mScheduleNeeded = true;
- }
-
- /**
- * Removes all jobs from this queue.
- */
- public synchronized void clear() {
- mPriorityQueue.clear();
- }
-
- /**
- * @return true if this queue contains no jobs, false otherwise.
- */
- public synchronized boolean isEmpty() {
- return mPriorityQueue.isEmpty();
- }
-
- /**
- * @return the most important job from this queue or null, if empty.
- */
- public synchronized MapGeneratorJob poll() {
- if (mScheduleNeeded) {
- mScheduleNeeded = false;
- schedule();
- }
- return mPriorityQueue.poll();
- }
-
- /**
- * Request a scheduling of all jobs that are currently in this queue.
- */
- public synchronized void requestSchedule() {
- mScheduleNeeded = true;
- }
-
- /**
- * Schedules all jobs in this queue.
- */
- private void schedule() {
- PriorityQueue tempJobQueue = new PriorityQueue(
- INITIAL_CAPACITY);
-
- TileScheduler.time = SystemClock.uptimeMillis();
- TileScheduler.mapPosition = mMapView.getMapPosition().getMapPosition();
-
- while (!mPriorityQueue.isEmpty()) {
- MapGeneratorJob mapGeneratorJob = mPriorityQueue.poll();
- double priority = TileScheduler.getPriority(mapGeneratorJob, mMapView);
- mapGeneratorJob.setPriority(priority);
- tempJobQueue.offer(mapGeneratorJob);
- }
-
- mPriorityQueue = tempJobQueue;
- }
-
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/mapgenerator/MapGeneratorJob.java b/VectorTileMap/src/org/mapsforge/android/mapgenerator/MapGeneratorJob.java
deleted file mode 100644
index bab438b..0000000
--- a/VectorTileMap/src/org/mapsforge/android/mapgenerator/MapGeneratorJob.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.mapgenerator;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.Serializable;
-
-import org.mapsforge.android.DebugSettings;
-
-import android.graphics.Bitmap;
-
-/**
- * A MapGeneratorJob holds all immutable rendering parameters for a single map image together with a mutable priority
- * field, which indicates the importance of this job.
- */
-public class MapGeneratorJob implements Comparable, Serializable {
- private static final long serialVersionUID = 1L;
-
- /**
- * The debug settings for this job.
- */
- public final DebugSettings debugSettings;
-
- /**
- * The rendering parameters for this job.
- */
- // public final JobParameters jobParameters;
-
- /**
- * The tile which should be generated.
- */
- public final MapTile tile;
-
- private transient int mHashCodeValue;
- private transient double mPriority;
-
- /**
- * bitmap passed to renderer
- */
- private Bitmap mBitmap;
-
- /**
- * @return ...
- */
- public Bitmap getBitmap() {
- return mBitmap;
- }
-
- /**
- * @param bitmap
- * ..
- */
- public void setBitmap(Bitmap bitmap) {
- mBitmap = bitmap;
- }
-
- private float mScale;
-
- /**
- * @return scale the tile is rendered with
- */
- public float getScale() {
- return mScale;
- }
-
- /**
- * @param _scale
- * for the tile to be rendered
- */
- public void setScale(float _scale) {
- mScale = _scale;
- }
-
- /**
- * Creates a new job for a MapGenerator with the given parameters.
- *
- * @param _tile
- * the tile which should be generated.
- * @param _jobParameters
- * the rendering parameters for this job.
- * @param _debugSettings
- * the debug settings for this job.
- */
- public MapGeneratorJob(MapTile _tile, JobParameters _jobParameters,
- DebugSettings _debugSettings) {
- tile = _tile;
- // jobParameters = _jobParameters;
- debugSettings = _debugSettings;
- calculateTransientValues();
- }
-
- @Override
- public int compareTo(MapGeneratorJob o) {
- if (mPriority < o.mPriority) {
- return -1;
- } else if (mPriority > o.mPriority) {
- return 1;
- }
- return 0;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (!(obj instanceof MapGeneratorJob)) {
- return false;
- }
- MapGeneratorJob other = (MapGeneratorJob) obj;
-
- if (debugSettings == null) {
- if (other.debugSettings != null) {
- return false;
- }
- } else if (!debugSettings.equals(other.debugSettings)) {
- return false;
- }
- // if (jobParameters == null) {
- // if (other.jobParameters != null) {
- // return false;
- // }
- // } else if (!jobParameters.equals(other.jobParameters)) {
- // return false;
- // }
-
- if (tile == null) {
- if (other.tile != null) {
- return false;
- }
- } else if (!tile.equals(other.tile)) {
- return false;
- }
- return true;
- }
-
- @Override
- public int hashCode() {
- return mHashCodeValue;
- }
-
- /**
- * @return the hash code of this object.
- */
- private int calculateHashCode() {
- int result = 1;
- result = 31 * result + ((debugSettings == null) ? 0 : debugSettings.hashCode());
- // result = 31 * result + ((jobParameters == null) ? 0 : jobParameters.hashCode());
- result = 31 * result + ((tile == null) ? 0 : tile.hashCode());
- return result;
- }
-
- /**
- * Calculates the values of some transient variables.
- */
- private void calculateTransientValues() {
- mHashCodeValue = calculateHashCode();
- }
-
- private void readObject(ObjectInputStream objectInputStream) throws IOException,
- ClassNotFoundException {
- objectInputStream.defaultReadObject();
- calculateTransientValues();
- }
-
- void setPriority(double priority) {
- mPriority = priority;
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/mapgenerator/MapRendererFactory.java b/VectorTileMap/src/org/mapsforge/android/mapgenerator/MapRendererFactory.java
deleted file mode 100644
index 5b20ca6..0000000
--- a/VectorTileMap/src/org/mapsforge/android/mapgenerator/MapRendererFactory.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.mapgenerator;
-
-import org.mapsforge.android.IMapRenderer;
-import org.mapsforge.android.MapView;
-
-import android.util.AttributeSet;
-
-/**
- * A factory for the internal MapGenerator implementations.
- */
-public final class MapRendererFactory {
- private static final String MAP_GENERATOR_ATTRIBUTE_NAME = "mapGenerator";
-
- /**
- * @param mapView
- * ...
- * @param attributeSet
- * A collection of attributes which includes the desired MapGenerator.
- * @return a new MapGenerator instance.
- */
- public static IMapRenderer createMapRenderer(MapView mapView,
- AttributeSet attributeSet) {
- String mapGeneratorName = attributeSet.getAttributeValue(null,
- MAP_GENERATOR_ATTRIBUTE_NAME);
- if (mapGeneratorName == null) {
- return new org.mapsforge.android.glrenderer.MapRenderer(mapView);
- }
-
- MapRenderers mapGeneratorInternal = MapRenderers.valueOf(mapGeneratorName);
- return MapRendererFactory.createMapRenderer(mapView, mapGeneratorInternal);
- }
-
- public static MapRenderers getMapGenerator(AttributeSet attributeSet) {
- String mapGeneratorName = attributeSet.getAttributeValue(null,
- MAP_GENERATOR_ATTRIBUTE_NAME);
- if (mapGeneratorName == null) {
- return MapRenderers.GL_RENDERER;
- }
-
- return MapRenderers.valueOf(mapGeneratorName);
- }
-
- /**
- * @param mapView
- * ...
- * @param mapGeneratorInternal
- * the internal MapGenerator implementation.
- * @return a new MapGenerator instance.
- */
- public static IMapRenderer createMapRenderer(MapView mapView,
- MapRenderers mapGeneratorInternal) {
- switch (mapGeneratorInternal) {
- case SW_RENDERER:
- return new org.mapsforge.android.swrenderer.MapRenderer(mapView);
- case GL_RENDERER:
- return new org.mapsforge.android.glrenderer.MapRenderer(mapView);
- }
-
- throw new IllegalArgumentException("unknown enum value: " + mapGeneratorInternal);
- }
-
- private MapRendererFactory() {
- throw new IllegalStateException();
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/mapgenerator/MapTile.java b/VectorTileMap/src/org/mapsforge/android/mapgenerator/MapTile.java
deleted file mode 100644
index 00b5a92..0000000
--- a/VectorTileMap/src/org/mapsforge/android/mapgenerator/MapTile.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2012 Hannes Janetzek
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.mapgenerator;
-
-import org.mapsforge.core.Tile;
-
-/**
- *
- */
-public class MapTile extends Tile {
- /**
- * tile is loaded and ready for drawing. (set and used by render thread after uploading data to gl).
- */
- public boolean isReady;
-
- /**
- * tile is removed from JobQueue and loading in DatabaseRenderer. set by MapWorker.
- */
- public boolean isLoading;
-
- /**
- * tile is in view region. (set and used by render thread)
- */
- public boolean isVisible;
-
- /**
- * tile is used by render thread. set by updateVisibleList (main thread).
- */
- public boolean isActive;
-
- /**
- * distance from center, used in TileScheduler set by updateVisibleList.
- */
- public float distance;
-
- /**
- * @param tileX
- * ...
- * @param tileY
- * ...
- * @param zoomLevel
- * ..
- */
- public MapTile(long tileX, long tileY, byte zoomLevel) {
- super(tileX, tileY, zoomLevel);
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/mapgenerator/TileCacheKey.java b/VectorTileMap/src/org/mapsforge/android/mapgenerator/TileCacheKey.java
deleted file mode 100644
index 041ee89..0000000
--- a/VectorTileMap/src/org/mapsforge/android/mapgenerator/TileCacheKey.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.mapgenerator;
-
-/**
- * @author jeff
- */
-public class TileCacheKey {
- long x, y;
- byte z;
- int hash;
-
- /**
- *
- */
- public TileCacheKey() {
- }
-
- /**
- * @param key
- * create new TileCacheKey for key
- */
- public TileCacheKey(TileCacheKey key) {
- this.x = key.x;
- this.y = key.y;
- this.z = key.z;
- this.hash = key.hash;
- }
-
- /**
- * @param x
- * Position
- * @param y
- * Position
- * @param z
- * Position
- */
- public TileCacheKey(long x, long y, byte z) {
- this.x = x;
- this.y = y;
- this.z = z;
- hash = 7 * z + 31 * ((int) (x ^ (x >>> 32)) + 31 * (int) (y ^ (y >>> 32)));
- }
-
- /**
- * @param x
- * Position
- * @param y
- * Position
- * @param z
- * Position
- * @return self
- */
- public TileCacheKey set(long x, long y, byte z) {
- this.x = x;
- this.y = y;
- this.z = z;
- hash = 7 * z + 31 * ((int) (x ^ (x >>> 32)) + 31 * (int) (y ^ (y >>> 32)));
- return this;
- }
-
- @Override
- public boolean equals(Object obj) {
- TileCacheKey other = (TileCacheKey) obj;
- return (x == other.x && y == other.y && z == other.z);
- }
-
- @Override
- public int hashCode() {
- return hash;
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/mapgenerator/TileScheduler.java b/VectorTileMap/src/org/mapsforge/android/mapgenerator/TileScheduler.java
deleted file mode 100644
index 32303d0..0000000
--- a/VectorTileMap/src/org/mapsforge/android/mapgenerator/TileScheduler.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.mapgenerator;
-
-import org.mapsforge.android.MapView;
-import org.mapsforge.core.MapPosition;
-
-final class TileScheduler {
-
- static long time;
- static MapPosition mapPosition;
-
- /**
- * Calculates the priority for the given tile based on the current position and zoom level of the supplied MapView.
- * The smaller the distance from the tile center to the MapView center, the higher its priority. If the zoom level
- * of a tile differs from the zoom level of the MapView, its priority decreases.
- *
- * @param mapGeneratorJob
- * the tile whose priority should be calculated.
- * @param mapView
- * the MapView whose current position and zoom level define the priority of the tile.
- * @return the current priority of the tile. A smaller number means a higher priority.
- */
- static double getPriority(MapGeneratorJob mapGeneratorJob, MapView mapView) {
- MapTile tile = mapGeneratorJob.tile;
-
- // if (tile.isDrawn) {
- // long diff = time - tile.loadTime;
- //
- // // check.. just in case
- // if (diff > 0.0) {
- // return (10000.0f / diff) * (tile.isVisible ? 1 : 5); // * tile.distance;
- // }
- // }
-
- // // calculate the center coordinates of the tile
- // double tileCenterLongitude = MercatorProjection.pixelXToLongitude(tile.getCenterX(), tileZoomLevel);
- // double tileCenterLatitude = MercatorProjection.pixelYToLatitude(tile.getCenterY(), tileZoomLevel);
- //
- // // calculate the Euclidian distance from the MapView center to the tile
- // // center
- // GeoPoint geoPoint = mapPosition.geoPoint;
- // double longitudeDiff = geoPoint.getLongitude() - tileCenterLongitude;
- // double latitudeDiff = geoPoint.getLatitude() - tileCenterLatitude;
- //
- // return Math.sqrt(longitudeDiff * longitudeDiff + latitudeDiff * latitudeDiff)
- // * (tile.visible ? 1.0 : 1000.0);
- // }
-
- return tile.distance / 1000.0;
- }
-
- private TileScheduler() {
- throw new IllegalStateException();
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/package-info.java b/VectorTileMap/src/org/mapsforge/android/package-info.java
deleted file mode 100644
index 747969d..0000000
--- a/VectorTileMap/src/org/mapsforge/android/package-info.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-/**
- * The mapsforge-map library allows applications to render and display a map without Internet connection. It can
- * be used on all Android devices running version 1.5 or higher. An application needs to extend the
- * {@link org.mapsforge.android.MapActivity} class in order to use a
- * {@link org.mapsforge.android.MapView}. More than one MapView instance may be used simultaneously.
- *
- * The most important classes and methods from the Google APIs Add-On are implemented. However, no API key is required and no abstract methods
- * must be overridden.
- *
- * This software is a part of the mapsforge project and
- * distributed under the LGPL3 license . All
- * map data (c) OpenStreetMap contributors, CC-BY-SA license .
- */
-package org.mapsforge.android;
-
diff --git a/VectorTileMap/src/org/mapsforge/android/rendertheme/MatchingCacheKey.java b/VectorTileMap/src/org/mapsforge/android/rendertheme/MatchingCacheKey.java
deleted file mode 100644
index c9a33e0..0000000
--- a/VectorTileMap/src/org/mapsforge/android/rendertheme/MatchingCacheKey.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.rendertheme;
-
-import org.mapsforge.core.Tag;
-
-class MatchingCacheKey {
- private final int mHashCodeValue;
- final Tag[] mTags;
- final byte mZoomLevel;
-
- MatchingCacheKey(Tag[] tags, byte zoomLevel) {
- mTags = tags;
- mZoomLevel = zoomLevel;
- mHashCodeValue = calculateHashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- } else if (!(obj instanceof MatchingCacheKey)) {
- return false;
- }
- MatchingCacheKey other = (MatchingCacheKey) obj;
-
- if (mZoomLevel != other.mZoomLevel)
- return false;
-
- if (mTags == null) {
- return (other.mTags == null);
- } else if (other.mTags == null)
- return false;
-
- int length = mTags.length;
- if (length != other.mTags.length) {
- return false;
- }
-
- for (int i = 0; i < length; i++)
- if (mTags[i] != other.mTags[i])
- return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- return mHashCodeValue;
- }
-
- /**
- * @return the hash code of this object.
- */
- private int calculateHashCode() {
- int result = 7;
-
- for (int i = 0, n = mTags.length; i < n; i++) {
- if (mTags[i] == null) // FIXME
- break;
- result = 31 * result + mTags[i].hashCode();
- }
- result = 31 * result + mZoomLevel;
-
- return result;
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/rendertheme/RenderTheme.java b/VectorTileMap/src/org/mapsforge/android/rendertheme/RenderTheme.java
deleted file mode 100644
index 91d2115..0000000
--- a/VectorTileMap/src/org/mapsforge/android/rendertheme/RenderTheme.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.rendertheme;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.mapsforge.android.rendertheme.renderinstruction.RenderInstruction;
-import org.mapsforge.core.LRUCache;
-import org.mapsforge.core.Tag;
-import org.xml.sax.Attributes;
-
-import android.graphics.Color;
-
-/**
- * A RenderTheme defines how ways and nodes are drawn.
- */
-public class RenderTheme {
- private static final int MATCHING_CACHE_SIZE = 1024;
- private static final int RENDER_THEME_VERSION = 1;
-
- private static void validate(String elementName, Integer version,
- float baseStrokeWidth, float baseTextSize) {
- if (version == null) {
- throw new IllegalArgumentException("missing attribute version for element:"
- + elementName);
- } else if (version.intValue() != RENDER_THEME_VERSION) {
- throw new IllegalArgumentException("invalid render theme version:" + version);
- } else if (baseStrokeWidth < 0) {
- throw new IllegalArgumentException("base-stroke-width must not be negative: "
- + baseStrokeWidth);
- } else if (baseTextSize < 0) {
- throw new IllegalArgumentException("base-text-size must not be negative: "
- + baseTextSize);
- }
- }
-
- static RenderTheme create(String elementName, Attributes attributes) {
- Integer version = null;
- int mapBackground = Color.WHITE;
- float baseStrokeWidth = 1;
- float baseTextSize = 1;
-
- for (int i = 0; i < attributes.getLength(); ++i) {
- String name = attributes.getLocalName(i);
- String value = attributes.getValue(i);
-
- if ("schemaLocation".equals(name)) {
- continue;
- } else if ("version".equals(name)) {
- version = Integer.valueOf(Integer.parseInt(value));
- } else if ("map-background".equals(name)) {
- mapBackground = Color.parseColor(value);
- } else if ("base-stroke-width".equals(name)) {
- baseStrokeWidth = Float.parseFloat(value);
- } else if ("base-text-size".equals(name)) {
- baseTextSize = Float.parseFloat(value);
- } else {
- RenderThemeHandler.logUnknownAttribute(elementName, name, value, i);
- }
- }
-
- validate(elementName, version, baseStrokeWidth, baseTextSize);
- return new RenderTheme(mapBackground, baseStrokeWidth, baseTextSize);
- }
-
- private final float mBaseStrokeWidth;
- private final float mBaseTextSize;
- private int mLevels;
- private final int mMapBackground;
- private final ArrayList mRulesList;
-
- private final LRUCache mMatchingCacheNodes;
- private final LRUCache mMatchingCacheWay;
- private final LRUCache mMatchingCacheArea;
-
- RenderTheme(int mapBackground, float baseStrokeWidth, float baseTextSize) {
- mMapBackground = mapBackground;
- mBaseStrokeWidth = baseStrokeWidth;
- mBaseTextSize = baseTextSize;
- mRulesList = new ArrayList();
-
- mMatchingCacheNodes = new LRUCache(
- MATCHING_CACHE_SIZE);
- mMatchingCacheWay = new LRUCache(
- MATCHING_CACHE_SIZE);
- mMatchingCacheArea = new LRUCache(
- MATCHING_CACHE_SIZE);
- }
-
- /**
- * Must be called when this RenderTheme gets destroyed to clean up and free resources.
- */
- public void destroy() {
- mMatchingCacheNodes.clear();
- mMatchingCacheArea.clear();
- mMatchingCacheWay.clear();
-
- for (int i = 0, n = mRulesList.size(); i < n; ++i) {
- mRulesList.get(i).onDestroy();
- }
- }
-
- /**
- * @return the number of distinct drawing levels required by this RenderTheme.
- */
- public int getLevels() {
- return mLevels;
- }
-
- /**
- * @return the map background color of this RenderTheme.
- * @see Color
- */
- public int getMapBackground() {
- return mMapBackground;
- }
-
- /**
- * @param renderCallback
- * ...
- * @param tags
- * ...
- * @param zoomLevel
- * ...
- * @return ...
- */
- public synchronized RenderInstruction[] matchNode(IRenderCallback renderCallback,
- Tag[] tags,
- byte zoomLevel) {
-
- RenderInstruction[] renderInstructions = null;
-
- MatchingCacheKey matchingCacheKey;
-
- matchingCacheKey = new MatchingCacheKey(tags, zoomLevel);
- boolean found = mMatchingCacheNodes.containsKey(matchingCacheKey);
- if (found) {
- renderInstructions = mMatchingCacheNodes.get(matchingCacheKey);
- } else {
- // cache miss
- List matchingList = new ArrayList(4);
- for (int i = 0, n = mRulesList.size(); i < n; ++i)
- mRulesList.get(i)
- .matchNode(renderCallback, tags, zoomLevel, matchingList);
-
- int size = matchingList.size();
- if (size > 0) {
- renderInstructions = new RenderInstruction[size];
- matchingList.toArray(renderInstructions);
- }
- mMatchingCacheNodes.put(matchingCacheKey, renderInstructions);
- }
-
- if (renderInstructions != null) {
- for (int i = 0, n = renderInstructions.length; i < n; i++)
- renderInstructions[i].renderNode(renderCallback, tags);
- }
-
- return renderInstructions;
-
- }
-
- /**
- * Matches a way with the given parameters against this RenderTheme.
- *
- * @param renderCallback
- * the callback implementation which will be executed on each match.
- * @param tags
- * the tags of the way.
- * @param zoomLevel
- * the zoom level at which the way should be matched.
- * @param closed
- * way is Closed
- * @param render
- * ...
- * @return currently processed render instructions
- */
- public synchronized RenderInstruction[] matchWay(IRenderCallback renderCallback,
- Tag[] tags,
- byte zoomLevel,
- boolean closed, boolean render) {
- RenderInstruction[] renderInstructions = null;
-
- LRUCache matchingCache;
- MatchingCacheKey matchingCacheKey;
-
- if (closed) {
- matchingCache = mMatchingCacheArea;
- } else {
- matchingCache = mMatchingCacheWay;
- }
-
- matchingCacheKey = new MatchingCacheKey(tags, zoomLevel);
- boolean found = matchingCache.containsKey(matchingCacheKey);
- if (found) {
- renderInstructions = matchingCache.get(matchingCacheKey);
- } else {
- // cache miss
- int c = (closed ? Closed.YES : Closed.NO);
- List matchingList = new ArrayList(4);
- for (int i = 0, n = mRulesList.size(); i < n; ++i) {
- mRulesList.get(i).matchWay(renderCallback, tags, zoomLevel, c,
- matchingList);
- }
- int size = matchingList.size();
- if (size > 0) {
- renderInstructions = new RenderInstruction[size];
- matchingList.toArray(renderInstructions);
- }
- matchingCache.put(matchingCacheKey, renderInstructions);
- }
-
- if (render && renderInstructions != null) {
- for (int i = 0, n = renderInstructions.length; i < n; i++)
- renderInstructions[i].renderWay(renderCallback, tags);
- }
-
- return renderInstructions;
- }
-
- void addRule(Rule rule) {
- mRulesList.add(rule);
- }
-
- void complete() {
- mRulesList.trimToSize();
- for (int i = 0, n = mRulesList.size(); i < n; ++i) {
- mRulesList.get(i).onComplete();
- }
-
- }
-
- /**
- * Scales the stroke width of this RenderTheme by the given factor.
- *
- * @param scaleFactor
- * the factor by which the stroke width should be scaled.
- */
- public void scaleStrokeWidth(float scaleFactor) {
- for (int i = 0, n = mRulesList.size(); i < n; ++i) {
- mRulesList.get(i).scaleStrokeWidth(scaleFactor * mBaseStrokeWidth);
- }
- }
-
- /**
- * Scales the text size of this RenderTheme by the given factor.
- *
- * @param scaleFactor
- * the factor by which the text size should be scaled.
- */
- public void scaleTextSize(float scaleFactor) {
- for (int i = 0, n = mRulesList.size(); i < n; ++i) {
- mRulesList.get(i).scaleTextSize(scaleFactor * mBaseTextSize);
- }
- }
-
- void setLevels(int levels) {
- mLevels = levels;
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/rendertheme/renderinstruction/Caption.java b/VectorTileMap/src/org/mapsforge/android/rendertheme/renderinstruction/Caption.java
deleted file mode 100644
index 3c4ec79..0000000
--- a/VectorTileMap/src/org/mapsforge/android/rendertheme/renderinstruction/Caption.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.rendertheme.renderinstruction;
-
-import java.util.Locale;
-
-import org.mapsforge.android.rendertheme.IRenderCallback;
-import org.mapsforge.android.rendertheme.RenderThemeHandler;
-import org.mapsforge.core.Tag;
-import org.xml.sax.Attributes;
-
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Paint.Align;
-import android.graphics.Paint.FontMetrics;
-import android.graphics.Paint.Style;
-import android.graphics.Typeface;
-import android.util.FloatMath;
-
-/**
- * Represents a text label on the map.
- */
-public final class Caption extends RenderInstruction {
- /**
- * @param elementName
- * the name of the XML element.
- * @param attributes
- * the attributes of the XML element.
- * @return a new Caption with the given rendering attributes.
- */
- public static Caption create(String elementName, Attributes attributes) {
- String textKey = null;
- float dy = 0;
- FontFamily fontFamily = FontFamily.DEFAULT;
- FontStyle fontStyle = FontStyle.NORMAL;
- float fontSize = 0;
- int fill = Color.BLACK;
- int stroke = Color.BLACK;
- float strokeWidth = 0;
-
- for (int i = 0; i < attributes.getLength(); ++i) {
- String name = attributes.getLocalName(i);
- String value = attributes.getValue(i);
-
- if ("k".equals(name)) {
- textKey = TextKey.getInstance(value);
- } else if ("dy".equals(name)) {
- dy = Float.parseFloat(value);
- } else if ("font-family".equals(name)) {
- fontFamily = FontFamily.valueOf(value.toUpperCase(Locale.ENGLISH));
- } else if ("font-style".equals(name)) {
- fontStyle = FontStyle.valueOf(value.toUpperCase(Locale.ENGLISH));
- } else if ("font-size".equals(name)) {
- fontSize = Float.parseFloat(value);
- } else if ("fill".equals(name)) {
- fill = Color.parseColor(value);
- } else if ("stroke".equals(name)) {
- stroke = Color.parseColor(value);
- } else if ("stroke-width".equals(name)) {
- strokeWidth = Float.parseFloat(value);
- } else {
- RenderThemeHandler.logUnknownAttribute(elementName, name, value, i);
- }
- }
-
- validate(elementName, textKey, fontSize, strokeWidth);
- Typeface typeface = Typeface.create(fontFamily.toTypeface(), fontStyle.toInt());
- return new Caption(textKey, dy, typeface, fontSize, fill, stroke, strokeWidth);
- }
-
- private static void validate(String elementName, String textKey, float fontSize,
- float strokeWidth) {
- if (textKey == null) {
- throw new IllegalArgumentException("missing attribute k for element: "
- + elementName);
- } else if (fontSize < 0) {
- throw new IllegalArgumentException("font-size must not be negative: "
- + fontSize);
- } else if (strokeWidth < 0) {
- throw new IllegalArgumentException("stroke-width must not be negative: "
- + strokeWidth);
- }
- }
-
- public final float dy;
- public float fontSize;
- public final Paint paint;
- public final Paint stroke;
- public final String textKey;
- public final float fontHeight;
- public final float fontDescent;
-
- private Caption(String textKey, float dy, Typeface typeface, float fontSize,
- int fillColor, int strokeColor, float strokeWidth) {
- super();
-
- this.textKey = textKey.intern();
- this.dy = dy;
- this.fontSize = fontSize;
-
- paint = new Paint(Paint.ANTI_ALIAS_FLAG);
- paint.setTextAlign(Align.CENTER);
- paint.setTypeface(typeface);
- paint.setColor(fillColor);
-
- stroke = new Paint(Paint.ANTI_ALIAS_FLAG);
- stroke.setStyle(Style.STROKE);
- stroke.setTextAlign(Align.CENTER);
- stroke.setTypeface(typeface);
- stroke.setColor(strokeColor);
- stroke.setStrokeWidth(strokeWidth);
-
- paint.setTextSize(fontSize);
- stroke.setTextSize(fontSize);
-
- FontMetrics fm = paint.getFontMetrics();
- fontHeight = FloatMath.ceil(Math.abs(fm.bottom) + Math.abs(fm.top));
- fontDescent = FloatMath.ceil(Math.abs(fm.descent));
- }
-
- @Override
- public void renderNode(IRenderCallback renderCallback, Tag[] tags) {
- renderCallback.renderPointOfInterestCaption(this);
- }
-
- @Override
- public void renderWay(IRenderCallback renderCallback, Tag[] tags) {
- renderCallback.renderAreaCaption(this);
- }
-
- @Override
- public void scaleTextSize(float scaleFactor) {
- paint.setTextSize(fontSize * scaleFactor);
- stroke.setTextSize(fontSize * scaleFactor);
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/swrenderer/CanvasRasterer.java b/VectorTileMap/src/org/mapsforge/android/swrenderer/CanvasRasterer.java
deleted file mode 100644
index 7fa60d3..0000000
--- a/VectorTileMap/src/org/mapsforge/android/swrenderer/CanvasRasterer.java
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.swrenderer;
-
-import java.util.List;
-
-import org.mapsforge.core.Tile;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Typeface;
-
-/**
- * A CanvasRasterer uses a Canvas for drawing.
- *
- * @see Canvas
- */
-class CanvasRasterer {
- private static final Paint PAINT_BITMAP_FILTER = new Paint(Paint.FILTER_BITMAP_FLAG);
- private static final Paint PAINT_TILE_COORDINATES = new Paint(Paint.ANTI_ALIAS_FLAG);
- private static final Paint PAINT_TILE_COORDINATES_STROKE = new Paint(Paint.ANTI_ALIAS_FLAG);
- private static final Paint PAINT_TILE_FRAME = new Paint();
-
- private static final Paint PAINT_MARK = new Paint();
- static final int COLOR_MARK = Color.argb(30, 0, 255, 0);
-
- // private static final float[] TILE_FRAME = new float[] { 0, 0, 0, Tile.TILE_SIZE, 0, Tile.TILE_SIZE,
- // Tile.TILE_SIZE,
- // Tile.TILE_SIZE, Tile.TILE_SIZE, Tile.TILE_SIZE, Tile.TILE_SIZE, 0 };
-
- private static void configurePaints() {
- PAINT_TILE_COORDINATES.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
- PAINT_TILE_COORDINATES.setTextSize(12);
-
- PAINT_TILE_COORDINATES_STROKE.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
- PAINT_TILE_COORDINATES_STROKE.setStyle(Paint.Style.STROKE);
- PAINT_TILE_COORDINATES_STROKE.setStrokeWidth(1);
- PAINT_TILE_COORDINATES_STROKE.setTextSize(6);
- PAINT_TILE_COORDINATES_STROKE.setColor(Color.WHITE);
- PAINT_MARK.setColor(COLOR_MARK);
-
- }
-
- private final Canvas mCanvas;
- private final Path mPath;
- private final Matrix mSymbolMatrix;
-
- private float mScaleFactor;
-
- CanvasRasterer() {
- mCanvas = new Canvas();
- mSymbolMatrix = new Matrix();
- mPath = new Path();
- mPath.setFillType(Path.FillType.EVEN_ODD);
- mScaleFactor = 1;
- configurePaints();
-
- }
-
- private void drawTileCoordinate(String string, int offsetY) {
- mCanvas.drawText(string, 20, offsetY, PAINT_TILE_COORDINATES);
- }
-
- void drawNodes(List pointTextContainers) {
-
- for (int index = pointTextContainers.size() - 1; index >= 0; --index) {
- PointTextContainer pointTextContainer = pointTextContainers.get(index);
-
- if (pointTextContainer.paintBack != null) {
-
- mCanvas.drawText(pointTextContainer.text, pointTextContainer.x * mScaleFactor, pointTextContainer.y
- * mScaleFactor, pointTextContainer.paintBack);
- }
-
- mCanvas.drawText(pointTextContainer.text, pointTextContainer.x * mScaleFactor, pointTextContainer.y
- * mScaleFactor, pointTextContainer.paintFront);
- }
- }
-
- void drawSymbols(List symbolContainers) {
- for (int index = symbolContainers.size() - 1; index >= 0; --index) {
- SymbolContainer symbolContainer = symbolContainers.get(index);
-
- if (symbolContainer.alignCenter) {
- int pivotX = symbolContainer.symbol.getWidth() >> 1;
- int pivotY = symbolContainer.symbol.getHeight() >> 1;
- mSymbolMatrix.setRotate(symbolContainer.rotation, pivotX, pivotY);
- mSymbolMatrix.postTranslate(symbolContainer.x - pivotX, symbolContainer.y - pivotY);
- } else {
- mSymbolMatrix.setRotate(symbolContainer.rotation);
- mSymbolMatrix.postTranslate(symbolContainer.x, symbolContainer.y);
- }
- mSymbolMatrix.postTranslate(mScaleFactor, mScaleFactor);
-
- // symbolMatrix.postScale(zoomFactor, zoomFactor);
- mCanvas.drawBitmap(symbolContainer.symbol, mSymbolMatrix, PAINT_BITMAP_FILTER);
- }
- }
-
- void drawTileCoordinates(Tile tile, long time_load, long time_draw, long blub, long blah) {
-
- drawTileCoordinate(tile.tileX + " / " + tile.tileY + " / " + tile.zoomLevel + " " + mScaleFactor, 20);
-
- drawTileCoordinate("l:" + time_load, 40);
- drawTileCoordinate("d:" + time_draw, 60);
- drawTileCoordinate("+:" + blub, 80);
- drawTileCoordinate("-:" + blah, 100);
-
- }
-
- void drawTileFrame() {
- float size = (Tile.TILE_SIZE * mScaleFactor);
- float[] frame = new float[] { 0, 0, 0, size - 1, 0, size - 1, size - 1, size - 1, size - 1, size - 1, size - 1,
- 0 };
- mCanvas.drawLines(frame, PAINT_TILE_FRAME);
- }
-
- void drawWayNames(float[] coords, List wayTextContainers) {
-
- for (int index = wayTextContainers.size() - 1; index >= 0; --index) {
- WayTextContainer wayTextContainer = wayTextContainers.get(index);
- mPath.rewind();
-
- int first = wayTextContainer.first;
- int last = wayTextContainer.last;
-
- // int len = wayTextContainer.wayDataContainer.length[0];
- // int pos = wayTextContainer.wayDataContainer.position[0];
-
- // System.arraycopy(floats, pos, coords, 0, len);
-
- if (coords[first] < coords[last]) {
- mPath.moveTo(coords[first], coords[first + 1]);
-
- for (int i = first + 2; i <= last; i += 2) {
- mPath.lineTo(coords[i], coords[i + 1]);
- }
- } else {
- mPath.moveTo(coords[last], coords[last + 1]);
-
- for (int i = last - 2; i >= first; i -= 2) {
- mPath.lineTo(coords[i], coords[i + 1]);
- }
- }
- mCanvas.drawTextOnPath(wayTextContainer.text, mPath, 0, 3, wayTextContainer.paint);
-
- // if (wayTextContainer.match)
- // canvas.drawRect(wayTextContainer.x1,
- // wayTextContainer.top, wayTextContainer.x2,
- // wayTextContainer.bot, PAINT_MARK);
- }
- }
-
- void drawWays(float[] coords, LayerContainer[] drawWays) {
- int levels = drawWays[0].mLevelActive.length;
-
- for (LayerContainer layerContainer : drawWays) {
- if (!layerContainer.mActive)
- continue;
-
- for (int level = 0; level < levels; level++) {
-
- if (!layerContainer.mLevelActive[level])
- continue;
-
- // mPath.rewind();
-
- LevelContainer levelContainer = layerContainer.mLevels[level];
-
- for (int way = levelContainer.mShapeContainers.size() - 1; way >= 0; way--) {
- mPath.rewind();
- // switch (shapePaintContainer.shapeContainer.getShapeType()) {
- //
- // case WAY:
- WayDataContainer wayDataContainer = (WayDataContainer) levelContainer.mShapeContainers.get(way);
- // (WayDataContainer) shapePaintContainer.shapeContainer;
-
- // if (wayDataContainer.closed) {
- for (int i = 0, n = wayDataContainer.length.length; i < n; i++) {
-
- int len = wayDataContainer.length[i];
- int pos = wayDataContainer.position[i];
- if (len > 2) {
- mPath.moveTo(coords[pos], coords[pos + 1]);
-
- for (int j = pos + 2; j < len + pos; j += 2)
- mPath.lineTo(coords[j], coords[j + 1]);
- }
- }
- mCanvas.drawPath(mPath, levelContainer.mPaint[0]);
- if (levelContainer.mPaint[1] != null)
- mCanvas.drawPath(mPath, levelContainer.mPaint[1]);
-
- // }else {
- // for (int i = 0, n = wayDataContainer.length.length; i < n; i++) {
- // // levelContainer.mPaint[0].setStrokeJoin(Join.ROUND);
- //
- // int len = wayDataContainer.length[i];
- // int pos = wayDataContainer.position[i];
- // if (len > 2) {
- // mCanvas.drawPoints(coords, pos, len, levelContainer.mPaint[0]);
- // if (levelContainer.mPaint[1] != null)
- // mCanvas.drawPoints(coords, pos, len, levelContainer.mPaint[1]);
- // }
- //
- // }
- // }
- // break;
-
- // case CIRCLE:
- // CircleContainer circleContainer =
- // (CircleContainer) shapePaintContainer.shapeContainer;
- //
- // mPath.rewind();
- //
- // mPath.addCircle(circleContainer.mX, circleContainer.mY,
- // circleContainer.mRadius, Path.Direction.CCW);
- //
- // mCanvas.drawPath(mPath, shapePaintContainer.paint);
- // break;
- // }
-
- }
- }
- }
- }
-
- void fill(int color) {
- mCanvas.drawColor(color);
- }
-
- void setCanvasBitmap(Bitmap bitmap, float scale) {
- mCanvas.setBitmap(bitmap);
- // add some extra pixels to avoid < 1px blank edges while scaling
- mCanvas.clipRect(0, 0, Tile.TILE_SIZE * scale + 2, Tile.TILE_SIZE * scale + 2);
- mScaleFactor = scale;
- }
-}
diff --git a/VectorTileMap/src/org/mapsforge/android/swrenderer/DependencyCache.java b/VectorTileMap/src/org/mapsforge/android/swrenderer/DependencyCache.java
deleted file mode 100644
index 9ba3bae..0000000
--- a/VectorTileMap/src/org/mapsforge/android/swrenderer/DependencyCache.java
+++ /dev/null
@@ -1,985 +0,0 @@
-/*
- * Copyright 2010, 2011, 2012 mapsforge.org
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see .
- */
-package org.mapsforge.android.swrenderer;
-
-import java.util.ArrayList;
-import java.util.Hashtable;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-import org.mapsforge.core.Tile;
-
-import android.graphics.Bitmap;
-import android.graphics.Paint;
-import android.graphics.Rect;
-
-/**
- * This class process the methods for the Dependency Cache. It's connected with the LabelPlacement class. The main goal
- * is, to remove double labels and symbols that are already rendered, from the actual tile. Labels and symbols that,
- * would be rendered on an already drawn Tile, will be deleted too.
- */
-class DependencyCache {
- /**
- * The class holds the data for a symbol with dependencies on other tiles.
- *
- * @param
- * only two types are reasonable. The DependencySymbol or DependencyText class.
- */
- private static class Dependency {
- ImmutablePoint point;
- final Type value;
-
- Dependency(Type value, ImmutablePoint point) {
- this.value = value;
- this.point = point;
- }
- }
-
- /**
- * This class holds all the information off the possible dependencies on a tile.
- */
- private static class DependencyOnTile {
- boolean drawn;
- List> labels;
- List> symbols;
-
- /**
- * Initialize label, symbol and drawn.
- */
- DependencyOnTile() {
- this.labels = null;
- this.symbols = null;
- this.drawn = false;
- }
-
- /**
- * @param toAdd
- * a dependency Symbol
- */
- void addSymbol(Dependency toAdd) {
- if (this.symbols == null) {
- this.symbols = new ArrayList>();
- }
- this.symbols.add(toAdd);
- }
-
- /**
- * @param toAdd
- * a Dependency Text
- */
- void addText(Dependency