double vtIcoGlobe::AddSurfaceLineToMesh(vtGeomFactory *pMF, const DLine2 &line) { DPoint2 g1, g2; DPoint3 p1, p2; double scale = 1.0002; int length = 0; DMatrix3 rot3; pMF->PrimStart(); int i, j, size = line.GetSize(); for (i = 0; i < size-1; i++) { g1 = line.GetAt(i); g2 = line.GetAt(i+1); // for each pair of points, determine how many more points are needed // for a smooth arc geo_to_xyz(1.0, g1, p1); geo_to_xyz(1.0, g2, p2); double angle = acos(p1.Dot(p2)); int segments = (int) (angle * 2000); if (segments < 1) segments = 1; if (segments > 1) { // calculate the axis of rotation DPoint3 cross = p1.Cross(p2); cross.Normalize(); rot3.AxisAngle(cross, angle / segments); } // curved arc on great-circle path for (j = 0; j < segments; j++) { FPoint3 fp = p1 * 1.0002; pMF->AddVertex(fp); length++; if (j < segments-1) { rot3.Transform(p1, p2); p1 = p2; } } } // last vertex if (size > 1) { g2 = line.GetAt(size-1); geo_to_xyz(1.0, g2, p2); pMF->AddVertex(p2 * scale); length++; } pMF->PrimEnd(); return 0.0; }
/** * Given a geographic coordinate (lon, lat), find the corresponding * face, subface, and UVW coordinates on surface of the icosahedron. */ void DymaxIcosa::FindFaceUV(const DPoint2 &p, int &face, int &subface, DPoint3 &uvw) { DPoint3 p3; geo_to_xyz(p, p3); FindFace(p3, face, subface); FindUV(p3, face, uvw); }
double vtIcoGlobe::AddSurfaceLineToMesh(vtGeomFactory *pMF, const DPoint2 &g1, const DPoint2 &g2) { // first determine how many points we should use for a smooth arc DPoint3 p1, p2; geo_to_xyz(1.0, g1, p1); geo_to_xyz(1.0, g2, p2); double dot = p1.Dot(p2); double angle = acos(dot); int points = (int) (angle * 3000); if (points < 2) points = 2; pMF->PrimStart(); if (points == 2) { // simple case pMF->AddVertex(p1*1.0002); pMF->AddVertex(p2*1.0002); } else { // calculate the axis of rotation DPoint3 cross = p1.Cross(p2); cross.Normalize(); double angle_spacing = angle / (points-1); DMatrix4 rot4; rot4.AxisAngle(cross, angle_spacing); DMatrix3 rot3; rot3.SetByMatrix4(rot4); // curved arc on great-circle path for (int i = 0; i < points; i++) { FPoint3 fp = p1 * 1.0002; pMF->AddVertex(fp); rot3.Transform(p1, p2); p1 = p2; } } pMF->PrimEnd(); return angle; }
/** * Given a geographic coordinate (lon, lat), find the corresponding * point on the surface of the icosahedron. */ void DymaxIcosa::GeoToFacePoint(const DPoint2 &p, int &face, int &subface, DPoint3 &p_out) { DPoint3 p3, uvw; geo_to_xyz(p, p3); FindFace(p3, face, subface); FindUV(p3, face, uvw); p_out = m_face[face].base + (m_face[face].vec_a * uvw.x) + (m_face[face].vec_b * uvw.y); }
void vtIcoGlobe::BuildSphericalPoints(GlobeLayer *glay, float fSize) { vtFeatureSet *feat = glay->m_pSet; int i, j, size; vtArray<FSphere> spheres; size = feat->GetNumEntities(); spheres.SetSize(size); vtFeatureSetPoint2D *pSetP2 = dynamic_cast<vtFeatureSetPoint2D*>(feat); if (!pSetP2) return; DPoint2 p; for (i = 0; i < size; i++) { pSetP2->GetPoint(i, p); if (p.x == 0.0 && p.y == 0.0) // ignore some continue; FPoint3 loc; geo_to_xyz(1.0, p, loc); spheres[i].center = loc; spheres[i].radius = fSize; } FPoint3 diff; // volume of sphere, 4/3 PI r^3 // surface area of sphere, 4 PI r^2 // area of circle of sphere as seen from distance, PI r^2 int merges; do { merges = 0; // Try merging overlapping points together, so that information // is not lost in the overlap. // To consider: do we combine the blobs based on their 2d radius, // their 2d area, their 3d radius, or their 3d volume? See // Tufte, http://www.edwardtufte.com/ // Implemented here: preserve 2d area for (i = 0; i < size-1; i++) { for (j = i+1; j < size; j++) { if (spheres[i].radius == 0.0f || spheres[j].radius == 0.0f) continue; diff = spheres[i].center - spheres[j].center; // if one sphere contains the center of the other if (diff.Length() < spheres[i].radius || diff.Length() < spheres[j].radius) { // combine float area1 = PIf * spheres[i].radius * spheres[i].radius; float area2 = PIf * spheres[j].radius * spheres[j].radius; float combined = (area1 + area2); float newrad = sqrtf( combined / PIf ); // larger eats the smaller if (area1 > area2) { spheres[i].radius = newrad; spheres[j].radius = 0.0f; } else { spheres[j].radius = newrad; spheres[i].radius = 0.0f; } merges++; break; } } } } while (merges != 0); // Now create and place the little geometry objects to represent the // point data. #if 0 // create simple hemisphere mesh int res = 6; vtMesh *mesh = new vtMesh(osg::PrimitiveSet::TRIANGLE_STRIP, 0, res*res*2); FPoint3 scale(1.0f, 1.0f, 1.0f); mesh->CreateEllipsoid(scale, res, true); #else // create cylinder mesh instead int res = 14; int verts = res * 2; vtMesh *mesh = new vtMesh(osg::PrimitiveSet::TRIANGLE_STRIP, 0, verts); mesh->CreateCylinder(1.0f, 1.0f, res, true, false, false); #endif // use Area to show amount, otherwise height bool bArea = true; // create and place the geometries size = spheres.GetSize(); for (i = 0; i < size; i++) { if (spheres[i].radius == 0.0f) continue; vtGeode *geode = new vtGeode; geode->SetMaterials(m_coremats); geode->AddMesh(mesh, m_yellow); vtMovGeode *mgeom = new vtMovGeode(geode); mgeom->setName("GlobeShape"); mgeom->PointTowards(spheres[i].center); mgeom->RotateLocal(FPoint3(1,0,0), -PID2f); mgeom->SetTrans(spheres[i].center); if (bArea) { // scale just the radius of the cylinder mgeom->Scale3(spheres[i].radius, 0.001f, spheres[i].radius); } else { // scale just the height of the cylinder double area = PIf * spheres[i].radius * spheres[i].radius; mgeom->Scale3(0.002f, (float)area*1000, 0.002f); } m_SurfaceGroup->addChild(mgeom); glay->addChild(mgeom); } }
void geo_to_xyz(double radius, const DPoint2 &geo, FPoint3 &p) { DPoint3 dp; geo_to_xyz(radius, geo, dp); p = dp; }
/** * Given a geographic coordinate (lon, lat), find the corresponding * dymaxion map coordinate, on the classic flattened dymaxion map. * The output is unit-edge triangles, which means the whole output * extents are x [0, 5.5] and y [0, 2.6]. */ bool DymaxIcosa::GeoToDymax(const DPoint2 &geo, DPoint2 &dymax) { DPoint3 p3, uvw; int face, subface; geo_to_xyz(geo, p3); FindFace(p3, face, subface); FindUV(p3, face, uvw); DPoint2 uv(uvw.x, uvw.y); //assert(uv.x <= 1 && uv.y <= 1); // Not exactly sure why we need to do this - apparently the UV are // assuming a unit-radius, rather than unit-edge icosahedron uv /= m_edge_length; int a, b, c; switch (face) { case 0: a = 17; b = 22; c = 16; break; case 1: a = 17; b = 16; c = 10; break; case 2: a = 17; b = 10; c = 11; break; case 3: a = 17; b = 11; c = 18; break; case 4: a = 17; b = 23; c = 22; break; case 5: a = 21; b = 16; c = 22; break; case 6: a = 16; b = 15; c = 9; break; case 7: a = 9; b = 10; c = 16; break; case 8: if (subface == 4 || subface == 5) { a = 10; b = 1; c = 2; } else { a = 10; b = 9; c = 1; } break; case 9: a = 2; b = 11; c = 10; break; case 10: a = 11; b = 3; c = 12; break; case 11: a = 12; b = 18; c = 11; break; case 12: a = 18; b = 12; c = 19; break; case 13: a = 19; b = 24; c = 18; break; case 14: a = 25; b = 19; c = 20; break; case 15: if (subface == 0 || subface == 4 || subface == 5) { a = 13; b = 26; c = 20; } else { a = 0; b = 9; c = 8; } break; case 16: a = 0; b = 1; c = 9; break; case 17: a = 4; b = 12; c = 3; break; case 18: a = 13; b = 19; c = 12; break; case 19: a = 13; b = 20; c = 19; break; default: return false; } DPoint2 base = m_flatverts[a]; DPoint2 vec1 = m_flatverts[b] - base; DPoint2 vec2 = m_flatverts[c] - base; dymax = base + (vec1 * uv.x) + (vec2 * uv.y); return true; }