//---------------------------------------------------------------------------- void Boolean2D::DrawPolySolid (BspPolygon2& polygon, ColorRGB color) { Vector2d v0, v1; Edge2 edge; int i, x0, y0, x1, y1; // Draw the edges. for (i = 0; i < polygon.GetNumEdges(); ++i) { polygon.GetEdge(i, edge); polygon.GetVertex(edge.I0, v0); polygon.GetVertex(edge.I1, v1); x0 = (int)(v0.X() + 0.5f); y0 = GetWidth() - 1 - (int)(v0.Y() + 0.5f); x1 = (int)(v1.X() + 0.5f); y1 = GetWidth() - 1 - (int)(v1.Y() + 0.5f); DrawLine(x0, y0, x1, y1, color); } // Draw the vertices. ColorRGB black(0, 0, 0); for (i = 0; i < polygon.GetNumVertices(); ++i) { polygon.GetVertex(i, v0); x0 = (int)(v0.X() + 0.5f); y0 = GetWidth() - 1 - (int)(v0.Y() + 0.5f); SetThickPixel(x0, y0, 1, black); } }
//---------------------------------------------------------------------------- BspPolygon2* Boolean2D::ConstructPentagon () { const int numVertices = 5; double primitiveAngle = Mathd::TWO_PI/numVertices; double radius = 0.35*GetWidth(); double cx = 0.5*GetWidth(), cy = 0.5*GetWidth(); Vector2d vertices[numVertices]; for (int i = 0; i < numVertices; ++i) { double angle = i*primitiveAngle; vertices[i].X() = cx + radius*Mathd::Cos(angle); vertices[i].Y() = cy + radius*Mathd::Sin(angle); } BspPolygon2* poly = new0 BspPolygon2(); for (int i0 = numVertices - 1, i1 = 0; i1 < numVertices; i0 = i1++) { poly->InsertVertex(vertices[i1]); poly->InsertEdge(Edge2(i0, i1)); } poly->Finalize(); return poly; }
//---------------------------------------------------------------------------- BspPolygon2* Boolean2D::ConstructInvertedEll () { double w = (double)GetWidth(); double d1d8 = 0.125*w; double d2d8 = 0.250*w; double d3d8 = 0.375*w; double d5d8 = 0.625*w; double d6d8 = 0.750*w; double d7d8 = 0.875*w; const int numVertices = 10; Vector2d vertices[numVertices] = { Vector2d(d1d8, d1d8), Vector2d(d3d8, d1d8), Vector2d(d3d8, d3d8), Vector2d(d2d8, d3d8), Vector2d(d2d8, d6d8), Vector2d(d5d8, d6d8), Vector2d(d5d8, d5d8), Vector2d(d7d8, d5d8), Vector2d(d7d8, d7d8), Vector2d(d1d8, d7d8) }; BspPolygon2* poly = new0 BspPolygon2(); for (int i0 = numVertices - 1, i1 = 0; i1 < numVertices; i0 = i1++) { poly->InsertVertex(vertices[i1]); poly->InsertEdge(Edge2(i0, i1)); } poly->Finalize(); return poly; }
//---------------------------------------------------------------------------- BspPolygon2 BspPolygon2::operator& (const BspPolygon2& polygon) const { assertion(mTree != 0, "Tree must exist.\n"); // intersection BspPolygon2 intersect; GetInsideEdgesFrom(polygon, intersect); polygon.GetInsideEdgesFrom(*this, intersect); intersect.Finalize(); return intersect; }
//---------------------------------------------------------------------------- void BspTree2::GetNegPartition (const BspPolygon2& polygon, const Vector2d& v0, const Vector2d& v1, BspPolygon2& pos, BspPolygon2& neg, BspPolygon2& coSame, BspPolygon2& coDiff) const { if (mNegChild) { mNegChild->GetPartition(polygon, v0, v1, pos, neg, coSame, coDiff); } else { int i0 = neg.InsertVertex(v0); int i1 = neg.InsertVertex(v1); neg.InsertEdge(Edge2(i0, i1)); } }
//---------------------------------------------------------------------------- BspPolygon2 BspPolygon2::operator~ () const { assertion(mTree != 0, "Tree must exist.\n"); // negation BspPolygon2 neg; neg.mVMap = mVMap; neg.mVArray = mVArray; ECIterator iter = mEMap.begin(); ECIterator end = mEMap.end(); for (/**/; iter != end; ++iter) { neg.InsertEdge(Edge2(iter->first.I1, iter->first.I0)); } neg.mTree = mTree->GetCopy(); neg.mTree->Negate(); return neg; }
//---------------------------------------------------------------------------- BspPolygon2* Boolean2D::ConstructSShape () { double w = (double)GetWidth(); double d10d32 = 10.0*w/32.0; double d12d32 = 12.0*w/32.0; double d13d32 = 13.0*w/32.0; double d16d32 = 16.0*w/32.0; double d19d32 = 19.0*w/32.0; double d20d32 = 20.0*w/32.0; double d22d32 = 22.0*w/32.0; double d24d32 = 24.0*w/32.0; double d26d32 = 26.0*w/32.0; double d28d32 = 28.0*w/32.0; const int numVertices = 12; Vector2d vertices[numVertices] = { Vector2d(d24d32, d10d32), Vector2d(d28d32, d10d32), Vector2d(d28d32, d16d32), Vector2d(d22d32, d16d32), Vector2d(d22d32, d19d32), Vector2d(d24d32, d19d32), Vector2d(d24d32, d22d32), Vector2d(d20d32, d22d32), Vector2d(d20d32, d13d32), Vector2d(d26d32, d13d32), Vector2d(d26d32, d12d32), Vector2d(d24d32, d12d32) }; BspPolygon2* poly = new0 BspPolygon2(); for (int i0 = numVertices - 1, i1 = 0; i1 < numVertices; i0 = i1++) { poly->InsertVertex(vertices[i1]); poly->InsertEdge(Edge2(i0, i1)); } poly->Finalize(); return poly; }
//---------------------------------------------------------------------------- BspPolygon2* Boolean2D::ConstructSquare () { double w = (double)GetWidth(); double d2d8 = 0.250*w; double d6d8 = 0.750*w; const int numVertices = 4; Vector2d vertices[numVertices] = { Vector2d(d2d8, d2d8), Vector2d(d6d8, d2d8), Vector2d(d6d8, d6d8), Vector2d(d2d8, d6d8) }; BspPolygon2* poly = new0 BspPolygon2(); for (int i0 = numVertices - 1, i1 = 0; i1 < numVertices; i0 = i1++) { poly->InsertVertex(vertices[i1]); poly->InsertEdge(Edge2(i0, i1)); } poly->Finalize(); return poly; }
//---------------------------------------------------------------------------- void BspPolygon2::GetInsideEdgesFrom (const BspPolygon2& polygon, BspPolygon2& inside) const { assertion(mTree != 0, "Tree must exist.\n"); BspPolygon2 ignore; const int numEdges = polygon.GetNumEdges(); for (int i = 0; i < numEdges; ++i) { int v0 = polygon.mEArray[i].I0; int v1 = polygon.mEArray[i].I1; Vector2d vertex0 = polygon.mVArray[v0]; Vector2d vertex1 = polygon.mVArray[v1]; mTree->GetPartition(*this, vertex0, vertex1, ignore, inside, inside, ignore); } }
//---------------------------------------------------------------------------- BspPolygon2* Boolean2D::ConstructPolyWithHoles () { double w = (double)GetWidth(); double d2d16 = 2.0*w/16.0; double d3d16 = 3.0*w/16.0; double d4d16 = 4.0*w/16.0; double d6d16 = 6.0*w/16.0; double d14d16 = 14.0*w/16.0; const int numVertices = 6; Vector2d vertices[numVertices] = { // outer boundary Vector2d(d2d16, d2d16), Vector2d(d14d16, d2d16), Vector2d(d2d16, d14d16), // inner boundary Vector2d(d4d16, d3d16), Vector2d(d6d16, d6d16), Vector2d(d6d16, d3d16) }; BspPolygon2* poly = new0 BspPolygon2(); for (int i = 0; i < numVertices; ++i) { poly->InsertVertex(vertices[i]); } poly->InsertEdge(Edge2(0, 1)); poly->InsertEdge(Edge2(1, 2)); poly->InsertEdge(Edge2(2, 0)); poly->InsertEdge(Edge2(3, 4)); poly->InsertEdge(Edge2(4, 5)); poly->InsertEdge(Edge2(5, 3)); poly->Finalize(); return poly; }
//---------------------------------------------------------------------------- void BspTree2::GetCoPartition (const BspPolygon2& polygon, const Vector2d& v0, const Vector2d& v1, BspPolygon2& pos, BspPolygon2& neg, BspPolygon2& coSame, BspPolygon2& coDiff) const { const double epsilon = 0.00001; // Segment the line containing V0 and V1 by the coincident intervals that // intersect <V0,V1>. Vector2d dir = v1 - v0; double tmax = dir.Dot(dir); Vector2d end0, end1; double t0, t1; bool sameDir; std::list<Interval> intervalList; std::list<Interval>::iterator iter; const int numEdges = (int)mCoincident.size(); for (int i = 0; i < numEdges; ++i) { end0 = polygon.mVArray[mCoincident[i].I0]; end1 = polygon.mVArray[mCoincident[i].I1]; t0 = dir.Dot(end0 - v0); if (Mathd::FAbs(t0) <= epsilon) { t0 = 0.0; } else if (Mathd::FAbs(t0 - tmax) <= epsilon) { t0 = tmax; } t1 = dir.Dot(end1 - v0); if (Mathd::FAbs(t1) <= epsilon) { t1 = 0.0; } else if (Mathd::FAbs(t1 - tmax) <= epsilon) { t1 = tmax; } sameDir = (t1 > t0); if (!sameDir) { double save = t0; t0 = t1; t1 = save; } if (t1 > 0.0 && t0 < tmax) { if (intervalList.empty()) { intervalList.push_front(Interval(t0, t1, sameDir, true)); } else { iter = intervalList.begin(); for (/**/; iter != intervalList.end(); ++iter) { if (Mathd::FAbs(t1 - iter->T0) <= epsilon) { t1 = iter->T0; } if (t1 <= iter->T0) { // [t0,t1] is on the left of [I.t0,I.t1] intervalList.insert(iter, Interval(t0, t1, sameDir, true)); break; } // Theoretically, the intervals are disjoint or intersect // only at an end point. The assert makes sure that // [t0,t1] is to the right of [I.t0,I.t1]. if (Mathd::FAbs(t0 - iter->T1) <= epsilon) { t0 = iter->T1; } assertion(t0 >= iter->T1, "Invalid ordering.\n"); std::list<Interval>::iterator last = intervalList.end(); --last; if (iter == last) { intervalList.push_back(Interval(t0, t1, sameDir, true)); break; } } } } } if (intervalList.empty()) { GetPosPartition(polygon, v0, v1, pos, neg, coSame, coDiff); GetNegPartition(polygon, v0, v1, pos, neg, coSame, coDiff); return; } // Insert outside intervals between the touching intervals. It is // possible that two touching intervals are adjacent, so this is not just // a simple alternation of touching and outside intervals. Interval& front = intervalList.front(); if (front.T0 > 0.0) { intervalList.push_front(Interval(0.0, front.T0, front.SameDir, false)); } else { front.T0 = 0.0; } Interval& back = intervalList.back(); if (back.T1 < tmax) { intervalList.push_back(Interval(back.T1, tmax, back.SameDir, false)); } else { back.T1 = tmax; } std::list<Interval>::iterator iter0 = intervalList.begin(); std::list<Interval>::iterator iter1 = intervalList.begin(); for (++iter1; iter1 != intervalList.end(); ++iter0, ++iter1) { t0 = iter0->T1; t1 = iter1->T0; if (t1 - t0 > epsilon) { iter0 = intervalList.insert(iter1, Interval(t0, t1, true, false)); } } // Process the segmentation. double invTMax = 1.0/tmax; t0 = intervalList.front().T0*invTMax; end1 = v0 + (intervalList.front().T0*invTMax)*dir; iter = intervalList.begin(); for (/**/; iter != intervalList.end(); ++iter) { end0 = end1; t1 = iter->T1*invTMax; end1 = v0 + (iter->T1*invTMax)*dir; if (iter->Touching) { Edge2 edge; if (iter->SameDir) { edge.I0 = coSame.InsertVertex(end0); edge.I1 = coSame.InsertVertex(end1); if (edge.I0 != edge.I1) { coSame.InsertEdge(edge); } } else { edge.I0 = coDiff.InsertVertex(end1); edge.I1 = coDiff.InsertVertex(end0); if (edge.I0 != edge.I1) { coDiff.InsertEdge(edge); } } } else { GetPosPartition(polygon, end0, end1, pos, neg, coSame, coDiff); GetNegPartition(polygon, end0, end1, pos, neg, coSame, coDiff); } } }
//---------------------------------------------------------------------------- BspTree2::BspTree2 (BspPolygon2& polygon, const EArray& edges) { assertion(edges.size() > 0, "Invalid input.\n"); // Construct splitting line from first edge. Vector2d end0 = polygon.mVArray[edges[0].I0]; Vector2d end1 = polygon.mVArray[edges[0].I1]; // Add edge to coincident list. mCoincident.push_back(edges[0]); // Split remaining edges. EArray posArray, negArray; int imax = (int)edges.size(); for (int i = 1; i < imax; ++i) { int v0 = edges[i].I0; int v1 = edges[i].I1; Vector2d vertex0 = polygon.mVArray[v0]; Vector2d vertex1 = polygon.mVArray[v1]; Vector2d intr; int vmid; switch (Classify(end0, end1, vertex0, vertex1, intr)) { case TRANSVERSE_POSITIVE: // modify edge <V0,V1> to <V0,I> and add new edge <I,V1> vmid = polygon.InsertVertex(intr); polygon.SplitEdge(v0, v1, vmid); posArray.push_back(Edge2(vmid, v1)); negArray.push_back(Edge2(v0, vmid)); break; case TRANSVERSE_NEGATIVE: // modify edge <V0,V1> to <V0,I> and add new edge <I,V1> vmid = polygon.InsertVertex(intr); polygon.SplitEdge(v0, v1, vmid); posArray.push_back(Edge2(v0, vmid)); negArray.push_back(Edge2(vmid, v1)); break; case ALL_POSITIVE: posArray.push_back(edges[i]); break; case ALL_NEGATIVE: negArray.push_back(edges[i]); break; default: // COINCIDENT mCoincident.push_back(edges[i]); break; } } if (posArray.size() > 0) { mPosChild = new0 BspTree2(polygon, posArray); } else { mPosChild = 0; } if (negArray.size() > 0) { mNegChild = new0 BspTree2(polygon, negArray); } else { mNegChild = 0; } }