Skip to main content

Node highlighting on mouse enter/exit.

1 reply [Last post]
william13163
Offline
Joined: 2008-03-19
Points: 0

Greetings,

Did I find a "pick" bug?

I am trying to draw a rectangle around a SGShape when the mouse enters. I call it highlighting. I have this sort-of working based on a modification of the Focus demo. When the mouse enters the circle, a SGLeaf node, is made visible which draws a rectangle around it. This works, however, the mouse exit event is now based on the rectangle not the circle shape anymore. So the exit event occurs when the mouse leaves the visible rectangle.
This picture shows the app with the circles drawn. "B" is where I expect an exit event to already have occurred.
http://william.quartz.googlepages.com/temp_Screenshot.png

I went through the scene source code and I can see where some sort of "accumulation" of the bounding areas are happening but I can also see where the node type is checked and if it is a rectangle that bound is used.

The code is pretty simple as shown below
SGShape _circle2 = createCircle(0.0f, 0.0f, 50.0f);
_circle2.setFillPaint(new Color(166, 182, 236));
_circle2.setDrawStroke(new BasicStroke(50.0f / 5f));
_circle2.setDrawPaint(new Color(111, 136, 223));
_circle2.setMode(SGShape.Mode.STROKE_FILL);
SGHighlightEffectGroup circleGroup2 = new SGHighlightEffectGroup(_circle2);
_exampleCircleTranslate2 = SGTransform.createTranslation(250.0f, 250.0f,
circleGroup2);

Notice in the SGHighlightEffectLeaf getBounds() method that I always use the Shapes bounding rectangle. I think what I want is the Shapes's bounding area used instead but the getBounds method only does rectangles.

My questions is: how do I tell the scenegraph to continue to use the Shape's bounds and not a rectangle's bounds for enter/exit events?

This is a scene graph picture:
http://william.quartz.googlepages.com/temp_scenegraph.png

Based on the Focus demo I created a SGGroup and SGLeaf node(all the code is in the same package):

<br />
public class SGHighlightEffectGroup extends SGGroup implements SGMouseListener<br />
{<br />
	final SGNode child;<br />
	SGHighlightEffectLeaf highlightEffectLeaf = null;</p>
<p>	public SGHighlightEffectGroup(SGNode child)<br />
	{<br />
		this.child = child;</p>
<p>		highlightEffectLeaf = new SGHighlightEffectLeaf();<br />
		highlightEffectLeaf.setVisible(false);</p>
<p>		addMouseListener(this);</p>
<p>		add(child);<br />
		add(highlightEffectLeaf);<br />
	}</p>
<p>	public void mouseClicked(MouseEvent e, SGNode node){	}</p>
<p>	public void mouseDragged(MouseEvent e, SGNode node){	}</p>
<p>	public void mouseEntered(MouseEvent e, SGNode node)<br />
	{<br />
		System.out.println("SGHighlightEffectGroup.mouseEntered");<br />
		highlightEffectLeaf.setVisible(true);<br />
	}</p>
<p>	public void mouseExited(MouseEvent e, SGNode node)<br />
	{<br />
		highlightEffectLeaf.setVisible(false);<br />
	}</p>
<p>	public void mouseMoved(MouseEvent e, SGNode node){	}</p>
<p>	public void mousePressed(MouseEvent e, SGNode node){	}</p>
<p>	public void mouseReleased(MouseEvent e, SGNode node){	}</p>
<p>	public void mouseWheelMoved(MouseWheelEvent e, SGNode node){	}</p>
<p>}<br />

I also created a SGLeaf:
<br />
public class SGHighlightEffectLeaf extends SGLeaf<br />
{<br />
	private Color lineColor = new Color(0.5f, 0.2f, 0.2f);<br />
	private BasicStroke stroke = new BasicStroke(1.0f);</p>
<p>	public void paint(Graphics2D g)<br />
	{<br />
		// Paint the highlight as rectangle.<br />
		if (isVisible())<br />
		{<br />
			SGHighlightEffectGroup parentGroup = (SGHighlightEffectGroup) getParent();<br />
			Rectangle2D rectangle = parentGroup.child.getBounds();</p>
<p>			// BEGIN State management<br />
			AffineTransform gt = g.getTransform();</p>
<p>			Color c = g.getColor();<br />
			Stroke s = g.getStroke();</p>
<p>			g.setColor(lineColor);<br />
			/*<br />
			 * We only want to negate the scaling of the stroke because of clipping.<br />
			 * Without it the line scales in thickness and becomes so thick that it exceeds<br />
			 * the bounding rectangle, which then causes drawing artifacts<br />
			 * based on the graphs rendering optimization tricks.<br />
			 */<br />
			stroke = new BasicStroke(1.0f / (float) gt.getScaleX());<br />
			g.setStroke(stroke);</p>
<p>			System.out.println(rectangle.getMinX());<br />
			g.drawRect((int) rectangle.getMinX(), (int) rectangle.getMinY(),<br />
					(int) rectangle.getWidth() - 1, (int) rectangle.getHeight() - 1);</p>
<p>			// END State management<br />
			g.setColor(c);<br />
			g.setStroke(s);<br />
			g.setTransform(gt);<br />
		}<br />
	}</p>
<p>	public Rectangle2D getBounds(AffineTransform transform)<br />
	{<br />
		SGHighlightEffectGroup parentGroup = (SGHighlightEffectGroup) getParent();<br />
		Rectangle2D rectangle = parentGroup.child.getBounds(transform);<br />
		return rectangle;<br />
	}</p>
<p>}

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
william13163
Offline
Joined: 2008-03-19
Points: 0

I thought I would give an update to this issue.

I went ahead and approached the problem a bit differently. I instead created subclasses of SGShape and SGImage and inherited from SGMouselistener. This works but now I have to inherit when ever I want to highlight/decorate an object. I was hoping to use a Group and Leaf so that I could highlight anything simply by adding a Group/Leaf combination but the bounds gets changed by the scene graph which is not what I was expecting

The image below shows the highlight:

http://william.quartz.googlepages.com/Screenshot-highlight.png

Is it possible to add methods on the scenegraph to allow objects to elect whether they participate in the accumulation of bounds? Perhaps through some sort of enable/disable feature. Basically have finer granular control over some of the optimizations and functions the scene graph does.

For any newbie (like myself) here are my subclasses (one for a Shape and another for an Image):

[code]
public class SGHighlightShape extends SGShape implements SGMouseListener {
private boolean _highlightVisible = false;
private Color _lineColor = new Color(0.5f, 0.2f, 0.2f);
private Color _fillColor = new Color(0.5f, 0.2f, 0.2f, 0.2f);
private Color _nameColor = Color.BLACK;
private BasicStroke _stroke = new BasicStroke(1.0f);
private String _name = "";

public SGHighlightShape(Shape shape) {
super();
addMouseListener(this);
setShape(shape);
}

@Override
public void paint(Graphics2D g) {

// Paint rectangle as highlight.
if (_highlightVisible)
{
Rectangle2D rectangle = getBounds();

// BEGIN State management
AffineTransform gt = g.getTransform();
Color c = g.getColor();
Stroke s = g.getStroke();

/*
* We only want to negate the scaling of the stroke because of clipping.
* Without it the line scales in thickness and becomes so thick that it exceeds
* the bounding rectangle, which then causes drawing artifacts
* based on the graphs rendering optimization tricks.
*/
_stroke = new BasicStroke(1.0f / (float) gt.getScaleX());
g.setStroke(_stroke);

g.setColor(_lineColor);
g.drawRect((int)Math.round(rectangle.getMinX()), (int)Math.round(rectangle.getMinY()),
(int) Math.round(rectangle.getWidth())-1, (int) Math.round(rectangle.getHeight())-1);
g.setColor(_fillColor);
g.fillRect((int)Math.round(rectangle.getMinX()+0.5f), (int)Math.round(rectangle.getMinY()+0.5f),
(int) Math.round(rectangle.getWidth()-0.5f)-1, (int) Math.round(rectangle.getHeight()-0.5f)-1);

// END State management
g.setColor(c);
g.setStroke(s);
g.setTransform(gt);
}
super.paint(g);
if (_highlightVisible)
{
Rectangle2D rectangle = getBounds();

// BEGIN State management
AffineTransform gt = g.getTransform();
Color c = g.getColor();

g.setColor(_nameColor);
g.drawString(_name, (float)rectangle.getMinX()+2.0f, (float)rectangle.getMinY()+10.0f);

// END State management
g.setColor(c);
g.setTransform(gt);
}
}

@Override
public void mouseClicked(MouseEvent arg0, SGNode arg1) {}

@Override
public void mouseDragged(MouseEvent arg0, SGNode arg1) {}

@Override
public void mouseEntered(MouseEvent arg0, SGNode arg1) {
_highlightVisible = true;
repaint(false);
}

@Override
public void mouseExited(MouseEvent arg0, SGNode arg1) {
_highlightVisible = false;
repaint(false);
}

@Override
public void mouseMoved(MouseEvent arg0, SGNode arg1) {}

@Override
public void mousePressed(MouseEvent arg0, SGNode arg1) {}

@Override
public void mouseReleased(MouseEvent arg0, SGNode arg1) {}

@Override
public void mouseWheelMoved(MouseWheelEvent arg0, SGNode arg1) {}

public String getName() {
return _name;
}

public void setName(String name) {
_name = name;
}

}

public class SGHighlightImage extends SGImage implements SGMouseListener {
private boolean _highlightVisible = false;
private Color _lineColor = new Color(0.5f, 0.2f, 0.2f);
private Color _fillColor = new Color(0.5f, 0.2f, 0.2f, 0.2f);
private Color _nameColor = Color.BLACK;
private BasicStroke _stroke = new BasicStroke(1.0f);
private String _name = "";

public SGHighlightImage() {
super();
addMouseListener(this);
}

@Override
public void paint(Graphics2D g) {

// Paint rectangle as highlight.
if (_highlightVisible)
{
Rectangle2D rectangle = getBounds();

// BEGIN State management
AffineTransform gt = g.getTransform();
Color c = g.getColor();

g.setColor(_fillColor);
g.fillRect((int)Math.round(rectangle.getMinX()-0.5f), (int)Math.round(rectangle.getMinY()-0.5f),
(int) Math.round(rectangle.getWidth()-1.5f), (int) Math.round(rectangle.getHeight()-1.5f));

// END State management
g.setColor(c);
g.setTransform(gt);
}

super.paint(g);

if (_highlightVisible)
{
Rectangle2D rectangle = getBounds();

// BEGIN State management
AffineTransform gt = g.getTransform();
Color c = g.getColor();
Stroke s = g.getStroke();

_stroke = new BasicStroke(1.0f / (float) gt.getScaleX());
g.setStroke(_stroke);

g.setColor(_lineColor);
g.drawRect((int)Math.round(rectangle.getMinX()+0.0f), (int)Math.round(rectangle.getMinY()+0.0f),
(int) Math.round(rectangle.getWidth()-1.0f), (int) Math.round(rectangle.getHeight()-1.0f));

g.setColor(_nameColor);
g.drawString(_name, (float)rectangle.getMinX()+2.0f, (float)rectangle.getMinY()+10.0f);

// END State management
g.setColor(c);
g.setStroke(s);
g.setTransform(gt);
}
}

@Override
public void mouseClicked(MouseEvent arg0, SGNode arg1) {}

@Override
public void mouseDragged(MouseEvent arg0, SGNode arg1) {}

@Override
public void mouseEntered(MouseEvent arg0, SGNode arg1) {
_highlightVisible = true;
repaint(false);
}

@Override
public void mouseExited(MouseEvent arg0, SGNode arg1) {
_highlightVisible = false;
repaint(false);
}

@Override
public void mouseMoved(MouseEvent arg0, SGNode arg1) {}

@Override
public void mousePressed(MouseEvent arg0, SGNode arg1) {}

@Override
public void mouseReleased(MouseEvent arg0, SGNode arg1) {}

@Override
public void mouseWheelMoved(MouseWheelEvent arg0, SGNode arg1) {}

public String getName() {
return _name;
}

public void setName(String name) {
_name = name;
}
}

[/code]