public override Matrix4?GetTransformationMatrix(Viewport2D viewport, ViewportEvent e, BaseBoxTool.BoxState state, Document doc, IEnumerable <Widget> activeWidgets) { var origin = viewport.ZeroUnusedCoordinate((state.PreTransformBoxStart + state.PreTransformBoxEnd) / 2); var rw = activeWidgets.OfType <RotationWidget>().FirstOrDefault(); if (rw != null) { origin = rw.GetPivotPoint(); } var forigin = viewport.Flatten(origin); var origv = (state.MoveStart - forigin).Normalise(); var newv = (viewport.ScreenToWorld(e.X, viewport.Height - e.Y) - forigin).Normalise(); var angle = DMath.Acos(Math.Max(-1, Math.Min(1, origv.Dot(newv)))); if ((origv.Cross(newv).Z < 0)) { angle = 2 * DMath.PI - angle; } var shf = KeyboardState.Shift; var def = Select.RotationStyle; var snap = (def == RotationStyle.SnapOnShift && shf) || (def == RotationStyle.SnapOffShift && !shf); if (snap) { var deg = angle * (180 / DMath.PI); var rnd = Math.Round(deg / 15) * 15; angle = rnd * (DMath.PI / 180); } Matrix4 rotm; if (viewport.Direction == Viewport2D.ViewDirection.Top) { rotm = Matrix4.CreateRotationZ((float)angle); } else if (viewport.Direction == Viewport2D.ViewDirection.Front) { rotm = Matrix4.CreateRotationX((float)angle); } else { rotm = Matrix4.CreateRotationY((float)-angle); // The Y axis rotation goes in the reverse direction for whatever reason } var mov = Matrix4.CreateTranslation((float)-origin.X, (float)-origin.Y, (float)-origin.Z); var rot = Matrix4.Mult(mov, rotm); return(Matrix4.Mult(rot, Matrix4.Invert(mov))); }