/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.scenegraph.g2d.nodes; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import org.simantics.scenegraph.ILookupService; import org.simantics.scenegraph.g2d.IG2DNode; /** * A node that delegates {@link #render(Graphics2D)} and * {@link #getBoundsInLocal()} methods to a delegate {@link IG2DNode} it * contains. * *
* CAUTION: this node must be used with extreme care! It can be
* used to generate cyclic scene graphs which may cause rendering to crash due
* to infinite recursion and in any case rendering will not work as intended.
* E.g. a scene graph could have a {@link NavigationNode} under its root node
* and under which a LinkNode
could link back to the navigation
* node, which would cause everything to be rendered twice and with double
* transformations. As a safety measure against cyclic cases, this node contains
* state that prevents it from being invoked recursively.
*
*
* CAVEAT 1: Nodes with internal state that is updated during rendering. * Such nodes should not be used with this. * *
* CAVEAT 2: Only intended for local rendering. Will not work with remote * rendering. For remote support, use {@link ILookupService} and * {@link LinkNode} instead. * * @author Tuukka Lehtonen * * @see LinkNode * @see ILookupService */ public class LocalDelegateNode extends StateMaskNode { private static final long serialVersionUID = -7465071303188585400L; /** * The delegate node. */ IG2DNode delegate; /** * @param delegate */ public void setDelegate(IG2DNode delegate) { this.delegate = delegate; } public IG2DNode getDelegate() { return delegate; } @Override public void render(Graphics2D g2d) { // Safety against cyclic cases. if (hasFlags(IN_RENDER)) return; if (delegate == null) return; setFlags(IN_RENDER); AffineTransform oldTransform = null; if (transform != null && !transform.isIdentity()) { g2d.transform(transform); oldTransform = g2d.getTransform(); } try { delegate.render(g2d); } finally { if (oldTransform != null) g2d.setTransform(oldTransform); clearFlags(IN_RENDER); } } @Override public Rectangle2D getBoundsInLocal() { // Safety against cyclic cases. if (hasFlags(IN_GET_BOUNDS)) return new Rectangle2D.Double(); if (delegate == null) return new Rectangle2D.Double(); setFlags(IN_GET_BOUNDS); try { Rectangle2D bounds = delegate.getBoundsInLocal(); if (transform != null && !transform.isIdentity()) bounds = transform.createTransformedShape(bounds).getBounds2D(); return bounds; } finally { clearFlags(IN_GET_BOUNDS); } } }