text:
//
//   Copyright 2013 Pixar
//
//   Licensed under the Apache License, Version 2.0 (the "Apache License")
//   with the following modification; you may not use this file except in
//   compliance with the Apache License and the following modification to it:
//   Section 6. Trademarks. is deleted and replaced with:
//
//   6. Trademarks. This License does not grant permission to use the trade
//      names, trademarks, service marks, or product names of the Licensor
//      and its affiliates, except as required to comply with Section 4(c) of
//      the License and to reproduce the content of the NOTICE file.
//
//   You may obtain a copy of the Apache License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the Apache License with the above modification is
//   distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
//   KIND, either express or implied. See the Apache License for the specific
//   language governing permissions and limitations under the Apache License.
//

#ifndef HBRBILINEAR_H
#define HBRBILINEAR_H

/*#define HBR_DEBUG */
#include "../hbr/subdivision.h"

#include "../version.h"

namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {

template <class T>
class HbrBilinearSubdivision : public HbrSubdivision<T> {
public:
    HbrBilinearSubdivision<T>()
        : HbrSubdivision<T>() {}

    virtual HbrSubdivision<T>* Clone() const {
        return new HbrBilinearSubdivision<T>();
    }

    virtual void Refine(HbrMesh<T>* mesh, HbrFace<T>* face);
    virtual HbrFace<T>* RefineFaceAtVertex(HbrMesh<T>* mesh, HbrFace<T>* face, HbrVertex<T>* vertex);
    virtual void GuaranteeNeighbor(HbrMesh<T>* mesh, HbrHalfedge<T>* edge);
    virtual void GuaranteeNeighbors(HbrMesh<T>* mesh, HbrVertex<T>* vertex);

    virtual bool HasLimit(HbrMesh<T>* mesh, HbrFace<T>* face);
    virtual bool HasLimit(HbrMesh<T>* mesh, HbrHalfedge<T>* edge);
    virtual bool HasLimit(HbrMesh<T>* mesh, HbrVertex<T>* vertex);

    virtual HbrVertex<T>* Subdivide(HbrMesh<T>* mesh, HbrFace<T>* face);
    virtual HbrVertex<T>* Subdivide(HbrMesh<T>* mesh, HbrHalfedge<T>* edge);
    virtual HbrVertex<T>* Subdivide(HbrMesh<T>* mesh, HbrVertex<T>* vertex);

    virtual bool VertexIsExtraordinary(HbrMesh<T> const * /* mesh */, HbrVertex<T>* vertex) { return vertex->GetValence() != 4; }
    virtual bool FaceIsExtraordinary(HbrMesh<T> const * /* mesh */, HbrFace<T>* face) { return face->GetNumVertices() != 4; }

    virtual int GetFaceChildrenCount(int nvertices) const { return nvertices; }

private:

    // Transfers facevarying data from a parent face to a child face
    void transferFVarToChild(HbrMesh<T>* mesh, HbrFace<T>* face, HbrFace<T>* child, int index);

    // Transfers vertex and edge edits from a parent face to a child face
    void transferEditsToChild(HbrFace<T>* face, HbrFace<T>* child, int index);
};

template <class T>
void
HbrBilinearSubdivision<T>::transferFVarToChild(HbrMesh<T>* mesh, HbrFace<T>* face, HbrFace<T>* child, int index) {

    typename HbrMesh<T>::InterpolateBoundaryMethod fvarinterp = mesh->GetFVarInterpolateBoundaryMethod();
    const int fvarcount = mesh->GetFVarCount();
    int fvarindex = 0;
    const int nv = face->GetNumVertices();
    bool extraordinary = (nv != 4);
    HbrVertex<T> *v = face->GetVertex(index), *childVertex;
    HbrHalfedge<T>* edge;

    // We do the face subdivision rule first, because we may reuse the
    // result (stored in fv2) for the other subdivisions.
    float weight =  1.0f / nv;

    // For the face center vertex, the facevarying data can be cleared
    // and averaged en masse, since the subdivision rules don't change
    // for any of the data - we use the smooth rule for all of it.
    // And since we know that the fvardata for this particular vertex
    // is smooth and therefore shareable amongst all incident faces,
    // we don't have to allocate extra storage for it. We also don't
    // have to compute it if some other face got to it first (as
    // indicated by the IsInitialized() flag).
    HbrFVarData<T>& fv2 = child->GetFVarData(extraordinary ? 2 : (index+2)%4);
    if (!fv2.IsInitialized()) {
        const int totalfvarwidth = mesh->GetTotalFVarWidth();
        fv2.ClearAll(totalfvarwidth);
        for (int j = 0; j < nv; ++j) {
            fv2.AddWithWeightAll(face->GetFVarData(j), totalfvarwidth, weight);
        }
    }
    assert(fv2.IsInitialized());

    v->GuaranteeNeighbors();

    // Make sure that that each of the vertices of the child face have
    // the appropriate facevarying storage as needed. If there are
    // discontinuities in any facevarying datum, the vertex must
    // allocate a new block of facevarying storage specific to the
    // child face.
    bool fv0IsSmooth, fv1IsSmooth, fv3IsSmooth;

    childVertex = child->GetVertex(extraordinary ? 0 : (index+0)%4);
    fv0IsSmooth = v->IsFVarAllSmooth();
    if (!fv0IsSmooth) {
        childVertex->NewFVarData(child);
    }
    HbrFVarData<T>& fv0 = childVertex->GetFVarData(child);

    edge = face->GetEdge(index);
    GuaranteeNeighbor(mesh, edge);
    assert(edge->GetOrgVertex() == v);
    childVertex = child->GetVertex(extraordinary ? 1 : (index+1)%4);
    fv1IsSmooth = !edge->IsFVarInfiniteSharpAnywhere();
    if (!fv1IsSmooth) {
        childVertex->NewFVarData(child);
    }
    HbrFVarData<T>& fv1 = childVertex->GetFVarData(child);

    edge = edge->GetPrev();
    GuaranteeNeighbor(mesh, edge);
    assert(edge == face->GetEdge((index + nv - 1) % nv));
    assert(edge->GetDestVertex() == v);
    childVertex = child->GetVertex(extraordinary ? 3 : (index+3)%4);
    fv3IsSmooth = !edge->IsFVarInfiniteSharpAnywhere();
    if (!fv3IsSmooth) {
        childVertex->NewFVarData(child);
    }
    HbrFVarData<T>& fv3 = childVertex->GetFVarData(child);
    fvarindex = 0;
    for (int fvaritem = 0; fvaritem < fvarcount; ++fvaritem) {
        // Vertex subdivision rule. Analyze whether the vertex is on the
        // boundary and whether it's an infinitely sharp corner. We
        // determine the last by checking the propagate corners flag on
        // the mesh; if it's off, we check the two edges of this face
        // incident to that vertex and determining whether they are
        // facevarying boundary edges - this is analogous to what goes on
        // for the interpolateboundary tag (which when set to
        // EDGEANDCORNER marks vertices with a valence of two as being
        // sharp corners). If propagate corners is on, we check *all*
        // faces to see if two edges side by side are facevarying boundary
        // edges. The facevarying boundary check ignores geometric
        // sharpness, otherwise we may swim at geometric creases which
        // aren't actually discontinuous.
        bool infcorner = false;
        const int fvarwidth = mesh->GetFVarWidths()[fvaritem];
        const unsigned char fvarmask = v->GetFVarMask(fvaritem);
        if (fvarinterp == HbrMesh<T>::k_InterpolateBoundaryEdgeAndCorner) {
            if (fvarmask >= HbrVertex<T>::k_Corner) {
                infcorner = true;
            } else if (mesh->GetFVarPropagateCorners()) {
                if (v->IsFVarCorner(fvaritem)) {
                    infcorner = true;
                }
            } else {
                if (face->GetEdge(index)->GetFVarSharpness(fvaritem, true) && face->GetEdge(index)->GetPrev()->GetFVarSharpness(fvaritem, true)) {
                    infcorner = true;
                }
            }
        }

        // Infinitely sharp vertex rule. Applied if the vertex is:
        // - undergoing no facevarying boundary interpolation;
        // - at a geometric crease, in either boundary interpolation case; or
        // - is an infinitely sharp facevarying vertex, in the EDGEANDCORNER case; or
        // - has a mask equal or greater than one, in the "always
        //   sharp" interpolate boundary case
        if (fvarinterp == HbrMesh<T>::k_InterpolateBoundaryNone ||
            (fvarinterp == HbrMesh<T>::k_InterpolateBoundaryAlwaysSharp &&
             fvarmask >= 1) ||
            v->GetSharpness() > HbrVertex<T>::k_Smooth ||
            infcorner) {
            fv0.SetWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 1.0f);
        }
        // Dart rule: unlike geometric creases, because there's two
        // discontinuous values for the one incident edge, we use the
        // boundary rule and not the smooth rule
        else if (fvarmask == 1) {
            assert(!v->OnBoundary());

            // Use 0.75 of the current vert
            fv0.SetWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.75f);

            // 0.125 of "two adjacent edge vertices", which in actuality
            // are the facevarying values of the same vertex but on each
            // side of the single incident facevarying sharp edge
            HbrHalfedge<T>* start = v->GetIncidentEdge(), *nextedge;
            edge = start;
            while (edge) {
                if (edge->GetFVarSharpness(fvaritem)) {
                    break;
                }
                nextedge = v->GetNextEdge(edge);
                if (nextedge == start) {
                    assert(0); // we should have found it by now
                    break;
                } else if (!nextedge) {
                    // should never get into this case - if the vertex is
                    // on a boundary, it can never be a facevarying dart
                    // vertex
                    assert(0);
                    edge = edge->GetPrev();
                    break;
                } else {
                    edge = nextedge;
                }
            }
            HbrVertex<T>* w = edge->GetDestVertex();
            HbrFace<T>* bestface = edge->GetLeftFace();
            int j;
            for (j = 0; j < bestface->GetNumVertices(); ++j) {
                if (bestface->GetVertex(j) == w) break;
            }
            assert(j != bestface->GetNumVertices());
            fv0.AddWithWeight(bestface->GetFVarData(j), fvarindex, fvarwidth, 0.125f);
            bestface = edge->GetRightFace();
            for (j = 0; j < bestface->GetNumVertices(); ++j) {
                if (bestface->GetVertex(j) == w) break;
            }
            assert(j != bestface->GetNumVertices());
            fv0.AddWithWeight(bestface->GetFVarData(j), fvarindex, fvarwidth, 0.125f);
        }
        // Boundary vertex rule
        else if (fvarmask != 0) {

            // Use 0.75 of the current vert
            fv0.SetWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.75f);

            // Compute 0.125 of two adjacent edge vertices. However the
            // two adjacent edge vertices we use must be part of the
            // facevarying "boundary". To find the first edge we cycle
            // counterclockwise around the current vertex v and look for
            // the first boundary edge

            HbrFace<T>* bestface = face;
            HbrHalfedge<T>* bestedge = face->GetEdge(index)->GetPrev();
            HbrHalfedge<T>* starte = bestedge->GetOpposite();
            HbrVertex<T>* w = 0;
            if (!starte) {
                w = face->GetEdge(index)->GetPrev()->GetOrgVertex();
            } else {
                HbrHalfedge<T>* e = starte, *next;
                assert(starte->GetOrgVertex() == v);
                do {
                    if (e->GetFVarSharpness(fvaritem) || !e->GetLeftFace()) {
                        bestface = e->GetRightFace();
                        bestedge = e;
                        break;
                    }
                    next = v->GetNextEdge(e);
                    if (!next) {
                        bestface = e->GetLeftFace();
                        w = e->GetPrev()->GetOrgVertex();
                        break;
                    }
                    e = next;
                } while (e && e != starte);
            }
            if (!w) w = bestedge->GetDestVertex();
            int j;
            for (j = 0; j < bestface->GetNumVertices(); ++j) {
                if (bestface->GetVertex(j) == w) break;
            }
            assert(j != bestface->GetNumVertices());
            fv0.AddWithWeight(bestface->GetFVarData(j), fvarindex, fvarwidth, 0.125f);

            // Look for the other edge by cycling clockwise around v
            bestface = face;
            bestedge = face->GetEdge(index);
            starte = bestedge;
            w = 0;
            if (HbrHalfedge<T>* e = starte) {
                assert(starte->GetOrgVertex() == v);
                do {
                    if (e->GetFVarSharpness(fvaritem) || !e->GetRightFace()) {
                        bestface = e->GetLeftFace();
                        bestedge = e;
                        break;
                    }
                    assert(e->GetOpposite());
                    e = v->GetPreviousEdge(e);
                } while (e && e != starte);
            }
            if (!w) w = bestedge->GetDestVertex();
            for (j = 0; j < bestface->GetNumVertices(); ++j) {
                if (bestface->GetVertex(j) == w) break;
            }
            assert(j != bestface->GetNumVertices());
            fv0.AddWithWeight(bestface->GetFVarData(j), fvarindex, fvarwidth, 0.125f);

        }
        // Smooth rule. Here, we can take a shortcut if we know that
        // the vertex is smooth and some other vertex has completely
        // computed the facevarying values
        else if (!fv0IsSmooth || !fv0.IsInitialized()) {
            int valence = v->GetValence();
            float invvalencesquared = 1.0f / (valence * valence);

            // Use n-2/n of the current vertex value
            fv0.SetWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, invvalencesquared * valence * (valence - 2));

            // Add 1/n^2 of surrounding edge vertices and surrounding face
            // averages. We loop over all surrounding faces..
            HbrHalfedge<T>* start = v->GetIncidentEdge(), *edge;
            edge = start;
            while (edge) {
                HbrFace<T>* g = edge->GetLeftFace();
                weight = invvalencesquared / g->GetNumVertices();
                // .. and compute the average of each face. At the same
                // time, we look for the edge on that face whose origin is
                // the same as v, and add a contribution from its
                // destination vertex value; this takes care of the
                // surrounding edge vertex addition.
                for (int j = 0; j < g->GetNumVertices(); ++j) {
                    fv0.AddWithWeight(g->GetFVarData(j), fvarindex, fvarwidth, weight);
                    if (g->GetEdge(j)->GetOrgVertex() == v) {
                        fv0.AddWithWeight(g->GetFVarData((j + 1) % g->GetNumVertices()), fvarindex, fvarwidth, invvalencesquared);
                    }
                }
                edge = v->GetNextEdge(edge);
                if (edge == start) break;
            }
        }

        // Edge subdivision rule
        edge = face->GetEdge(index);

        if (fvarinterp == HbrMesh<T>::k_InterpolateBoundaryNone ||
            edge->GetFVarSharpness(fvaritem) || edge->IsBoundary()) {

            // Sharp edge rule
            fv1.SetWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.5f);
            fv1.AddWithWeight(face->GetFVarData((index + 1) % nv), fvarindex, fvarwidth, 0.5f);
        } else if (!fv1IsSmooth || !fv1.IsInitialized()) {
            // Smooth edge subdivision. Add 0.25 of adjacent vertices
            fv1.SetWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.25f);
            fv1.AddWithWeight(face->GetFVarData((index + 1) % nv), fvarindex, fvarwidth, 0.25f);
            // Local subdivided face vertex
            fv1.AddWithWeight(fv2, fvarindex, fvarwidth, 0.25f);
            // Add 0.25 * average of neighboring face vertices
            HbrFace<T>* oppFace = edge->GetRightFace();
            weight = 0.25f / oppFace->GetNumVertices();
            for (int j = 0; j < oppFace->GetNumVertices(); ++j) {
                fv1.AddWithWeight(oppFace->GetFVarData(j), fvarindex, fvarwidth, weight);
            }
        }

        // Edge subdivision rule
        edge = edge->GetPrev();

        if (fvarinterp == HbrMesh<T>::k_InterpolateBoundaryNone ||
            edge->GetFVarSharpness(fvaritem) || edge->IsBoundary()) {

            // Sharp edge rule
            fv3.SetWithWeight(face->GetFVarData((index + nv - 1) % nv), fvarindex, fvarwidth, 0.5f);
            fv3.AddWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.5f);
        } else if (!fv3IsSmooth || !fv3.IsInitialized()) {
            // Smooth edge subdivision. Add 0.25 of adjacent vertices
            fv3.SetWithWeight(face->GetFVarData((index + nv - 1) % nv), fvarindex, fvarwidth, 0.25f);
            fv3.AddWithWeight(face->GetFVarData(index), fvarindex, fvarwidth, 0.25f);
            // Local subdivided face vertex
            fv3.AddWithWeight(fv2, fvarindex, fvarwidth, 0.25f);
            // Add 0.25 * average of neighboring face vertices
            HbrFace<T>* oppFace = edge->GetRightFace();
            weight = 0.25f / oppFace->GetNumVertices();
            for (int j = 0; j < oppFace->GetNumVertices(); ++j) {
                fv3.AddWithWeight(oppFace->GetFVarData(j), fvarindex, fvarwidth, weight);
            }
        }

        fvarindex += fvarwidth;
    }
    fv0.SetInitialized();
    fv1.SetInitialized();
    fv3.SetInitialized();
}

template <class T>
void
HbrBilinearSubdivision<T>::transferEditsToChild(HbrFace<T>* face, HbrFace<T>* child, int index) {

    // Hand down hole tag
    child->SetHole(face->IsHole());

    // Hand down pointers to hierarchical edits
    if (HbrHierarchicalEdit<T>** edits = face->GetHierarchicalEdits()) {
        while (HbrHierarchicalEdit<T>* edit = *edits) {
            if (!edit->IsRelevantToFace(face)) break;
            if (edit->GetNSubfaces() > face->GetDepth() &&
                (edit->GetSubface(face->GetDepth()) == index)) {
                child->SetHierarchicalEdits(edits);
                break;
            }
            edits++;
        }
    }
}

template <class T>
void
HbrBilinearSubdivision<T>::Refine(HbrMesh<T>* mesh, HbrFace<T>* face) {

    // Create new quadrilateral children faces from this face
    HbrFace<T>* child;
    HbrVertex<T>* vertices[4];
    HbrHalfedge<T>* edge = face->GetFirstEdge();
    HbrHalfedge<T>* prevedge = edge->GetPrev();
    HbrHalfedge<T>* childedge;
    int nv = face->GetNumVertices();
    float sharpness;
    bool extraordinary = (nv != 4);
    // The funny indexing on vertices is done only for
    // non-extraordinary faces in order to correctly preserve
    // parametric space through the refinement. If we split an
    // extraordinary face then it doesn't matter.
    for (int i = 0; i < nv; ++i) {
        if (!face->GetChild(i)) {
#ifdef HBR_DEBUG
            std::cerr << "Kid " << i << "\n";
#endif
            HbrVertex<T>* vertex = edge->GetOrgVertex();
            if (extraordinary) {
                vertices[0] = vertex->Subdivide();
                vertices[1] = edge->Subdivide();
                vertices[2] = face->Subdivide();
                vertices[3] = prevedge->Subdivide();
            } else {
                vertices[i] = vertex->Subdivide();
                vertices[(i+1)%4] = edge->Subdivide();
                vertices[(i+2)%4] = face->Subdivide();
                vertices[(i+3)%4] = prevedge->Subdivide();
            }
            child = mesh->NewFace(4, vertices, face, i);
#ifdef HBR_DEBUG
            std::cerr << "Creating face " << *child << " during refine\n";
#endif

            // Hand down edge sharpnesses
            childedge = vertex->Subdivide()->GetEdge(edge->Subdivide());
            assert(childedge);
            if ((sharpness = edge->GetSharpness()) > HbrHalfedge<T>::k_Smooth) {
                HbrSubdivision<T>::SubdivideCreaseWeight(edge, edge->GetOrgVertex(), childedge);
            }
            childedge->CopyFVarInfiniteSharpness(edge);

            childedge = prevedge->Subdivide()->GetEdge(vertex->Subdivide());
            assert(childedge);
            if ((sharpness = prevedge->GetSharpness()) > HbrHalfedge<T>::k_Smooth) {
                HbrSubdivision<T>::SubdivideCreaseWeight(prevedge, prevedge->GetDestVertex(), childedge);
            }
            childedge->CopyFVarInfiniteSharpness(prevedge);

            if (mesh->GetTotalFVarWidth()) {
                transferFVarToChild(mesh, face, child, i);
            }

            // Special handling of ptex index for extraordinary faces: make
            // sure the children get their indices reassigned to be
            // consecutive within the block reserved for the parent.
            if (face->GetNumVertices() != 4 && face->GetPtexIndex() != -1) {
                child->SetPtexIndex(face->GetPtexIndex() + i);
            }

            transferEditsToChild(face, child, i);
        }
        prevedge = edge;
        edge = edge->GetNext();
    }
}

template <class T>
HbrFace<T>*
HbrBilinearSubdivision<T>::RefineFaceAtVertex(HbrMesh<T>* mesh, HbrFace<T>* face, HbrVertex<T>* vertex) {

#ifdef HBR_DEBUG
    std::cerr << "    forcing refine on " << *face << " at " << *vertex << '\n';
#endif

    // Create new quadrilateral children faces from this face
    HbrHalfedge<T>* edge = face->GetFirstEdge();
    HbrHalfedge<T>* prevedge = edge->GetPrev();
    HbrHalfedge<T>* childedge;
    int nv = face->GetNumVertices();
    float sharpness;
    bool extraordinary = (nv != 4);
    // The funny indexing on vertices is done only for
    // non-extraordinary faces in order to correctly preserve
    // parametric space through the refinement. If we split an
    // extraordinary face then it doesn't matter.
    for (int i = 0; i < nv; ++i) {
        if (edge->GetOrgVertex() == vertex) {
            if (!face->GetChild(i)) {
                HbrFace<T>* child;
                HbrVertex<T>* vertices[4];
                if (extraordinary) {
                    vertices[0] = vertex->Subdivide();
                    vertices[1] = edge->Subdivide();
                    vertices[2] = face->Subdivide();
                    vertices[3] = prevedge->Subdivide();
                } else {
                    vertices[i] = vertex->Subdivide();
                    vertices[(i+1)%4] = edge->Subdivide();
                    vertices[(i+2)%4] = face->Subdivide();
                    vertices[(i+3)%4] = prevedge->Subdivide();
                }
#ifdef HBR_DEBUG
                std::cerr << "Kid " << i << "\n";
                std::cerr << "  subdivision created " << *vertices[0] << '\n';
                std::cerr << "  subdivision created " << *vertices[1] << '\n';
                std::cerr << "  subdivision created " << *vertices[2] << '\n';
                std::cerr << "  subdivision created " << *vertices[3] << '\n';
#endif
                child = mesh->NewFace(4, vertices, face, i);
#ifdef HBR_DEBUG
                std::cerr << "Creating face " << *child << " during refine\n";
#endif
                // Hand down edge sharpness
                childedge = vertex->Subdivide()->GetEdge(edge->Subdivide());
                assert(childedge);
                if ((sharpness = edge->GetSharpness()) > HbrHalfedge<T>::k_Smooth) {
                    HbrSubdivision<T>::SubdivideCreaseWeight(edge, edge->GetOrgVertex(), childedge);
                }
                childedge->CopyFVarInfiniteSharpness(edge);

                childedge = prevedge->Subdivide()->GetEdge(vertex->Subdivide());
                assert(childedge);
                if ((sharpness = prevedge->GetSharpness()) > HbrHalfedge<T>::k_Smooth) {
                    HbrSubdivision<T>::SubdivideCreaseWeight(prevedge, prevedge->GetDestVertex(), childedge);
                }
                childedge->CopyFVarInfiniteSharpness(prevedge);

                if (mesh->GetTotalFVarWidth()) {
                    transferFVarToChild(mesh, face, child, i);
                }

                // Special handling of ptex index for extraordinary faces: make
                // sure the children get their indices reassigned to be
                // consecutive within the block reserved for the parent.
                if (face->GetNumVertices() != 4 && face->GetPtexIndex() != -1) {
                    child->SetPtexIndex(face->GetPtexIndex() + i);
                }

                transferEditsToChild(face, child, i);
                return child;
            } else {
                return face->GetChild(i);
            }
        }
        prevedge = edge;
        edge = edge->GetNext();
    }
    return 0;
}

template <class T>
void
HbrBilinearSubdivision<T>::GuaranteeNeighbor(HbrMesh<T>* mesh, HbrHalfedge<T>* edge) {
    if (edge->GetOpposite()) {
        return;
    }

    // For the given edge: if the parent of either of its incident
    // vertices is itself a _face_, then ensuring that this parent
    // face has refined at a particular vertex is sufficient to
    // ensure that both of the faces on each side of the edge have
    // been created.
    bool destParentWasEdge = true;
    HbrFace<T>* parentFace = edge->GetOrgVertex()->GetParentFace();
    HbrHalfedge<T>* parentEdge = edge->GetDestVertex()->GetParentEdge();
    if (!parentFace) {
        destParentWasEdge = false;
        parentFace = edge->GetDestVertex()->GetParentFace();
        parentEdge = edge->GetOrgVertex()->GetParentEdge();
    }

    if (parentFace) {

        // Make sure we deal with a parent halfedge which is
        // associated with the parent face
        if (parentEdge->GetFace() != parentFace) {
            parentEdge = parentEdge->GetOpposite();
        }
        // If one of the vertices had a parent face, the other one MUST
        // have been a child of an edge
        assert(parentEdge && parentEdge->GetFace() == parentFace);
#ifdef HBR_DEBUG
        std::cerr << "\nparent edge is " << *parentEdge << "\n";
#endif

        // The vertex to refine at depends on whether the
        // destination or origin vertex of this edge had a parent
        // edge
        if (destParentWasEdge) {
            RefineFaceAtVertex(mesh, parentFace, parentEdge->GetOrgVertex());
        } else {
            RefineFaceAtVertex(mesh, parentFace, parentEdge->GetDestVertex());
        }

        // It should always be the case that the opposite now exists -
        // we can't have a boundary case here
        assert(edge->GetOpposite());
    } else {
        HbrVertex<T>* parentVertex = edge->GetOrgVertex()->GetParentVertex();
        parentEdge = edge->GetDestVertex()->GetParentEdge();
        if (!parentVertex) {
            parentVertex = edge->GetDestVertex()->GetParentVertex();
            parentEdge = edge->GetOrgVertex()->GetParentEdge();
        }

        if (parentVertex) {

            assert(parentEdge);

#ifdef HBR_DEBUG
            std::cerr << "\nparent edge is " << *parentEdge << "\n";
#endif

            // 1. Go up to the parent of my face

            parentFace = edge->GetFace()->GetParent();
#ifdef HBR_DEBUG
            std::cerr << "\nparent face is " << *parentFace << "\n";
#endif

            // 2. Ask the opposite face (if it exists) to refine
            if (parentFace) {

                // A vertex can be associated with either of two
                // parent halfedges. If the parent edge that we're
                // interested in doesn't match then we should look at
                // its opposite
                if (parentEdge->GetFace() != parentFace)
                    parentEdge = parentEdge->GetOpposite();
                assert(parentEdge->GetFace() == parentFace);

                // Make sure the parent edge has its neighbor as well
                GuaranteeNeighbor(mesh, parentEdge);

                // Now access that neighbor and refine it
                if (parentEdge->GetRightFace()) {
                    RefineFaceAtVertex(mesh, parentEdge->GetRightFace(), parentVertex);

                    // FIXME: assertion?
                    assert(edge->GetOpposite());
                }
            }
        }
    }
}

template <class T>
void
HbrBilinearSubdivision<T>::GuaranteeNeighbors(HbrMesh<T>* mesh, HbrVertex<T>* vertex) {

#ifdef HBR_DEBUG
    std::cerr << "\n\nneighbor guarantee at " << *vertex << " invoked\n";
#endif

    // If the vertex is a child of a face, guaranteeing the neighbors
    // of the vertex is simply a matter of ensuring the parent face
    // has refined.
    HbrFace<T>* parentFace = vertex->GetParentFace();
    if (parentFace) {

#ifdef HBR_DEBUG
        std::cerr << "  forcing full refine on parent face\n";
#endif
        Refine(mesh, parentFace);
        return;
    }

    // Otherwise if the vertex is a child of an edge, we need to
    // ensure that the parent faces on either side of the parent edge
    // 1) exist, and 2) have refined at both vertices of the parent
    // edge
    HbrHalfedge<T>* parentEdge = vertex->GetParentEdge();
    if (parentEdge) {

#ifdef HBR_DEBUG
        std::cerr << "  forcing full refine on adjacent faces of parent edge\n";
#endif
        HbrVertex<T>* dest = parentEdge->GetDestVertex();
        HbrVertex<T>* org = parentEdge->GetOrgVertex();
        GuaranteeNeighbor(mesh, parentEdge);
        parentFace = parentEdge->GetLeftFace();
        RefineFaceAtVertex(mesh, parentFace, dest);
        RefineFaceAtVertex(mesh, parentFace, org);

#ifdef HBR_DEBUG
        std::cerr << "    on the right face?\n";
#endif
        parentFace = parentEdge->GetRightFace();
        // The right face may not necessarily exist even after
        // GuaranteeNeighbor
        if (parentFace) {
            RefineFaceAtVertex(mesh, parentFace, dest);
            RefineFaceAtVertex(mesh, parentFace, org);
        }
#ifdef HBR_DEBUG
        std::cerr << "  end force\n";
#endif
        return;
    }

    // The last case: the vertex is a child of a vertex. In this case
    // we have to first recursively guarantee that the parent's
    // adjacent faces also exist.
    HbrVertex<T>* parentVertex = vertex->GetParentVertex();
    if (parentVertex) {

#ifdef HBR_DEBUG
        std::cerr << "  recursive parent vertex guarantee call\n";
#endif
        parentVertex->GuaranteeNeighbors();

        // And then we refine all the face neighbors of the
        // parentVertex
        HbrHalfedge<T>* start = parentVertex->GetIncidentEdge(), *edge;
        edge = start;
        while (edge) {
            HbrFace<T>* f = edge->GetLeftFace();
            RefineFaceAtVertex(mesh, f, parentVertex);
            edge = parentVertex->GetNextEdge(edge);
            if (edge == start) break;
        }
    }
}

template <class T>
bool
HbrBilinearSubdivision<T>::HasLimit(HbrMesh<T>* mesh, HbrFace<T>* face) {

    if (face->IsHole()) return false;
    // A limit face exists if all the bounding edges have limit curves
    for (int i = 0; i < face->GetNumVertices(); ++i) {
        if (!HasLimit(mesh, face->GetEdge(i))) {
            return false;
        }
    }
    return true;
}

template <class T>
bool
HbrBilinearSubdivision<T>::HasLimit(HbrMesh<T>* /* mesh */, HbrHalfedge<T>* /* edge */) {
    return true;
}

template <class T>
bool
HbrBilinearSubdivision<T>::HasLimit(HbrMesh<T>* /* mesh */, HbrVertex<T>* vertex) {
    vertex->GuaranteeNeighbors();
    switch (vertex->GetMask(false)) {
        case HbrVertex<T>::k_Smooth:
        case HbrVertex<T>::k_Dart:
            return !vertex->OnBoundary();
            break;
        case HbrVertex<T>::k_Crease:
        case HbrVertex<T>::k_Corner:
        default:
            return true;
    }
}

template <class T>
HbrVertex<T>*
HbrBilinearSubdivision<T>::Subdivide(HbrMesh<T>* mesh, HbrFace<T>* face) {

    // Face rule: simply average all vertices on the face
    HbrVertex<T>* v = mesh->NewVertex();
    T& data = v->GetData();
    int nv = face->GetNumVertices();
    float weight = 1.0f / nv;

    HbrHalfedge<T>* edge = face->GetFirstEdge();
    for (int i = 0; i < face->GetNumVertices(); ++i) {
        HbrVertex<T>* w = edge->GetOrgVertex();
        // If there are vertex edits we have to make sure the edit
        // has been applied
        if (mesh->HasVertexEdits()) {
            w->GuaranteeNeighbors();
        }
        data.AddWithWeight(w->GetData(), weight);
        data.AddVaryingWithWeight(w->GetData(), weight);
        edge = edge->GetNext();
    }
#ifdef HBR_DEBUG
    std::cerr << "Subdividing at " << *face << "\n";
#endif

    // Set the extraordinary flag if the face had anything other than
    // 4 vertices
    if (nv != 4) v->SetExtraordinary();

#ifdef HBR_DEBUG
    std::cerr << "  created " << *v << "\n";
#endif
    return v;
}

template <class T>
HbrVertex<T>*
HbrBilinearSubdivision<T>::Subdivide(HbrMesh<T>* mesh, HbrHalfedge<T>* edge) {

#ifdef HBR_DEBUG
    float esharp = edge->GetSharpness();
    std::cerr << "Subdividing at " << *edge << " (sharpness = " << esharp << ")";
#endif

    HbrVertex<T>* v = mesh->NewVertex();
    T& data = v->GetData();

    // If there's the possibility of a crease edits, make sure the
    // edit has been applied
    if (mesh->HasCreaseEdits()) {
        edge->GuaranteeNeighbor();
    }

    // If there's the possibility of vertex edits on either vertex, we
    // have to make sure the edit has been applied
    if (mesh->HasVertexEdits()) {
        edge->GetOrgVertex()->GuaranteeNeighbors();
        edge->GetDestVertex()->GuaranteeNeighbors();
    }

    // Average the two end points
    data.AddWithWeight(edge->GetOrgVertex()->GetData(), 0.5f);
    data.AddWithWeight(edge->GetDestVertex()->GetData(), 0.5f);

    // Varying data is always the average of two end points
    data.AddVaryingWithWeight(edge->GetOrgVertex()->GetData(), 0.5f);
    data.AddVaryingWithWeight(edge->GetDestVertex()->GetData(), 0.5f);

#ifdef HBR_DEBUG
    std::cerr << "  created " << *v << "\n";
#endif
    return v;
}

template <class T>
HbrVertex<T>*
HbrBilinearSubdivision<T>::Subdivide(HbrMesh<T>* mesh, HbrVertex<T>* vertex) {

    HbrVertex<T>* v;

    // If there are vertex edits we have to make sure the edit has
    // been applied by guaranteeing the neighbors of the
    // vertex. Unfortunately in this case, we can't share the data
    // with the parent
    if (mesh->HasVertexEdits()) {
        vertex->GuaranteeNeighbors();

        v = mesh->NewVertex();
        T& data = v->GetData();

        // Just copy the old value
        data.AddWithWeight(vertex->GetData(), 1.0f);

        // Varying data is always just propagated down
        data.AddVaryingWithWeight(vertex->GetData(), 1.0f);

    } else {
        // Create a new vertex that just shares the same data
        v = mesh->NewVertex(vertex->GetData());
    }

#ifdef HBR_DEBUG
    std::cerr << "Subdividing at " << *vertex << "\n";
    std::cerr << "  created " << *v << "\n";
#endif
    // Inherit extraordinary flag and sharpness
    if (vertex->IsExtraordinary()) v->SetExtraordinary();
    float sharp = vertex->GetSharpness();
    if (sharp >= HbrVertex<T>::k_InfinitelySharp) {
        v->SetSharpness(HbrVertex<T>::k_InfinitelySharp);
    } else if (sharp > HbrVertex<T>::k_Smooth) {
        sharp -= 1.0f;
        if (sharp < (float) HbrVertex<T>::k_Smooth) {
            sharp = (float) HbrVertex<T>::k_Smooth;
        }
        v->SetSharpness(sharp);
    } else {
        v->SetSharpness(HbrVertex<T>::k_Smooth);
    }
    return v;
}

} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;

} // end namespace OpenSubdiv

#endif /* HBRBILINEAR_H */


Striking out on your own to build a website is easy and only takes a few minutes, but without the proper instruction, you could waste hours of your time and accomplish nothing but a massive headache. Trust me, I tried.
I found out I could build a website with WordPress for free, and I knew I had an existing site that came with my Internet account. No problem- I downloaded WordPress and got to work, but after several failed attempts, I discovered that I needed more software and more experience to come anywhere near having my own site.
I went with Blue Host because of the low cost and the easy set up. Choosing a domain name was easy; I used my name since it wasn't going to be the name of my website or anything, just something to identify my account. Once you have signed up, login to your account using the domain name and password you chose.
When you have logged in, you will have a control panel screen. Scroll down to the bottom and find the Software/Services section. Find the WordPress icon and click it to go to the download screen. You will see "Step 1." Choose WordPress 3.0 (Stable), and select your domain name from the "Where to install" menu.
Go to Step 2 and give your site a name, yourself an administrator name and password, and check the "Automatically create a new database" box. Read the terms and conditions, and check the Step 3 box if you accept the terms. The final step is to click "Complete." The software will be installed and your page will be created.
Now that your page is created, you get to do the fun stuff, like choose the layout, or WordPress theme for your site. There are hundreds of free themes to choose from, and you can preview them before you make your selection. You can always change your theme later without losing any of your content.
When you login, you will find your "Dashboard." This is the control room for your site. Along the left side, you will find all the controls to personalize your website and decide on what bells and whistles you'll need for your site. For more detailed instructions as to how to personalize your site you can visit How to install Graphic Themes and also how to install feature add ins, or Plugins. Use the "Help" button to find out what each option does, or click here to find more, detailed instructions on how to build a website to your own specifications.


The nice people at United Limsun, the authorized Philippine distributor of Pacsafe gave me another item for review, which compliments the Toursafe Handbag: the Pacsafe Toursafe Wallet. The Toursafe Wallet comes in the same design and colors as the handbag: black, taupe and tabasco.
The wallet matches my handbag perfectly: it's also black outside and lilac inside. The wallet comes with a slash-proof chain that you can hook to any Toursafe bag, giving you peace of mind that your wallet's not going anywhere.
The wallet is an accordion style checkbook wallet*, which gives you plenty of space to store and organize your bills, coins, receipts, cards and even your passport! It measures 7.4″ x 3.9″ x 0.6″ — a pretty standard size, yet slim and compact.
In the past years, I've stopped using wallets, opting for simple zippered pouches. I was hesitant to review the wallet at first, since I wasn't too keen to use it, but I've grown to like it after using the wallet for two months. What I love about it is the compartments that let me separate different currencies. Since I travel overseas fairly often, I have to deal with a variety of paper bills and coins whenever I cross borders. My practice before the wallet is that I keep a second wallet to hold all the currency that can't be used for the country I'm currently in. With the Toursafe wallet, I can put my Philippine peso in one compartment, my Thai baht in the other, my US dollar in another.
I like that there's plenty of card slots inside the wallet. It comes handy for organizing my card collection, which includes my credit cards, ATM, Timezone game cards, loyalty/rewards cards, fare cards and the Pacsafe Prosafe 750 key card. There are also slip pockets for receipts or keeping travel notes.
All in all, it's a pretty good buy at Php 1,199 Php 1,999**, considering that a lot of wallets go for around that price range.
You can actually fit your passport inside the Toursafe Wallet!
Pacsafe is available in Manila in all Travel Club branches, Brat Pack, Rustan's, Hahn, ROX, I Studio, I Gig, Flight 360 and Bombproofgear. A huge thank you go out to United Limsun for the wallet.
The compartments would help me with all the receipts, IDs and other things I stuff my wallet with.
wow. this is a very nice review and thank you for sharing it Nina. You're the best!
I want that cat bag!
This will be my top of the list if I need to get a new wallet!
whoa, kala ko naliligaw ako ng blog.
Hey Nina! Great review, your wallet looks very organized!
We posted your review on our Facebook page and our blog!
Kyameel: It can fit your calculator!
rainne rafols: Tambling ako sa duct tape.
Who loves Nadal the most?


text:
The Role of Enhanced Books in the Future of Publishing
Guest Blog by bestselling author David Farland
Right now, the publishing world is in turmoil. People are buying electronic books in huge numbers. In fact, it appears that as of today, more than fifty percent of all sales are electronic. This puts traditional paper book publishers in a bind. You see, most books earn only a modest profit. But if paper books are shipped to bookstores and then returned, they get destroyed, and thus don't make any money at all. In fact, the publisher then goes into the hole on every book he publishes.
The losses right now are so large in the industry, that as one agent put it, "Nobody in New York wants to be in this business right now." That's why bookstore chains like Borders and major distributors like Anderson News have gone bankrupt.
So where do the publishers make up for those losses? By selling electronic books for the Kindle, Nook, iPad and similar devices. The problem is, so many electronic books will come out in the next year, according to Bowker's Identifier Services (the guys who make the ISBNs that you see on the back of a book), that the market will be flooded with over three million new books.
Why? Because authors who couldn't find agents or publishers last year are self-publishing their novels this year. I was talking to a bestseller last night who groused that in the past week, he'd run into three different "authors," none of whom had sold more than fifty books, all of whom were self-published.
That creates a problem for readers. It means that we now have to try to figure out which of those novels are worth buying and reading and which should never have been published in the first place.
Some of those novels may look good on the outside. They might have cover quotes from the author's friends. They might have gorgeous illustrations. But inside, maybe halfway through a book, you might find that the story falls apart.
In fact, a lot of criminals are out there right now trying to sell e-books which Tracy Hickman has labeled "Frankensteins." These are novels stolen from bits of other novels and cobbled together in a way to look like a legitimate book. The "author" hopes to steal a couple of dollars from unwary readers. Sure, it's not a lot of money, but in some countries, like Nigeria, a few dollars goes a long way. If there are no laws against it (and in some countries there aren't), the thief doesn't even have to worry about getting punished.
How are we going to combat crummy novels? How are we going to get past the Frankensteins? Ten years ago we had gatekeepers in the industry—literary agents and editors—who made sure that only the best novels got published. It's true that the system was flawed, but at least there was a system.
So who are our new gatekeepers going to be?
The truth is that there will be new kinds of publishers. Right now, I'm starting a company with my partner Mile Romney, called East India Press. We're going to published "enhanced novels."
Enhanced books are text files, like regular books, but they also combine elements like film clips, music, video games, author interviews, audio files, illustrations, and animations. They're part book, part movie, part game, perhaps. These books are then then sold electronically to be read on your iPad, phone, computer, and so on.
Are enhanced books the real future of publishing? There is good reason to think so. You see, making a beautiful book in this market will cost tens of thousands of dollars. That's a bar to most wannabe authors. So money alone will limit the competition.
These new publishers will still have to establish their own credibility. They'll have to select great books, create superior products, and develop a "brand" presence. In other words, you'll want to read the books because of who the publisher is and what they represent.
A hundred years ago, that's the way that books were bought in the first place. If you went to the bookstore, the books were ordered by publisher. You might pick through the piles and find that a certain editor liked the same kind of "science fictional stories" that you did, and that became the place that you visited over and over again.
There will be other ways to judge a book. It might come from an author with a long list of awards, or great cover quotes from independent review agencies, or maybe the fact that the book is a bestseller will give it a lot of credibility.
So I expect enhanced books to become the dominant art form for novels in the next two years, replacing and outselling simple e-books on the bestseller lists, and even outselling hardbacks and paperbacks within a couple of years. As my agent, Russell Galen put it, "Enhanced books are the entire future of publishing."
Now, I've published some fifty books in science fiction and fantasy. I've won a number of awards and my books have been translated into thirty languages. I worked for years as the lead judge for one of the largest writing contest in the world. I've trained authors like Brandon Mull, Brandon Sanderson, and Stephenie Meyer who have gone on to become #1 international bestsellers.
So I know books. I know a good story when I see one, and I know how to fix a story when it needs fixing. Given this, and my own background as a novelist, videogame designer, and movie producer, it seemed like starting a new type of publishing company was a must.
In fact, I believe in this new medium so much, I'm even putting out my next novel through this publishing company. It's called Nightingale, and tells the story of Bron Jones, a young man abandoned at birth and raised in foster care. He discovers that he's not quite human, and suddenly finds himself at the center of international intrigue.
This is a model for the new publishing industry. I think it's a great book, and I could have sold it through normal channels. But this is the best way to go. So we're offering the book on our site at www.nightingalenovel.com. You can buy it in hard cover, for your e-reader, or in enhanced mode for the more advanced e-readers, or we even have an emulator so that you can run it in enhanced mode on any computer. It also has a forty-five minute soundtrack, lots of art, optional notes from the author and other features. In the future we may add a game or trailers. I believe this is the way books–good books–will be done in the future. I invite you to check it out, and check out our new company, East India Press.
If you're a writer, look into our short story writing contest while you're there. You could win $1000. You can find out about more about the East India Press or the writing contest at www.EastIndiaPress.com as well.
learn more about David Farland at http://www.davidfarland.net/

redpajama_set_name: RedPajamaCommonCrawl


The first African-American major league baseball player of the modern era, Jackie Robinson joined the Brooklyn Dodgers in 1947. In his 10 seasons with the Dodgers, Robinson played in six World Series, and was part of the 1955 world championship team. He played in six consecutive all-star games, from 1949-1954. Robinson was inducted into the Baseball Hall of Fame in 1962, and in 1997 Major League Baseball retired his uniform number, 42, across all major league teams. Following his playing career, he went on to be the first African-American television baseball analyst and the first African-American vice president of a major U.S. corporation.


text:
Pasadena PD reveals officers' names in McDade shooting
PASADENA, Calif.
Officer Jeffery Newlen, hired 5 years ago, and Officer Mathew Griffin, hired nearly 6 years ago, were the two officers who responded to the 911 robbery call last Saturday. The caller had falsely reported that McDade was armed. Neither officer has been involved in an officer-involved shooting before.
The department also released its Use of Force Policy, which is the same one used in hundreds of communities across the country. It states that officers are authorized to use deadly force after a suspect is warned to stop and a verbal warning is issued, if feasible.
The police chief's adjutant Lt. Phlunte Riddle says officers did not use lights and sirens in the pursuit, yet she says there was no question McDade knew the police wanted him to stop.
"He was within feet of that black and white vehicle, there was no way to misinterpret what that car was, and the officers were in uniform," Riddle said.
She also said the officers did not use sirens or lights because at the time, there was no threat of violence even though the officers believed McDade was armed. Officers say they fired after McDade allegedly reached for his waistband and feared he was going for a gun.
Oscar Carrillo, the man who called 911 and said that McDade was armed, remains in jail. Community leaders say it's a warning to all.
"So the community, yes, has a special responsibility put simply, to tell the truth," said Earl Ofari Hutchinson of the Los Angeles Urban Round Table.
The Pasadena police chief was expected to make an appearance 9 a.m. Saturday at New Revelation Missionary Church to answer more questions from the community.


Shop our new range of Assisted Living products from top brands. Make accessibility a priority in your commercial setting or a home environment. New products include Doc M Packs, which adhere to Document M public building regulations, meaning they're safe to install and use in a public space.
Make your bathroom fit your needs with new grab rails, accessible basins, shower seats and back rests, all ergonomically designed to support the user and give them bathing independence.


I don't think I know anyone as forgetful as me. Sometimes I make myself a cup of tea and forget to drink it. Then I heat it up in the microwave and discover it three days later when I'm heating up another cup of tea. I now live in an apartment on the fifth floor with no lift (the perks of Paris living), so really, the thought of forgetting to turn the iron off or leaving the chicken curry cooking on the stove (not unknown to happen before) strikes my heart with fear.
As long a I can remember (haha, I contradict myself), I've always been forgetful. My mum used to despair at my forgetfulness and prescribed fish roe to boost my memory. I love fish and fish roe but my memory over the years never improved in the least bit.
When Anais was a baby, I called for an appointment with the paediatrician. I was asked to give her date of birth. When I struggled with it, the receptionist asked "How many children do you have?" I wanted to lie and said eight, but being much much too young (and still am...) to have had eight children, I couldn't have passed it off. For someone who works in a pediatrician's clinic, you'd think she would know that pregnancy causes memory loss. And that it continues even until after childbirth.
But my friends, I digress. The other day I thought I remembered having taken some nice photos of our short stay at my father-in law's-house in St Gervais near Bordeaux. And I thought I had downloaded them all on the computer. But no, I had forgotten. This I can attribute to jetlag, which is now recognised as a clinical condition. So a little bit delayed, but nevertheless, here are some photos from when we first arrived.
Always a sight for sore eyes, this house.
Pony riding is the main activity for the girls in St Gervais.
Although I haven't forgotten how to ride a bicycle, it gets very foggy up there on a lot of things.
Tea is not yet so at ease on the bicycle but if there is a Tour de France via cartwheels, she is ready. Grandpere is the one who teaches the kids to ride.
These people seem to have forgotten that at their age (or at any age in my case), performing cartwheels is no easy feat.
Fish and fish roe aside, there does seem to be a consensus that the kitchen is a great place to start boosting your memory. Here are some other suggestions.
And a prune tart to finish off the evening on a sweet note as the sun sets over us.
Well, that's done. Excuse me while I go put my feet up and do some memory and brainpower boosting Sudoku and snack on some almonds.


Anonymous, 04 Sep 2017Xperia is deadXperia improved!
Someone Else, 05 Sep 2017If I hated big bezels, I wouldn't have mention how awesome the iPhone 7 is, right? I actual... moreI don't mocking or dissing to others brand. I give them suggestions and asking them to improve, because every smartphone isn't perfect, it is only suit for personal usages.
I know the competitors trolling bad to Xperia and that is why I wish to defend, I hope Sony continues making phone for us.
TechniquelogyPh, 04 Sep 2017It's ok to be a fanboy of something/someone, but it's not ok when you only see the positive of... morethis is true. And its really weird seeing LGV30 with the best sound quality. Hoenstly it should be sony given their reputation and history in sound. Yet they LGv30 is outdoing them.
granted they are collaborating with the european brand B&O so its understandable. But still it should at least be better than HTC.
The BlackBerry KEYone Black Edition would be my favourite device from IFA.
If I'm choosing between one of those listed above, I'd go the XZ1 Compact. Great to see more smaller devices.
Anonymous, 04 Sep 2017were will u see 512 gb sd card.. The highest announced is 400Read my comment again dude. I will wait for it.
UltimateZeroSeconds, 04 Sep 2017You don't see how good Xperia is and only see the bezel, it is unfair and bias, perhaps you ar... moreIf I hated big bezels, I wouldn't have mention how awesome the iPhone 7 is, right?
Someone Else, 04 Sep 2017Okay. You need to stop with your fanboy attitude. "claiming a phone made of glass to be mos... moreYou don't see how good Xperia is and only see the bezel, it is unfair and bias, perhaps you are the fans but from some of the competitors with bezel-less smartphone.
From all these models I prefer... The redhead model holding the phone on post header.
Seriously, what's her name? I wanna date..
I'm going with the XZ1 Compact, simply because there is a hole in the current flagship crop. There is no compact option, nothing that really puts everything in a small form factor as well as Sony has done, and still does. And after numerous polls, we can see that the majority of people prefer smaller screens, and that hasn't been showing. It's like phone makers weren't making phones for the users, but for them to compete with other makers.
Anonymous, 04 Sep 2017Xperia is dead...it's still alive, and I have an XZ Premium and is the best 2017 smartphone... since i have in my hands and tested everything, i wouldn't listen the online reviewers who most of them doesn't appreciate what that phone has.... and Sony has their fans / customers... of course you're not one of them... so, saying it's dead wouldn't change the fact that it's not!
it's just a word you say it.. so it's just according to your limited thoughts!
It was an easy poll, no competition at all.
And you wouldn't have notice that 9.3mm difference in real life anyway.
But yeah, 61 Hours Endurance is a joke. Lol.
Someone Else, 04 Sep 2017Yes. But it's width is almost the same, which what matters most for one handed usage. The iPho... moreI visualized and iPhone 7 is enormously bigger.
"iPhone 7 is also thinner".
Aesthetically speaking, the 2 xperias look like the xperia when it was simply named XPERIA. The useless gigantic bezels. It's ok to have bezels but pls put some justice to it.
I will buy phone after two years. It will be Sony flagship for sure. Also I will wait for SanDisk's 512GB micro SD card.
Anonymous, 04 Sep 2017"The iPhone 7 is just a tad bit taller than the XZ1 Compact" Seriously, dude? Iphone is ... moreYes. But it's width is almost the same, which what matters most for one handed usage. The iPhone 7 is also thinner, so more bonus point for one handed usage.
Someone Else, 04 Sep 2017Okay. You need to stop with your fanboy attitude. "claiming a phone made of glass to be mos... more"The iPhone 7 is just a tad bit taller than the XZ1 Compact"
Iphone is a almost a centimeter taller (9.3mm exactly) than XZ1C.
And XZ1C has 38% more battery capacity.


text:
Slow start for Uber's Europe to Asia on-demand boat service
Uber has launched UberBOAT in Turkey, an on-demand boat taxi service in Istanbul, to ferry customers across the Bosphorus Strait, which separates the city across two continents – Europe and Asia.
The strait is notorious for traffic jams in rush hour, and the journey can be known to take almost an hour in a car. Of course, if you're lucky enough to have access to a boat, you can bypass the congestion.
UberBOAT, which has teamed with Turkish boat company Navette, lets people order the boats the same way that they do Uber cars. Through an app, users locate the nearest boat, and when the captain calls to confirm availability, the boat arrives and the journey starts. Fares start at 50 Turkish liras (about £13) but can be split between passengers. Each boat can hold up 10 people at a time.
The service was launched in June, but is struggling to gain momentum in the same way that regular Uber services have. "We have more clients at weekends. I would say that we carry around 50-60 people per week," said the captain of one of the boats when speaking to Euronews.
But why is this? Well, there's lots of competition between various transport services ferrying across the Bosphorus Strait, which is dotted with seaside villas, restaurants and historic sites. Public ferries between the European and Asian sides of Istanbul can cost as little as 2.15 lira (about 50p) per ride, and water cabs operate almost 24/7.
This latest deviation into another transport method may well be a step too far, but it's still early days to see for sure. Would you use the UberBOAT to get from one side of Istanbul to the other? Let us know at @Liftshare!


text:
WINTER SEASON AT WHEATONARTS BEGINS JANUARY 1, 2022
MILLVILLE, NJ (December 31, 2021) – The Wheaton Arts and Cultural Center campus will be closed to the public during the winter months of January, February, and March. The Museum of American Glass, Folklife Center, and Artist Studios will be closed for exhibition changes, inventory purposes, and repairs during this period. The campus will reopen on Friday, April 1, 2022, when WheatonArts returns to its seasonal operating schedule, open April through December. The Museum Stores will be available online at shop.wheatonarts.org or by scheduled appointment.
During the 2022 winter season, WheatonArts will continue online programs, including the Family Art Workshop video tutorials posted every first and third Tuesday of the month, sponsored by PNC Arts Alive. The virtual Wheaton Conversations series returns on Thursday, January 13, with artists Kait Rhoads and Jen Elek, and January 27 with Dan Dailey and Richard Royal. Registration is free for these live discussions, and links to both are on the WheatonArts website. Past episodes of the Wheaton Conversations series and Family Art Workshops are available on the WheatonArts YouTube channel.
Dedicated to the mission of engaging artists and audiences in an evolving exploration of creativity, WheatonArts is proud to partner with the New Jersey Theater Alliance's Stages Festival and the Pushcart Players to offer a free afternoon of performing arts. On Saturday, March 5, the Pushcart Players will perform a free play production of Stone Soup and Other Stories. The Stages Festival is the state's largest annual theatre festival, offering discounted and free theatre performances, workshops, and events for all ages. Details regarding The Stages Festival can be found at www.njtheatrealliance.org and www.stagesfestival.org.
For more information about WheatonArts' programs, call 856-825-6800 or 800-998-4552, or visit wheatonarts.org. Winter hours at WheatonArts run January through March, with the entire campus closed to the public. WheatonArts is open April through December; days and hours may vary. Closed Easter Sunday, Memorial Day, Labor Day, Thanksgiving Day, Christmas Day, and New Year's Day.
WheatonArts strives to ensure the accessibility of its exhibitions, events, and programs to all persons with disabilities. Please provide two weeks' notice of additional needs. Patrons with hearing and speech disabilities may contact WheatonArts through the New Jersey Relay Service (TRS) 800-852-7899 or by dialing 711.
Funding has been made possible in part by the New Jersey State Council on the Arts, a Partner Agency of the National Endowment for the Arts, the Geraldine R. Dodge Foundation, the National Endowment for the Arts, and the New Jersey Cultural Trust. WheatonArts receives general operating support from the New Jersey Historical Commission, Division of Cultural Affairs in the New Jersey Department of State, and is supported in part by the New Jersey Department of State, Division of Travel and Tourism.
About WheatonArts: With a history spanning five decades, WheatonArts has earned regional, national and international recognition for its unique collections and programs. The Museum of American Glass (accredited by the American Alliance of Museums) houses one of the most comprehensive collections of American glass in the country – from the first glass bottles made in America to celebrated works by Dale Chihuly, Paul Stankard, and other contemporary glass artists. Visitors experience the art of glassmaking, ceramics, and flameworking in the Artists Studios. From April through December, the Museum of American Glass and Down Jersey Folklife Center present special exhibitions. WheatonArts also offers traditional and multi-cultural programs, classes, workshops, performances, and weekend festivals. The award-winning Museum Stores offer traditional and contemporary art and craft in all mediums.


text:
GARDENIA PLEDGES OVER RM80,000 IN AID TO VARSITY FOOD BANK IN CONJUNCTION WITH WORLD BREAD DAY
Contributions To Food Bank Aims To Assist Students In Need
Kuantan, Pahang, 16 October 2019 – Celebrating bread in its simplicity and ability to nourish households, Gardenia Bakeries (KL) Sdn Bhd (Gardenia) commemorated World Bread Day by pledging over RM80,000 in aid to support varsity students in need through the Food Bank Siswa programme under the Yayasan Food Bank Malaysia (YFBM).
"Today, we celebrate World Bread Day in a meaningful way by working hand-in-hand with the Food Bank Siswa programme to drive an important cause; helping varsity students in need by providing access to wholesome Gardenia products," said Gardenia Bakeries (KL) Sdn Bhd General Manager, Marketing / Public Relations, Hazlinah Harun, at the launch of the Food Bank Siswa programme at Universiti Malaysia Pahang (UMP) in Pekan, Pahang.
The World Bread Day celebration coincided with the launch of the food bank programme at UMP recently which was officiated by Minister of Domestic Trade and Consumer Affairs, Datuk Seri Saifuddin Nasution Ismail. The Food Bank Siswa programme was formed after the successful implementation of the Food Bank Malaysia programme mooted by the Ministry of Domestic Trade and Consumer Affairs.
In conjunction with World Bread Day celebrated on 16 October, Gardenia organised a host of activities and contributed 1,000 individual packs of Toast'em Butter Raisin and Delicia Raspberry bread for the students of UMP to savour. 500 students under the Food Bank Siswa programme also received Gardenia goody bags following the launch of the food bank in UMP.
Extending the celebration to benefit more students in need, Gardenia is also working with YFBM to identify a university in order to complement its food bank pantry with a variety of goods including Gardenia Original Classic bread, Auntie Rosie's Natural Kaya, Say Cheese Rolls, Delicia Cream Rolls, Fluffy Buns, Sambal Bilis Buns, Butter Toffee Soft Rolls, and Red Bean Buns interchangeably for a period of 40 weeks. The food items will be freshly delivered by Gardenia to the university's food bank on a weekly basis and is expected to benefit about 1,000 students in need.
Speaking on the importance of quality food for the youth to thrive, Hazlinah said, "Gardenia's food products are made fresh and delivered to the food bank directly from our bakeries. When quality and consistency is concerned, Gardenia's commitment to the food bank programme is backed by a solid track record of delivering fresh products, 365 days a year, come rain or shine."
In addition to UMP, Gardenia has also contributed its bread products to Universiti Malaysia Perlis, Universiti Sains Malaysia and Universiti Utara Malaysia's food bank recipients bringing the total value of aid to over RM80,000. Gardenia has been actively involved in charitable causes throughout the years. Every day, Gardenia delivers complimentary fresh bread to over 65 charitable organisations such as old folks homes, orphanages, centres and schools for the differently abled, eight of which have been on Gardenia's delivery list since 1991.
Gardenia began its World Bread Day celebrations in 2018 by engaging consumers in a campaign to support 20 charitable organisations. As part of the campaign, for every one loaf purchased, Gardenia pledged 10 loaves of bread to charitable causes. 20,000 loaves were donated to 20 homes at the end of the campaign. This year, Gardenia continues its tradition with varsity students from financially challenged backgrounds through the Food Bank Siswa programme.


text:
Q: Dependency inversion. Object creation According to SOLID principles a class cannot depend on other classes, dependencies have to be injected. It's simple:
class Foo
{
    public Foo(IBar bar)
    {
        this.bar = bar;
    }

    private IBar bar;
}

interface IBar
{
}

class Bar: IBar
{
}

But what if I want my Foo class to be able to create Bar's, not knowing the exact implementation behind IBar?
I can think of 4 solutions here, but all of them seem to have drawbacks:

*

*injecting the type of object and using reflection

*using Generics

*using "Service Locator" and calling the Resolve() method.

*creating a separated factory class and injecting it into Foo:

class Foo
{
    public void DoSmth(IBarCreator barCreator)
    {
        var newBar = barCreator.CreateBar();
    }
}

interface IBarCreator
{
    IBar CreateBar();
}

class BarCreator : IBarCreator
{
    public IBar CreateBar()
    {
        return new Bar();
    }
}

Last case seems natural, but BarCreator class has too litle code.
So how do you think, which is best?

A: I like to "inject" a Func<IBar> in this case. Like so:
class Foo
{
    public Foo(Func<IBar> barCreator)
    {
        this.bar = barCreator();
    }

    private IBar bar;
}

interface IBar
{
}

A: This is what factories were made for.
If you feel your factory has too little code, ask yourself what benefit it's giving you over just creating the instance inline. If the benefits outweigh the costs of the added code, then don't worry about it.
I'd personally avoid service location, or if you really must use it I'd hide it behind a factory anyway. Service location tends to be easily abused, and can lead to your container finding its way into code it shouldn't have anything to do with.
For convenience, some containers allow you to specify a factory to be used by the container when it creates an instance of a component. In this case, your class could depend on IBar directly but your container would call IBarCreator when it needs a new instance. Castle Windsor has the methods UseFactory and UseFactoryMethod in their API, for example.

A: it all depends on your exact scenario and your needs.
I think the most used approach is, as you've mentioned, a factory.
If you're using an IoC framework (such as Ninject, or Sprint.Net, Castle Windsor etc. see here), a service locator is also a viable solution.

A: If you have a problem with redundant factory interface, you can take two approaches here.
Make it reusable with generics:
interface IFactory<T>
{
 T Create();
}

class DefaultConstructorFactory<T> : IFactory<T>, where T: new()
{
 public T Create() { return new T();}
}

Or use anonymous function as a factory:
public void DoSomething(Func<IBar> barCreator)
{
 var newBar = barCreator();
 //...
}

A: I feel when you say "I want my Foo class to be able to create Bar's" one more rule comes to play that is "Separation of Concerns". So you should delegate the task of class creation to something else and Foo should not be worried about that task.

redpajama_set_name: RedPajamaStackExchange


Product Consists: Set of 2 Threads of Velvet Beads with Diamond Studded Tiny Rings Rakhi.
If you want to celebrate Rakhi in the best way, the festival which is considered as the most important festival to celebrate sibling love, most of the online and retail store takes special steps to design special rakhi for making your celebration complete. From online, you can find out your most favorite deigns to buy. By presenting the rakhi, you will be able to bring out the smile and happiness on the receivers face. Hence, you have to go with the Set of Two Velvet Touch Rakhi to present to your loveable brothers. When you come online, you can get a special discount by ordering for the rakhi festival. Product Consists: Set of 2 Threads of Velvet Beads with Diamond Studded Tiny Rings Rakhi.


text:
Home > Contents > People > Photographers > J.G. Crawford
Other: James Crawford
Other: James G. Crawford
Other: Taylor and Crawford
Joint: Crawford & Paxton
Active: US
James G. Crawford (1850-1929) was the second child of Dr. G.F. and Mary Gilmour Crawford, pioneers who crossed the country on the Oregon Trail in 1852.They settled on a farm near Shedd, on flat land along the Calapooia River. James went to a local school taught by his father, and to a school taught by the Rev. Dr. Thomas Condon which was near the Crawford farm. He finished school at the Lebanon Academy in 1868.
James Crawford lived his life as a professional photographer. In fact, if you have looked at 19th century photos of Albany or many other places in Oregon, you have seen his work, though you probably did not know it at the time. He was taught the photographic skills by A.B. Paxton, thefirst photographer in Albany, and after a year or less, he took over a portrait studio in Eugene. This and his second studio, in Lebanon, were unsuccessful, so he took a photographic job in Portland to learn the business side. By 1873 he had found a wife, Clara, and started a new gallery in Harrisburg, which was to last ten years. There he developed into a skillful portraitist and began selling views (large prints) and stereographs of Oregon scenery and most anything else.
By 1883 the photo technology had changed enough so that he again took a job for a Portland studio (that of I.G. Davidson) to get up to date. In 1885 he and a brother bought Paxton's studio in Albany. Besides the portrait business, Crawford travelled over much of western Oregon collecting views of towns, the coast, mountains, waterfalls, political events, disasters, fairs and circuses, monuments, and hundreds of other subjects. He had a series of partners (his brother Orville, Paxton, Littler, Paxton a second time, Harnish) and his daughter Lillie also became a photographer. He remained in the photography business until his retirement in 1925.At that time he said that he had seen 23 competitors come and go, and he probably holds a record with his 50-some years recording Oregon history.
Besides his photography (amounting to tens of thousands of pictures), he was well-known in his time for his work as an amateur scientist. He and a group of local men did some of the earliest archeological work in Oregon, though they working in an area with no available information. Crawford set up a museum in his photo gallery where his collections were publicly displayed. He corresponded with the Smithsonian and worked with Condon and Dr. Horner, historian at what is now OSU .His collections ended up at the Horner Museum in Corvallis.
Eric P. Gustafson (16 May 2018)
Preparing biographies
Further research
Premium content for those who want to understand photography
References are available for subscribers.There is so much more to explore when you subscribe.
J.G. Crawford
Studio portrait of J.G. Crawford in fancy dress
1887 (ca)
If you are related to this photographer and interested in tracking down your extended family we can place a note here for you to help. It is free and you would be amazed who gets in touch.
alan@luminous-lint.com
Visual indexes for this photographer are available for subscribers.There is so much more to explore when you subscribe.


My daughter's name is Gayatri Pillai. We belong from upper middle class family with moderate and traditional values. She did her schooling from Bokaro Steel city, Jharkhand. She did her graduation and post graduation in Bangalore from reputed colleges and is now currently working as social media executive with reputed firm.
My son has a Master's degree and he is employed in Private Sector as an Executive currently based in Bokaro. We come from a Rich, Nuclear family background with Liberal values.


Mahmoud Zubaidi – GattaCo Inc. / Pte. Ltd.
Mahmoud is GattaCo's Chief Science Officer. Formerly a senior Research Scientist and Technology Development Lead for Merck Millipore, Mahmoud has been involved in numerous collaboration projects between Merck and external clients to support new product development efforts in the areas of molecular diagnostics, immuno-diagnostic and blood testing.
At GattaCo Mahmoud is responsible for leading our collaboration activities that include application specific customization and optimization of existing GattaCo technologies and new product development efforts. Mahmoud received an M.S. Degree in Microbiology and Molecular Cell Sciences from the University of Memphis.


Home Dreams	Why Can't We Remember Our Dreams?
You spend a third of your life asleep, a good chunk of which involves dreaming. But most often, you don't remember any of your dreams. And even on those lucky days when you wake up with a memory of the dream still floating in your mind, there's a good chance that in just a minute the memory will vanish into thin air and back to dreamland.
In waking life, such a case of quickly forgetting recent experiences would surely land you in a doctor's office. With dreams, however, forgetting is normal. Why?
"We have a tendency to immediately forget dreams, and it's likely that people who rarely report dreams are just forgetting them more easily," said Thomas Andrillon, a neuroscientist at Monash University in Melbourne, Australia. It might be hard to believe that you had a dream if you don't remember anything, but studies consistently show that even people who haven't recalled a single dream in decades or even their entire lifetime, do, in fact, recall them if they are awakened at the right moment, Andrillon said.
While the exact reason is not fully known, scientists have gained some insight into memory processes during sleep, leading to several ideas that may explain our peculiar forgetfulness.
You are awake, but is your hippocampus?
When we fall asleep, not all the brain's regions go offline at the same time, according to a 2011 study in the journal Neuron. Researchers have found one of the last regions to go to sleep is the hippocampus, a curved structure that sits inside each brain hemisphere and is critical for moving information from short-term memory into long-term memory.
If the hippocampus is the last to go to sleep, it could very well be the last to wake up, Andrillon said. "So, you could have this window where you wake up with a dream in your short-term memory, but since the hippocampus is not fully awake yet, your brain is not able to keep that memory," Andrillon told Live Science.
While this might explain why dream memories are so fleeting, it doesn't mean that your hippocampus has been inactive throughout the night. In fact, this region is quite active during sleep, and appears to be storing and caring for existing memories to consolidate them, instead of listening for incoming new experiences.
Upon awakening, the brain may need at least 2 minutes to jump-start its memory-encoding abilities. In a 2017 study published in the journal Frontiers in Human Neuroscience, researchers in France monitored sleep patterns in 18 people who reported remembering their dreams almost every day, and 18 others who rarely remembered their dreams. The team found that compared with low-dream recallers, high recallers woke up more frequently during the night. These middle-of-the-night awakenings lasted an average of 2 minutes for high recallers, whereas low-recallers' awakenings lasted for an average of 1 minute.
Our poor ability to encode new memories during sleep is also linked to changes in the levels of two neurotransmitters, acetylcholine and noradrenaline, which are especially important for retaining memories. When we fall asleep, acetylcholine and noradrenaline drop dramatically.
Then, something strange happens as we enter the rapid eye movement (REM) stage of sleep, where the most vivid dreams occur. In this stage, acetylcholine returns to wakefulness levels, but noradrenaline stays low.
Scientists have yet to work out this puzzle, but some suggest that this particular combination of neurotransmitters might be the reason we forget our dreams. The boost in acetylcholine puts the cortex in an aroused state similar to wakefulness, while low noradrenaline reduces our ability to recall our mental escapades during this time, according to a 2017 study in the journal Behavioral and Brain Sciences.
Do you remember what you were thinking about this morning when brushing your teeth? Our minds wander all the time, but we discard most of those thoughts as nonessential information. Dreams, especially mundane ones, may be just like daydreaming thoughts and deemed by the brain to be too useless to remember, the late dream researcher Ernest Hartmann, who was a professor of psychiatry at Tufts University School of Medicine, wrote in Scientific American.
But dreams that are more vivid, emotional and coherent seem to be better remembered — perhaps because they trigger more awakening, and their organized narrative makes them easier to store, Andrillon said.
If you are intent on improving your dream recall, there are a few tricks to try. Robert Stickgold, an associate professor of psychiatry at Harvard Medical School, suggests drinking water before bed, because it will make you wake up at night to use the bathroom. These "middle-of-the-night awakenings are frequently accompanied by dream recall," Stickgold told The New York Times.
Once in bed, repeatedly reminding yourself that you want to remember your dreams may increase your chances, and so does keeping a dream journal, some studies have suggested. Upon waking up, hang on to that fragile dream memory: Keep your eyes closed, stay still and replay the dream memory, until your hippocampus catches up and properly stores the memory.


text:
The Neo Naturists is a performance based live art practice started during the early 1980s in London, UK.

History
The Neo Naturists were started by Christine Binnie, Jennifer Binnie, and Wilma Johnson in 1981 in London. Grayson Perry appeared in many performances.  The most recent performance was at the Hayward Gallery in June 2012.  This was the first time the three main founders had performed together since 1986.  There were many performances in the interim years.

In the world of urban Thatcherism The Neo Naturists were seen as unfashionable hippies. With an enthusiastic spirit of anarchy, they used this unfashionability to enact a challenge. They challenged the highly fashionable Blitz Kids of the early 1980s by creating performances which brought smudged body paint, nudity, cooking, fish fingers, pancakes, calor gas, modern hunter gatherer carrier bags, bosoms, patchouli, sweat and messy exuberance into the heart of the self-conscious New Romantic club scene. Their work brought the aesthetics and vision of William Blake and Samuel Palmer, Cecil Collins and Neo-Romanticism of the 1940s into direct collision with the slick gesturing of New Image painting and Neo Expressionism.

The Neo Naturists subtextually, used their own female bodies in the context of the, often gay and exquisitely dandyesque, club scene, such as The Blitz, to play with feminist sexuality issues and sexual politics.  As living, naked paintings they performed ancient and modern rituals, everyday actions and rituals on stages lit like kitchens.  They juxtaposed ritual action with 'common sense' to create messy exuberant happenings.

The Neo Naturists have an intention to become Neo Naturist octogenarians and to continue performing with body paint on, into their 80s.

Timeline
 2006 – Neo Naturist Archive, B2, My Dead Gallery, London
 2006 – Neo Naturist Archive, Secret Public, Kunstverien, Munich
 2007 – Neo Naturist Archive, Secret Public, Institute of Contemporary Arts (ICA), London
 2007 – Neo Naturist Archive England and Co, London
 2008 – Neo Naturist Films Derek Jarman Super 8 film festival, The Gate Notting Hill and Ritzy Brixton
 2011 – Neo Naturist Archive, Camulodurum, First Site, Colchester
 2012 – Neo Naturist Films, Camulodruum, First Site, Colchester
 2012 – Neo Naturist Films, ICA, London
 2012 – Neo Naturist Life Class, Hayward Wide Open School
 2012 – Neo Naturist Films taken into BFI Collection

References

External links

Performance artist collectives

redpajama_set_name: RedPajamaWikipedia