Skip to main content

Brighten effect on a portion of an SGLeaf.

7 replies [Last post]
william13163
Offline
Joined: 2008-03-19
Points: 0

Greetings,

First I will describe what it is I am trying to achieve:

Pretend you have a flashlight pointing straight down upon a photograph. But where the beam hits the paper, it produces a bright spot; the other portions of the image are dimmer. When you move the flashlight, the bright spot moves.

Ultimately, what I would like to do is something similar to the SGComposite transparency effect, where a Brighten effect is used on a path (in essence renderFinalImage()) instead of changing transparency on a child shape.

What have I tried so far? I took one of the composite examples and modified it to the point that I get "sort of" what I am looking for, but with Clipping that clips out the surrounding area, which is half of what I need.

pseudo code follows:

<br />
    SGShape beamShape = new SGShape();<br />
    beamShape.setShape(new Ellipse2D.Float(0f, 0f, 200f, 200f));<br />
    beamShape.setAntialiasingHint(RenderingHints.VALUE_ANTIALIAS_OFF);</p>
<p>    SGClip clip = new SGClip();</p>
<p>    clip.setShape(beamShape);</p>
<p>    float[] rbg = new float[] { 1.3f, 1.3f, 1.3f, 1.0f };<br />
    float[] offs = new float[] { 0.0f, 0.0f, 0.0f, 0.0f };<br />
    RescaleOp brighten = new RescaleOp(rbg, offs, null);</p>
<p>    sgImageOp.setImageOps(brighten);<br />
    sgImageOp.setChild(translate2);   // The mouse updates the translate2 node for the animation.<br />

What the above produces is a Brightened area but the clip chops off everything outside, so all I can see is the effect itself, which isn't what I am looking for. Note: I use two transforms such that the effect moves but the Leaf stay fixed. Basically, I surrounded the Filter like:

<br />
            translate<br />
            brighten filter         <--- RescaleOp<br />
            inverse translate<br />
            leaf                         <--- the image that has the light "shined" at it.<br />

I also, created a typical two-pass approach just confirm that the graph was a DAC.
Basically, the typical approach--without a scenegraph--would be to paint the background first, then paint the effect restricted by a bounding/clipping area/shape. This is the definition of a two-pass effect.

The only way of doing that with scenario would be to have two seperate "copies/branches". The first branch/path is the background and the second path is the effect. Because the graph is a DAC, I can't save resource space and have two paths pointing at the same leafs. So I have to create copies, which is bad for me because the leafs could be quite large.

I also, tried creating my own composite, but I am not that strong at composites when dealing with the scenegraph. I am having a hard time understanding some of the finer details of the Effect and SGEffect classes and how renderFinalImage comes into play. I thought I could make a custom two-pass node but I can't seem to wrap my head around it.

So my actually question would be; can this be done?

For now, I can live with using an SGComposite effect against a White shape. A true "brighten" effect is a Scaling operation not an Adding operation like the SGComposite produces. For example, here is a Brighten effect using the RescaleOp:

<br />
    SGImageOp sgImageOp = new SGImageOp();<br />
    float[] rbg = new float[] { 1.3f, 1.3f, 1.3f, 1.0f };<br />
    float[] offs = new float[] { 0.0f, 0.0f, 0.0f, 0.0f };<br />
    RescaleOp brighten = new RescaleOp(rbg, offs, null);<br />
    sgImageOp.setImageOps(brighten);<br />

The above Op scales the bands, where as, below, the variation effectively does what the SGComposite opacity parameter does:

<br />
    SGImageOp sgImageOp = new SGImageOp();<br />
    float[] rbg = new float[] { 1.0f, 1.0f, 1.0f, 1.0f };<br />
    float[] offs = new float[] { 10.0f, 10.0f, 10.0f, 0.0f };<br />
    RescaleOp brighten = new RescaleOp(rbg, offs, null);<br />
    sgImageOp.setImageOps(brighten);<br />

Above, the scale has been removed (1.0f) and the offsets set to (10.0f), and as a result you get what the translucently does, which isn't a Brighten effect.

I can live with the SGComposite, but it isn't correct for what I want to do. You basically end up with a "washed" out lighting effect. Keep in mind I am trying to restrict my lighting effect to the area defined by a Shape. This is how the SGComposite works, but the Shape is being affected rather than the underlying leaf.

There must be a way to create a Composite like node effect that Brightens an "Area" bounded by a Shape and applied to the Leaf below. Any thoughts?

Cheers.

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
liquid
Offline
Joined: 2005-06-16
Points: 0

Depending on the version of scenario you use, i believe FXNode has a setClipNode method that might be of interest.
It's not shape clipping so i'm wondering whether it might help, for instance maybe you could have your ellipse representing a bright spot, blur it, and put it on top of a semi transparent dark rectangle a la lightbox. I believe you could still throw a blend and bright pass too somewhere if need be.

Jim what do you think ?

I'm not sure it does, but i hope it helps

Jim Graham

You can create a tree of effects and the current node will be inserted
wherever their is a Source() node. Using that technique you could have
a Blend effect with the bottom input being a Source() and the top input
being the spotlight effect you just perfected. Install that on the node
and it will blend the node with a spotlighted version of the node...

...jim

scenario@javadesktop.org wrote:
> Greetings,
>
> First I will describe what it is I am trying to achieve:
>
> Pretend you have a flashlight pointing straight down upon a photograph. But where the beam hits the paper, it produces a bright spot; the other portions of the image are dimmer. When you move the flashlight, the bright spot moves.
>
> Ultimately, what I would like to do is something similar to the SGComposite transparency effect, where a Brighten effect is used on a path (in essence renderFinalImage()) instead of changing transparency on a child shape.
>
> What have I tried so far? I took one of the composite examples and modified it to the point that I get "sort of" what I am looking for, but with Clipping that clips out the surrounding area, which is half of what I need.
>
> pseudo code follows:
>
> [code]
> SGShape beamShape = new SGShape();
> beamShape.setShape(new Ellipse2D.Float(0f, 0f, 200f, 200f));
> beamShape.setAntialiasingHint(RenderingHints.VALUE_ANTIALIAS_OFF);
>
> SGClip clip = new SGClip();
>
> clip.setShape(beamShape);
>
> float[] rbg = new float[] { 1.3f, 1.3f, 1.3f, 1.0f };
> float[] offs = new float[] { 0.0f, 0.0f, 0.0f, 0.0f };
> RescaleOp brighten = new RescaleOp(rbg, offs, null);
>
> sgImageOp.setImageOps(brighten);
> sgImageOp.setChild(translate2); // The mouse updates the translate2 node for the animation.
> [/code]
> What the above produces is a Brightened area but the clip chops off everything outside, so all I can see is the effect itself, which isn't what I am looking for. Note: I use two transforms such that the effect moves but the Leaf stay fixed. Basically, I surrounded the Filter like:
>
> [code]
> translate
> brighten filter <--- RescaleOp
> inverse translate
> leaf <--- the image that has the light "shined" at it.
> [/code]
>
> I also, created a typical two-pass approach just confirm that the graph was a DAC.
> Basically, the typical approach--without a scenegraph--would be to paint the background first, then paint the effect restricted by a bounding/clipping area/shape. This is the definition of a two-pass effect.
>
> The only way of doing that with scenario would be to have two seperate "copies/branches". The first branch/path is the background and the second path is the effect. Because the graph is a DAC, I can't save resource space and have two paths pointing at the same leafs. So I have to create copies, which is bad for me because the leafs could be quite large.
>
> I also, tried creating my own composite, but I am not that strong at composites when dealing with the scenegraph. I am having a hard time understanding some of the finer details of the Effect and SGEffect classes and how renderFinalImage comes into play. I thought I could make a custom two-pass node but I can't seem to wrap my head around it.
>
> So my actually question would be; can this be done?
>
> For now, I can live with using an SGComposite effect against a White shape. A true "brighten" effect is a Scaling operation not an Adding operation like the SGComposite produces. For example, here is a Brighten effect using the RescaleOp:
>
> [code]
> SGImageOp sgImageOp = new SGImageOp();
> float[] rbg = new float[] { 1.3f, 1.3f, 1.3f, 1.0f };
> float[] offs = new float[] { 0.0f, 0.0f, 0.0f, 0.0f };
> RescaleOp brighten = new RescaleOp(rbg, offs, null);
> sgImageOp.setImageOps(brighten);
> [/code]
> The above Op scales the bands, where as, below, the variation effectively does what the SGComposite opacity parameter does:
>
> [code]
> SGImageOp sgImageOp = new SGImageOp();
> float[] rbg = new float[] { 1.0f, 1.0f, 1.0f, 1.0f };
> float[] offs = new float[] { 10.0f, 10.0f, 10.0f, 0.0f };
> RescaleOp brighten = new RescaleOp(rbg, offs, null);
> sgImageOp.setImageOps(brighten);
> [/code]
> Above, the scale has been removed (1.0f) and the offsets set to (10.0f), and as a result you get what the translucently does, which isn't a Brighten effect.
>
> I can live with the SGComposite, but it isn't correct for what I want to do. You basically end up with a "washed" out lighting effect. Keep in mind I am trying to restrict my lighting effect to the area defined by a Shape. This is how the SGComposite works, but the Shape is being affected rather than the underlying leaf.
>
> There must be a way to create a Composite like node effect that Brightens an "Area" bounded by a Shape and applied to the Leaf below. Any thoughts?
>
> Cheers.
> [Message sent by forum member 'william13163' (william13163)]
>
> http://forums.java.net/jive/thread.jspa?messageID=328770
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@scenegraph.dev.java.net
> For additional commands, e-mail: dev-help@scenegraph.dev.java.net
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@scenegraph.dev.java.net
For additional commands, e-mail: dev-help@scenegraph.dev.java.net

william13163
Offline
Joined: 2008-03-19
Points: 0

Hi Jim,

Thanks for the response. Before I read it, I had already messed around with creating my own SGFilter. It works but it can perform bad if the image is large because the op.filter() is working on the whole image before the clip. I am trying to figure out how to filter on just the subimage and figure out how to integrate an AffineTransform for the filter. I should be able to apply the filter on a subregion and then draw that based on a transform.

This is the filter:

[code]
public class SGEllipseBrighten extends SGFilter
{
private Shape _shape;
private final float[] _rbga = new float[] { 1.3f, 1.3f, 1.3f, 1.0f };
private final float[] _offsets = new float[] { 0.0f, 0.0f, 0.0f, 0.0f };
private final RescaleOp _brighten = new RescaleOp(_rbga, _offsets, null);

public SGEllipseBrighten()
{
super();
}

public void renderFinalImage(Graphics2D g, SGSourceContent srcContent)
{
// Render the first pass as normal
Image image = srcContent.getTransformedImage();
g.drawImage(image, 0, 0, null);

// Render the second pass with Op and Clipping.
if (_shape == null)
return;

if (image instanceof BufferedImage)
{
// To bad I can't clip during the brighten. If the image is large and the
// clip small, there is still a lot of work done because of the large
// image.
BufferedImage dstImage = _brighten.filter((BufferedImage) image, null);

Graphics2D g2 = (Graphics2D) g.create();
g2.clip(_shape);

g2.drawImage(dstImage, 0, 0, null);

g2.dispose();
}

}

public int needsSourceContent()
{
return TRANSFORMED;
}

public void setShape(Shape shape)
{
_shape = shape;
}

public void setLocation(double x, double y)
{
_shape = new Ellipse2D.Float((float) x, (float) y, 200f, 200f);
}
}
[/code]

Anyway, I am interested in what you mentioned because I was thinking about the Blend effect but can't figure out how to use it.

I "kinda-sorta" understand what you are saying...I think. ;) I am a bit weak on your definitions. I think by "wherever their is a Source() node" you mean the leaf in a rendered state, something that can be feed to an effect?

If I understand correct you mean something like this?

[code]
brighten filter
|
Blend
|
leaf (renderFinalImage)
[/code]

Can the top input be an actual Effect? I guess it could because the effect itself would get a final image too, correct?

Hmmm...

Jim Graham

scenario@javadesktop.org wrote:
> Anyway, I am interested in what you mentioned because I was thinking about the Blend effect but can't figure out how to use it.

The Blend would be the effect you set on the node. One of its inputs
would be a Source effect (which is the default effect if you don't
specify anything). The other input would be the "spotlight" effect you
already created. Like this:

Blend b = new Blend();
b.setTopInput(
);
node.setEffect(b);

I think that might do it.

> I "kinda-sorta" understand what you are saying...I think. ;) I am a bit weak on your definitions. I think by "wherever their is a Source() node" you mean the leaf in a rendered state, something that can be feed to an effect?

Source() is a special input that says "use an image of the node here".
It is the default input for any inputs on any filter that you don't
explicitly set to something else. In essence, it could be viewed as
"the node is used as input for any unspecified inputs in the effect
chain", except that this is implemented by using a "Source()" effect as
the default for every input in the chain so I was speaking from that
understanding...

> If I understand correct you mean something like this?
>
> [code]
> brighten filter
> |
> Blend
> |
> leaf (renderFinalImage)
> [/code]

I look at it like this:

Source
| |
V V
+-----+-----+
|
V
Blend
|
V

> Can the top input be an actual Effect? I guess it could because the effect itself would get a final image too, correct?

Blend has a bottom input and a top input. Either can be another effect,
or a Source() to use the node as that input...

...jim

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@scenegraph.dev.java.net
For additional commands, e-mail: dev-help@scenegraph.dev.java.net

william13163
Offline
Joined: 2008-03-19
Points: 0

I looked into the Blend and Merge effects and put together what I think you are talking about. I must have implied I had created a clip Effect when I actually meant I created a SGEffect. Nevertheless, I went about creating a clip effect (shown way below) that I could blend with the ColorAdjust effect, using the brightness control on it. However, my knowledge of the core pieces of scenario limit me what I can do when creating effects. As you can probably tell from looking at my clip effect class, I don't know what I am doing nor do I know how to figure out the transformations; the clip just sits in one place because I need to figure out how to use of the current transform that surround the effect node.

This is what I have cooked up to test the Blend and Clip effects:

[code]
root
|
translate1
|
group1
| |<---- Clip effect
SGEffect <---- Blend --|
| |<---- ColorAdjust
|
translate2 (the inverse of translate1, I use the mouse to control translate1 and translate2)
|
group2
|
SGImage leaf
[/code]
The above is the graph layout of the test harness. This is the code snippet:

[code]
Clip effClip = new Clip();
effClip.setShape(path);

ColorAdjust effCA = new ColorAdjust();
effCA.setBrightness(0.3f);

Blend effBlend = new Blend(Mode.SRC_OVER, effClip, effCA);
// Blend effBlend = new Blend(Mode.SRC_OVER, effCA, effClip);
// Merge effBlend = new Merge(effClip, effCA);

SGEffect sgEffect = new SGEffect();
sgEffect.setEffect(effBlend);
sgEffect.setChild(translate2);

group2.add(image);
[/code]
I have tried just about every Blend Mode, with HARD_LIGHT coming the closest, but still not quite right.

I don't know how to work with the transforms at this level so my Clip doesn't actually work correctly in terms of following the translate1 node (I freely admit I rely on scenario to handle transforms at lower levels.)

Before:
http://william.quartz.googlepages.com/blendbefore.png
After:
http://william.quartz.googlepages.com/blendhardlight.png

You can see that in the pictures where the circle is clipped by the bounding rectangle.

This is my Clip Effect copied from the Crop Effect.
You can see in the filter method that I need to understand how transforms work at that level. That code is nearly a straight copy from the Crop Effect except for the addition of the Shape.
[code]
public class Clip extends Effect
{
private Shape _shape = new Ellipse2D.Float(0, 0, 200f, 200f); // Default

public Clip()
{
this(new Source(true));
}

public Clip(Effect input)
{
super(input);
}

public final Effect getInput()
{
return getInputs().get(0);
}

public void setInput(Effect input)
{
setInput(0, input);
}

public Image filter(GraphicsConfiguration config)
{
Rectangle2D trect = getTransformedBounds();
System.out.println(trect);

Rectangle fullBounds = _shape.getBounds();
int w = fullBounds.width;
int h = fullBounds.height;
Image dst = getCompatibleImage(config, w, h);

Effect input = getInputs().get(0);
Rectangle inputBounds = input.getBounds().getBounds();
Image src = input.filter(config);

int dx = 0;
int dy = 0;
int sx = fullBounds.x - inputBounds.x;
int sy = fullBounds.y - inputBounds.y;

Graphics2D gdst = (Graphics2D) dst.getGraphics();

gdst.clip(_shape);

gdst.drawImage(src, dx, dy, dx + w, dy + h, sx, sy, sx + w, sx + h, null);
gdst.dispose();

return dst;
}

public AccelType getAccelType(GraphicsConfiguration config)
{
return AccelType.NONE;
}

public Rectangle2D getBounds()
{
// I think i should return the bounds of the clip shape instead.
Rectangle2D r = new Rectangle2D.Float();
if (getInputs().get(0).isInDeviceSpace())
{
r.setRect(getSourceContent().getTransformedBounds());
} else
{
r.setRect(getSourceContent().getUntransformedBounds());
}
return r;
}

public void setShape(Shape shape)
{
_shape = shape;
}

}
[/code]

Oh, and thanks btw for your help and guidance. I am sure the Blend approach is the correct approach. If only I could figure out how to make a proper Clip effect then I think I would be good.

As a side note, the closest I have ever come to a working solution was to create an SGFilter that man-handled the clipping-transforms. But it has its own set of issues when the shape is initially outside of the Source(). If initially inside the Source() things work out great. :) But I don't think this is proper; it is more like taking a wrecking ball to a nail.

[code]
public class SGBrighten extends SGFilter
{
private double _x = 0.0;
private double _y = 0.0;

private Shape _shape = new Ellipse2D.Float(0, 0, 200f, 200f); // Default

private final float[] _rbga = new float[] { 1.3f, 1.3f, 1.3f, 1.0f };
private final float[] _offsets = new float[] { 0.0f, 0.0f, 0.0f, 0.0f };
private final RescaleOp _op = new RescaleOp(_rbga, _offsets, null);
// Or a Convolve
// private final float[] kdata = new float[] { 1.3f };
// private final Kernel kernel = new Kernel(1, 1, kdata);
// private final ConvolveOp _op = new ConvolveOp(kernel);

private final Rectangle2D _shapeBounds = new Rectangle2D.Double();
private final Rectangle2D _sourceBounds = new Rectangle2D.Double();
private final Rectangle2D _intersectBounds = new Rectangle2D.Double();

public SGBrighten()
{
super();
}

public void renderFinalImage(Graphics2D g, SGSourceContent srcContent)
{
// Render the first pass as normal
Image image = srcContent.getTransformedImage();
g.drawImage(image, 0, 0, null);

// Render the second pass with Op and Clipping.
if (_shape == null)
return;

if (image instanceof BufferedImage)
{
BufferedImage bImage = (BufferedImage) image;

_shapeBounds.setFrame(_x, _y, _shape.getBounds().getWidth(), _shape.getBounds().getHeight());

_sourceBounds.setFrame(bImage.getMinX(), bImage.getMinY(), bImage.getWidth(), bImage.getHeight());

Rectangle2D.intersect(_shapeBounds, _sourceBounds, _intersectBounds);

double maxx = Math.min(_sourceBounds.getWidth(), _intersectBounds.getWidth());
double maxy = Math.min(_sourceBounds.getHeight(), _intersectBounds.getHeight());
if (maxx <= 0 || maxy <= 0)
return; // The shape is beyond the right and/or bottom.

double minx = Math.max(_sourceBounds.getMinX(), _intersectBounds.getMinX());
double miny = Math.max(_sourceBounds.getMinY(), _intersectBounds.getMinY());

// Subimage needs to be replaced by a fixed size buffer if performance
// problems arise.
BufferedImage subImage = ((BufferedImage) image).getSubimage((int) minx, (int) miny, (int) maxx, (int) maxy);
BufferedImage dstImage = _op.filter(subImage, null);

Graphics2D g2 = (Graphics2D) g.create();

AffineTransform af = g.getTransform();

// translate the clip shape
af.translate(_x, _y);
g2.setTransform(af);
g2.clip(_shape);

// draw image--and--remove previous shape translation.
if (minx == 0 && miny == 0)
g2.drawImage(dstImage, (int) -_x, (int) -_y, null);
else if (miny == 0)
g2.drawImage(dstImage, 0, (int) -_y, null);
else if (minx == 0)
g2.drawImage(dstImage, (int) -_x, 0, null);
else
g2.drawImage(dstImage, 0, 0, null);

g2.dispose();
}

}

public int needsSourceContent()
{
return TRANSFORMED;
}

public void setShape(Shape shape)
{
_shape = shape;
}

public void setLocation(double x, double y)
{
_x = x;
_y = y;
}
}

[/code]

Jim Graham

You are trying to blend the clip with the adjustment. One of those
needs to work _on_ the other, not be blended with it. Like this:

|<--ColorAdjust<--Clip<--default(Source)
Blend <--|
|<--default(Source)

In code that would be something like:

ClipEffect ce = new Clip();
ce.setShape(...);

ColorAdjust ca = new ColorAdjust(ce);
ca.setParams...;

Blend b = new Blend();
b.setTopInput(ca);

node.setEffect(b);

Two things to worry about in getting the transforms and positioning
right would be to watch what boolean you use to construct the Source()
effects with (that controls whether the effect will work in device space
or transformed user space) - and to use an Offset filter to reposition
the output of the effects against each other.

I hope that helps get you on the right track...

...jim

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@scenegraph.dev.java.net
For additional commands, e-mail: dev-help@scenegraph.dev.java.net

william13163
Offline
Joined: 2008-03-19
Points: 0

Jim, I appreciate the ideas, suggestions and patient you have given me, but I must admit I didn't come up with a predictable working solution in the end. However, I did learn more about Effects and how to chain them together, and what Sources are. I spent another 5 or 6 hours and didn't get anything to work with decent performance and predictability.

I tried a couple of arrangments from your suggestions--I think I did it correctly but I am not sure.

I tried this sequence first.
[code]
|top <-- ColorAdjust <-- Offset <-- Clip <-- Source(false)
Blend <--|
|bottom <-- Source(true)
[/code]
Then I flipped Offset and Clip
[code]
|top <-- ColorAdjust <-- Clip <-- Offset <-- Source(false)
Blend <--|
|bottom <-- Source(true)
[/code]
In both cases I discovered that I could simply ignore the Source on the Clip path and just use a cleared transparent image.
And, because I use the counter balancing transforms I learned I didn't need the Offset effect either. So I ended up with this:
[code]
|top <-- ColorAdjust <-- Clip <-- Source(false)
Blend <--|
|bottom <-- Source(true)
[/code]

In the end the Clip basically became a mask generater, not really a clip. The code for the Clip::Effect ended up being this:

[code]
public Image filter(GraphicsConfiguration config)
{
Rectangle shapeBounds = _shape.getBounds();

int w = shapeBounds.width;
int h = shapeBounds.height;

// I simply create a blank slate
BufferedImage dst = config.createCompatibleImage(w, h, Transparency.OPAQUE);

Graphics2D gdst = (Graphics2D) dst.getGraphics();

// First I prep the effects area ignoring the source.
gdst.setComposite(AlphaComposite.SrcAtop); // I have no idea why this works.
// gdst.setPaint(new Color(1.0f, 1.0f, 1.0f, 0.1f)); // But I could use this just as well. The alpha can be 0.1->0.9, 1 or 0 it produces bad results.
gdst.fillRect(0, 0, w, h);

// make portions of the image opaque for the ColorAdjust.
gdst.setComposite(AlphaComposite.Clear);
gdst.fill(_shape);

gdst.dispose();

return dst; // return the configured blend mask.
}
public AccelType getAccelType(GraphicsConfiguration config)
{
return AccelType.NONE;
}

public Rectangle2D getBounds()
{
Rectangle2D r = new Rectangle2D.Float();

r = _shape.getBounds2D();
return r;
}
[/code]
The truth is I don't know why this works, and it only works as long as the _shape is in the positive quadrant. If the shape borders the quadrant, then it is clipped against the main bottom Source(). One real reason this fails for me is performance. A background image, say 1000x1000 pixels, with a 50pixel radius spotlight is really slow. The cursor lags behind very noticably. I do realize composites are expensive and I may try caching to see if that helps.

I knew composites effect were my weak spot going into this. At least I came out 50% ahead, as long as my shapes are created in the positive quadrant this Clip works ok--I just don't know why, but it does. My SGBrigthen::SGFilter worked the same way, but I understood that wrecking ball I created.

I will try again...