Files
opennurbs/opennurbs_sphere.cpp
2024-02-15 08:00:36 -08:00

364 lines
8.8 KiB
C++

//
// Copyright (c) 1993-2022 Robert McNeel & Associates. All rights reserved.
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
// McNeel & Associates.
//
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
//
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
//
////////////////////////////////////////////////////////////////
#include "opennurbs.h"
#if !defined(ON_COMPILING_OPENNURBS)
// This check is included in all opennurbs source .c and .cpp files to insure
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined
// and the opennurbs .h files alter what is declared and how it is declared.
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
#endif
ON_Sphere::ON_Sphere() : radius(0.0)
{}
ON_Sphere::ON_Sphere( const ON_3dPoint& center, double r)
{
Create(center,r);
}
ON_Sphere::~ON_Sphere()
{}
bool ON_Sphere::IsValid() const
{
return ( radius > 0.0 && radius < ON_UNSET_POSITIVE_VALUE && plane.IsValid() ) ? true : false;
}
bool ON_Sphere::Create( const ON_3dPoint& center, double r )
{
plane = ON_xy_plane;
plane.origin = center;
plane.UpdateEquation();
radius = r;
return (r > 0.0) ? true : false;
}
ON_3dPoint ON_Sphere::PointAt(double longitude, double latitude) const
{
return radius*NormalAt(longitude,latitude) + plane.origin;
}
ON_3dVector ON_Sphere::NormalAt(double longitude, double latitude) const
{
return cos(latitude)*(cos(longitude)*plane.xaxis + sin(longitude)*plane.yaxis) + sin(latitude)*plane.zaxis;
}
ON_Circle ON_Sphere::LatitudeRadians(double a) const
{
return ON_Circle(PointAt(0.0,a),PointAt(0.5*ON_PI,a),PointAt(ON_PI,a));
}
ON_Circle ON_Sphere::LatitudeDegrees(double a) const
{
return LatitudeRadians(a*ON_PI/180.0);
}
ON_Circle ON_Sphere::LongitudeRadians(double a) const
{
return ON_Circle(PointAt(a,0.0),NorthPole(),PointAt(a+ON_PI,0.0));
}
ON_Circle ON_Sphere::LongitudeDegrees(double a) const
{
return LongitudeRadians(a*ON_PI/180.0);
}
ON_3dPoint ON_Sphere::Center() const
{
return plane.origin;
}
ON_3dPoint ON_Sphere::NorthPole() const
{
return PointAt(0.0, 0.5*ON_PI);
}
ON_3dPoint ON_Sphere::SouthPole() const
{
return PointAt(0.0, -0.5*ON_PI);
}
double ON_Sphere::Radius() const
{
return radius;
}
double ON_Sphere::Diameter() const
{
return 2.0*radius;
}
bool ON_Sphere::ClosestPointTo(
ON_3dPoint point,
double* longitude,
double* latitude
) const
{
bool rc = true;
ON_3dVector v = point - plane.origin;
double h = v*plane.zaxis;
double x = v*plane.xaxis;
double y = v*plane.yaxis;
double r = 1.0;
if ( x == 0.0 && y == 0.0 ) {
if ( longitude )
*longitude = 0.0;
if ( latitude )
*latitude = (h>=0.0) ? 0.5*ON_PI : -0.5*ON_PI;
if ( h == 0.0 )
rc = false;
}
else {
if ( fabs(x) >= fabs(y) ) {
r = y/x;
r = fabs(x)*sqrt(1.0+r*r);
}
else {
r = x/y;
r = fabs(y)*sqrt(1.0+r*r);
}
if ( longitude ) {
*longitude = atan2(y,x);
if ( *longitude < 0.0 )
*longitude += 2.0*ON_PI;
if ( *longitude < 0.0 || *longitude >= 2.0*ON_PI)
*longitude = 0.0;
}
if ( latitude )
*latitude = atan(h/r);
}
return rc;
}
ON_BoundingBox ON_Sphere::BoundingBox() const
{
ON_BoundingBox bbox;
double r = Radius();
bbox.m_min = Center();
bbox.m_max = bbox.m_min;
bbox.m_min.x -= r;
bbox.m_min.y -= r;
bbox.m_min.z -= r;
bbox.m_max.x += r;
bbox.m_max.y += r;
bbox.m_max.z += r;
return bbox;
}
// returns point on cylinder that is closest to given point
ON_3dPoint ON_Sphere::ClosestPointTo( ON_3dPoint point ) const
{
ON_3dVector v = point - plane.origin;
v.Unitize();
return plane.origin + radius*v;
}
// rotate cylinder about its origin
bool ON_Sphere::Rotate(
double sin_angle,
double cos_angle,
const ON_3dVector& axis // axis of rotation
)
{
return Rotate(sin_angle, cos_angle, axis, plane.origin );
}
bool ON_Sphere::Rotate(
double angle, // angle in radians
const ON_3dVector& axis // axis of rotation
)
{
return Rotate(sin(angle), cos(angle), axis, plane.origin );
}
// rotate cylinder about a point and axis
bool ON_Sphere::Rotate(
double sin_angle,
double cos_angle,
const ON_3dVector& axis, // axis of rotation
const ON_3dPoint& point // center of rotation
)
{
return plane.Rotate( sin_angle, cos_angle, axis, point );
}
bool ON_Sphere::Rotate(
double angle, // angle in radians
const ON_3dVector& axis, // axis of rotation
const ON_3dPoint& point // center of rotation
)
{
return Rotate(sin(angle),cos(angle),axis,point);
}
bool ON_Sphere::Translate(
const ON_3dVector& delta
)
{
return plane.Translate(delta);
}
bool ON_Sphere::Transform( const ON_Xform& xform )
{
ON_Circle xc(plane,radius);
bool rc = xc.Transform(xform);
if (rc)
{
plane = xc.plane;
radius = xc.radius;
}
return rc;
}
int ON_Sphere::GetNurbForm( ON_NurbsSurface& s ) const
{
int rc = 0;
if ( IsValid() ) {
s.Create(3,true,3,3,9,5);
s.m_knot[0][0] = s.m_knot[0][1] = 0.0;
s.m_knot[0][2] = s.m_knot[0][3] = 0.5*ON_PI;
s.m_knot[0][4] = s.m_knot[0][5] = ON_PI;
s.m_knot[0][6] = s.m_knot[0][7] = 1.5*ON_PI;
s.m_knot[0][8] = s.m_knot[0][9] = 2.0*ON_PI;
s.m_knot[1][0] = s.m_knot[1][1] = -0.5*ON_PI;
s.m_knot[1][2] = s.m_knot[1][3] = 0.0;
s.m_knot[1][4] = s.m_knot[1][5] = 0.5*ON_PI;
ON_4dPoint* CV = (ON_4dPoint*)s.m_cv;
const ON_3dVector x = radius*plane.xaxis;
const ON_3dVector y = radius*plane.yaxis;
const ON_3dVector z = radius*plane.zaxis;
ON_3dPoint p[9] = {plane.origin+x,
plane.origin+x+y,
plane.origin+y,
plane.origin-x+y,
plane.origin-x,
plane.origin-x-y,
plane.origin-y,
plane.origin+x-y,
plane.origin+x};
const double w = 1.0/sqrt(2.0);
double w13;
int i;
ON_4dPoint southpole = plane.origin - z;
ON_4dPoint northpole = plane.origin + z;
for ( i = 0; i < 8; i++ ) {
CV[5*i ] = southpole;
CV[5*i+1] = p[i] - z;
CV[5*i+2] = p[i];
CV[5*i+3] = p[i] + z;
CV[5*i+4] = northpole;
if ( i%2) {
CV[5*i ].x *= w;
CV[5*i ].y *= w;
CV[5*i ].z *= w;
CV[5*i ].w = w;
CV[5*i+2].x *= w;
CV[5*i+2].y *= w;
CV[5*i+2].z *= w;
CV[5*i+2].w = w;
CV[5*i+4].x *= w;
CV[5*i+4].y *= w;
CV[5*i+4].z *= w;
CV[5*i+4].w = w;
w13 = 0.5;
}
else {
w13 = w;
}
CV[5*i+1].x *= w13;
CV[5*i+1].y *= w13;
CV[5*i+1].z *= w13;
CV[5*i+1].w = w13;
CV[5*i+3].x *= w13;
CV[5*i+3].y *= w13;
CV[5*i+3].z *= w13;
CV[5*i+3].w = w13;
}
CV[40] = CV[0];
CV[41] = CV[1];
CV[42] = CV[2];
CV[43] = CV[3];
CV[44] = CV[4];
rc = 2;
}
return rc;
}
ON_RevSurface* ON_Sphere::RevSurfaceForm( ON_RevSurface* srf ) const
{
return RevSurfaceForm(false,srf);
}
ON_RevSurface* ON_Sphere::RevSurfaceForm(
bool bArcLengthParameterization,
ON_RevSurface* srf
) const
{
if ( srf )
srf->Destroy();
ON_RevSurface* pRevSurface = nullptr;
if ( IsValid() )
{
ON_Arc arc;
arc.plane.origin = plane.origin;
arc.plane.xaxis = -plane.zaxis;
arc.plane.yaxis = plane.xaxis;
arc.plane.zaxis = -plane.yaxis;
arc.plane.UpdateEquation();
arc.radius = radius;
arc.SetAngleRadians(ON_PI);
ON_ArcCurve* arc_curve = new ON_ArcCurve( arc, -0.5*ON_PI, 0.5*ON_PI );
if ( srf )
pRevSurface = srf;
else
pRevSurface = new ON_RevSurface();
pRevSurface->m_angle.Set(0.0,2.0*ON_PI);
pRevSurface->m_t = pRevSurface->m_angle;
pRevSurface->m_curve = arc_curve;
pRevSurface->m_axis.from = plane.origin;
pRevSurface->m_axis.to = plane.origin + plane.zaxis;
pRevSurface->m_bTransposed = false;
pRevSurface->m_bbox.m_min = plane.origin;
pRevSurface->m_bbox.m_min.x -= radius;
pRevSurface->m_bbox.m_min.y -= radius;
pRevSurface->m_bbox.m_min.z -= radius;
pRevSurface->m_bbox.m_max = plane.origin;
pRevSurface->m_bbox.m_max.x += radius;
pRevSurface->m_bbox.m_max.y += radius;
pRevSurface->m_bbox.m_max.z += radius;
if ( bArcLengthParameterization )
{
double r = fabs(radius);
if ( !(r > ON_SQRT_EPSILON) )
r = 1.0;
r *= ON_PI;
pRevSurface->SetDomain(0,0.0,2.0*r);
pRevSurface->SetDomain(1,-0.5*r,0.5*r);
}
}
return pRevSurface;
}