Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » GEF » GEF4 version 0.2.0 problem with self loop(problem with self loops while dragging a node)
GEF4 version 0.2.0 problem with self loop [message #1753078] Wed, 01 February 2017 16:38 Go to next message
Arthur Ryd is currently offline Arthur RydFriend
Messages: 2
Registered: January 2017
Junior Member
Hi,

I have a problem with self loops in my graph (picture attached): Whenever I drag a StatePart (the circles labelled S1-S3 in the picture) and there are self loops of transitions between them, the self loop kind of explodes and I haven't been able to track down the problem so far.

This problem only occurs with self loops as you can see on the picture, transitions between two states are moved correctly.

EditorModule Transition bindings
	@SuppressWarnings("serial")
	protected void bindTransitionContentPartPolicies(MapBinder<AdapterKey<?>, Object> p_adapterMapBinder) {
		// Make part focus & selectable
		p_adapterMapBinder.addBinding(
				AdapterKey.get(FXClickDragTool.CLICK_TOOL_POLICY_KEY)).to(
						FXFocusAndSelectOnClickPolicy.class);
		
		// provide geometry for selection handles
		p_adapterMapBinder.addBinding(AdapterKey.get(new TypeToken<Provider<? extends IGeometry>>() {}, FXDefaultHandlePartFactory.SELECTION_HANDLES_GEOMETRY_PROVIDER)).to(VisualBoundsGeometryProvider.class);
		
		// Add policy chain for moving
		p_adapterMapBinder.addBinding(AdapterKey.get(FXResizeRelocatePolicy.class)).to(FXResizeRelocatePolicy.class);
		
		// Add policy for hovering
		p_adapterMapBinder.addBinding(AdapterKey.get(FXHoverTool.TOOL_POLICY_KEY)).to(FXHoverOnHoverPolicy.class);
		
		// resize & transform
		p_adapterMapBinder.addBinding(AdapterKey.get(FXBendPolicy.class)).to(TransitionBendPolicy.class);
		
		// Add policy for addition of events
		p_adapterMapBinder.addBinding(AdapterKey.get(AddEventLabelPolicy.class)).to(AddEventLabelPolicy.class);
	}


EditorModule StateContentPart bindings
	protected void bindStateContentPartPolicies(MapBinder<AdapterKey<?>, Object> p_adapterMapBinder) {
		// Make part focus & selectable
		p_adapterMapBinder.addBinding(
				AdapterKey.get(FXClickDragTool.CLICK_TOOL_POLICY_KEY)).to(
						FXFocusAndSelectOnClickPolicy.class);
		// Add policy chain for moving
		p_adapterMapBinder.addBinding(AdapterKey.get(FXClickDragTool.DRAG_TOOL_POLICY_KEY)).to(FXRelocateOnDragPolicy.class);
		p_adapterMapBinder.addBinding(AdapterKey.get(FXResizeRelocatePolicy.class)).to(FXResizeRelocatePolicy.class);
		p_adapterMapBinder.addBinding(AdapterKey.get(FXTransformPolicy.class)).to(StateTransformPolicy.class);

		// Add policy for hovering
		p_adapterMapBinder.addBinding(AdapterKey.get(FXHoverTool.TOOL_POLICY_KEY)).to(FXHoverOnHoverPolicy.class);
		// Add policy for addition of prohibition events
		p_adapterMapBinder.addBinding(AdapterKey.get(AddEventLabelPolicy.class)).to(AddEventLabelPolicy.class);
	}


TransitionBendPolicy
public class TransitionBendPolicy extends FXBendPolicy {

	@Override
	public IUndoableOperation commit() {
		return getHost().chainModelChanges(super.commit());
	}

	@Override
	public TransitionContentPart getHost() {
		return (TransitionContentPart) super.getHost();
	}

}


TransitionContentPart
public class TransitionContentPart extends AbstractConnectionContentPart<FXConnection> implements TransitionView.IChangeListener, IHoverable {
	
	public static class UpdateWayPointOperation extends AbstractOperation {
		
		private final TransitionView m_model;
		private final Collection<de.rwthaachen.embedded.syntacs.model.utils.Point> m_old;
		private final Collection<de.rwthaachen.embedded.syntacs.model.utils.Point> m_new;

		public UpdateWayPointOperation(TransitionView p_model, Collection<Point> p_old, Collection<Point> p_new) {
			super("Update transition way points");
			m_model = p_model;
			m_old = GeometryUtils.GEFPointToModelPoint(p_old);
			m_new = GeometryUtils.GEFPointToModelPoint(p_new);
		}

		@Override
		public IStatus execute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
			m_model.setWayPoints(m_new);
			return Status.OK_STATUS;
		}

		@Override
		public IStatus redo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
			return execute(monitor, info);
		}

		@Override
		public IStatus undo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
			m_model.setWayPoints(m_old);
			return Status.OK_STATUS;
		}
	}

	@SuppressWarnings("serial")
	@Override
	protected void attachToAnchorageVisual(IVisualPart<Node, ? extends Node> p_anchorage, String p_role) {
		IFXAnchor anchor = p_anchorage.getAdapter(new TypeToken<Provider<? extends IFXAnchor>>() {}).get();
		if("START".equals(p_role)) {
			getVisual().setStartAnchor(anchor);
		} else if("END".equals(p_role)) {
			getVisual().setEndAnchor(anchor);
		} else {
			throw new IllegalArgumentException("Cannot attach visual anchor with role \"" + p_role + "\"");
		}
	}

	@Override
	public void attachToContentAnchorage(Object p_contentAnchorage, String p_role) {
		if(!(p_contentAnchorage instanceof StateView)) {
			throw new IllegalArgumentException("Inappropriate content anchorage: wrong type");
		}
		
		final StateView state = (StateView) p_contentAnchorage;
		final TransitionView trans = getContent();
		try {
			if("START".equals(p_role)) {
				trans.setFrom(state);
			} else if("END".equals(p_role)) {
				trans.setTo(state);
			}
		} catch (ConsistencyException e) {
			throw new IllegalArgumentException("Inappropriate content anchorage: cannot be attached here");
		}
		if(trans.isUsed() && 0 == trans.getWayPointCount()) {
			final TransitionView other = trans.getTo().getTransition(trans.getFrom());
			if(null != other) {
				trans.addNormalizedWayPoint(new de.rwthaachen.embedded.syntacs.model.utils.Point(0, 20));
				if(0 == other.getWayPointCount()) {
					other.addNormalizedWayPoint(new de.rwthaachen.embedded.syntacs.model.utils.Point(0, 20));
				}
			}
		}
	}

	public IUndoableOperation chainModelChanges(IUndoableOperation p_visualOperation) {
		if(null == p_visualOperation) {
			return null;
		}
		
		// Update waypoints in model
		final Collection<Point> oldWayPoints = GeometryUtils.ModelPointToGEFPoint(getContent().getWayPoints());
		final List<Point> newWayPoints = getVisual().getWayPoints();
		final UpdateWayPointOperation wpOp = new UpdateWayPointOperation(getContent(), oldWayPoints, newWayPoints);
		
		// Update anchorages in model
		StateView from = getAnchorageContent(getVisual().getStartAnchor());
		StateView to = getAnchorageContent(getVisual().getEndAnchor());
		
		final ContentPolicy<Node> contentPolicy = getAdapter(ContentPolicy.class);
		
		// Detach old anchorages if there was a change
		contentPolicy.init();
		SetMultimap<IVisualPart<Node, ? extends Node>, String> anchorages = HashMultimap.create(getAnchorages());
		for(IVisualPart<Node, ? extends Node> anchorage: anchorages.keySet()) {
			if(anchorage instanceof IContentPart) {
				for(String role: anchorages.get(anchorage)) {
					final Object contentAnchorage = ((IContentPart<Node, ? extends Node>) anchorage).getContent();
					if("START".equals(role)) {
						if(from != contentAnchorage) {
							// changed, remove the old one
							contentPolicy.detachFromContentAnchorage(contentAnchorage, role);
						} else {
							// not changed, keep the old one
							from = null;
						}
					} else if("END".equals(role)) {
						if(to != contentAnchorage) {
							// changed, remove the old one
							contentPolicy.detachFromContentAnchorage(contentAnchorage, role);
						} else {
							// not changed, keep the old one
							to = null;
						}
					}
				}
			}
		}
		final IUndoableOperation detachOp = contentPolicy.commit();
		
		// Attach current anchorages
		contentPolicy.init();
		if(null != from) {
			contentPolicy.attachToContentAnchorage(from, "START");
		}
		if(null != to) {
			contentPolicy.attachToContentAnchorage(to, "END");
		}
		final IUndoableOperation attachOp = contentPolicy.commit();
		
		// Build resulting operation
		return new ForwardUndoCompositeOperation(p_visualOperation.getLabel()) {
			{
				add(p_visualOperation);
				add(wpOp);
				if(detachOp != null || attachOp != null) {
					add(new ReverseUndoCompositeOperation("Update anchorages") {
						{
							if(null != detachOp) {
								add(detachOp);
							}
							if(null != attachOp) {
								add(attachOp);
							}
						}
					}.unwrap());
				}
			}
		}.unwrap();
	}

	@Override
	protected FXConnection createVisual() {
		final FXConnection visual = new FXConnection();
		visual.setRouter(new FXPolyBezierConnectionRouter());
		visual.setEndDecoration(new ArrowHead());
		visual.getCurveNode().setStrokeWidth(2.);
		
		final DropShadow shadow = new DropShadow();
		visual.setEffect(shadow);
		return visual;
	}

	@Override
	protected void detachFromAnchorageVisual(IVisualPart<Node, ? extends Node> p_anchorage, String p_role) {
		// Keep position if anchorage is gone
		if("START".equals(p_role)) {
			getVisual().setStartPoint(getVisual().getStartPoint());
		} else if("END".equals(p_role)) {
			getVisual().setEndPoint(getVisual().getEndPoint());
		} else {
			throw new IllegalArgumentException("Cannot detach visual anchor with role \"" + p_role + "\"");
		}
	}

	@Override
	public void detachFromContentAnchorage(Object p_contentAnchorage, String p_role) {
		if(null != getContent()) {
			try {
				if("START".equals(p_role)) {
					getContent().setFrom(null);
				} else if("END".equals(p_role)) {
					getContent().setTo(null);
				} else {
					throw new IllegalArgumentException("Cannot detach anchor with role \"" + p_role + "\"");
				}
			} catch(ConsistencyException e) {
				// ignored
			}
		}
	}

	@Override
	protected void doActivate() {
		super.doActivate();
		
		getContent().addChangeListener(this);
	}
	
	@Override
	protected void doDeactivate() {
		getContent().removeChangeListener(this);
		
		super.doDeactivate();
	}
	
	@Override
	protected void doRefreshVisual(FXConnection p_visual) {
		// Convert SynTACS UI Model points to GEF points
		final Collection<de.rwthaachen.embedded.syntacs.model.utils.Point> wayPoints = getContent().getWayPoints();
		System.out.print("ModelPoints" + getContent().getWayPoints() + "\n");
		final List<Point> wps = new ArrayList<Point>(wayPoints.size());
		for(de.rwthaachen.embedded.syntacs.model.utils.Point p: wayPoints) {
			wps.add(new Point(p.x, p.y));
			System.out.print("WPS" + wps + "\n");
		}
		
		// Apply way points
		//p_visual.setWayPoints(wps);
		
		
		// TODO HOTFIX FOR BUG IN FXConnection.setWayPoints:
			int waySize = p_visual.getWayAnchorsSize();
			int i = 0;
			for (; i < waySize && i < wps.size(); i++) {
				p_visual.setWayPoint(i, wps.get(i));
			}
			for (; i < wps.size(); i++) {
				p_visual.addWayPoint(i, wps.get(i));
			}
			for (; i < waySize; i++) {
				p_visual.removeWayPoint(wps.size());
			}
		// END HOTFIX
	}

	protected StateView getAnchorageContent(IFXAnchor p_anchor) {
		final Node node = p_anchor.getAnchorage();
		if(node != getVisual()) {
			final IVisualPart<Node, ? extends Node> part = getViewer().getVisualPartMap().get(node);
			if(part instanceof IContentPart) {
				final Object content = ((IContentPart<Node, ? extends Node>) part).getContent();
				if(content instanceof StateView) {
					return (StateView) content;
				}
			}
		}
		return null;
	}

	@Override
	public TransitionView getContent() {
		return (TransitionView) super.getContent();
	}

	@Override
	public SetMultimap<Object, String> getContentAnchorages() {
		final SetMultimap<Object, String> anchorages = HashMultimap.create();
		final TransitionView data = getContent();
		
		if(null != data) {
			if(null != data.getFrom()) {
				anchorages.put(data.getFrom(), "START");
			}
			if(null != data.getTo()) {
				anchorages.put(data.getTo(), "END");
			}
		}
		
		return anchorages;
	}

	@Override
	public void labelAdded(TransitionView p_trans, Label p_label) {
		// ignored
	}

	@Override
	public void labelRemoved(TransitionView p_trans, Label p_label) {
		// ignored
	}

	@Override
	public void layoutChanged(TransitionView p_trans) {
		final ContentBehavior<Node> contentBehavior = getAdapter(ContentBehavior.class);
		if(null != contentBehavior) {
			contentBehavior.synchronizeContentAnchorages(getContentAnchorages());
		}
		refreshVisual();
	}
	
	@Override
	public void transitionRemoved(TransitionView p_trans) {
		// ignored
	}

	@Override
	public void updateHoverState(int p_state) {
		Color color = null;
		switch(p_state) {
		case HOVER_NONE:
			color = Color.BLACK;
			break;
		case HOVER_ELEMENT:
			color = Color.DARKBLUE;
			break;
		case HOVER_LABEL:
			color = Color.BROWN;
			break;
		}
		
		if(null != color) {
			getVisual().getCurveNode().setStroke(color);
			((ArrowHead) getVisual().getEndDecoration()).setColor(color);
		}
	}

}


Any help would be very much appreciated. I have taken over this part the GEF part of the project from a previous colleague so progress has been slow as I also need to find my way around his code and am quite new to GEF.

Best regards
Re: GEF4 version 0.2.0 problem with self loop [message #1753177 is a reply to message #1753078] Thu, 02 February 2017 13:24 Go to previous messageGo to next message
Matthias Wienand is currently offline Matthias WienandFriend
Messages: 163
Registered: March 2015
Senior Member
Hi Arthur,

if I am not mistaken, all states changed position in the attached screenshots. However, I assume the erroneous behavior can be observed when dragging a single state, and the self-loop at that state explodes. Is that correct?

Per default, processing of user input is divided into multiple phases in GEF: 1) Tools use listeners to observe user input (e.g. FXClickDragTool); 2) Tools forward input events to the registered applicable interaction policies for processing (e.g. FXRelocateOnDragPolicy); 3) Interaction policies make use of transactional policies to perform their actions (e.g. StateTransformPolicy). 4) The operations are executed on the domain in the context of a single transaction (i.e. they can be undone/redone in a single step).

In order to identify the underlying problem, you need to investigate what happens when the operations are executed, because one of the operations is probably causing the transition to explode. Other than that, you should verify that the way points are correct. A good place for a breakpoint would be the FXRelocateOnDragPolicy#release() method, as well as the Connection#refresh() method.

Best regards,
Matthias
Re: GEF4 version 0.2.0 problem with self loop [message #1753190 is a reply to message #1753177] Thu, 02 February 2017 15:31 Go to previous messageGo to next message
Arthur Ryd is currently offline Arthur RydFriend
Messages: 2
Registered: January 2017
Junior Member
Hi,

yes you are correct, I just wanted to show that dragging of states works per se, just not when they have self loops.

As for your sugggestions, I will try that out and hopefully be able to track the error from there on out.

Thank you for your reply

Best regards

Edit: I managed to track the error down: there was an error in the model, where start and end point were not changed in case of a self loop (only one was changed in case of self loops as from==to)

[Updated on: Fri, 03 February 2017 10:52]

Report message to a moderator

Re: GEF4 version 0.2.0 problem with self loop [message #1753299 is a reply to message #1753190] Fri, 03 February 2017 14:54 Go to previous message
Matthias Wienand is currently offline Matthias WienandFriend
Messages: 163
Registered: March 2015
Senior Member
Hi Arthur,

glad to hear that you could figure it out yourself. I was afraid that intensive debugging in the old Connection code would be needed once you find the problematic code Wink

Best regards,
Matthias
Previous Topic:Enable to create GEF_legacy logic plugin
Next Topic:How to apply css for gef geometry
Goto Forum:
  


Current Time: Sat Sep 22 16:30:09 GMT 2018

Powered by FUDForum. Page generated in 0.02520 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top