-
-
Notifications
You must be signed in to change notification settings - Fork 126
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
Local transformation and internal SVG element access - Element translation #166
Comments
@rossanoparis When working with SVG elements and applying transformations such as scaling, rotating, skewing, and translating, it's often useful to understand the mathematical principles behind these transformations. Specifically, when you want to apply a transformation around a specific point (transform origin), you need to use a sequence of transformations. Mathematical ExplanationTo transform an SVG element around a specific point, you typically use the following transformation sequence:
In terms of matrices, this is represented as: T(transformOriginX, transformOriginY) * additionalTransform * T(-transformOriginX, -transformOriginY) * originalTransform Practical Example with SVG
<svg width="400" height="400" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(100, 100)">
<rect id="rect1" fill="red" x="50" y="50" width="100" height="100"/>
<rect id="rect2" opacity="0.8" fill="green" transform="translate(50, 50)" x="0" y="0" width="100" height="100"/>
</g>
</svg> OriginalRotate(45)int main(int argc, char** argv)
{
std::string filename("/home/sammycage/Projects/hello.svg");
auto document = Document::loadFromFile(filename);
// Retrieve the SVG element to animate by its ID
auto element = document->getElementById("rect2");
auto originalTransform = element.getLocalTransform();
auto boundingBox = element.getBBox().transformed(originalTransform);
auto transformOriginX = boundingBox.x;
auto transformOriginY = boundingBox.y;
// Optionally, adjust the transform origin to the center of the bounding box
// transformOriginX += boundingBox.w / 2.f;
// transformOriginY += boundingBox.h / 2.f;
auto additionalTransform = Matrix::translated(transformOriginX, transformOriginY);
additionalTransform.rotate(45);
additionalTransform.translate(-transformOriginX, -transformOriginY);
// Combine the new transformation with the original transformation
additionalTransform.premultiply(originalTransform);
// Convert the transformation matrix to a string representation for the SVG "transform" attribute
std::string additionalTransformString("matrix(");
additionalTransformString += std::to_string(additionalTransform.a);
additionalTransformString += ' ';
additionalTransformString += std::to_string(additionalTransform.b);
additionalTransformString += ' ';
additionalTransformString += std::to_string(additionalTransform.c);
additionalTransformString += ' ';
additionalTransformString += std::to_string(additionalTransform.d);
additionalTransformString += ' ';
additionalTransformString += std::to_string(additionalTransform.e);
additionalTransformString += ' ';
additionalTransformString += std::to_string(additionalTransform.f);
additionalTransformString += ')';
// Output the transformation matrix string
std::cout << additionalTransformString << std::endl;
// Apply the new transformation to the SVG element
element.setAttribute("transform", additionalTransformString);
// Update the layout of the document to apply the transformation
document->updateLayout();
// Render the updated SVG document to a bitmap
auto bitmap = document->renderToBitmap();
if(!bitmap.valid()) return 1;
bitmap.convertToRGBA();
// Create a filename for the PNG file based on the transformation type.
std::string basename("rotate" ".png");
stbi_write_png(basename.c_str(), bitmap.width(), bitmap.height(), 4, bitmap.data(), 0);
std::cout << "Generated PNG file : " << basename << std::endl;
return 0;
} SkewX(45)...
auto additionalTransform = Matrix::translated(transformOriginX, transformOriginY);
additionalTransform.shear(45, 0);
additionalTransform.translate(-transformOriginX, -transformOriginY);
// Combine the new transformation with the original transformation
additionalTransform.premultiply(originalTransform);
... Translate(50, 50)...
auto additionalTransform = Matrix::translated(transformOriginX, transformOriginY);
additionalTransform.translate(50, 50);
additionalTransform.translate(-transformOriginX, -transformOriginY);
// Combine the new transformation with the original transformation
additionalTransform.premultiply(originalTransform);
... Translate(50, 50) Rotate(45)...
auto additionalTransform = Matrix::translated(transformOriginX, transformOriginY);
additionalTransform.translate(50, 50);
additionalTransform.rotate(45);
additionalTransform.translate(-transformOriginX, -transformOriginY);
// Combine the new transformation with the original transformation
additionalTransform.premultiply(originalTransform);
... Demoint main(int argc, char** argv)
{
std::string filename("/home/sammycage/Projects/hello.svg");
auto document = Document::loadFromFile(filename);
// Retrieve the SVG element to animate by its ID
auto element = document->getElementById("rect2");
auto originalTransform = element.getLocalTransform();
auto boundingBox = element.getBBox().transformed(originalTransform);
auto transformOriginX = boundingBox.x;
auto transformOriginY = boundingBox.y;
// Optionally, adjust the transform origin to the center of the bounding box
// transformOriginX += boundingBox.w / 2.f;
// transformOriginY += boundingBox.h / 2.f;
// Loop through angles from 0 to 360 degrees, incrementing by 30 degrees
for(auto angle = 0; angle <= 360; angle += 30) {
auto additionalTransform = Matrix::translated(transformOriginX, transformOriginY);
additionalTransform.rotate(angle);
additionalTransform.translate(-transformOriginX, -transformOriginY);
// Combine the new transformation with the original transformation
additionalTransform.premultiply(originalTransform);
// Convert the transformation matrix to a string representation for the SVG "transform" attribute
std::string additionalTransformString("matrix(");
additionalTransformString += std::to_string(additionalTransform.a);
additionalTransformString += ' ';
additionalTransformString += std::to_string(additionalTransform.b);
additionalTransformString += ' ';
additionalTransformString += std::to_string(additionalTransform.c);
additionalTransformString += ' ';
additionalTransformString += std::to_string(additionalTransform.d);
additionalTransformString += ' ';
additionalTransformString += std::to_string(additionalTransform.e);
additionalTransformString += ' ';
additionalTransformString += std::to_string(additionalTransform.f);
additionalTransformString += ')';
// Output the transformation matrix string
std::cout << additionalTransformString << std::endl;
// Apply the new transformation to the SVG element
element.setAttribute("transform", additionalTransformString);
// Update the layout of the document to apply the transformation
document->updateLayout();
// Render the updated SVG document to a bitmap
auto bitmap = document->renderToBitmap();
if(!bitmap.valid()) return 1;
bitmap.convertToRGBA();
// Create a filename for the PNG file based on the current angle
std::string basename("hello-" + std::to_string(angle) + ".png");
stbi_write_png(basename.c_str(), bitmap.width(), bitmap.height(), 4, bitmap.data(), 0);
std::cout << "Generated PNG file : " << basename << std::endl;
}
return 0;
} Output
OutputDemo 2 (with your SVG file
|
Thank you @sammycage for these precious explanation. |
Data
This issue refers to: issue 98
Library version: v2.3.9 (master)
Testing SVG file: groups.svg.zip (InkScape)
document
W: 163 H: 288
Top left (0, 0)
Bottom rigth (163, 288)
Center (81.5, 144)
g1 (group 1)
W: 163 H: 152
B1 (0, 0)
Bottom rigth (163, 152)
C1 (81.5, 76)
g2 (group 2)
W: 108 H: 111
B2 (50, 178)
Bottom rigth (158, 288)
C2 (104, 232.5)
Scope
I want to translate in X the element g1 applying a translation of -81.5px, which is half of document width.
Testing code
Results
The element g1 disappears from the document; it happens using both methods
auto transformg1 = elementg1.getLocalTransform();
or
auto transformg1 = elementg1.getAbsoluteTransform();
The text was updated successfully, but these errors were encountered: