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

accessing the internal objects #98

Open
rossanoparis opened this issue Aug 23, 2022 · 16 comments
Open

accessing the internal objects #98

rossanoparis opened this issue Aug 23, 2022 · 16 comments
Labels
documentation Improvements or additions to documentation enhancement New feature or request question Further information is requested

Comments

@rossanoparis
Copy link

Hi @sammycage

This point probably requires a deep discussion, below I try to explain my needs.
It would be useful to identify an object inside SVG code in order to change some properties after the SVG file has been loaded in memory.

Perhaps the attribute ID ( https://www.w3.org/TR/SVG2/struct.html#IDAttribute ) is the right one to identify objects.

Mainly the purpose is for being able to change colours, lines width, visibility, etc …
It would be very useful to get the position and size occupied by every addressable SVG objects

LunaSVG library should keep a list of SVG objects and expose it in some way
The user should inform the library about the object ID and the attribute to change with the new value.

Regads
Rossano

@sammycage sammycage added documentation Improvements or additions to documentation enhancement New feature or request question Further information is requested labels Aug 23, 2022
@jry2
Copy link

jry2 commented Aug 24, 2022

We are looking for SVG rendering library for our icons and we also need modify some object properties such as color, opacity, or stroke width. It is possible with NanoSVG.

@sammycage
Copy link
Owner

@jry2 Are you still searching?

@jry2
Copy link

jry2 commented Aug 24, 2022

Yes. We also need to hide group with specified id.

@sammycage
Copy link
Owner

@jry2 I'll let you know when it's ready.

@kariem2k
Copy link

kariem2k commented Nov 8, 2022

Maybe classes as well? It will help mass manipulation based on the class

@sammycage
Copy link
Owner

sammycage commented Jan 7, 2024

@rossanoparis @kariem2k @jry2 Added with this commit 760e7e8

@sammycage
Copy link
Owner

Example:

    auto document = Document::loadFromData("<svg viewBox='0 0 200 200'><circle id='a' cx='50' cy='50' r='40' fill='green'/></svg>");
    auto element = document->getElementById("a");
    element.setAttribute("cx", "100");
    element.setAttribute("cy", "100");
    element.setAttribute("r", "100");
    element.setAttribute("fill", "red");
    document->updateLayout();

@rossanoparis Reminder: Don't forget to call Document::updateLayout after modifying the document.

sammycage added a commit that referenced this issue Jan 7, 2024
@rossanoparis
Copy link
Author

rossanoparis commented Jan 8, 2024

@sammycage it sounds great! Thank you for starting this kind of implementation

I couldn't wait to test it ... what about gradients

gradients.zip

Is there a chance to operate with gradients handled by using urls instead of constant colours
image

@sammycage
Copy link
Owner

sammycage commented Jan 8, 2024

@rossanoparis Hello! You have the flexibility to modify any attribute using the versatile setAttribute method. Take a look at the code snippet below for a practical example. This snippet demonstrates how to transition the fill of a circle seamlessly, progressing from a solid color to a gradient and finally to a pattern:

    std::string content = R"SVG(
        <svg width="200" height="200">
            <defs>
            <!-- Gradient -->
            <linearGradient id="myGradient" x1="0%" y1="0%" x2="100%" y2="100%">
                <stop offset="0%" style="stop-color: #ff0000;"/>
                <stop offset="100%" style="stop-color: #0000ff;"/>
            </linearGradient>

            <!-- Pattern -->
            <pattern id="myPattern" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
                <circle cx="10" cy="10" r="8" fill="#00ff00"/>
            </pattern>
        </defs>
        <circle id="myCircle" cx="100" cy="100" r="50" fill="red"/>
        </svg>)SVG";

    auto document = Document::loadFromData(content);
    writeToPng(*document, "circle-with-solid-color.png");

    auto circle = document->getElementById("myCircle");
    circle.setAttribute("fill", "url(#myGradient)");
    document->updateLayout();
    writeToPng(*document, "circle-with-gradient.png");

    circle.setAttribute("fill", "url(#myPattern)");
    document->updateLayout();
    writeToPng(*document, "circle-with-pattern.png");

circle-with-solid-color
circle-with-gradient
circle-with-pattern

@rossanoparis
Copy link
Author

Yes, It's clear, but my question was different.
What I meant was related to gradient itself, how can I modify gradient or pattern attributes

@sammycage
Copy link
Owner

@rossanoparis Yes, I understand now. To modify the attributes of a gradient or a pattern, you can use a similar approach with the setAttribute method. For example:

// To modify gradient attributes
auto gradient = document->getElementById("yourGradientId");
gradient.setAttribute("x1", "newX1Value");
gradient.setAttribute("y1", "newY1Value");
// Add more attributes as needed

// To modify pattern attributes
auto pattern = document->getElementById("yourPatternId");
pattern.setAttribute("x", "newXValue");
pattern.setAttribute("y", "newYValue");
// Add more attributes as needed

Feel free to adapt this code based on the specific attributes you want to modify. Let me know if you have any further questions or if there's anything else I can assist you with. But it seems like you've got the method now!

@rossanoparis
Copy link
Author

Thank you @sammycage, it is clear

Regarding attributes I'd like to introduce two new requests/discussions.

1) Groups or single object "transformations"
With "transformation" I mainly mean:

  1. rotation
  2. scale
  3. move
  4. vertical or horizontal flip

image

It would be very useful, using the same approach as attributes, to apply transformations to all objects contained in a group or even to a single object.

I imagine something like:

auto group1 = document->getElementById("Group-01");
group1.Rotate(angle, center-x, center-y);
group1.Move(new-x, new-y);
group1.Scale(scale, center-x, center-y);
group1.FlipVertical(reference-x, reference-y);
group1.FlipHorizontal(reference-x, reference-y);

auto triangle = document->getElementById("OBJ2");
triangle.Rotate(angle, center-x, center-y);
triangle.FlipVertical(reference-x, reference-y);
triangle.Move(new-x, new-y);

2) Groups or single object "references"
With "reference" I mainly mean methodd based on which a user can get information from objects regarding the area occupied by a group or single objects; even for texts which is not drawn by the library, but that could be drawn in other way by the user.

image

It would be very useful, using the same approach as attributes, to get such information.

I imagine something like:

auto group1 = document->getElementById("Group-01");
group1.getP1();
group1.getP2();
group1.getP3();
group1.getP4();

auto triangle = document->getElementById("OBJ2");
triangle.getP1();
triangle.getP2();

auto text = document->getElementById("MyText1");
text.getP1();
text.getP2();
text.getP3();
text.getP4();

Transformations and references features would be very powerful to "animate" a SVG document ... what do you think about them.

@sammycage
Copy link
Owner

@rossanoparis I appreciate your detailed suggestions and the illustrative diagrams you provided. I'm confident that incorporating the DomElement::getBBox and DomElement::getLocalTransform methods will effectively address the concerns you raised regarding "transformations" and "references. 6947ee1

This code snippet illustrates the usage of DomElement::getBBox to reposition an SVG group to a new reference position.

    std::string content = R"SVG(
        <svg width="200" height="200">
            <g id="myGroup">
              <rect width="50" height="50" fill="blue"/>
            </g>
        </svg>)SVG";

    auto document = Document::loadFromData(content);
    writeToPng(*document, "original.png");

    // Get the SVG element and its bounding box
    auto element = document->getElementById("myGroup");
    auto bbox = element.getBBox();

    // Calculate the new position (e.g., move 150 units to the right and 30 units down)
    auto newX = 150;
    auto newY = 30;

    // Calculate the translation values
    auto deltaX = newX - bbox.x;
    auto deltaY = newY - bbox.y;

    // Apply the transform attribute to move the SVG group
    element.setAttribute("transform", "translate(" + std::to_string(deltaX) + " " + std::to_string(deltaY) + ")");
    document->updateLayout();
    writeToPng(*document, "translated.png");

original
translated

@rossanoparis
Copy link
Author

Kind @sammycage thank you for aving accepted my proposal and ... wow! how fast you are :)

I don't have time in this week to test the latest commits.
Just for my information, did you already implemented the two points above described ?

Again thank you, regards

@sammycage
Copy link
Owner

@rossanoparis You can adjust SVG transformations with the following code. Feel free to ask any further questions!

void writeToPng(Document& document, const char* filename)
{
    auto bitmap = document.renderToBitmap();

    stbi_write_png(filename, bitmap.width(), bitmap.height(), 4, bitmap.data(), 0);

    std::cout << "Generated PNG file : " << filename << std::endl;
}

void setTransformAttribute(DomElement& element, const Matrix& matrix)
{
    std::string transform("matrix(");
    transform += std::to_string(matrix.a);
    transform += ' ';
    transform += std::to_string(matrix.b);
    transform += ' ';
    transform += std::to_string(matrix.c);
    transform += ' ';
    transform += std::to_string(matrix.d);
    transform += ' ';
    transform += std::to_string(matrix.e);
    transform += ' ';
    transform += std::to_string(matrix.f);
    transform += ')';

    element.setAttribute("transform", transform);
}

int main(int argc, char** argv)
{
    std::string content = R"SVG(
        <svg width="200" height="200">
            <g id="myGroup">
              <rect width="50" height="50" fill="blue"/>
            </g>
        </svg>)SVG";

    auto document = Document::loadFromData(content);
    writeToPng(*document, "original.png");

    // Retrieve the SVG element and its transformation matrix.
    auto element = document->getElementById("myGroup");
    auto transform = element.getLocalTransform();

    // Modify the transformation matrix.
    transform.translate(50, 50);
    transform.rotate(45);

    // Apply the modified transformation to the SVG element.
    setTransformAttribute(element, transform);

    document->updateLayout();
    writeToPng(*document, "transformed.png");

    return 0;
}

@rossanoparis
Copy link
Author

Thank you @sammycage

There are some points I don't understand their behaviour, perhaps because of my lack of knowledge ...

I'm debugging this feature, and I'm going to open different issues for each test I'm doing related to: scale, rotate, translate
In this way, treating one by one it results in tidy resources for all, I think

...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation enhancement New feature or request question Further information is requested
Projects
None yet
Development

No branches or pull requests

4 participants