Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

Get bounds only of what is visible?

Explorer ,
Jan 14, 2014 Jan 14, 2014

Okay, So when you use a clipping mask there can sometimes be a lot of art that is invisible. If you "Select All" you'll get a rectangle only around what's visible.  I want the bounds of that rectangle.

I tried itterating the art but I don't think that the art type is the best way to figure this one out.  And if you use AIGroupSuite to test if an art group is "clipped" with GetGroupClipped, you get the bounds of the larger art that I'm trying to avoid.

I didn't see anything about bounds of a "selection" in the docs. 

Anyone got a hint on where to look for this one?

TOPICS
SDK
3.8K
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe
Enthusiast ,
Jan 15, 2014 Jan 15, 2014

What you could do is:

1- to get all selected arts. (use SDK api to get selection or do a recursive treewalk). 

2- parse selected arts, and for each art, check art property (visible).

3- and then compute selection bounds if art is visible.

-> this way you will get selection bounds.

Thomas.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jan 15, 2014 Jan 15, 2014

It is #3 that I'm unsure about.  I couldn't find "selection bounds" anywhere in the AIArt suite.  Is there a specific suite I should be poking around in?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Jan 15, 2014 Jan 15, 2014

you should do this way:

//Get Art Bounds for each AIArtHandle

AIRealRect artBounds;

error = sAIArt->GetArtBounds(*iter, &artBounds);

aisdk::check_ai_error(error);

//Compute selection bouding box.

AIRealRect& boundingBox;

//for each selected AIArtHandle, bouding box needs to be updated. (loop)

if(boundingBox.left > artBounds.left)

     boundingBox.left = artBounds.left;

if(boundingBox.top < artBounds.top)

     boundingBox.top = artBounds.top;

if(boundingBox.right < artBounds.right)

     boundingBox.right = artBounds.right;

if(boundingBox.bottom > artBounds.bottom)

     boundingBox.bottom = artBounds.bottom;

Finally, you will have your selection bounding box.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jan 15, 2014 Jan 15, 2014

Oh yes, I see what you mean.  I thought you meant that there was a specific method for getting selection bounds.  I have similar code to what you posted for getting the bounding box of some other art.  But the problem is with masks.

Even though the visible selected art shows a smaller bounding rectangle in Illustrator, when one itterates through the art to get the bounds, one receives the entire bounds of each visible art object weather or not all of that art is masked.

For example: The first image shows the path of the art that is masked by the green square.  If you itterate the art you will get the bounding box that includes the path of the green square PLUS the path of the line. The second image shows what you get when you do a "select all".  I want to get the bounds of the selection in the second image.

artbounds.png  selectall.png

I know the bounds I'm looking for must exist somewhere because Illustrator draws the proper box every time "select all" is performed.  I just don't know if those bounds are accessable to me and I haven't found a "work around" to compute them yet.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Jan 15, 2014 Jan 15, 2014

1- first thing that you have to do is to check object type.

2- if AIArtHandle is kGroupArt, you need to check the clipped property.

//Check object type:

if((objectType == kGroupArt))

{

       error = sAIGroup->GetGroupClipped(*iter, &isClippingGroup);

       aisdk::check_ai_error(error);

}

3- And if is the case, you have to get the bounding box of the clipping path.

    here is the trick.

AIBoolean isClippingGroup = false;

AIRealRect artBounds;

if(isClippingGroup)

{

   // get clipping art bounds instead

    AIArtHandle child;

    error = sAIArt->GetArtFirstChild(*iter, &child);

    aisdk::check_ai_error(error);

    error = sAIArt->GetArtBounds(child, &artBounds);

    aisdk::check_ai_error(error);

}

This should work!

Thomas.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jan 15, 2014 Jan 15, 2014

Ahhh, I see.  Previously I was getting using GetGroupClipped, but was then getting the bounds of the "clipped" layer.  I was not aware that the "first child" would be the clipping bounds.  Will that always be true?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Jan 15, 2014 Jan 15, 2014

When a mask is created by Adobe Illustrator, the clipping group’s first child

is the clipping path.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jan 16, 2014 Jan 16, 2014

Okay... Sadly that is not true.  It is possible to create a situation where the clipping group's first child is not the clipping path.

As you can see here the pink elipse is above the clipping path in the layers window AND that is also true of the art objects when I step through the code:

tree.png

But, like I said, if illustrator can compute the bounding box that I need when "select all" is performed, why shouldn't I be able to as well?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guest
Jan 18, 2014 Jan 18, 2014

To determine which object in the group is a mask, use the function GetArtUserAttr and check by kArtIsClipMask attribute

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jan 18, 2014 Jan 18, 2014

Yes, thank you.  I should have reported that I'm now aware of several ways to get the clipping mask.

We know how to tell if an art object is "clipping group" (AIGroupSuite::GetGroupClipped).  And I believe that AIArtSuite::IsArtClipping tells if the art object is a "clipping path".  The documentation says that AIArtSuite::IsArtClipping basically checks for the kArtIsClipMask attribute that you speak of. 

However without a way to tell which items are the ones that are being partially hidden by the clipping mask, I am unable to weed out those objects' bounds as part of my computation to get the box of only visibly displayed material.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guest
Jan 19, 2014 Jan 19, 2014

You can, for example, temporarily flatten group and then take a bounds.

However, there is a serious problem with Opacity Mask 😞

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jan 21, 2014 Jan 21, 2014

Yeah, I'm realizing now that this problem may be more difficult than I thought.  The "select all" bounding box creates a bounding rectangle using the points of the visible art that are most extreme, but it does not include stroke.  So even if I COULD get that specific bounding box, it might cut off some of the art, which is worse than having white space around it.

This is a tough one.  I know how to see which art objects are clipping masks.  If I could just figure out how to exclude all the art objects that are affected by those masks then I could exclude those and include the other art objects on the page.  Then I'd have what I need.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Aug 22, 2014 Aug 22, 2014

Hi DB,

I'm trying to solve the same issue you described; and I have discovered the same complexities that you have. Did you ever figure out the code to do this?

Thanks!

-- Jim

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Sep 16, 2014 Sep 16, 2014

I never found a solution.  I'm pretty sure it's impossible.  I even contacted adobe and they pretty much told me that it couldn't be done with the current sdk.  I'm haven't delved much into scripting but I doubt there's anything in there that would do it.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Aug 26, 2014 Aug 26, 2014

Iterate and add up all clipping path bounds using:

sAIArt->GetArtTransformBounds and specify the flag to include stroke in the bound.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Sep 25, 2014 Sep 25, 2014

Thanks for the follow-up DB!

After some research, I think that one can probably write some code to approximate a visible bounding box rectangle by parsing each piece of art and then have it work out what clipping masks and compound paths interact with it. Sadly, too much work for too little reward for me at this point...

Cheers!

-- Jim

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Mar 02, 2017 Mar 02, 2017

This one caused me a bit of a headache and I thought the least I could do was share the solution. If anyone sees any flaws in this please do point them out. The function below iteratively checks the tree to items to ensure that all parents have been taken into account and reports back the actual visible area.

The user passes in the AiArt of interest, a ptr to a bounds rect and the initialbounds should be 'true' when called outside.

ASErr GetVisibleBounds(AIArtHandle aiArt, AIRealRect* bounds, bool initBounds = true);

ASErr GetVisibleBounds(AIArtHandle aiArt, AIRealRect* bounds, bool initBounds)

{

  //Get the bounds of an AiArt taking into account any clipping

  ASErr error = kNoErr;

  if(initBounds)

  {

       //First time in? Then init the bounds, the default setting

       if(!aiArt)

       {

            return kBadParameterErr;

       }

       error = sAIArt->GetArtBounds( aiArt, bounds );

       if(error){

            return error;

       }

  }

  //In case a null is passed in for a parent, just return, its not really an error.

  //See where we call the function recursively below.

  if(!aiArt)

  {

       return error;

  }

  //Set the art target for bounds as the current input

  AIArtHandle aiArtBounds = aiArt;

  //Get the item's parent

  AIArtHandle aiArtParent = nullptr;

  error = sAIArt->GetArtParent( aiArtBounds, &aiArtParent );

  if(error){

       return error;

  }

  //See if the item is a group

  short artType = kUnknownArt;

  error = sAIArt->GetArtType(aiArtBounds, &artType);

  if(error){

       return error;

  }

  //See if it is a group, if its a group it will have the clipping child within itself, so carry on

  if(artType != kGroupArt)

  {

       //Its *not* a group, use its parent when checking the clipping bounds. The parent will hold the

       //clipping child within itself

       aiArtBounds = aiArtParent;

  }

  //Excessive safety check...

  if(aiArtBounds)

  {

       //Ensure the parent group has its clipping taken into account, this will climb up the tree of items checking each group in

       //turn as it finds them. Note: pass in false for the initBounds to ensure we don't mess up the initial bounds from the source item.

       error = GetVisibleBounds(aiArtParent, bounds, false);

       if(error)

       {

            return error;

       }

       //Ensure we are only looking for clipping in a Group, can't think how we could up up here without it being a group, but

       //who knows what tomorrow will bring.

       short artTargetType = kUnknownArt;

       error = sAIArt->GetArtType(aiArtBounds, &artTargetType);

       if(artTargetType == kGroupArt)

       {

            //See if the group has a group clipping item

            AIBoolean clippedGrp = false;

            error = sAIGroupSuite->GetGroupClipped( aiArtBounds,&clippedGrp );

            if(!error && clippedGrp)

            {

                 //It does, iterate over all the children, usually its the 1st one, but not always. Also, in case its possible to have

                 //2 clipping items, best ensure we check all children. Safety First!

                 AIArtHandle child = nullptr;

                 sAIArt->GetArtFirstChild(aiArtBounds, &child);

                 while(child)

                 {

                      if(sAIArt->IsArtClipping(child))

                      {

                           //Found a clipping child, get its bounds and update the incoming items to within them if needs be

                           AIRealRect clippingBounds;

                          error = sAIArt->GetArtBounds( child, &clippingBounds );

                           if(!error)

                           {

                                //Clip to the bounds

                                if(clippingBounds.left  > bounds->left ) bounds->left = clippingBounds.left;

                                if(clippingBounds.bottom > bounds->bottom ) bounds->bottom = clippingBounds.bottom;

                                if(clippingBounds.right  < bounds->right ) bounds->right = clippingBounds.right;

                                if(clippingBounds.top    < bounds->top ) bounds->top = clippingBounds.top;

                           }

                      }

                      //Get the next item, and try again

                      AIArtHandle sibling = nullptr;              

                      error = sAIArt->GetArtSibling(child, &sibling );

                      child = sibling;

                 }

            }

       }

  }

  return error;

}

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Mar 02, 2017 Mar 02, 2017

This thread may also be of interest: Determine if an art object is hidden due to being clipped

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Mar 02, 2017 Mar 02, 2017
LATEST

Yep seems to do roughly the same thing. I might add in the math intersection stuff, its neater.

Thanks

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines