How would I rotate a curve using the Cetz package?

I’m recreating diagrams from old textbooks, and I would like to rotate an oblong shape. Below is a hobby curve with 5 coordinate points. I applied a counterclockwise rotation to each point; however, the output doesn’t match the original, see provided image.

Math:
I’m applying the rotation matrix where the angle is pi/6 to each point:
[cos(pi/6) - sin(pi/6)]
[cos(pi/6) sin(pi/6)]

This might be a rounding issue, but I’m not sure if anyone has already attempted this?

#import "@preview/cetz:0.5.0"
#set page(width: auto, height: auto, margin: .5cm)
#let rigidBody() = cetz.canvas({

  hobby(
    (3, 3),
    (3, 2),
    (4.7, 0.5),
    (4, 2),
    (4, 3),
    close: true,
    fill: rgb("#e3d9ff80"),
  )
  hobby( 
    (calc.cos(calc.pi/6)*3-calc.sin(calc.pi/6)*3, calc.cos(calc.pi/6)*3+calc.sin(calc.pi/6)*3),
    (calc.cos(calc.pi/6)*3-calc.sin(calc.pi/6)*2, calc.cos(calc.pi/6)*3+calc.sin(calc.pi/6)*2),
    (calc.cos(calc.pi/6)*4.7-calc.sin(calc.pi/6)*0.5, calc.cos(calc.pi/6)*4.7+calc.sin(calc.pi/6)*0.5),
    (calc.cos(calc.pi/6)*4-calc.sin(calc.pi/6)*2, calc.cos(calc.pi/6)*4+calc.sin(calc.pi/6)*2),
    (calc.cos(calc.pi/6)*4-calc.sin(calc.pi/6)*3, calc.cos(calc.pi/6)*4+calc.sin(calc.pi/6)*3),
    close: true,
    fill: rgb("#e3d9ff80"),
  )

})
// to view
#block()[#rigidBody()]

Your rotation matrix is wrong. The diagonal elements both need to have cosine, whereas the off-diagonal elements must have sine. A quick way to check this is to plug in the angle zero. In that case your rotation matrix should be equal to the identity matrix.

I would also recommend you to define a transformation function here to make your code more readable and easier to modify.

#let transform(point, angle) = {
  let x, y = point
  (..., ...) // Apply your transformation matrix here
}
1 Like

Maybe this is what you want:

#import "@preview/cetz:0.5.2"

#let rotate-2d(graph, angle) = {
  cetz.draw.rotate(angle)
  // or `cetz.draw.transform(cetz.matrix.transform-rotate-z(angle))`
  graph
  // Note: rotate back, so that graph outside this func
  //   won't be rotated.
  cetz.draw.rotate(-angle)
}

#cetz.canvas({
  cetz.draw.circle((0, 0), radius: .1)
  let triangle = cetz.draw.line((1, 0), (2, 0), (2, 2), close: true)

  triangle
  rotate-2d(triangle, calc.pi / 6)

  cetz.draw.circle((1, 0), radius: .1)
})
1 Like

Corrected rotation matrix

$ mat(cos phi, -sin phi ; sin phi, cos phi) $

That’s it, I swapped my cos/sin, sorry about that.

I’ll implement a transform function for readability. Thank you for the suggestion!

1 Like

Pre-existing functions for curve transformations

This also works great! I didn’t mark it as the solution because I wanted to use my math reasoning, but this is a great implementation of the same thing tysm!

#import "@preview/cetz:0.5.2"

#let rotate-2d(graph, angle) = {
  cetz.draw.rotate(angle)
  // or `cetz.draw.transform(cetz.matrix.transform-rotate-z(angle))`
  graph
  // Note: rotate back, so that graph outside this func
  //   won't be rotated.
  cetz.draw.rotate(-angle)
}

#cetz.canvas({

  let curve = cetz.draw.hobby(
    (3, 3),
    (3, 2),
    (4.7, 0.5),
    (4, 2),
    (4, 3), 
    close: true)
  curve
  rotate-2d(curve, calc.pi / 6)
})