Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix 9265: Axis names overlap with labels #19534

Merged
merged 5 commits into from
Aug 9, 2024
Merged

Conversation

robin-gerling
Copy link

@robin-gerling robin-gerling commented Jan 23, 2024

Follow-up of #16825 and #12236

Brief Information

This pull request is in the type of:

  • bug fixing
  • new feature
  • others

What does this PR do?

This PR shrinks the grid size such that the axis labels and names are completely contained (Option (B) of #9265 (comment)).

Fixed issues

Details

Before: What was the problem?

When grid.containLabel is set to true, the axis names overlap with the labels, when there is no magical number for nameGap specified. Furthermore, the names may be not visible when specifying the nameGap, because the names get pushed out of the grid. Therefore, also the distance between the grid and the container needs to be adjusted.

before

After: How does it behave after the fixing?

When grid.containLabel is set to true, the axis names do not overlap with labels and the grid size is shrinked such that the names are completely visible without the need to specify the grid to container distance or nameGap.

After

Document Info

One of the following should be checked.

Misc

ZRender Changes

  • This PR depends on ZRender changes (ecomfe/zrender#xxx).

Related test cases or examples to use the new APIs

N.A.

Others

Merging options

  • Please squash the commits into a single one when merging.

Other information

Copy link

echarts-bot bot commented Jan 23, 2024

Thanks for your contribution!
The community will review it ASAP. In the meanwhile, please checkout the coding standard and Wiki about How to make a pull request.

Document changes are required in this PR. Please also make a PR to apache/echarts-doc for document changes and update the issue id in the PR description. When the doc PR is merged, the maintainers will remove the PR: awaiting doc label.

@DanielBogenrieder
Copy link

Hey @Ovilia, @100pah,

is there any chance we could get feedback on this PR?
We are happy to improve the PR if there is something wrong/missing, but it would be nice to get this into a release as soon as possible.

Greetings,
Daniel

@admsev
Copy link

admsev commented May 25, 2024

@gooroodev review

@gooroodev
Copy link

Summary of Changes

  1. AxisBuilder.ts:

    • Imported CartesianAxisModel.
    • Modified the calculation of gap in the axisName builder to include axisToNameGapStartGap from CartesianAxisModel.
  2. axisHelper.ts:

    • Added computeNameBoundingRect function to calculate the bounding rectangle for the axis name.
    • Added isNameLocationCenter, isNameLocationStart, and isNameLocationEnd helper functions.
    • Added types CartesianAxisPositionMargins and ReservedSpace.
    • Added computeReservedSpace function to calculate the reserved space for axis labels and names.
  3. AxisModel.ts:

    • Added inverseCartesianAxisPositionMap for mapping axis positions.
    • Added axisToNameGapStartGap property to CartesianAxisModel.
  4. Grid.ts:

    • Imported additional functions and types from axisHelper.
    • Modified the logic to compute the reserved space for axis labels and names.
    • Adjusted the grid rectangle dimensions based on the reserved space.
  5. axis-containLabelAndName.html:

    • Added a new test HTML file to ensure axis labels and names do not overlap.
    • Included various test cases with different configurations for axis names and labels.

Issues, Bugs, and Typos

  1. Unused Imports:
    • In Grid.ts, reduce is imported but not used.

Proposed Improvement:

-import {isObject, each, indexOf, retrieve3, keys, reduce, map} from 'zrender/src/core/util';
+import {isObject, each, indexOf, retrieve3, keys, map} from 'zrender/src/core/util';
  1. Redundant Code:
    • The function isNameLocationCenter is defined twice, once in AxisBuilder.ts and again in axisHelper.ts.

Proposed Improvement:

-function isNameLocationCenter(nameLocation: string) {
-    return nameLocation === 'middle' || nameLocation === 'center';
-}

General Review of Code Quality and Style

  • Code Organization: The code is well-organized, with clear separation of concerns between different modules. Functions and types are logically grouped.
  • Naming Conventions: The naming conventions are consistent and descriptive, making it easy to understand the purpose of each function and variable.
  • Documentation: The code includes comments and JSDoc annotations, which enhance readability and maintainability.
  • Test Coverage: The addition of axis-containLabelAndName.html demonstrates a thorough approach to testing different configurations, ensuring robustness.
  • Style: The code follows a consistent style, with proper indentation and spacing, making it easy to read.

Overall, the pull request addresses the issue effectively and maintains high code quality. The proposed improvements are minor and would enhance the clarity and efficiency of the code.

Yours, Gooroo.dev. To receive reviews automatically, install Github App

Copy link
Contributor

@Ovilia Ovilia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the late reply and thanks for your contribution!

I feel the changes are somewhat too complicated. Could we add the following code to the end of axisHelper.ts#estimateLabelUnionRect?

...
    const axisName = axisModel.get('name');
    if (axisName) {
        const nameLocation = axisModel.get('nameLocation');
        const nameGap = axisModel.get('nameGap') || 0;
        const nameRotate = axisModel.get('nameRotate') || 0;
        const nameTruncate = axisModel.get('nameTruncate');
        const textStyle = axisModel.getModel('nameTextStyle');

        const unrotatedSingleRect = textStyle.getTextRect(axisName);
        if (nameTruncate.maxWidth) {
            unrotatedSingleRect.width = Math.min(unrotatedSingleRect.width, nameTruncate.maxWidth);
        }

        const singleRect = rotateTextRect(unrotatedSingleRect, nameRotate);
        rect.union(singleRect);

        // TODO: also consider nameLocation, nameGap and etc. to control the rect
    }

    return rect;
}

@robin-gerling
Copy link
Author

Unfortunately, yes, those changes are pretty complex.
If I understand your proposed changes, they would result in less functionality. However, those would simplify the logic.
My current proposed solution considers the labels of other axes, too. Those need to be taken into account in case the name location is either start or end, because otherwise the labels would overlap with the name, as shown in the provided figure. On the left is the proposed solution, on the right is the result of a simpler computation within estimateLabelUnionRect which includes the computation of the name and the computation of the label.
Therefore, by combining name and label to a single rect, the problem would not be solved for nameLocations start and end.
LabelNameOverlap

@DanielBogenrieder
Copy link

Hey @Ovilia,

I just saw that you gave a thumbs up. What does that mean? Do you prefer the simpler not always working solution or should we go for the more complex solution?

Greetings and thanks,
Daniel

@DanielBogenrieder
Copy link

Hey @Ovilia,

sorry to ping you again, but it would be really really nice to get this into some new version soon.

Let me know how we can help.

Greetings,
Daniel

@DanielBogenrieder
Copy link

Hey,

any updates on this one?

Cheers,
Daniel

Copy link
Contributor

@Ovilia Ovilia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution and sorry for the late reply. I don't check the notification regularly because there are too many messages. Please pin me by email oviliazhang at gmail.com if I haven't reply for some time.

This PR has changed a lot so it may be challenging for me to understand. I need you to provide some extra explanation on some of the comments.

src/coord/axisHelper.ts Outdated Show resolved Hide resolved
src/coord/axisHelper.ts Outdated Show resolved Hide resolved
Comment on lines +527 to +548
if (axis.isHorizontal() && nameRotationIsMultipleOf90degrees
|| !axis.isHorizontal() && nameRotationIsMultipleOf180degrees) {
reservedSpace.name[namePositionOrthogonalAxis] = nameBoundingRectSize / 2 - reservedLabelSpace;
}
else if (
axis.isHorizontal() && (
nameLocationIsStart && orthogonalAxisPositionIsTop && nameRotationIsSecondOrFourthQuadrant
|| nameLocationIsStart && orthogonalAxisPositionIsBottom && nameRotationIsFirstOrThirdQuadrant
|| nameLocationIsEnd && orthogonalAxisPositionIsTop && nameRotationIsFirstOrThirdQuadrant
|| nameLocationIsEnd && orthogonalAxisPositionIsBottom && nameRotationIsSecondOrFourthQuadrant
)
|| !axis.isHorizontal() && (
nameLocationIsStart && orthogonalAxisPositionIsLeft && nameRotationIsFirstOrThirdQuadrant
|| nameLocationIsStart && orthogonalAxisPositionIsRight && nameRotationIsSecondOrFourthQuadrant
|| nameLocationIsEnd && orthogonalAxisPositionIsLeft && nameRotationIsSecondOrFourthQuadrant
|| nameLocationIsEnd && orthogonalAxisPositionIsRight && nameRotationIsFirstOrThirdQuadrant
)
) {
reservedSpace.name[namePositionOrthogonalAxis] = reservedNameSpace;
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain the basic idea of these lines?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important to understand lines 527 to 548 are also lines 489-493 where the case for nameLocation: center is handled. That means, lines 527 to 548 are only valid for nameLocation: start | end.
The main reason for these conditions is the different anchoring of the axis names based on the rotation quadrant.

On a horizontal axis, all multiples of 90 degrees are anchored at their mid point, why it is necessary to reserve half of the length of the name. Additionally we need to remove the space needed by the labels since the grid will later be reduced by the sum of axisLabel.margin, axis label size, nameGap, and the axis name size. But, e.g., for nameLocation: start and axis position: left, the space needed for the labels to the left and the name to the left overlaps, since the labels are to the left of the axis and half of the name is left of the axis. (E.g. the name needs 50px left of the axis and the labels 30px. Since we sum up the reserved space, it would be 80px in total. However, the farthest point left of the axis is still the start of the name 50px to the left. So the name only needs an additional space of 50px-30px.)

The else if contains all conditions for having the name rotation in different quadrants (excluding multiples of 90°). The different quadrants on different axes have different anchors resulting in a different reserved space. E.g. for axis with position: left

  • 45°: Anchor is at end of the name and name is to the left of the axis => need to reserve space to the left
  • 135°: Anchor is at end of the name and name is to the right of the axis => no need to reserve space to the right since there will be the plot
  • 225°: Anchor is at start of the name and name is to the left of the axis => need to reserve space to the left
  • 315°: Anchor is at start of the name and name is to the right of the axis => no need to reserve space to the right since there will be the plot

* The gap between the axis and the name gap.
* Injected outside.
*/
axisToNameGapStartGap: number = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you further explain what is the gap between the axis and the name gap?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are 4 potential properties for each axis reducing the space of the grid. These are in order from the axis to the grid boundary: axisLabel.margin, axis label size (determined by estimateLabelUnionRect, axisHelper), nameGap, and the axis name size (determined by computeNameBoundingRect).
The axisToNameGapStart gap is the sum of the axisLabel.margin and the axis label size, so from the axis to the start of the name gap.

@Ovilia
Copy link
Contributor

Ovilia commented Aug 1, 2024

I've run with npm run test:visual and found that this PR may contain break changes like this:

image

I think the new result is better. It would be great to have this as default. Can we make this feature in the next major release 6.0.0, which may be released around 2025 Feb. If you want to use this feature earlier than this, you can still use the nightly build version under next tag after this PR is merged.

@Ovilia
Copy link
Contributor

Ovilia commented Aug 1, 2024

Can you check the diff with test/custom-shape-morphing2.html?

image

It seems that the position of axis name and grid are changed even without overlapping. Can you explain why this break change happens?

@robin-gerling
Copy link
Author

robin-gerling commented Aug 2, 2024

The test/custom-shape-morphing2.htmllooks different because the axis names are currently not included in the grid. With the new version, they are included and therefore decrease the size of the grid.
The green frame indicates the gridRect (with the default margins) before the calculations in if (containLabel) { (red frame afterwards). Therefore, with the current behaviour, the names would not be visible for a grid margin of e.g. 0.

CurrentBehaviour Current Behaviour NewBehaviour New Behaviour

Copy link
Contributor

@Ovilia Ovilia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution! This PR should be released in our next major release 6.0.0 since it contains break changes that may change the position of grid area.

@Ovilia Ovilia added this to the 6.0.0 milestone Aug 8, 2024
Copy link
Contributor

github-actions bot commented Aug 8, 2024

The changes brought by this PR can be previewed at: https://echarts.apache.org/examples/editor?version=PR-19534@ad4d68a

@Ovilia Ovilia changed the base branch from master to v6 August 9, 2024 02:29
@Ovilia Ovilia merged commit 31fc8cc into apache:v6 Aug 9, 2024
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants