I am trying to generate snippets on the fly and I having hard times trying to get some item rotated. Well I can rotate it. Problem is that it's then moved away.
I understand this displacement is due to the relation between itemTransform and the Spread coordinate space but I have no clue how to compute anything regarding to this in the snippet xml structure.
For example, here, the black frame is the non rotated frame. I wish I can rotate so it matches the magenta frame. Right now, all I get is the blue one for as long as I express the itemTransform attribute that way:
itemTransform=".86 -.5 .5 .86 0 0"
Obviously I have to set tx and ty so the "blue" box is moved back to the magenta box actual position. Question is how to compute tx and ty in this case ?
<Rectangle Self="u348" ContentType="Unassigned" StoryTitle="$ID/" ECPageItemData="" ECPaginationPageItemData="1 0" ParentInterfaceChangeCount="" TargetInterfaceChangeCount="" LastUpdatedInterfaceChangeCount="" OverriddenPageItemProps="" HorizontalLayoutConstraints="FlexibleDimension FixedDimension FlexibleDimension" VerticalLayoutConstraints="FlexibleDimension FixedDimension FlexibleDimension" FillColor="Color/Black" StrokeWeight="0" StrokeColor="Swatch/None" GradientFillStart="0 0" GradientFillLength="0" GradientFillAngle="0" GradientStrokeStart="0 0" GradientStrokeLength="0" GradientStrokeAngle="0" ItemLayer="uc5" Locked="false" LocalDisplaySetting="Default" GradientFillHiliteLength="0" GradientFillHiliteAngle="0" GradientStrokeHiliteLength="0" GradientStrokeHiliteAngle="0" AppliedObjectStyle="ObjectStyle/$ID/[Normal Graphics Frame]" Visible="true" Name="$ID/"
ItemTransform=".86 -.5 .5 .86 0 0">
<PathPointType Anchor="1188.5 -1731.4685039368005" LeftDirection="1188.5 -1731.4685039368005" RightDirection="1188.5 -1731.4685039368005" />
<PathPointType Anchor="1188.5 -1220.5" LeftDirection="1188.5 -1220.5" RightDirection="1188.5 -1220.5" />
<PathPointType Anchor="3409.5 -1220.5" LeftDirection="3409.5 -1220.5" RightDirection="3409.5 -1220.5" />
<PathPointType Anchor="3409.5 -1731.4685039368005" LeftDirection="3409.5 -1731.4685039368005" RightDirection="3409.5 -1731.4685039368005" />
TIA for any hints.
Ok, so I found this other post:
And read over idml spec
Still not obvious
You need to take into account the <PathPointArray> anchors since they represent the shape in its own space. As we shall see, there is a huge difference between a translation (which occurs through the transform matrix and after any other task) and the positioning of an object in its inner coordinate system.
It is easy to see that a rotation matrix applied to an off-centered object will cause it to move with respect to its barycenter. As we all learned from this internationaly-known guide the result of a pure rotation θ in InDesign is:
In your case you seem to have θ=30°. You didn't specify whether the initial matrix was [1 0 0 1 0 0], or if some tx,ty translation attributes were already in the place, but for my discussion I will assume the black rectangle originally untransformed. Hence, considering the anchor coordinates found in the IDML, we start from something like this:
If you apply the formula
So, that's the actual result of applying the matrix [0.866 -0.5 0.5 0.866 0 0 ] and indeed you need to compensate the trailing attributes if you want to reposition the barycenter of the rectangle where it originally was.
This “translation fix” (dx, dy) should be computable from your data. First, calculate the center coordinates (x0,y0) of the original rectangle using the (min+max)/2 trick on the existing anchor points. Then calculate dx and dy as shown above.
In more generic terms:
// Rotation angle (deg -> rad)
const a = 30 * Math.PI/180;
// Rotation matrix will be
// [cosa -sina sina cosa dx dy]
const cosa = Math.cos(a);
const sina = Math.sin(a);
// From IDML's <PathPointArray>
var x1 = 1188.5,
x2 = 3409.5,
y1 = -1731.4685039368005,
y2 = -1220.5;
var x0 = (x1+x2)/2,
y0 = (y1+y2)/2;
// Translation fix (dx,dy)
// [dx dy] = [1-cosa -sina sina 1-cosa] × [x0 y0]
var k = 1 - cosa;
var dx = k*x0 - sina*y0,
dy = k*y0 + sina*x0;
// Final matrix.
var mx = [cosa, -sina, sina, cosa, dx, dy];
alert( '"'+mx.join(' ')+'"' );
// => "0.86602540378444 -0.5 0.5 0.86602540378444 1045.99972268378 951.755605822006"
Maybe a cleaner solution would be to change the anchor coordinates themselves to keep the rectangle originally centered (which seems even easier to do) and then avoid composing the rotation with a last-minute translation. Up to you to choose the best approach. IDML sucks in any case 😉
First of all, thanks for the answer and time taken. I have already downloaded, printed and read the internationaly-known guide you are talking about years ago But indeed those intricacies are really complex (a pure science topic by itself).
I see IDML handling as a way of doing most of the pageItem generation and manipulation out of the box. But yes it has a price.
I will let you know how that go.
Thanks for the help, it was helpful. I wouldn't say I am out of troubles but my understanding of deep geometry is better
Anyway, I am confused on one point and maybe you or another brilliant mind can help.
I understood the formula :
I am converting AI templates to InDesign Snippets. Some shapes are rotated in AI and I am translating this in IDML.
So here is a shape in AI rotated by say 20°. I tried to compute the non rotated points by applying the upper formula with the reverse angle. PathPoints in AI have the same properties than InDesign (anchor,leftDirection and rightDirection).
So I was naively thinking that for example, pointA with x and y like [100,200] could be rotated back programmatically this way :
But results are nothing like the coordinates AI returns.
452.696629213484,-365.887640449439 //x,y coordinates of the first point with 0° rotation
388.202515466231,-456.253631357617 //x,y coordinates of the first point with -35.6° rotation*
And applying the formula with positive 35.6 to reverse the rotation gives me
Shouldn't oppositing the applied rotation angle set the rotation back to 0? Shouldn't we get the initial geometric values ?
(My full answer just vanished so I need to start again but shall make it shorter.)
2. If you need to reverse-engineer a rotation whose angle is known, a point M and its transformed point M', the first question is to determine the center of that rotation.
3. Your data tell us that:
M = (452.696629213484, -365.887640449439)
M' = (388.202515466231, -456.253631357617)
when it undergoes a rotation of angle -35.6°.
It is easy to see that the origin of this coordinate system cannot be the center of the rotation. It's less easy to compute its exact coordinates but I made a quick script to do the job for us:
var theta = (-35.6)*Math.PI/180;
// M1 -> M2
var x1 = 452.696629213484,
y1 = -365.887640449439;
var x2 = 388.202515466231,
y2 = -456.253631357617;
var dx = x2-x1,
dy = y2-y1,
d = Math.sqrt(dx*dx+dy*dy);
var OI = d/(2*Math.tan(theta/2));
var xi = (x1+x2)/2,
yi = (y1+y2)/2,
alpha = Math.atan(-dy/dx);
var x0 = xi + OI*Math.sin(alpha),
y0 = yi + OI*Math.cos(alpha);
alert( [x0,y0].join('\r') );
O = (561.178125659198, -511.50845807588)
And indeed this seems to match:
Now you have your translation components and can use them to reverse the operation.
Hope that helps.
Thanks a lot Marc for your help on that difficult topic.
Just to keep on talking about this. As I was trying to keep on getting this, I applied the rotation formula inside Illustrator. I centered object on the 0,0 point and duplicated the initial page item. Then I set all path points so they are "rotated" through the formula.
What's weird is that the object doesn't seem well rotated. It's like over rotated. The blue shape is the starting point. Green is the correctly yet manually rotated and the orange is the result of the path points computation. I would have expected the orange to be on the green position.
Anyway, it's food for thought. You gave me great code I need to digest and reproduce. I will score your post as correct as I don't doubt a second it will be. But I am even more interesting in getting this than just copy pasting your generous code
Sorry for asking a stupid question: Are you sure you don't forget to convert degree angles into radians?
rad = deg * (Math.PI / 180);
Indeed, Math.cos, Math.sin, etc always expect radians.
This code works for me:
var a = -36*(Math.PI/180),
cosa = Math.cos(a),
sina = Math.sin(a);
// Assuming your rectangle is selected in InDesign.
var poly = app.selection.duplicate(),
ep = poly.paths.entirePath,
for( i=ep.length ; i-- ; )
x = ep;
y = ep;
ep = x*cosa + y*sina; // x'
ep = -x*sina + y*cosa; // y'
poly.paths.entirePath = ep;
"Stupid is as stupid does" as said a great mind once
You got me. My main issue was indeed in that non degree to rad conversion. Once I did that, everything has felt in the right place. Pieces of the jigsaw start to fit together and you was really helpful on this one. Not only by providing snippets but by taking the time to clarify those kind of complex geometric intricacies.
Thanks a lot Marc Autret !