Skip to main content

[JAVA2D] Connecting two semi-transparent shapes by a line

13 replies [Last post]
Anonymous

Okay here is the scenario (and I'm hoping there is something in the
API that permits this because at this point I haven't found anything).

What I have are two RoundRectangle2Ds that have an alpha of 50
percent. I want to draw a line connecting the centers of both of these
RoundRectangle2Ds.

A----B

If the RR2Ds were opaque I wouldn't have a problem, I would just draw
all of the links first and then draw all of the RR2Ds. Piece of cake.
However since the RR2Ds allow you to see through them, this clearly
won't work.

I've looked at using Graphics2D.setClip(), but this will only allow me
to clip the line against one of the shapes. I also looked into solving
the problem using CAG, but Area doesn't work with lines (though I will
try to adapt my pathing algorithms to use rectangles if that solves my
drawing problem (here's hoping).

It would be nice if there was a way to do a Graphics2D.addClip(Shape)
so that I could clip the graphics region by a near infinite number of
shapes. Same for intersect() and the like.

Would be nice to see the OpenGL statemachine style of rendering start
bubbling up to Java2D. Now that you're accelerating it via OpenGL,
expect to see a lot of requests :)

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Jim Graham

>> g2d.setClip( clipArea );
>>
>> // draw all of the links
>> Iterator linkIterator = links.iterator();
>> while (linkIterator.hasNext())
>> {
>> WidgetIF widget = (WidgetIF) linkIterator.next();
>>
>> widget.draw( g2d );
>> }

One minor optimization here. Rather than draw all of the links
individually clipped, draw them all to an intermediate buffer and then
render that buffer to the screen clipped. That will only work, of
course, if your background is simple as that last "clipped drawImage"
will obliterate everything outside of the rectangles.

...jim

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Jim Graham

> 2) Do a CAG adding all of the rounded rectangles together into one
> gigantic clipping shape. This is probably.... Er definitely the

BTW, Area is great for doing lots of complex CAG operations on
arbitrary geometry, but for a specific subset of its capabilities:

- All geometry is known to have the same winding direction
(whether it is clockwise or counter-clockwise)
- No single element of the geometry is self-intersecting
(or if it does self-intersect, it doesn't reverse
its direction to do so)
- You want to do an Area "add" operation

then the fastest way to get a union of all of those pieces is just to
append them to a GeneralPath created with a "Nonzero Winding Direction"
rule. The Nonzero rule naturally unions all geometry that "winds" the
same way...

...jim

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Jim Graham

> 2) Do a CAG adding all of the rounded rectangles together into one
> gigantic clipping shape. This is probably.... Er definitely the
> easiest to code since I'm already iterating through the RR2Ds anyways
> and can make this master shape. I'll probably do that one before I go
> to sleep. Since I'm at a loss as to how Java handles Areas and the
> documentation just isn't 'there' enough this one is difficult to
> judge. I'm assuming that Java will have to do the same clipping logic
> in 'java code' that I would have to do so this one is likely going to
> be close to 1 in overall work and scalability.

Some info that may help you to make some judgment calls on
implementation here.

Area works on double precision geometry. Also, it has no cutoff for
how much precision is required. This, it must hunt down the exact
intersection point of every piece of geometry. This can take a while
as the number of objects increases.

If you perform an operation on an Area, it must dice up the geometry to
do its tests. When it is done testing and putting the pieces back
together into something that can yeild a path, it tries to keep the
original geometries that it was given as "whole" as it can, but if it
must slice some of them up then it needs to create a new geometry at
the appropriate slice point. Due to double precision ideosyncracies,
it may not be possible to exactly represent the needed slice and so now
it ends up with slightly different geometry. Now when you do the next
operation it must compare all the pieces to each other again, but this
time they've been perturbed. This has the potential to cause yet
another piece of geometry to be created in the final extraction stages.
There are a couple of things we could do to improve this in the
implementation, but we don't have the resources at this point,
unfortunately.

...jim

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Dmitri Trembovetski

On Mon, Sep 06, 2004 at 01:10:02PM -0400, Gregory Pierce wrote:
> Note: What is attached below is a copy from the Apple java-dev mailing
> list to provide additional insight into the problem:
>
> I have started looking at solving this problem 3 ways:
>
> 1) Cheat and get a point on the bounding box and draw the line from that
> point. It will look funky, but quite possibly no one but me will notice
> since the arcs are only 10 pixels in width and height. I have a sneaking
> suspicion this will be the fastest way until the number of shapes and links
> being drawn gets large. Since this is something I expect it will be an
> interesting control for the others.

Speaking of cheating. How about this: render the lines first, then
render RR2Ds with the background color to remove the unneeded parts
of the lines, and then render the translucent RR2Ds..
(and if you want to get the most benefit, it may be better to render
all of the lines first, then all of the opaque RR2Ds, and then all of
the translucent ones).

Of course, this works only if you have a single-colored background.

Thanks,
Dmitri

>
> 2) Do a CAG adding all of the rounded rectangles together into one gigantic
> clipping shape. This is probably.... Er definitely the easiest to code since
> I'm already iterating through the RR2Ds anyways and can make this master
> shape. I'll probably do that one before I go to sleep. Since I'm at a loss
> as to how Java handles Areas and the documentation just isn't 'there' enough
> this one is difficult to judge. I'm assuming that Java will have to do the
> same clipping logic in 'java code' that I would have to do so this one is
> likely going to be close to 1 in overall work and scalability.
>
> 3) A little more involved render of the lines to one buffered image, then
> rendering a knockout pass to perform clipping in the frame buffer, then
> blending that image with the buffered image that just has the buffered
> images rendered. This will probably give a more constant performance over
> large numbers of objects since the work is really being done by the graphics
> hardware and there isn't any 'per object' computation being done.
>
> ===========================================================================
> To unsubscribe, send email to listserv@java.sun.com and include in the body
> of the message "signoff JAVA2D-INTEREST". For general help, send email to
> listserv@java.sun.com and include in the body of the message "help".

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Gregory Pierce

On Mon, 6 Sep 2004 22:57:40 -0700, Dmitri Trembovetski
wrote:
> On Mon, Sep 06, 2004 at 01:10:02PM -0400, Gregory Pierce wrote:
> > Note: What is attached below is a copy from the Apple java-dev mailing
> > list to provide additional insight into the problem:
> >
> > I have started looking at solving this problem 3 ways:
> >
> > 1) Cheat and get a point on the bounding box and draw the line from that
> > point. It will look funky, but quite possibly no one but me will notice
> > since the arcs are only 10 pixels in width and height. I have a sneaking
> > suspicion this will be the fastest way until the number of shapes and links
> > being drawn gets large. Since this is something I expect it will be an
> > interesting control for the others.
>
> Speaking of cheating. How about this: render the lines first, then
> render RR2Ds with the background color to remove the unneeded parts
> of the lines, and then render the translucent RR2Ds..
> (and if you want to get the most benefit, it may be better to render
> all of the lines first, then all of the opaque RR2Ds, and then all of
> the translucent ones).
>
> Of course, this works only if you have a single-colored background.
>

Yeah I did that EXACTLY. That was done as a variant of #3. It was
actually slower than using the clipArea - probably because of the
extra draws into the frame buffer. All of the RR2Ds are translucent so
it becomes a basic (pseudo coding here)

Iterator lineIterator = lines.iterator();
while (lineIterator.hasNext())
{
WidgetIF widget = (WidgetIF) lineIterator.next();

widget.draw( g2d );
}

Iterator nodeIterator = widgets.iterator();
while (nodeIterator.hasNext())
{
WidgetIF widget = (WidgetIF) nodeIterator.next();
widget.setColor( backgroundColor );
widget.draw( g2d );
widget.setColor( translucentColor );
}

Iterator nodeIterator = widgets.iterator();
while (nodeIterator.hasNext())
{
WidgetIF widget = (WidgetIF) nodeIterator.next();
widget.draw( g2d );
}

I'll get more precise numbers this afternoon. Right now I can't tell
how all of this stuff is implemented under the covers but its a lot
slower than anything I've done in raw GL and when I worked on the JOGL
port for OSX I was able to do similar a lot faster. I'm hoping that I
don't have to rely on my own native layer to do the drawing :(

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Jim Graham

--On 09/03/04 09:52:59 PM -0400 Gregory Pierce wrote:
> Okay here is the scenario (and I'm hoping there is something in the
> API that permits this because at this point I haven't found anything).
>
> What I have are two RoundRectangle2Ds that have an alpha of 50
> percent. I want to draw a line connecting the centers of both of these
> RoundRectangle2Ds.
>
> A----B
>
> If the RR2Ds were opaque I wouldn't have a problem, I would just draw
> all of the links first and then draw all of the RR2Ds. Piece of cake.
> However since the RR2Ds allow you to see through them, this clearly
> won't work.

Are the connectors translucent as well? You could use a separate
buffer to render them. It gets a little more intensive if you have
items of different translucencies, though. Layers may be the easiest
and most flexible solution:

Solution 1 - everthing at the same translucency:

Create intermediate INT_ARGB buffer.
Fill it with transparency (it is created that way)
Render all objects to it opaquely.
Render it to the screen using a translucent
AlphaComposite object.

Solution 2 - things at different translucencies:

Create intermediate INT_ARGB buffer.
Fill it with transparency (it is created that way)
Get a graphics from intermediate buffer.
Set SRC mode on that graphics.
Render all objects to it with their respective
alphas in SRC mode
Render the intermediate buffer to the screen in
regular SRC_OVER mode (the default
rendering mode).

> I've looked at using Graphics2D.setClip()

Clipping can be used here, but you need to worry about the difference
between calculating a clip shape from geometry and then expecting it to
clip out the exact same pixels as were drawn from those shapes. But,
rendering shapes involves lots of tradeoffs and calculations which
produce roundoffs in different ways that may not be reflected in your
"clip" calculations.

If you are going to go the "clip" route then you need to either:

- Accept some amount of "off by 1" problems

- Do absolutely all rendering using the "clip" mechanism.
No calls to draw() or fill() on a shape, just clip to a
shape and then call fill(largerect) and rely on clipping.

- Understand the specifics of the implementation well enough
to know where the pitfalls are and adjust for them. But,
there is enough leeway in the specs to make this impossible
to work for all implementations.

Also, this kind of a system would not be very compatible with
Antialiasing. The layers approach above would be compatible.

> but this will only allow me
> to clip the line against one of the shapes. I also looked into solving
> the problem using CAG, but Area doesn't work with lines (though I will
> try to adapt my pathing algorithms to use rectangles if that solves my
> drawing problem (here's hoping).

Area works with "fillable geometry". If you want to use it with
"lines" then they enclose no area. If you want to use it with "what
would be drawn when I use the "draw()" method on a line", then you need
to turn it into a fillable shape using the Stroke.createStrokedShape()
method.

> It would be nice if there was a way to do a Graphics2D.addClip(Shape)
> so that I could clip the graphics region by a near infinite number of
> shapes. Same for intersect() and the like.

The workaround, as others have suggested is to construct your big union
first and then call clip() once with the entire shape.

...jim

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Andrei Kouznetsov

>> Graphics2D g2d = (Graphics2D) g;
>>
>> // draw the foreground layer
>> //
>> Area clipArea = new Area( this.getBounds() );
>>
>> Iterator nodeIterator = widgets.iterator();
>> while (nodeIterator.hasNext())
>> {
>> WidgetIF widget = (WidgetIF) nodeIterator.next();
>>
>> widget.draw( g2d );
>>
>> clipArea.subtract( new Area( widget.getBounds() ) );
>> }
>>
>>
>> g2d.setClip( clipArea );
>>
>>
>> // draw all of the links
>> Iterator linkIterator = links.iterator();
>> while (linkIterator.hasNext())
>> {
>> WidgetIF widget = (WidgetIF) linkIterator.next();
>>
>> widget.draw( g2d );
>> }
>

I think that you can optimize this if you don't create your clipping area
every paint.

Why you use widget.getBounds ? is your widget rectangular? if not then
widget.getShape would be better.

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Gregory Pierce

I would love to do this but there are many problems with not creating
the clip every frame, that being the determination of what the new
clip is after all of my objects have moved.

Not using widget.getShape() would be a mistake on my part, however its
taken a back seat to the problem of the whole kit being much too slow.

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Dmitri Trembovetski

Hi Gregory,

please see my comments below.

On Fri, Sep 03, 2004 at 09:52:59PM -0400, Gregory Pierce wrote:
> Okay here is the scenario (and I'm hoping there is something in the
> API that permits this because at this point I haven't found anything).
>
> What I have are two RoundRectangle2Ds that have an alpha of 50
> percent. I want to draw a line connecting the centers of both of these
> RoundRectangle2Ds.
>
> A----B
>
> If the RR2Ds were opaque I wouldn't have a problem, I would just draw
> all of the links first and then draw all of the RR2Ds. Piece of cake.
> However since the RR2Ds allow you to see through them, this clearly
> won't work.
>
> I've looked at using Graphics2D.setClip(), but this will only allow me
> to clip the line against one of the shapes. I also looked into solving
> the problem using CAG, but Area doesn't work with lines (though I will
> try to adapt my pathing algorithms to use rectangles if that solves my
> drawing problem (here's hoping).
>
> It would be nice if there was a way to do a Graphics2D.addClip(Shape)
> so that I could clip the graphics region by a near infinite number of
> shapes. Same for intersect() and the like.

I may be missing something, but why can't you construct a clip Area
by subtracting (Area.subtract) the two RR2Ds you have from a rectangular
clip, and set that area as the clip?

Something along the lines of
Area clipA = new Area(new Rectangle2D.Float(0, 0, winWidth, winHeight));
clipA.subtract(new Area(rr2d_1)); // cut out rr2d_1, which is the first RoundRect2D
clipA.subtract(new Area(rr2d_2)); // ... the second ...
g2d.setClip(clipA);

g2d.drawLine(...)

Or take a look at demo/jfc/Java2D/src/java2d/demos/Clipping..

Thanks,
Dmitri

>
> Would be nice to see the OpenGL statemachine style of rendering start
> bubbling up to Java2D. Now that you're accelerating it via OpenGL,
> expect to see a lot of requests :)
>
> ===========================================================================
> To unsubscribe, send email to listserv@java.sun.com and include in the body
> of the message "signoff JAVA2D-INTEREST". For general help, send email to
> listserv@java.sun.com and include in the body of the message "help".

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Gregory Pierce

Hi Dmitri, I did indeed look into this (sorry the main discussion
ended up in the java-dev list at Apple but I will CC the relevant
portions here. The problem is that with a large number of objects
(i.e. 1000) the time to draw is well over 2 seconds. With a number of
objects close to what I anticipate (100) the drawing is on average 257
miliseconds per frame. Only in the 10 object case was the time to draw
a reasonable 37ms. Since I have to update the clipping area every
frame as multiple RR2Ds can be moving at a time, this approach quickly
becomes to slow.

On Sun, 5 Sep 2004 21:40:37 -0700, Dmitri Trembovetski
wrote:
>
> Hi Gregory,
>
> please see my comments below.
>
> On Fri, Sep 03, 2004 at 09:52:59PM -0400, Gregory Pierce wrote:
> > Okay here is the scenario (and I'm hoping there is something in the
> > API that permits this because at this point I haven't found anything).
> >
> > What I have are two RoundRectangle2Ds that have an alpha of 50
> > percent. I want to draw a line connecting the centers of both of these
> > RoundRectangle2Ds.
> >
> > A----B
> >
> > If the RR2Ds were opaque I wouldn't have a problem, I would just draw
> > all of the links first and then draw all of the RR2Ds. Piece of cake.
> > However since the RR2Ds allow you to see through them, this clearly
> > won't work.
> >
> > I've looked at using Graphics2D.setClip(), but this will only allow me
> > to clip the line against one of the shapes. I also looked into solving
> > the problem using CAG, but Area doesn't work with lines (though I will
> > try to adapt my pathing algorithms to use rectangles if that solves my
> > drawing problem (here's hoping).
> >
> > It would be nice if there was a way to do a Graphics2D.addClip(Shape)
> > so that I could clip the graphics region by a near infinite number of
> > shapes. Same for intersect() and the like.
>
> I may be missing something, but why can't you construct a clip Area
> by subtracting (Area.subtract) the two RR2Ds you have from a rectangular
> clip, and set that area as the clip?
>
> Something along the lines of
> Area clipA = new Area(new Rectangle2D.Float(0, 0, winWidth, winHeight));
> clipA.subtract(new Area(rr2d_1)); // cut out rr2d_1, which is the first RoundRect2D
> clipA.subtract(new Area(rr2d_2)); // ... the second ...
> g2d.setClip(clipA);
>
> g2d.drawLine(...)
>
> Or take a look at demo/jfc/Java2D/src/java2d/demos/Clipping..
>
> Thanks,
> Dmitri
>
>
>
>
> >
> > Would be nice to see the OpenGL statemachine style of rendering start
> > bubbling up to Java2D. Now that you're accelerating it via OpenGL,
> > expect to see a lot of requests :)
> >
> > ===========================================================================
> > To unsubscribe, send email to listserv@java.sun.com and include in the body
> > of the message "signoff JAVA2D-INTEREST". For general help, send email to
> > listserv@java.sun.com and include in the body of the message "help".
>

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Gregory Pierce

Note: What is attached below is a copy from the Apple java-dev mailing
list to provide additional insight into the problem:

I have started looking at solving this problem 3 ways:

1) Cheat and get a point on the bounding box and draw the line from that
point. It will look funky, but quite possibly no one but me will notice
since the arcs are only 10 pixels in width and height. I have a sneaking
suspicion this will be the fastest way until the number of shapes and links
being drawn gets large. Since this is something I expect it will be an
interesting control for the others.

2) Do a CAG adding all of the rounded rectangles together into one gigantic
clipping shape. This is probably.... Er definitely the easiest to code since
I'm already iterating through the RR2Ds anyways and can make this master
shape. I'll probably do that one before I go to sleep. Since I'm at a loss
as to how Java handles Areas and the documentation just isn't 'there' enough
this one is difficult to judge. I'm assuming that Java will have to do the
same clipping logic in 'java code' that I would have to do so this one is
likely going to be close to 1 in overall work and scalability.

3) A little more involved render of the lines to one buffered image, then
rendering a knockout pass to perform clipping in the frame buffer, then
blending that image with the buffered image that just has the buffered
images rendered. This will probably give a more constant performance over
large numbers of objects since the work is really being done by the graphics
hardware and there isn't any 'per object' computation being done.

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Gregory Pierce

Well round one is done (yeah I didn't think that would take long) and it
works perfectly. Performance is 'less than stellar', but it is within
accepted norms for a Java application. I'll have to post to the Java2D list
and find out what the JVM is actually doing with that clipping region and
whether or not that is something that will get accelerated in the new OpenGL
pipeline. Then again, perhaps someone from Apple could chime in about how
Graphics2D.setClip() is implemented under the covers.

To be honest I could probably get away with this, but I've never been one to
slack off from find the right answer so I'll press on in the morning (3AM
here).

OH! And for all of those helping me out and giving me suggestions - THANK
YOU! Wanted to make sure I said it before I moved on and starting testing
the other results.

> Graphics2D g2d = (Graphics2D) g;
>
> // draw the foreground layer
> //
> Area clipArea = new Area( this.getBounds() );
>
> Iterator nodeIterator = widgets.iterator();
> while (nodeIterator.hasNext())
> {
> WidgetIF widget = (WidgetIF) nodeIterator.next();
>
> widget.draw( g2d );
>
> clipArea.subtract( new Area( widget.getBounds() ) );
> }
>
>
> g2d.setClip( clipArea );
>
>
> // draw all of the links
> Iterator linkIterator = links.iterator();
> while (linkIterator.hasNext())
> {
> WidgetIF widget = (WidgetIF) linkIterator.next();
>
> widget.draw( g2d );
> }

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Gregory Pierce

CAG in Java2D is slow with a large number of objects.... Really really
painfully slow. Now for some data points:

* In the test I randomly created 'n' round rectangle 2ds and drew them on
the screen.

* Each frame I do a substract operation from an area representing the
screen.

* Every frame I clip the line 'buffer' with this area and then draw the
rectangles. This is similar to method 3 - only a lot less performance savvy.

When n=10, average drawing time per frame was 37 ms. Yay (I thought), this
might be fast enough that I cold be lazy.

When n=100, average drawing time per frame increases to 257 ms. Uh oh. I
know I'll have on average this number of RR2Ds and other shapes floating
around and I can only draw at roughly 5FPS and it shows.

When n=1000, average drawing time per frame crushes the system at 2456 ms.
While this isn't my target number of shapes, there is nothing saying that at
some point I won't get to this point. Overall this is showing the
inefficiencies of this approach and it definitely doesn't hold up under a
large number of shapes.

So Method 2 is tossed. It is just way to slow to be useful for this
particular domain of problem. I'm not sure how its implemented underneath
the covers, but its not really all that useful if its this show.

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".