JavaFX - Endlos Scrollen im Aufbau, Problem mit dem Zoomen

takidoso

Erfahrenes Mitglied
Hallo un dHalli,
leider scheine ich irgendwie auf dem Schlauch zu stehen.
ich habe vor einen alten Risiko-Spiel-Prototypen von Swing auf JavaFX teiweise umzustellen.
Dabei möchte in der neuen Version den Versuch machen, die Karte Tubusring mäßig scrollen zu können, also endlos.
Da meine Länder aus Polygonen bestehen habe ich mir überlegt einen Mechanismus zu erschaffen, der um einen erkennt, ob die Kartenränder im Ausschnitt (quasi "Viewport") zu seen sind oder nicht. Wenn ja soll dann erkannt weden welche Polygone ofenbar noch zusätzlich in di eSicht kommen und entspechend von der eigentlich Karte geklont werden.
z.Z. bin ich dabei die Erkennung, ob Kartenränder in die Sicht geraten zu erkennen. Der Einfachheit halber habe ich es erst mal nur mit der X-Achse gebastelt für die Y-Achse würde wenn alles da klappt nachschieben.
Das funktioniert sogar mit der Ausnahme, wenn die Karte verkleinert oder Vergrößert wurde.

Das Problem liegt hier in dem Changelistener für die gruppe (root) ab Zeile 192. Ich weiß das hier offenbar falsch bezüglich des Einbringens des Zoomfaktors root.getScaleX(). Ich habe da einerseits gedacht und diverses ausprobiert, leider komme ich da nicht auf die richtige Lösung.

hier zunächst mal der Code des Prototypen soweit:
Java:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import javafx.application.Application;
import javafx.beans.property.SimpleSetProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

import org.yaml.snakeyaml.Yaml;



public class CMKRiskTest extends Application
{

	private List <NamedPolygon> m_sektors              = new ArrayList<NamedPolygon>();
	private Point2D             m_mostDistantPoint     = null;
	
	public void init() throws Exception
	{
		Map<String,String> namedParms = getParameters().getNamed();
		File               mapFile    = new File(namedParms.get("mapfile"));
		loadContentFromYaml(mapFile);
	}
	
	
	public void start(Stage stage) throws Exception
	{
		final Group root  = new Group();
		final Scene scene = new Scene(root, 640, 420);
		
		Rectangle r = new Rectangle(m_mostDistantPoint.getX(), m_mostDistantPoint.getY(), Color.AQUA);
		r.setStroke(Color.RED);
		root.getChildren().add(r);
		
		for (NamedPolygon sektor : m_sektors)
		{
			root.getChildren().add(sektor);
			
			sektor.setOnMouseEntered(new EventHandler<MouseEvent>()
			{
				@Override
				public void handle(MouseEvent mouseEvent)
				{
					NamedPolygon[] sektors = ((NamedPolygon) mouseEvent.getSource()).getReachables();
					for (NamedPolygon sektor : sektors)
					{
						sektor.setFill(Color.WHITESMOKE);
					}
				}
			});
			
			sektor.setOnMouseExited(new EventHandler<MouseEvent>()
			{
				@Override
				public void handle(MouseEvent mouseEvent)
				{
					NamedPolygon[] sektors = ((NamedPolygon) mouseEvent.getSource()).getReachables();
					for (NamedPolygon sektor : sektors)
					{
						sektor.setFill(sektor.getColor());
					}
				}
			});

		}// end of  for (m_sektors)
		
		stage.addEventHandler(ScrollEvent.SCROLL, new EventHandler<ScrollEvent>()
		{
			public void handle(ScrollEvent scrollEvent)
			{
				System.out.println(scrollEvent.toString());
				if (scrollEvent.isAltDown())
				{
					double factor = scrollEvent.getDeltaY() > 0 ?  1.01 : .99; 
					//root.setTranslateX(scrollEvent.getScreenX()*factor);
					//root.setTranslateY(scrollEvent.getScreenY()*factor);
					root.setScaleX(root.getScaleX()*factor);
					root.setScaleY(root.getScaleY()*factor);
				}
			}
		});
		

/*			
		stage.addEventHandler(ZoomEvent.ZOOM, new EventHandler<ZoomEvent>()
		{
			public void handle(ZoomEvent zoomEvent)
			{
				System.out.println(zoomEvent.toString());
				if (zoomEvent.isAltDown())
				{ 
					//root.setTranslateX(scrollEvent.getScreenX()*factor);
					//root.setTranslateY(scrollEvent.getScreenY()*factor);
					root.setScaleX(root.getScaleX()*zoomEvent.getZoomFactor());
					root.setScaleY(root.getScaleY()*zoomEvent.getZoomFactor());
				}
			}
		});
*/
		
		EventHandler<MouseEvent> mouseEvtHandler = new EventHandler<MouseEvent>()
		{
			private double previousX; 
			private double previousY;
			
			@Override
			public void handle(MouseEvent evt)
			{
				if (evt.getEventType() == MouseEvent.MOUSE_DRAGGED)
				{
					root.getScene().setCursor(Cursor.HAND);
					
					double deltaX = evt.getSceneX() - previousX;
					double deltaY = evt.getSceneY() - previousY;
					
					root.setTranslateX(root.getTranslateX()+deltaX);
					root.setTranslateY(root.getTranslateY()+deltaY);
					
					previousX = evt.getSceneX();
					previousY = evt.getSceneY();

				}
				else if (evt.getEventType() == MouseEvent.MOUSE_PRESSED)
				{
					root.getScene().setCursor(Cursor.MOVE);
					previousX = evt.getSceneX();
					previousY = evt.getSceneY();
				}
				else if (evt.getEventType() == MouseEvent.MOUSE_RELEASED)
				{
					root.getScene().setCursor(Cursor.DEFAULT);
					System.out.println("px="+previousX+", py="+previousY);
//					System.out.println("x ="+evt.getX()+", y ="+evt.getY());
					System.out.println("tx="+root.getTranslateX()+", ty="+root.getTranslateY());
					ObservableMap<Object, Object> om = root.getProperties();
					
					System.out.println(om.size());
					
					for (Map.Entry<Object,Object> entry : om.entrySet())
					{
						System.out.println(entry.getKey().toString()+"="+entry.getValue().toString());
					}
					
				}
				else
				{
					System.out.println("Mouse event not in use******");
				}
			}
		};
		
		// lets listen to translateX and translateY of our root group
		// and generate certain custom events if the edges of the map appearwith in our viewPort
		root.translateXProperty().addListener(new ChangeListener<Number>()
		{

			@Override
			public void changed(ObservableValue<? extends Number> observable, Number oldTransX, Number newTransX)
			{
				switch (Double.compare(newTransX.doubleValue(), 0.0))
				{
					case 0  :
						// no vertical edge of map can be seen
						break;
					case 1  :
						// vertical edge of map is supposed to be seen on the left
						System.out.println("Vertical edge is supposed to be seen ******");
						// check what polygons are to be cloned and placed within a new group
						break;
					case -1 :
						// vertical edge of map could be seen on the right
						System.out.println("("+newTransX.doubleValue()+"-"+scene.getWidth()+"/"+root.getScaleX()+")*(-1) > +"+m_mostDistantPoint.getX() );
						System.out.println((newTransX.doubleValue()-scene.getWidth()/root.getScaleX())*(-1)+">"+m_mostDistantPoint.getX());
						if ((newTransX.doubleValue()-scene.getWidth()/root.getScaleX())*(-1) > m_mostDistantPoint.getX())
						{
							System.out.println("Vertical edge is supposed to be seen ******");
							// vertical edge is supposed to be seen so ... 
							// check what polygons are to be cloned and placed within a new group
							
						}
						else
						{
							System.out.println("Vertical edge cannot be seen so polygon-clones can be deleted");
						}
						break;
				}

			}
			
		});
		
		
		
		
		stage.addEventHandler(MouseEvent.MOUSE_PRESSED, mouseEvtHandler);
		stage.addEventHandler(MouseEvent.MOUSE_DRAGGED, mouseEvtHandler);
		stage.addEventHandler(MouseEvent.MOUSE_RELEASED, mouseEvtHandler);
		
		
		stage.setTitle("Polygon Sample");
		stage.setScene(scene);
		stage.show();
	}
	
	
	
    @SuppressWarnings("unchecked")
	public void loadContentFromYaml(File sectorMapFile) throws FileNotFoundException, MalformedURLException
    {
    	Yaml yaml = new Yaml();
    	
    	Iterable<?> l = yaml.loadAll(new FileReader(sectorMapFile));
    	Map<String, Object> mainMap = (Map<String, Object>)l.iterator().next();
    	unmarshalContent(mainMap);
    	figureOutNeighbourhoods();
    	
        //setDirty(false);
        calcMostDistantPoint();
    }
	
    
    /**
     * deserialisiert den Inhalt der vorhaer in eine Map gelesen wurde
     * Diese Routine ist, ähnlich wie ihr Gegenstück marshalContent, dazu gedacht
     * abgeleiteten Klassen die Möglichkeit zu geben durch erweiterndes Überschreiben
     * weitere Informationen einzubringen
     * @param mainMap
     * @throws MalformedURLException
     */
    @SuppressWarnings("unchecked")
	protected void unmarshalContent(Map<String, Object> mainMap) throws MalformedURLException
    {
    	/* TODO
    	String urlStr = (String)mainMap.get("artworkimage");
    	if (urlStr!=null && urlStr.length()>0)
    	{
    		loadPlanBild(new URL(urlStr));
    	}
    	*/
    	Map<String, Map<String, Object>> sectorMap = (Map<String, Map<String, Object>>)mainMap.get("countries");
    	for (String name : sectorMap.keySet())
    	{
    		NamedPolygon sec = new NamedPolygon(name, sectorMap);
    		m_sektors.add(sec);
    		//sec.addPropertyChangeListener(this);
    	}
    }
    
    protected void calcMostDistantPoint()
    {
    	double x = 0;
    	double y = 0;
    	
    	for (NamedPolygon sektor : m_sektors)
    	{
    		ObservableList<Double> points = sektor.getPoints();
    		
    		
    		for (int i=0; i<points.size(); i++)
    		{
    			x = Math.max(points.get(i), x);
    			y = Math.max(points.get(++i), y);
    		}
    	}
    	
    	m_mostDistantPoint = new Point2D(x,y);
    }
    
	
    public void figureOutNeighbourhoods()
    {
    	for (int i=0; i<m_sektors.size(); i++)
		{
    		int k = 0;
    		int l = 0;
			NamedPolygon poly1 = m_sektors.get(i);
			
			for (int j=0; j<m_sektors.size(); j++)
			{
				if (i!=j)
				{		
					NamedPolygon poly2 = m_sektors.get(j);
					System.out.println("Sektor1="+poly1.getName()+"/Sektor2="+poly2.getName());
					if (poly1.intersects(poly2))
					{
						k++;
						poly1.addReachable(poly2);
					}
					else
					{
						l++;
					}
				}
			}
			//System.out.println(k+"+"+l+"="+(k+l)+"?"+m_sektors.size());
		}
    }
    
    
	public static void main(String... args)
	{
		launch(args);
	}
	
	
	
	public class NamedPolygon extends Polygon
	{
		final static public String _NAME    = "name";
		final static public String _POLYGON = "polygon";
		final static public String _COLOR   = "color";
		
		private SimpleStringProperty            m_name       = new SimpleStringProperty();
		
		private SimpleSetProperty<NamedPolygon> m_reachables = new SimpleSetProperty<>(FXCollections.observableSet(new HashSet<NamedPolygon>()));
		
		private Color                           m_color      = Color.WHITESMOKE;
		
		
		public NamedPolygon(String name, Map<String, Map<String, Object>> sectorMap)
		{
			m_name.set(name);
			retrieve (sectorMap);
		}
		
		public Color getColor()
		{
			return m_color;
		}
		
		public String getName()
		{
			return m_name.get();
		}
		
		public Map<String,Object> retrieve(Map<String, Map<String,Object>> sectorMap)
		{
			Map<String,Object> propsMap = sectorMap.get( m_name.get());
			
			@SuppressWarnings("unchecked")
			List<Double> pointCoord = (List<Double>) propsMap.get(_POLYGON);
			Long x;
			
			x = ((Number) propsMap.get(_COLOR)).longValue();
			if (x!=null)
			{
				java.awt.Color colorAWT;
				colorAWT = new java.awt.Color(x.intValue());
				m_color  = Color.rgb(colorAWT.getRed(), colorAWT.getGreen(), colorAWT.getBlue(), 0.7);
			}
			
			getPoints().addAll(pointCoord);
			setFill(m_color);
			setStrokeWidth(1);
			setStroke(Color.BLACK);
			

			return propsMap;
		}
		
		
		public void addReachable(NamedPolygon p)
		{
			m_reachables.add(p);
		}
		
		public NamedPolygon [] getReachables()
		{
			return m_reachables.toArray(new NamedPolygon[m_reachables.size()]);
		}
		
		
		/**
		 * Returns true if this polygon intersects the specified polygon.
		 *
		 * @param otherPoly  the polygon to test for intersection.
		 * @return true if the two polygons intersect, false otherwise.
		 *
		 */
		public boolean intersects(Polygon otherPoly)
		{
			int thisPointAmount  = getPoints().size();
			int otherPointAmount = otherPoly.getPoints().size();
			
			double thisMaxX = Double.NEGATIVE_INFINITY;
			double thisMaxY = Double.NEGATIVE_INFINITY;
			double thisMinX = Double.POSITIVE_INFINITY;
			double thisMinY = Double.POSITIVE_INFINITY;
			double otherMaxX = Double.NEGATIVE_INFINITY;
			double otherMaxY = Double.NEGATIVE_INFINITY;
			double otherMinX = Double.POSITIVE_INFINITY;
			double otherMinY = Double.POSITIVE_INFINITY;
			for (int i = 0; i < thisPointAmount; i++)
			{
				double x = getPoints().get(i);
				double y = getPoints().get(++i);
				
				thisMaxX = Math.max(x,thisMaxX);
				thisMinX = Math.min(x,thisMinX);
				
				thisMaxY = Math.max(y,thisMaxY);
				thisMinY = Math.min(y,thisMinY);
			}
			
			for (int i = 0; i < otherPointAmount; i++)
			{
				double x = otherPoly.getPoints().get(i);
				double y = otherPoly.getPoints().get(++i);
				
				otherMaxX = Math.max(x,otherMaxX);
				otherMinX = Math.min(x,otherMinX);
				
				otherMaxY = Math.max(y,otherMaxY);
				otherMinY = Math.min(y,otherMinY);
			}
			System.out.print("thisMaxX="+thisMaxX+", ");
			System.out.print("thisMaxY="+thisMaxY+", ");
			System.out.print("thisMinX="+thisMinX+", ");
			System.out.print("thisMinY="+thisMinY+"\n");
			System.out.print("otherMaxX="+otherMaxX+", ");
			System.out.print("otherMaxY="+otherMaxY+", ");
			System.out.print("otherMinX="+otherMinX+", ");
			System.out.print("otherMinY="+otherMinY+"\n");
			
			// Overestimate boundaries
			thisMaxX++;
			thisMaxY++;
			thisMinX--;
			thisMinY--;
			otherMaxX++;
			otherMaxY++;
			otherMinX--;
			otherMinY--;
			// Polys are beside each other
			if (thisMaxX < otherMinX || otherMaxX < thisMinX)
			{
				return false;
			}
			// Polys above/below each other
			if (thisMaxY < otherMinY || otherMaxY < thisMinY)
			{
				return false;
			}
			
			
			for (int thisLin = 0; thisLin < thisPointAmount; thisLin++)
			{
				Point2D  thisLinePoint1 = new Point2D(getPoints().get(thisLin),
						                              getPoints().get(++thisLin));
				Point2D  thisLinePoint2 = new Point2D(getPoints().get((thisLin+1) % thisPointAmount), 
						                              getPoints().get((thisLin+2) % thisPointAmount));
				
				for (int otherLin = 0; otherLin < otherPointAmount; otherLin++)
				{
					Point2D  otherLinePoint1 = new Point2D(otherPoly.getPoints().get(otherLin), 
							                               otherPoly.getPoints().get(++otherLin));
					Point2D  otherLinePoint2 = new Point2D(otherPoly.getPoints().get((otherLin+1) % otherPointAmount), 
							                               otherPoly.getPoints().get((otherLin+2) % otherPointAmount));
					
					if (linesIntersect(thisLinePoint1, thisLinePoint2, otherLinePoint1, otherLinePoint2))
					{
						return true;
					}
				}
			}
			return false;

		}

		/**
		 * Tests whether two line segments intersect.
		 *
		 * @param p1  the first point of the first line segment.
		 * @param p2  the second point of the first line segment.
		 * @param p3  the first point of the second line segment.
		 * @param p4  the second point of the second line segment.
		 * @return true if the two segments intersect.
		 */
		public final boolean linesIntersect(Point2D p1, Point2D p2, Point2D p3, Point2D p4)
		{
			System.out.println("LineA="+p1.getX() +","+p1.getY()+"/"+p2.getX() +","+p2.getY());
			System.out.println("LineB="+p3.getX() +","+p3.getY()+"/"+p4.getX() +","+p4.getY());
			
			// From Graphics Gems II
			double r, s, rsDenom, rNumer;
			double dp1yp3y, dp4xp3x, dp1xp3x, dp4yp3y, dp2xp1x, dp2yp1y;
			dp1yp3y = p1.getY() - p3.getY();
			dp4xp3x = p4.getX() - p3.getX();
			dp1xp3x = p1.getX() - p3.getX();
			dp4yp3y = p4.getY() - p3.getY();
			dp2xp1x = p2.getX() - p1.getX();
			dp2yp1y = p2.getY() - p1.getY();
			rsDenom = dp2xp1x * dp4yp3y - dp2yp1y * dp4xp3x;
			rNumer = ( dp1yp3y * dp4xp3x - dp1xp3x * dp4yp3y );
			// if rsDenom is zero, lines are parallel
			// if rNumer is also zero, lines are collinear
			if (rsDenom == 0)
				if (rNumer != 0)
					return false;
				else
				{
					// Check for overlap
					double hix1, hiy1, lox1, loy1;
					if (p1.getX() > p2.getX())
					{
						hix1 = p1.getX();
						lox1 = p2.getX();
					}
					else
					{
						hix1 = p2.getX();
						lox1 = p1.getX();
					}
					if (p1.getY() > p2.getY())
					{
						hiy1 = p1.getY();
						loy1 = p2.getY();
					}
					else
					{
						hiy1 = p2.getY();
						loy1 = p1.getY();
					}
					// Check intervals
					return !( ( ( lox1 > p3.getX() && lox1 > p4.getX() ) || ( hix1 < p3.getX() && hix1 < p4.getX() ) ) || ( ( loy1 > p3.getY() && loy1 > p4.getY() ) || ( hiy1 < p3.getY() && hiy1 < p4.getY() ) ) );
				}
			r = rNumer / rsDenom;
			s = ( dp1yp3y * dp2xp1x - dp1xp3x * dp2yp1y ) / rsDenom;
			return !( r < 0 || r > 1 || s < 0 || s > 1 );
		}
	}
	
}

Um das bisherige laufen zu lassen benötigt man Java 7 und Java FX 2.x
Außerdem benötigt man Snakeyaml (die bei mir verwendete jar datei füge ich hinzu) und eine Kartedatei, di eaich ebenfalls hier dannhänge.
Kan mir jemand auf die Sprünge helfen, irgendwie blicke ich es mathematisch wohl nicht so ganz wo der Scalefaktor tatsächlich in die "Formel" in Zeile 196 hin muss :-/, damit es auch bei Karten Vergrößerungen bzw. Verkleinerung klappt.
-------------------
leider ist die snakeyaml.jar zu groß und kann dahe nicht hochgeladen werden.
ein link zum runterladen ist dieser:
http://code.google.com/p/snakeyaml/
 

Anhänge

  • gaga.txt
    31,3 KB · Aufrufe: 12
Zuletzt bearbeitet: