// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkSpherePuzzleArrows.h" #include "vtkCellArray.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkMath.h" #include "vtkObjectFactory.h" #include "vtkPolyData.h" #include "vtkSpherePuzzle.h" #include VTK_ABI_NAMESPACE_BEGIN vtkStandardNewMacro(vtkSpherePuzzleArrows); //------------------------------------------------------------------------------ // Construct a new puzzle. vtkSpherePuzzleArrows::vtkSpherePuzzleArrows() { int idx; for (idx = 0; idx < 32; ++idx) { this->Permutation[idx] = idx; } this->Radius = 0.51; this->SetNumberOfInputPorts(0); } //------------------------------------------------------------------------------ // Destruct the puzzle. vtkSpherePuzzleArrows::~vtkSpherePuzzleArrows() = default; //------------------------------------------------------------------------------ void vtkSpherePuzzleArrows::SetPermutationComponent(int comp, int val) { if (this->Permutation[comp] == val) { return; } this->Permutation[comp] = val; this->Modified(); } //------------------------------------------------------------------------------ int vtkSpherePuzzleArrows::RequestData(vtkInformation* vtkNotUsed(request), vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* outputVector) { // get the info object vtkInformation* outInfo = outputVector->GetInformationObject(0); // get the output vtkPolyData* output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); vtkPoints* pts = vtkPoints::New(); vtkCellArray* polys = vtkCellArray::New(); int idx; for (idx = 0; idx < 32; ++idx) { if (this->CheckAbort()) { break; } if (this->Permutation[idx] != idx) { // this->AppendArrow(idx, this->Permutation[idx], pts, polys); this->AppendArrow(this->Permutation[idx], idx, pts, polys); } } output->SetPoints(pts); output->SetPolys(polys); pts->Delete(); polys->Delete(); return 1; } //------------------------------------------------------------------------------ // Draw an arrow for piece with id1 to piece with id2. void vtkSpherePuzzleArrows::AppendArrow(int id1, int id2, vtkPoints* pts, vtkCellArray* polys) { double phi1, phi2, dPhi; double theta1, theta2, dTheta; double phi = 0, theta = 0, phiOff, thetaOff; double length; double x, y, z; int num, idx; vtkIdType ptId1, ptId2, ptId3, ptId4, ptId5; // Convert the start and end piece ids into sphere coordinates. phi1 = vtkMath::Pi() * ((id1 / 8) + 0.5) / 4.0; theta1 = vtkMath::Pi() * ((id1 % 8) + 0.5) / 4.0; phi2 = vtkMath::Pi() * ((id2 / 8) + 0.5) / 4.0; theta2 = vtkMath::Pi() * ((id2 % 8) + 0.5) / 4.0; dPhi = phi2 - phi1; dTheta = theta2 - theta1; // Take the short way around. while (dPhi > vtkMath::Pi()) { dPhi -= 2 * vtkMath::Pi(); } while (dPhi < -vtkMath::Pi()) { dPhi += 2 * vtkMath::Pi(); } while (dTheta > vtkMath::Pi()) { dTheta -= 2 * vtkMath::Pi(); } while (dTheta < -vtkMath::Pi()) { dTheta += 2 * vtkMath::Pi(); } theta2 = theta1 + dTheta; phi2 = phi1 + dPhi; // Compute the length (world coords). length = dTheta * sin(0.5 * (phi1 + phi2)); length = sqrt(length * length + dPhi * dPhi); // How many division do we need. num = (int)(length / 0.1); // Compute the perpendicular phi theta step. thetaOff = dPhi; phiOff = -dTheta; // Normalize (sphere coords). length = sqrt(thetaOff * thetaOff + phiOff * phiOff); phiOff = 0.08 * phiOff / length; thetaOff = 0.08 * thetaOff / length; // First point. x = cos(theta1 + thetaOff) * sin(phi1 + phiOff); y = sin(theta1 + thetaOff) * sin(phi1 + phiOff); z = cos(phi1 + phiOff); ptId1 = pts->InsertNextPoint(this->Radius * x, this->Radius * y, this->Radius * z); x = cos(theta1 + 2 * thetaOff) * sin(phi1 + 2 * phiOff); y = sin(theta1 + 2 * thetaOff) * sin(phi1 + 2 * phiOff); z = cos(phi1 + 2 * phiOff); ptId2 = pts->InsertNextPoint(this->Radius * x, this->Radius * y, this->Radius * z); for (idx = 1; idx < num; ++idx) { // Interpolate angles. theta = theta1 + ((double)(idx) / (double)(num)) * dTheta; phi = phi1 + ((double)(idx) / (double)(num)) * (phi2 - phi1); x = cos(theta + thetaOff) * sin(phi + phiOff); y = sin(theta + thetaOff) * sin(phi + phiOff); z = cos(phi + phiOff); ptId3 = pts->InsertNextPoint(this->Radius * x, this->Radius * y, this->Radius * z); x = cos(theta + 2 * thetaOff) * sin(phi + 2 * phiOff); y = sin(theta + 2 * thetaOff) * sin(phi + 2 * phiOff); z = cos(phi + 2 * phiOff); ptId4 = pts->InsertNextPoint(this->Radius * x, this->Radius * y, this->Radius * z); // Create the rectangle. polys->InsertNextCell(4); polys->InsertCellPoint(ptId1); polys->InsertCellPoint(ptId2); polys->InsertCellPoint(ptId4); polys->InsertCellPoint(ptId3); // Initialize the next step. ptId1 = ptId3; ptId2 = ptId4; } // Now create the arrow. x = cos(theta) * sin(phi); y = sin(theta) * sin(phi); z = cos(phi); ptId3 = pts->InsertNextPoint(this->Radius * x, this->Radius * y, this->Radius * z); x = cos(theta + 3 * thetaOff) * sin(phi + 3 * phiOff); y = sin(theta + 3 * thetaOff) * sin(phi + 3 * phiOff); z = cos(phi + 3 * phiOff); ptId4 = pts->InsertNextPoint(this->Radius * x, this->Radius * y, this->Radius * z); x = cos(theta2 + 1.5 * thetaOff) * sin(phi2 + 1.5 * phiOff); y = sin(theta2 + 1.5 * thetaOff) * sin(phi2 + 1.5 * phiOff); z = cos(phi2 + 1.5 * phiOff); ptId5 = pts->InsertNextPoint(this->Radius * x, this->Radius * y, this->Radius * z); polys->InsertNextCell(5); polys->InsertCellPoint(ptId5); polys->InsertCellPoint(ptId4); polys->InsertCellPoint(ptId2); polys->InsertCellPoint(ptId1); polys->InsertCellPoint(ptId3); } //------------------------------------------------------------------------------ void vtkSpherePuzzleArrows::SetPermutation(vtkSpherePuzzle* puz) { this->SetPermutation(puz->GetState()); } //------------------------------------------------------------------------------ void vtkSpherePuzzleArrows::PrintSelf(ostream& os, vtkIndent indent) { int i; this->Superclass::PrintSelf(os, indent); os << indent << "Permutation: "; for (i = 0; i < 32; ++i) { os << this->Permutation[i] << " "; } os << endl; } VTK_ABI_NAMESPACE_END