I don’t think you need to project point_A onto the ground.
So then you don’t need point_E, because the rotation is around a vector [Z_AXIS] and any point sets the rotation’s transformation around itself using that vector - so point_A works fine as it is.
Otherwise your code is fine…
Since the OP didn’t specify the actual angle through which to rotate the new two points [or how else we might determine the two points’ locations], your idea to take the default circle segmentation at 360/24.0 seems OK for now…
But if the ‘offset’ of the points is a fixed distance from point_B then your earlier approach to project point_A onto the ‘ground’ could be adjusted to take account of point_B’s z value:
point_E = Geom::Point3d.new( point_A.x, point_A.y, point_B.z)
Then the radius of the ‘circle of rotation’ is:
radius = point_E.distance(point_B)
That can then be used to calculate a perpendicular offset from point_B for point_C & D